qTbot 0.2.112.2e440ab
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-2025 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 bool wasBusy = _currentParallelActiveNetworkThreads == _parallelActiveNetworkThreads;
119 static bool lastMessageWasFree = false;
120
121 _currentParallelActiveNetworkThreads = newParallelActiveNetworkThreads;
122
123 if (_currentParallelActiveNetworkThreads == _parallelActiveNetworkThreads) {
124 qInfo() << "All network threads are busy!";
125 lastMessageWasFree = false;
126
127 } else if (wasBusy) {
128 qInfo() << "Network threads are free! available: " << _currentParallelActiveNetworkThreads << " from " << _parallelActiveNetworkThreads;
129 lastMessageWasFree = false;
130
131 } else if (_currentParallelActiveNetworkThreads == 0 && !lastMessageWasFree) {
132 qInfo() << "All network threads are free!";
133 lastMessageWasFree = true;
134 }
135}
136
138 return _requestExecutor->interval() * 1000;
139}
140
141void IBot::setReqestLimitPerSecond(int newReqestLimitPerSecond) {
142 _requestExecutor->setInterval(1000 / newReqestLimitPerSecond);
143}
144
145QFuture<QByteArray>
146IBot::sendRequest(const QSharedPointer<iRequest> &rquest) {
147 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
148 responce->start();
149
150 {
151 QMutexLocker lock(&_mutex);
152 _requestQueue.insert(makeKey(rquest->priority()),
153 RequestData{rquest, "", responce});
154 }
155
156 if (!_requestExecutor->isActive()) {
157 handleEcxecuteRequest();
158 _requestExecutor->start();
159
160 }
161
162 return responce->future();
163}
164
165QFuture<QByteArray>
166IBot::sendRequest(const QSharedPointer<iRequest> &rquest,
167 const QString &pathToResult) {
168 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
169 responce->start();
170
171 {
172 QMutexLocker lock(&_mutex);
173 _requestQueue.insert(makeKey(rquest->priority()),
174 RequestData{rquest, pathToResult, responce});
175 }
176
177
178 if (!_requestExecutor->isActive()) {
179 handleEcxecuteRequest();
180 _requestExecutor->start();
181
182 }
183
184 return responce->future();
185
186}
187
188void IBot::markUpdateAsProcessed(const QSharedPointer<iUpdate> &message) {
189 _notProcessedUpdates.remove(message->updateId());
190}
191
192void IBot::markUpdateAsUnprocessed(const QSharedPointer<iUpdate> &message) {
193 return markUpdateAsUnprocessed(message->updateId());
194}
195
196void IBot::markUpdateAsUnprocessed(unsigned long long messageID) {
197 _processed.remove(messageID);
198}
199
201 return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
202}
203
204void IBot::handleIncomeNewUpdate(const QSharedPointer<iUpdate> & message) {
205 emit sigReceiveUpdate(message);
206}
207
208void IBot::handleEcxecuteRequest() {
209 QMutexLocker lock(&_mutex);
210
211 if (!_requestQueue.size()) {
212 _requestExecutor->stop();
213 return;
214 }
215
216 if (_currentParallelActiveNetworkThreads > _parallelActiveNetworkThreads) {
217 return;
218 }
219
220 auto&& requestData = _requestQueue.take(_requestQueue.firstKey());
221
222 if (requestData.responceFilePath.size()) {
223 sendRequestPrivate(requestData.request, requestData.responceFilePath, requestData.responce);
224 return;
225 }
226
227 sendRequestPrivate(requestData.request, requestData.responce);
228}
229
230unsigned long long IBot::makeKey(iRequest::RequestPriority priority) {
231 unsigned long long key = _totalRequest;
232 _totalRequest++;
233 key = key | (static_cast<unsigned long long>(iRequest::RequestPriority::MaxPriorityValue - priority) << 56);
234 return key;
235}
236
237void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
238 const QSharedPointer<QPromise<QByteArray> > &promise) {
239
240 QNetworkReply* networkReplay = sendRquestImpl(rquest);
241 if (!networkReplay) {
242 return;
243 }
244
245 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
246
247 connect(networkReplay, &QNetworkReply::finished, [this, networkReplay, promise](){
248 if (networkReplay->error() == QNetworkReply::NoError) {
249 promise->addResult(networkReplay->readAll());
250 promise->finish();
251
252 } else {
253 QByteArray msg = networkReplay->errorString().toLatin1() + networkReplay->readAll();
254 promise->setException(HttpException(networkReplay->error(), msg));
255#ifdef QTBOT_PRINT_ERRORS
256 qCritical() << msg;
257#endif
258
259 }
260
261 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
262
263 });
264
265 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
266
267 if (promise->future().progressMaximum() != bytesTotal)
268 promise->setProgressRange(0, bytesTotal);
269
270 promise->setProgressValue(bytesCurrent);
271 };
272
273 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
274 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
275}
276
277void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
278 const QString &pathToResult,
279 const QSharedPointer<QPromise<QByteArray>> & promise) {
280 auto&& file = QSharedPointer<QFile>::create(pathToResult);
281
282 if (!file->open(QIODeviceBase::WriteOnly | QIODevice::Truncate)) {
283 qCritical() << "Fail to wrote data into " << pathToResult;
284 return;
285 }
286
287 QNetworkReply* networkReplay = sendRquestImpl(rquest);
288 if (!networkReplay) {
289 return;
290 }
291
292 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
293 connect(networkReplay, &QNetworkReply::finished, [this, promise, networkReplay, pathToResult](){
294
295 if (networkReplay->error() == QNetworkReply::NoError) {
296 QByteArray msg = networkReplay->errorString().toLatin1();
297 promise->setException(HttpException(networkReplay->error(), msg));
298#ifdef QTBOT_PRINT_ERRORS
299 qCritical() << msg;
300#endif
301 } else {
302 promise->addResult(pathToResult.toUtf8()); // wil not work with UTF 8 path names
303 promise->finish();
304 }
305 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
306 });
307
308 connect(networkReplay, &QNetworkReply::readyRead, [networkReplay, promise, pathToResult, file](){
309 if (networkReplay->error() == QNetworkReply::NoError) {
310 file->write(networkReplay->readAll());
311 }
312
313 });
314
315 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
316
317 if (promise->future().progressMaximum() != bytesTotal)
318 promise->setProgressRange(0, bytesTotal);
319
320 promise->setProgressValue(bytesCurrent);
321 };
322
323 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
324 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
325
326}
327
328QSet<unsigned long long> IBot::processed() const {
329 return _processed;
330}
331
332void IBot::setProcessed(const QSet<unsigned long long> &newProcessed) {
333 _processed = newProcessed;
334}
335
336const QString &IBot::name() const {
337 return _name;
338}
339
340void IBot::setName(const QString &newName) {
341 _name = newName;
342}
343
344QSharedPointer<iUpdate> IBot::takeNextUnreadUpdate() {
345 if (_notProcessedUpdates.size()) {
346 auto toRemove = std::move(*_notProcessedUpdates.begin());
347 _notProcessedUpdates.erase(_notProcessedUpdates.cbegin());
348 return toRemove;
349 }
350
351 return nullptr;
352}
353
354
355}
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:340
void sigReceiveUpdate(const QSharedPointer< qTbot::iUpdate > &)
sigReceiveUpdate emit when but receive any updates from users.
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:336
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:192
void markUpdateAsProcessed(const QSharedPointer< iUpdate > &message)
markMessageAsProcessed This method remove message from the not processed messages store.
Definition ibot.cpp:188
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:204
int reqestLimitPerSecond() const
reqestLimitPerSecond this is request performence limitation. by default is 20 requests per second
Definition ibot.cpp:137
void setReqestLimitPerSecond(int newReqestLimitPerSecond)
setReqestLimitPerSecond this method sets new limitation of bot performance.
Definition ibot.cpp:141
virtual QString defaultFileStorageLocation() const
defaultFileStorageLocation This method return default file storage location.
Definition ibot.cpp:200
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:328
QFuture< QByteArray > sendRequest(const QSharedPointer< iRequest > &rquest)
sendRequest This method sent custom requests to the server.
Definition ibot.cpp:146
virtual void setProcessed(const QSet< unsigned long long > &newProcessed)
setProcessed This method sets new list of processed mesages.
Definition ibot.cpp:332
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
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:344
@ 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