Torc  0.1
torcpushbulletnotifier.cpp
Go to the documentation of this file.
1 /* Class TorcPushbulletNotifier
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2016-18
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Torc
24 #include "torclogging.h"
25 #include "torchttprequest.h"
26 #include "torcnetworkrequest.h"
27 #include "torcpushbulletnotifier.h"
28 
29 #define MAX_BAD_REQUESTS 2
30 
37  : TorcNotifier(Details),
38  m_resetTimer(),
39  m_networkAbort(0),
40  m_accessToken(QStringLiteral("")),
41  m_requests(),
42  m_badRequestCount(0)
43 {
44  // setup timer
45  m_resetTimer.setSingleShot(true);
46  connect(this, &TorcPushbulletNotifier::StartResetTimer, &m_resetTimer, static_cast<void (QTimer::*)(int)>(&QTimer::start));
47  connect(&m_resetTimer, &QTimer::timeout, this, &TorcPushbulletNotifier::ResetTimerTimeout);
48 
49  if (Details.contains(QStringLiteral("accesstoken")))
50  {
51  m_accessToken = Details.value(QStringLiteral("accesstoken")).toString();
52  SetValid(true);
53  }
54  else
55  {
56  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("No Pushbullet access token specified - disabling"));
57  m_badRequestCount = MAX_BAD_REQUESTS;
58  }
59 }
60 
62 {
63  QMutexLocker locker(&lock);
64 
65  m_networkAbort = 1;
66 
67  foreach (TorcNetworkRequest* request, m_requests)
68  {
69  TorcNetwork::Cancel(request);
70  request->DownRef();
71  }
72  m_requests.clear();
73 }
74 
76 {
77  return QStringList() << tr("Pushbullet");
78 }
79 
80 void TorcPushbulletNotifier::Notify(const QVariantMap &Notification)
81 {
82  if (m_accessToken.isEmpty() || m_badRequestCount >= MAX_BAD_REQUESTS || m_resetTimer.isActive())
83  {
84  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Ignoring Pushbullet notify request"));
85  return;
86  }
87 
89  {
90  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Not sending Pushbullet notification. Network is not available"));
91  return;
92  }
93 
94  QString title = Notification.contains(NOTIFICATION_TITLE) ? Notification.value(NOTIFICATION_TITLE).toString() : UNKNOWN_TITLE;
95  QString body = Notification.contains(NOTIFICATION_BODY) ? Notification.value(NOTIFICATION_BODY).toString() : UNKNOWN_BODY;
96 
97  QJsonObject object;
98  object.insert(QStringLiteral("title"), title);
99  object.insert(QStringLiteral("body"), body);
100  object.insert(QStringLiteral("type"), "note");
101  QJsonDocument doc(object);
102  QByteArray content = doc.toJson(QJsonDocument::Compact);
103 
104  QNetworkRequest qrequest(PUSHBULLET_PUSH_URL);
105  qrequest.setRawHeader("Access-token", m_accessToken.toUtf8());
106  qrequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
107  qrequest.setHeader(QNetworkRequest::ContentLengthHeader, content.size());
108 
109  TorcNetworkRequest *request = new TorcNetworkRequest(qrequest, content, &m_networkAbort);
110  TorcNetwork::GetAsynchronous(request, this);
111 
112  {
113  QMutexLocker locker(&lock);
114  m_requests.append(request);
115  }
116 }
117 
119 {
120  if (!Request)
121  return;
122 
123  QMutexLocker locker(&lock);
124  if (!m_requests.contains(Request))
125  {
126  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Response to unknown Pushbullet request"));
127  return;
128  }
129 
130  int status = Request->GetStatus();
131  if (status >= HTTP_BadRequest && status < HTTP_InternalServerError)
132  {
133  quint64 resetin = 3600; // default to restarting in 1 hour if needed
134 
135  if (status == HTTP_TooManyRequests)
136  {
137  QByteArray resettime = Request->GetHeader("X-Ratelimit-Reset");
138 
139  bool ok = false;
140  if (!resettime.isEmpty())
141  {
142  quint64 reset = resettime.toLongLong(&ok);
143  if (ok)
144  {
145  quint64 now = (QDateTime::currentMSecsSinceEpoch() + 500) / 1000;
146  if (reset >= now)
147  resetin = reset - now;
148  }
149  }
150 
151  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Pushbullet rate limit exceeded. Restarting in %1 seconds").arg(resetin));
152  }
153  else
154  {
155  QJsonDocument result = QJsonDocument::fromJson(Request->GetBuffer());
156  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Pushbullet notifier '%1' - bad request ('%2')").arg(uniqueId, TorcHTTPRequest::StatusToString((HTTPStatus)status)));
157  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Pushbullet replied with:\r\n%1").arg(result.toJson().constData()));
158  if (++m_badRequestCount >= MAX_BAD_REQUESTS)
159  {
160  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Disabling Pushbullet notifier '%1' - too many bad requests. Check your access token.").arg(uniqueId));
161  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Will try again in %1 seconds").arg(resetin));
162  }
163  else
164  {
165  resetin = 0;
166  }
167  }
168 
169  if (resetin > 0)
170  emit StartResetTimer(resetin * 1000);
171  }
172  else
173  {
174  m_badRequestCount = 0;
175  }
176 
177  Request->DownRef();
178  m_requests.removeAll(Request);
179 }
180 
182 {
183  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Re-enabling Pushbullet notifier '%1'").arg(uniqueId));
184  m_badRequestCount = 0;
185  SetValid(!m_accessToken.isEmpty());
186 }
187 
189 {
190  TorcNotifier* Create(const QString &Type, const QVariantMap &Details) override
191  {
192  if (Type == QStringLiteral("pushbullet") && Details.contains(QStringLiteral("accesstoken")))
193  return new TorcPushbulletNotifier(Details);
194  return nullptr;
195  }
197 
QString uniqueId
Definition: torcdevice.h:63
virtual void SetValid(bool Valid)
Definition: torcdevice.cpp:101
TorcPushbulletNotifier(const QVariantMap &Details)
A wrapper around QNetworkRequest.
static bool GetAsynchronous(TorcNetworkRequest *Request, QObject *Parent)
Queue an asynchronous HTTP request.
Definition: torcnetwork.cpp:94
static QString StatusToString(HTTPStatus Status)
#define PUSHBULLET_PUSH_URL
static bool IsAvailable(void)
Definition: torcnetwork.cpp:46
virtual bool DownRef(void)
QByteArray & GetBuffer(void)
#define UNKNOWN_BODY
Definition: torcnotifier.h:13
void StartResetTimer(int MSeconds)
TorcPushbulletNotifierFactory TorcPushbulletNotifierFactory
QMutex lock
Definition: torcdevice.h:66
QStringList GetDescription(void) override
HTTPStatus
#define NOTIFICATION_BODY
Definition: torcnotifier.h:11
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
#define MAX_BAD_REQUESTS
void Notify(const QVariantMap &Notification) override
#define NOTIFICATION_TITLE
Definition: torcnotifier.h:10
void RequestReady(TorcNetworkRequest *Request)
static void Cancel(TorcNetworkRequest *Request)
Definition: torcnetwork.cpp:73
int GetStatus(void) const
QByteArray GetHeader(const QByteArray &Header) const
Return the value of the given header, if present.
#define UNKNOWN_TITLE
Definition: torcnotifier.h:12