qTbot 0.2.107.d8fc923
qTbot is base back end library for your c++ Qt projects.
ibot.cpp
Go to the documentation of this file.
1//#
2//# Copyright (C) 2023-2024 QuasarApp.
3//# Distributed under the GPLv3 software license, see the accompanying
4//# Everyone is permitted to copy and distribute verbatim copies
5//# of this license document, but changing it is not allowed.
6//#
7
8#include "httpexception.h"
9#include "ibot.h"
10#include "qstandardpaths.h"
11
12#include <QNetworkReply>
13#include <QPromise>
14
15namespace qTbot {
16
18 _manager = new QNetworkAccessManager();
19 _manager->setAutoDeleteReplies(true);
20 _requestExecutor = new QTimer(this);
21 _requestExecutor->setInterval(1000 / 20); // 20 times per second.
22
23 connect(_requestExecutor, &QTimer::timeout, this , &IBot::handleEcxecuteRequest);
24}
25
27 delete _manager;
28}
29
31 setToken({});
32}
33
34const QByteArray &IBot::token() const {
35 return _token;
36}
37
38void IBot::setToken(const QByteArray &newToken) {
39 _token = newToken;
40 _startTime = QDateTime::currentDateTime();
41
42}
43
44void IBot::incomeNewUpdate(const QSharedPointer<iUpdate> &message) {
45 if (!message->isValid())
46 return;
47
48 auto id = message->updateId();
49
50 if (!_processed.contains(id)) {
51
52 _processed.insert(id);
53 _notProcessedUpdates[id] = message;
54
55 handleIncomeNewUpdate(message);
56 }
57}
58
59QNetworkReply* IBot::sendRquestImpl(const QSharedPointer<iRequest> &rquest) {
60 if (!rquest)
61 return {};
62
63 auto && url = makeUrl(rquest);
64
65#ifdef QTBOT_PRINT_RQUESTS
66 qDebug() << url;
67#endif
68
69 QNetworkReply* networkReplay = nullptr;
70
71 switch (rquest->method()) {
72 case iRequest::Get: {
73 networkReplay = _manager->get(QNetworkRequest(url));
74
75 break;
76 }
77
78 case iRequest::Post:
79 // req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
80 // reply = m_nam.post(req, params.toByteArray());
81
82 // break;
84 QNetworkRequest netRequest(url);
85
86 auto httpData = rquest->argsToMultipartFormData();
87 if (httpData) {
88 networkReplay = _manager->post(netRequest, httpData.data());
89 connect(networkReplay, &QNetworkReply::destroyed, [httpData](){});
90
91 } else {
92 return {};
93 }
94
95 break;
96 }
97
98 return networkReplay;
99}
100
101QDateTime IBot::startTime() const {
102 return _startTime;
103}
104
105unsigned long long IBot::totalSentRequests() const {
106 return _totalRequest;
107}
108
110 return _parallelActiveNetworkThreads;
111}
112
113void IBot::setParallelActiveNetworkThreads(int newParallelActiveNetworkThreads) {
114 _parallelActiveNetworkThreads = newParallelActiveNetworkThreads;
115}
116
117void IBot::setCurrentParallelActiveNetworkThreads(int newParallelActiveNetworkThreads) {
118 _currentParallelActiveNetworkThreads = newParallelActiveNetworkThreads;
119}
120
122 return _requestExecutor->interval() * 1000;
123}
124
125void IBot::setReqestLimitPerSecond(int newReqestLimitPerSecond) {
126 _requestExecutor->setInterval(1000 / newReqestLimitPerSecond);
127}
128
129QFuture<QByteArray>
130IBot::sendRequest(const QSharedPointer<iRequest> &rquest) {
131 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
132 responce->start();
133
134
135 _requestQueue.insert(makeKey(rquest->priority()),
136 RequestData{rquest, "", responce});
137
138 if (!_requestExecutor->isActive()) {
139 handleEcxecuteRequest();
140 _requestExecutor->start();
141
142 }
143
144 return responce->future();
145}
146
147QFuture<QByteArray>
148IBot::sendRequest(const QSharedPointer<iRequest> &rquest,
149 const QString &pathToResult) {
150 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
151 responce->start();
152
153 _requestQueue.insert(makeKey(rquest->priority()),
154 RequestData{rquest, pathToResult, responce});
155
156 if (!_requestExecutor->isActive()) {
157 handleEcxecuteRequest();
158 _requestExecutor->start();
159
160 }
161
162 return responce->future();
163
164}
165
166void IBot::markUpdateAsProcessed(const QSharedPointer<iUpdate> &message) {
167 _notProcessedUpdates.remove(message->updateId());
168}
169
170void IBot::markUpdateAsUnprocessed(const QSharedPointer<iUpdate> &message) {
171 return markUpdateAsUnprocessed(message->updateId());
172}
173
174void IBot::markUpdateAsUnprocessed(unsigned long long messageID) {
175 _processed.remove(messageID);
176}
177
179 return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
180}
181
182void IBot::handleIncomeNewUpdate(const QSharedPointer<iUpdate> & message) {
183 emit sigReceiveUpdate(message);
184}
185
186void IBot::handleEcxecuteRequest() {
187 if (!_requestQueue.size()) {
188 _requestExecutor->stop();
189 return;
190 }
191
192 if (_currentParallelActiveNetworkThreads > _parallelActiveNetworkThreads) {
193 return;
194 }
195
196 auto&& requestData = _requestQueue.take(_requestQueue.firstKey());
197
198 if (requestData.responceFilePath.size()) {
199 sendRequestPrivate(requestData.request, requestData.responceFilePath, requestData.responce);
200 return;
201 }
202
203 sendRequestPrivate(requestData.request, requestData.responce);
204}
205
206unsigned long long IBot::makeKey(iRequest::RequestPriority priority) {
207 unsigned long long key = _totalRequest;
208 _totalRequest++;
209 key = key | (static_cast<unsigned long long>(iRequest::RequestPriority::MaxPriorityValue - priority) << 56);
210 return key;
211}
212
213void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
214 const QSharedPointer<QPromise<QByteArray> > &promise) {
215
216 QNetworkReply* networkReplay = sendRquestImpl(rquest);
217 if (!networkReplay) {
218 return;
219 }
220
221 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
222
223 connect(networkReplay, &QNetworkReply::finished, [this, networkReplay, promise](){
224 if (networkReplay->error() == QNetworkReply::NoError) {
225 promise->addResult(networkReplay->readAll());
226 promise->finish();
227
228 } else {
229 QByteArray msg = networkReplay->errorString().toLatin1() + networkReplay->readAll();
230 promise->setException(HttpException(networkReplay->error(), msg));
231#ifdef QTBOT_PRINT_ERRORS
232 qCritical() << msg;
233#endif
234
235 }
236
237 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
238
239 });
240
241 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
242
243 if (promise->future().progressMaximum() != bytesTotal)
244 promise->setProgressRange(0, bytesTotal);
245
246 promise->setProgressValue(bytesCurrent);
247 };
248
249 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
250 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
251}
252
253void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
254 const QString &pathToResult,
255 const QSharedPointer<QPromise<QByteArray>> & promise) {
256 auto&& file = QSharedPointer<QFile>::create(pathToResult);
257
258 if (!file->open(QIODeviceBase::WriteOnly | QIODevice::Truncate)) {
259 qCritical() << "Fail to wrote data into " << pathToResult;
260 return;
261 }
262
263 QNetworkReply* networkReplay = sendRquestImpl(rquest);
264 if (!networkReplay) {
265 return;
266 }
267
268 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
269 connect(networkReplay, &QNetworkReply::finished, [this, promise, networkReplay, pathToResult](){
270
271 if (networkReplay->error() == QNetworkReply::NoError) {
272 QByteArray msg = networkReplay->errorString().toLatin1();
273 promise->setException(HttpException(networkReplay->error(), msg));
274#ifdef QTBOT_PRINT_ERRORS
275 qCritical() << msg;
276#endif
277 } else {
278 promise->addResult(pathToResult.toUtf8()); // wil not work with UTF 8 path names
279 promise->finish();
280 }
281 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
282 });
283
284 connect(networkReplay, &QNetworkReply::readyRead, [networkReplay, promise, pathToResult, file](){
285 if (networkReplay->error() == QNetworkReply::NoError) {
286 file->write(networkReplay->readAll());
287 }
288
289 });
290
291 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
292
293 if (promise->future().progressMaximum() != bytesTotal)
294 promise->setProgressRange(0, bytesTotal);
295
296 promise->setProgressValue(bytesCurrent);
297 };
298
299 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
300 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
301
302}
303
304QSet<unsigned long long> IBot::processed() const {
305 return _processed;
306}
307
308void IBot::setProcessed(const QSet<unsigned long long> &newProcessed) {
309 _processed = newProcessed;
310}
311
312const QString &IBot::name() const {
313 return _name;
314}
315
316void IBot::setName(const QString &newName) {
317 _name = newName;
318}
319
320QSharedPointer<iUpdate> IBot::takeNextUnreadUpdate() {
321 if (_notProcessedUpdates.size()) {
322 auto toRemove = std::move(*_notProcessedUpdates.begin());
323 _notProcessedUpdates.erase(_notProcessedUpdates.cbegin());
324 return toRemove;
325 }
326
327 return nullptr;
328}
329
330
331}
virtual QString makeUrl(const QSharedPointer< iRequest > &request) const =0
makeUrl This method prepare a prefix url for http requests.
void setName(const QString &newName)
setName This method sets new value for the IBot::name field.
Definition ibot.cpp:316
const QString & name() const
name This is name of the bot. usualy it fields will be received from the server after autication.
Definition ibot.cpp:312
unsigned long long totalSentRequests() const
totalSentRequests This is total prepared requests count of bot from the start.
Definition ibot.cpp:105
void markUpdateAsUnprocessed(const QSharedPointer< iUpdate > &message)
markMessageAsUnprocessed This method add the message into a not processed messages store.
Definition ibot.cpp:170
void markUpdateAsProcessed(const QSharedPointer< iUpdate > &message)
markMessageAsProcessed This method remove message from the not processed messages store.
Definition ibot.cpp:166
const QByteArray & token() const
token This is token value for authication on the remote server (bot)
Definition ibot.cpp:34
int parallelActiveNetworkThreads() const
parallelActiveNetworkThreads
Definition ibot.cpp:109
virtual void handleIncomeNewUpdate(const QSharedPointer< iUpdate > &)
handleIncomeNewUpdate This method just emit the sigReceiveUpdate signal.
Definition ibot.cpp:182
int reqestLimitPerSecond() const
reqestLimitPerSecond this is request performence limitation. by default is 20 requests per second
Definition ibot.cpp:121
void setReqestLimitPerSecond(int newReqestLimitPerSecond)
setReqestLimitPerSecond this method sets new limitation of bot performance.
Definition ibot.cpp:125
virtual QString defaultFileStorageLocation() const
defaultFileStorageLocation This method return default file storage location.
Definition ibot.cpp:178
void incomeNewUpdate(const QSharedPointer< iUpdate > &message)
incomeNewUpdate This method save incomed messages into store.
Definition ibot.cpp:44
void setParallelActiveNetworkThreads(int newParallelActiveNetworkThreads)
setParallelActiveNetworkThreads
Definition ibot.cpp:113
QSet< unsigned long long > processed() const
processed This method return list of processed mesages.
Definition ibot.cpp:304
QFuture< QByteArray > sendRequest(const QSharedPointer< iRequest > &rquest)
sendRequest This method sent custom requests to the server.
Definition ibot.cpp:130
virtual void setProcessed(const QSet< unsigned long long > &newProcessed)
setProcessed This method sets new list of processed mesages.
Definition ibot.cpp:308
void setToken(const QByteArray &newToken)
setToken This is setter of the IBot::token value.
Definition ibot.cpp:38
virtual void logout()
login This method remove login token of bot.
Definition ibot.cpp:30
void sigReceiveUpdate(const QSharedPointer< iUpdate > &)
sigReceiveUpdate emit when but receive any updates from users.
QDateTime startTime() const
startTime this is time when bol wil started.
Definition ibot.cpp:101
QSharedPointer< iUpdate > takeNextUnreadUpdate()
takeNextUnreadUpdate This method take a unread update and mark them as read.
Definition ibot.cpp:320
@ Post
general post request
Definition irequest.h:44
@ Upload
this is post request to upload a big data to telegram
Definition irequest.h:46
@ Get
general ger request, all request data sent as a url line
Definition irequest.h:42
RequestPriority
The RequestPriority enum.
Definition irequest.h:52
The RequestData class is simple wrapper of request object with path of responce. If Path of responce ...
Definition ibot.h:58