qTbot 0.2.102.bb22a69
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 QSharedPointer<QHttpMultiPart> httpData;
71
72 switch (rquest->method()) {
73 case iRequest::Get: {
74 networkReplay = _manager->get(QNetworkRequest(url));
75
76 break;
77 }
78
79 case iRequest::Post:
80 // req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
81 // reply = m_nam.post(req, params.toByteArray());
82
83 // break;
85 QNetworkRequest netRequest(url);
86
87 httpData = rquest->argsToMultipartFormData();
88 if (httpData) {
89 networkReplay = _manager->post(netRequest, httpData.data());
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 qDebug () << "current network active requests count : " << _currentParallelActiveNetworkThreads;
120}
121
123 return _requestExecutor->interval() * 1000;
124}
125
126void IBot::setReqestLimitPerSecond(int newReqestLimitPerSecond) {
127 _requestExecutor->setInterval(1000 / newReqestLimitPerSecond);
128}
129
130QFuture<QByteArray>
131IBot::sendRequest(const QSharedPointer<iRequest> &rquest) {
132 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
133 responce->start();
134
135
136 _requestQueue.insert(makeKey(rquest->priority()),
137 RequestData{rquest, "", responce});
138
139 _requestExecutor->start();
140
141 return responce->future();
142}
143
144QFuture<QByteArray>
145IBot::sendRequest(const QSharedPointer<iRequest> &rquest,
146 const QString &pathToResult) {
147 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
148 responce->start();
149 _requestQueue.insert(makeKey(rquest->priority()),
150 RequestData{rquest, pathToResult, responce});
151
152 _requestExecutor->start();
153
154 return responce->future();
155
156}
157
158void IBot::markUpdateAsProcessed(const QSharedPointer<iUpdate> &message) {
159 _notProcessedUpdates.remove(message->updateId());
160}
161
162void IBot::markUpdateAsUnprocessed(const QSharedPointer<iUpdate> &message) {
163 return markUpdateAsUnprocessed(message->updateId());
164}
165
166void IBot::markUpdateAsUnprocessed(unsigned long long messageID) {
167 _processed.remove(messageID);
168}
169
171 return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
172}
173
174void IBot::handleIncomeNewUpdate(const QSharedPointer<iUpdate> & message) {
175 emit sigReceiveUpdate(message);
176}
177
178void IBot::handleEcxecuteRequest() {
179 if (!_requestQueue.size()) {
180 _requestExecutor->stop();
181 return;
182 }
183
184 if (_currentParallelActiveNetworkThreads > _parallelActiveNetworkThreads) {
185 return;
186 }
187
188 auto&& requestData = _requestQueue.take(_requestQueue.firstKey());
189
190 if (requestData.responceFilePath.size()) {
191 sendRequestPrivate(requestData.request, requestData.responceFilePath, requestData.responce);
192 return;
193 }
194
195 sendRequestPrivate(requestData.request, requestData.responce);
196}
197
198unsigned long long IBot::makeKey(iRequest::RequestPriority priority) {
199 unsigned long long key = _totalRequest;
200 _totalRequest++;
201 key = key | (static_cast<unsigned long long>(iRequest::RequestPriority::MaxPriorityValue - priority) << 56);
202 return key;
203}
204
205void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
206 const QSharedPointer<QPromise<QByteArray> > &promise) {
207
208 QNetworkReply* networkReplay = sendRquestImpl(rquest);
209 if (!networkReplay) {
210 return;
211 }
212
213 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
214
215 connect(networkReplay, &QNetworkReply::finished, [this, networkReplay, promise](){
216 if (networkReplay->error() == QNetworkReply::NoError) {
217 promise->addResult(networkReplay->readAll());
218 promise->finish();
219
220 } else {
221 promise->setException(HttpException(networkReplay->error(), networkReplay->errorString().toLatin1() + networkReplay->readAll()));
222 }
223
224 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
225
226 });
227
228 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
229
230 if (promise->future().progressMaximum() != bytesTotal)
231 promise->setProgressRange(0, bytesTotal);
232
233 promise->setProgressValue(bytesCurrent);
234 };
235
236 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
237 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
238}
239
240void IBot::sendRequestPrivate(const QSharedPointer<iRequest> &rquest,
241 const QString &pathToResult,
242 const QSharedPointer<QPromise<QByteArray>> & promise) {
243 auto&& file = QSharedPointer<QFile>::create(pathToResult);
244
245 if (!file->open(QIODeviceBase::WriteOnly | QIODevice::Truncate)) {
246 qCritical() << "Fail to wrote data into " << pathToResult;
247 return;
248 }
249
250 QNetworkReply* networkReplay = sendRquestImpl(rquest);
251 if (!networkReplay) {
252 return;
253 }
254
255 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
256 connect(networkReplay, &QNetworkReply::finished, [this, promise, networkReplay, pathToResult](){
257
258 if (networkReplay->error() == QNetworkReply::NoError) {
259 promise->setException(HttpException(networkReplay->error(), networkReplay->errorString().toLatin1()));
260 } else {
261 promise->addResult(pathToResult.toUtf8()); // wil not work with UTF 8 path names
262 promise->finish();
263 }
264 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
265 });
266
267 connect(networkReplay, &QNetworkReply::readyRead, [networkReplay, promise, pathToResult, file](){
268 if (networkReplay->error() == QNetworkReply::NoError) {
269 file->write(networkReplay->readAll());
270 }
271
272 });
273
274 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
275
276 if (promise->future().progressMaximum() != bytesTotal)
277 promise->setProgressRange(0, bytesTotal);
278
279 promise->setProgressValue(bytesCurrent);
280 };
281
282 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
283 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
284
285}
286
287QSet<unsigned long long> IBot::processed() const {
288 return _processed;
289}
290
291void IBot::setProcessed(const QSet<unsigned long long> &newProcessed) {
292 _processed = newProcessed;
293}
294
295const QString &IBot::name() const {
296 return _name;
297}
298
299void IBot::setName(const QString &newName) {
300 _name = newName;
301}
302
303QSharedPointer<iUpdate> IBot::takeNextUnreadUpdate() {
304 if (_notProcessedUpdates.size()) {
305 auto toRemove = std::move(*_notProcessedUpdates.begin());
306 _notProcessedUpdates.erase(_notProcessedUpdates.cbegin());
307 return toRemove;
308 }
309
310 return nullptr;
311}
312
313
314}
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:299
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:295
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:162
void markUpdateAsProcessed(const QSharedPointer< iUpdate > &message)
markMessageAsProcessed This method remove message from the not processed messages store.
Definition ibot.cpp:158
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:174
int reqestLimitPerSecond() const
reqestLimitPerSecond this is request performence limitation. by default is 20 requests per second
Definition ibot.cpp:122
void setReqestLimitPerSecond(int newReqestLimitPerSecond)
setReqestLimitPerSecond this method sets new limitation of bot performance.
Definition ibot.cpp:126
virtual QString defaultFileStorageLocation() const
defaultFileStorageLocation This method return default file storage location.
Definition ibot.cpp:170
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:287
QFuture< QByteArray > sendRequest(const QSharedPointer< iRequest > &rquest)
sendRequest This method sent custom requests to the server.
Definition ibot.cpp:131
virtual void setProcessed(const QSet< unsigned long long > &newProcessed)
setProcessed This method sets new list of processed mesages.
Definition ibot.cpp:291
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:303
@ 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