diff --git a/src/libsnore/notification/notification.h b/src/libsnore/notification/notification.h index ba07a02..5457a51 100644 --- a/src/libsnore/notification/notification.h +++ b/src/libsnore/notification/notification.h @@ -87,6 +87,11 @@ public: * Some notification systems support this flag to filter notifications or indicate different prioritys by color. */ enum Prioritys { + /** + * Indicates the lowes priority. The backend might ignore the notification. + */ + LOWEST = -2, + /** * Indicates a low priority. */ @@ -100,7 +105,12 @@ public: /** * Indicates a priority above the normal level. */ - HIGH = +1 + HIGH = +1, + + /** + * Indicates a emegency priority, the notifications is sticky and should be acknowlegded. + */ + EMERGENCY = +2 }; Notification(); diff --git a/src/libsnore/notification/notification_p.cpp b/src/libsnore/notification/notification_p.cpp index 784ac40..9d418b0 100644 --- a/src/libsnore/notification/notification_p.cpp +++ b/src/libsnore/notification/notification_p.cpp @@ -34,7 +34,7 @@ uint NotificationData::m_idCount = 1; NotificationData::NotificationData(const Snore::Application &application, const Snore::Alert &alert, const QString &title, const QString &text, const Icon &icon, int timeout, Notification::Prioritys priority): m_id(m_idCount++), - m_timeout(timeout), + m_timeout(priority == Notification::EMERGENCY ? 0 : timeout), m_application(application), m_alert(alert), m_title(title), @@ -50,7 +50,7 @@ NotificationData::NotificationData(const Snore::Application &application, const Snore::NotificationData::NotificationData(const Notification &old, const QString &title, const QString &text, const Icon &icon, int timeout, Notification::Prioritys priority): m_id(m_idCount++), - m_timeout(timeout), + m_timeout(priority == Notification::EMERGENCY ? 0 : timeout), m_application(old.application()), m_alert(old.alert()), m_title(title), diff --git a/src/libsnore/plugins/plugins.h b/src/libsnore/plugins/plugins.h index 22902be..9358cd6 100644 --- a/src/libsnore/plugins/plugins.h +++ b/src/libsnore/plugins/plugins.h @@ -52,6 +52,9 @@ public: SnorePlugin(); virtual ~SnorePlugin(); + + + // TODO: remove need of recursive calling of parent methode.... virtual bool initialize(); virtual bool deinitialize(); bool isInitialized() const; diff --git a/src/plugins/backends/growl/growlbackend.cpp b/src/plugins/backends/growl/growlbackend.cpp index ed97c02..6900ed5 100644 --- a/src/plugins/backends/growl/growlbackend.cpp +++ b/src/plugins/backends/growl/growlbackend.cpp @@ -35,7 +35,7 @@ bool GrowlBackend::initialize() setDefaultValue("Host", "localhost"); setDefaultValue("Password", ""); - if(!SnoreFrontend::initialize()) { + if(!SnoreBackend::initialize()) { return false; } diff --git a/src/plugins/backends/snarl/snarl.cpp b/src/plugins/backends/snarl/snarl.cpp index 7d6b287..2ec9674 100644 --- a/src/plugins/backends/snarl/snarl.cpp +++ b/src/plugins/backends/snarl/snarl.cpp @@ -115,7 +115,7 @@ bool SnarlBackend::initialize() { setDefaultValue("Password", QString()); - if(!SnoreFrontend::initialize()) { + if(!SnoreBackend::initialize()) { return false; } @@ -205,16 +205,12 @@ void SnarlBackend::slotNotify(Notification notification) SnarlInterface *snarlInterface = m_applications.value(notification.application().name()); Snarl::V42::SnarlEnums::MessagePriority priority = Snarl::V42::SnarlEnums::PriorityUndefined; - switch (notification.priority()) { - case Notification::LOW: - priority = Snarl::V42::SnarlEnums::PriorityLow; - break; - case Notification::NORMAL: - priority = Snarl::V42::SnarlEnums::PriorityNormal; - break; - case Notification::HIGH: + if(notification.priority() > 1){ priority = Snarl::V42::SnarlEnums::PriorityHigh; - break; + } else if(notification.priority() < -1) { + priority = Snarl::V42::SnarlEnums::PriorityLow; + }else{ + priority = static_cast(notification.priority()); } ULONG32 id = 0; diff --git a/src/plugins/backends/snoretoast/snoretoast.cpp b/src/plugins/backends/snoretoast/snoretoast.cpp index 28a7498..1d30926 100644 --- a/src/plugins/backends/snoretoast/snoretoast.cpp +++ b/src/plugins/backends/snoretoast/snoretoast.cpp @@ -16,8 +16,7 @@ using namespace Snore; bool SnoreToast::initialize() { - - if(!SnoreFrontend::initialize()) { + if(!SnoreBackend::initialize()) { return false; } if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS8) { diff --git a/src/plugins/frontends/pushover/CMakeLists.txt b/src/plugins/frontends/pushover/CMakeLists.txt index f701e4a..b1410df 100644 --- a/src/plugins/frontends/pushover/CMakeLists.txt +++ b/src/plugins/frontends/pushover/CMakeLists.txt @@ -2,6 +2,7 @@ find_package(Qt5WebSockets REQUIRED) set( PUSHOVER_FRONTEND_SRC pushover_frontend.cpp + pushoversettings.cpp ) add_library(libsnore_frontend_pushover MODULE ${PUSHOVER_FRONTEND_SRC} ) diff --git a/src/plugins/frontends/pushover/pushover_frontend.cpp b/src/plugins/frontends/pushover/pushover_frontend.cpp index f365a21..a7508f0 100644 --- a/src/plugins/frontends/pushover/pushover_frontend.cpp +++ b/src/plugins/frontends/pushover/pushover_frontend.cpp @@ -17,6 +17,7 @@ */ #include "pushover_frontend.h" +#include "pushoversettings.h" #include "libsnore/snore.h" #include "libsnore/version.h" @@ -31,72 +32,108 @@ #include #include + using namespace Snore; // TODO: use qtkeychain to encrypt credentials? +// TODO: massive refactoring ... bool PushoverFrontend::initialize() { setDefaultValue("Secret", "", LOCAL_SETTING); - setDefaultValue("Device", "", LOCAL_SETTING); + setDefaultValue("DeviceID", "", LOCAL_SETTING); + setDefaultValue("Registered", false, LOCAL_SETTING); if(!SnoreFrontend::initialize()) { return false; } - if(device().isEmpty() || secret().isEmpty()) + if(value("Registered", LOCAL_SETTING).toBool()) { - return false; + connectToService(); } - m_socket = new QWebSocket("", QWebSocketProtocol::VersionLatest, this); - connect(m_socket, &QWebSocket::binaryMessageReceived, [&](const QByteArray &msg){ - qDebug() << "bin message" << msg; - char c = msg.at(0); - switch(c){ - case '#': - snoreDebug(SNORE_DEBUG) << "still alive"; - break; - case '!': - getMessages(); - break; - case 'R': - // TODO: implement - snoreDebug(SNORE_DEBUG) << "need to reconnect"; - break; - case 'E': - snoreDebug(SNORE_DEBUG) << "Connection Error"; - m_socket->close(); - m_socket->deleteLater(); - break; - } - }); - connect(m_socket, &QWebSocket::disconnected, [](){ - qDebug() << "disconnected"; - }); - connect(m_socket, static_cast(&QWebSocket::error), [&](QAbstractSocket::SocketError error){ - qDebug() << error << m_socket->errorString(); - }); - connect(m_socket, &QWebSocket::connected, [&](){ - qDebug() << "connect" << m_socket->sendBinaryMessage(QString("login:%1:%2\n").arg(device(), secret()).toUtf8().constData()); - - // TODO: how to delay until snore is initialized? - getMessages(); - }); - m_socket->open(QUrl("wss://client.pushover.net/push")); return true; } bool PushoverFrontend::deinitialize() { if (SnoreFrontend::deinitialize()) { - m_socket->close(); - m_socket->deleteLater(); + if(m_socket) { + m_socket->close(); + m_socket->deleteLater(); + } return true; } return false; } +PluginSettingsWidget *PushoverFrontend::settingsWidget() +{ + return new PushoverSettings(this); +} + +void PushoverFrontend::registerDevice(const QString &email, const QString &password, const QString &deviceName) +{ + + QNetworkRequest request(QUrl(QStringLiteral("https://api.pushover.net/1/users/login.json"))); + + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded")); + QNetworkReply *reply = m_manager.post(request, QString("email=%1&password=%2").arg(email, password).toUtf8().constData()); + + + connect(reply, &QNetworkReply::finished, [reply, deviceName, this]() { + snoreDebug(SNORE_DEBUG) << reply->error(); + QByteArray input = reply->readAll(); + reply->close(); + reply->deleteLater(); + + + QJsonObject message = QJsonDocument::fromJson(input).object(); + + if(message.value("status").toInt() == 1) + { + QString secret = message.value("secret").toString(); + QNetworkRequest request(QUrl(QStringLiteral("https://api.pushover.net/1/devices.json"))); + + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded")); + QNetworkReply *reply = m_manager.post(request, QString("secret=%1&name=%2&os=O").arg(secret, deviceName).toUtf8().constData()); + + + connect(reply, &QNetworkReply::finished, [reply, secret, this]() { + snoreDebug(SNORE_DEBUG) << reply->error(); + QByteArray input = reply->readAll(); + reply->close(); + reply->deleteLater(); + + + QJsonObject message = QJsonDocument::fromJson(input).object(); + + + if(message.value("status").toInt() == 1) { + setValue("Secret", secret, LOCAL_SETTING); + setValue("DeviceID", message.value("id").toString(), LOCAL_SETTING); + setValue("Registered", true, LOCAL_SETTING); + connectToService(); + } else { + snoreDebug(SNORE_WARNING) << "An error occure" << input; + } + + }); + }else { + snoreDebug(SNORE_WARNING) << "An error occure" << input; + } + }); +} + +void PushoverFrontend::slotActionInvoked(Notification notification) +{ + if(notification.priority() == Notification::EMERGENCY){ + snoreDebug(SNORE_WARNING) << "emergeency notification" << notification; + acknowledgeNotification(notification); + } +} + QString PushoverFrontend::secret() { return value("Secret", LOCAL_SETTING).toString(); @@ -104,7 +141,56 @@ QString PushoverFrontend::secret() QString PushoverFrontend::device() { - return value("Device", LOCAL_SETTING).toString(); + return value("DeviceID", LOCAL_SETTING).toString(); +} + +void PushoverFrontend::connectToService() +{ + + if(!value("Registered", LOCAL_SETTING).toBool()) + { + snoreDebug(SNORE_WARNING) << "not logged in"; + return; + } + m_socket = new QWebSocket("", QWebSocketProtocol::VersionLatest, this); + + connect(m_socket, &QWebSocket::binaryMessageReceived, [&](const QByteArray &msg){ + char c = msg.at(0); + switch(c){ + case '#': + snoreDebug(SNORE_DEBUG) << "still alive"; + break; + case '!': + getMessages(); + break; + case 'R': + snoreDebug(SNORE_DEBUG) << "need to reconnect"; + m_socket->close(); + m_socket->deleteLater(); + connectToService(); + break; + case 'E': + snoreDebug(SNORE_DEBUG) << "Connection Error"; + setValue("Registered", false, LOCAL_SETTING); + m_socket->close(); + m_socket->deleteLater(); + break; + default: + snoreDebug(SNORE_WARNING) << "unknown message recieved" << msg; + } + }); + connect(m_socket, &QWebSocket::disconnected, [](){ + snoreDebug(SNORE_DEBUG) << "disconnected"; + }); + connect(m_socket, static_cast(&QWebSocket::error), [&](QAbstractSocket::SocketError error){ + snoreDebug(SNORE_DEBUG) << error << m_socket->errorString(); + }); + connect(m_socket, &QWebSocket::connected, [&](){ + snoreDebug(SNORE_DEBUG) << "connecting"; + m_socket->sendBinaryMessage(QString("login:%1:%2\n").arg(device(), secret()).toUtf8().constData()); + getMessages(); + }); + m_socket->open(QUrl("wss://client.pushover.net/push")); } void PushoverFrontend::getMessages() @@ -120,8 +206,6 @@ void PushoverFrontend::getMessages() reply->close(); reply->deleteLater(); - qDebug() << input; - QJsonObject message = QJsonDocument::fromJson(input).object(); int latestID = -1; @@ -148,12 +232,18 @@ void PushoverFrontend::getMessages() Notification n(app, *app.alerts().begin(), notification.value("title").toString(), notification.value("message").toString(), app.icon(), Notification::defaultTimeout(), static_cast(notification.value("priority").toInt())); + if(n.priority() == Notification::EMERGENCY){ + n.hints().setValue("receipt", notification.value("receipt").toString()); + } SnoreCore::instance().broadcastNotification(n); } if(latestID != -1){ deleteMessages(latestID); } + } else { + snoreDebug(SNORE_WARNING) << "An error occure" << input; } + }); } @@ -175,3 +265,22 @@ void PushoverFrontend::deleteMessages(int latestMessageId) }); } +void PushoverFrontend::acknowledgeNotification(Notification notification) +{ + QString receipt = notification.constHints().value("receipt").toString(); + + QNetworkRequest request(QUrl(QString("https://api.pushover.net/1/receipts/%1/acknowledge.json").arg(receipt))); + + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded")); + QNetworkReply *reply = m_manager.post(request, QString("secret=%1").arg(secret()).toUtf8().constData()); + + + connect(reply, &QNetworkReply::finished, [reply]() { + snoreDebug(SNORE_DEBUG) << reply->error(); + snoreDebug(SNORE_DEBUG) << reply->readAll(); + reply->close(); + reply->deleteLater(); + }); +} + + diff --git a/src/plugins/frontends/pushover/pushover_frontend.h b/src/plugins/frontends/pushover/pushover_frontend.h index 4f71afa..1d10811 100644 --- a/src/plugins/frontends/pushover/pushover_frontend.h +++ b/src/plugins/frontends/pushover/pushover_frontend.h @@ -22,8 +22,9 @@ #include "libsnore/application.h" #include +#include +#include -class QWebSocket; class PushoverFrontend : public Snore::SnoreFrontend { @@ -36,16 +37,29 @@ public: bool initialize() override; bool deinitialize() override; + Snore::PluginSettingsWidget *settingsWidget() override; + + void registerDevice(const QString &email, const QString &password, const QString& deviceName); + +public slots: + void slotActionInvoked(Snore::Notification notification); + + private: QNetworkAccessManager m_manager; - QWebSocket *m_socket; + QPointer m_socket; QString secret(); QString device(); + void connectToService(); + void getMessages(); void deleteMessages(int latestMessageId); + void acknowledgeNotification(Snore::Notification notification); + + }; diff --git a/src/plugins/frontends/pushover/pushoversettings.cpp b/src/plugins/frontends/pushover/pushoversettings.cpp new file mode 100644 index 0000000..56f0d95 --- /dev/null +++ b/src/plugins/frontends/pushover/pushoversettings.cpp @@ -0,0 +1,90 @@ +/* + SnoreNotify is a Notification Framework based on Qt + Copyright (C) 2015 Patrick von Reth + + SnoreNotify is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SnoreNotify is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with SnoreNotify. If not, see . +*/ +#include "pushoversettings.h" +#include "pushover_frontend.h" + +#include "plugins/plugins.h" + +#include +#include +#include + +PushoverSettings::PushoverSettings(Snore::SnorePlugin *plugin, QWidget *parent) : + Snore::PluginSettingsWidget(plugin, parent), + m_emailLineEdit(new QLineEdit(this)), + m_passwordLineEdit(new QLineEdit(this)), + m_deviceLineEdit(new QLineEdit(this)), + m_registerButton(new QPushButton(this)) +{ + m_passwordLineEdit->setEchoMode(QLineEdit::Password); + addRow(tr("Email Address:"), m_emailLineEdit); + addRow(tr("Password:"), m_passwordLineEdit); + addRow(tr("Device Name:"), m_deviceLineEdit); + updateLoginState(); + addRow("", m_registerButton); + + PushoverFrontend *pushover = dynamic_cast(plugin); + connect(m_registerButton, &QPushButton::clicked, [pushover, this] () { + if(!value("Registered", Snore::LOCAL_SETTING).toBool()) { + pushover->registerDevice(m_emailLineEdit->text(), m_passwordLineEdit->text(), m_deviceLineEdit->text()); + setValue("DeviceName", m_deviceLineEdit->text(), Snore::LOCAL_SETTING); + QTimer *updateTimer = new QTimer(this); + updateTimer->setInterval(500); + connect(updateTimer, &QTimer::timeout, [updateTimer, this](){ + qDebug() << value("Registered").toBool(); + if (value("Registered", Snore::LOCAL_SETTING).toBool()) { + updateLoginState(); + updateTimer->deleteLater(); + } + }); + + updateTimer->start(); + }else{ + setValue("Registered", false, Snore::LOCAL_SETTING); + updateLoginState(); + } + }); +} + +PushoverSettings::~PushoverSettings() +{ +} + +void PushoverSettings::load() +{ + m_deviceLineEdit->setText(value("DeviceName", Snore::LOCAL_SETTING).toString()); +} + +void PushoverSettings::save() +{ +} + +void PushoverSettings::updateLoginState() +{ + if (value("Registered", Snore::LOCAL_SETTING).toBool()) { + m_emailLineEdit->setEnabled(false); + m_passwordLineEdit->setEnabled(false); + m_deviceLineEdit->setEnabled(false); + m_registerButton->setText(tr("Log out")); + } else { + m_emailLineEdit->setEnabled(true); + m_passwordLineEdit->setEnabled(true); + m_deviceLineEdit->setEnabled(true); + m_registerButton->setText(tr("Log in")); + } +} diff --git a/src/plugins/frontends/pushover/pushoversettings.h b/src/plugins/frontends/pushover/pushoversettings.h new file mode 100644 index 0000000..35cbfb7 --- /dev/null +++ b/src/plugins/frontends/pushover/pushoversettings.h @@ -0,0 +1,48 @@ +/* + SnoreNotify is a Notification Framework based on Qt + Copyright (C) 2015 Patrick von Reth + + SnoreNotify is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + SnoreNotify is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with SnoreNotify. If not, see . +*/ +#ifndef PUSHOVERSETTINGS_H +#define PUSHOVERSETTINGS_H + +#include "plugins/pluginsettingswidget.h" + +class QLineEdit; +class QPushButton; + + +class PushoverSettings : public Snore::PluginSettingsWidget +{ + Q_OBJECT +public: + explicit PushoverSettings(Snore::SnorePlugin *plugin, QWidget *parent = 0); + ~PushoverSettings(); + + void load() override; + void save() override; + +private: + QLineEdit *m_emailLineEdit; + QLineEdit *m_passwordLineEdit; + QLineEdit *m_deviceLineEdit; + QPushButton *m_registerButton; + + void updateLoginState(); + + +}; + +#endif // PUSHOVERSETTINGS_H diff --git a/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp b/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp index 23ec730..2e6a9fb 100644 --- a/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp +++ b/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp @@ -28,7 +28,6 @@ using namespace Snore; bool SnarlNetworkFrontend::initialize() { - if(!SnoreFrontend::initialize()) { return false; }