10#include "qstandardpaths.h"
12#include <QNetworkReply>
18 _manager =
new QNetworkAccessManager();
19 _manager->setAutoDeleteReplies(
true);
20 _manager->setTransferTimeout(60000);
22 _requestExecutor =
new QTimer(
this);
23 _requestExecutor->setInterval(1000 / 20);
25 connect(_requestExecutor, &QTimer::timeout,
this , &IBot::handleEcxecuteRequest);
42 _startTime = QDateTime::currentDateTime();
47 if (!message->isValid())
50 auto id = message->updateId();
52 if (!_processed.contains(
id)) {
54 _processed.insert(
id);
55 _notProcessedUpdates[id] = message;
61QNetworkReply* IBot::sendRquestImpl(
const QSharedPointer<iRequest> &rquest) {
67#ifdef QTBOT_PRINT_RQUESTS
71 QNetworkReply* networkReplay =
nullptr;
73 switch (rquest->method()) {
75 networkReplay = _manager->get(QNetworkRequest(url));
86 QNetworkRequest netRequest(url);
88 auto httpData = rquest->argsToMultipartFormData();
90 networkReplay = _manager->post(netRequest, httpData.data());
91 connect(networkReplay, &QNetworkReply::destroyed, [httpData](){});
100 return networkReplay;
108 return _totalRequest;
112 return _parallelActiveNetworkThreads;
116 _parallelActiveNetworkThreads = newParallelActiveNetworkThreads;
119void IBot::setCurrentParallelActiveNetworkThreads(
int newParallelActiveNetworkThreads) {
120 bool wasBusy = _currentParallelActiveNetworkThreads == _parallelActiveNetworkThreads;
121 static bool lastMessageWasFree =
false;
124 _currentParallelActiveNetworkThreads = newParallelActiveNetworkThreads;
126 if (_currentParallelActiveNetworkThreads == _parallelActiveNetworkThreads) {
127 qInfo() <<
"All network threads are busy!";
128 lastMessageWasFree =
false;
130 }
else if (wasBusy) {
131 qInfo() <<
"Network threads are free! available: " << _currentParallelActiveNetworkThreads <<
" from " << _parallelActiveNetworkThreads;
132 lastMessageWasFree =
false;
134 }
else if (_currentParallelActiveNetworkThreads == 0 && !lastMessageWasFree) {
135 qInfo() <<
"All network threads are free!";
136 lastMessageWasFree =
true;
141 return _requestExecutor->interval() * 1000;
145 _requestExecutor->setInterval(1000 / newReqestLimitPerSecond);
150 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
154 QMutexLocker lock(&_mutex);
155 _requestQueue.insert(makeKey(rquest->priority()),
159 if (!_requestExecutor->isActive()) {
160 handleEcxecuteRequest();
161 _requestExecutor->start();
165 return responce->future();
170 const QString &pathToResult) {
171 auto&& responce = QSharedPointer<QPromise<QByteArray>>::create();
175 QMutexLocker lock(&_mutex);
176 _requestQueue.insert(makeKey(rquest->priority()),
181 if (!_requestExecutor->isActive()) {
182 handleEcxecuteRequest();
183 _requestExecutor->start();
187 return responce->future();
192 _notProcessedUpdates.remove(message->updateId());
200 _processed.remove(messageID);
204 return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
211void IBot::handleEcxecuteRequest() {
212 QMutexLocker lock(&_mutex);
214 if (!_requestQueue.size()) {
215 _requestExecutor->stop();
219 if (_currentParallelActiveNetworkThreads > _parallelActiveNetworkThreads) {
223 auto&& requestData = _requestQueue.take(_requestQueue.firstKey());
225 if (requestData.responceFilePath.size()) {
226 sendRequestPrivate(requestData.request, requestData.responceFilePath, requestData.responce);
230 sendRequestPrivate(requestData.request, requestData.responce);
234 unsigned long long key = _totalRequest;
240void IBot::sendRequestPrivate(
const QSharedPointer<iRequest> &rquest,
241 const QSharedPointer<QPromise<QByteArray> > &promise) {
243 QNetworkReply* networkReplay = sendRquestImpl(rquest);
244 if (!networkReplay) {
248 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
250 connect(networkReplay, &QNetworkReply::finished, [
this, networkReplay, promise](){
251 if (networkReplay->error() == QNetworkReply::NoError) {
252 promise->addResult(networkReplay->readAll());
256 QByteArray msg = networkReplay->errorString().toLatin1() + networkReplay->readAll();
257 promise->setException(
HttpException(networkReplay->error(), msg));
258#ifdef QTBOT_PRINT_ERRORS
264 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
268 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
270 if (promise->future().progressMaximum() != bytesTotal)
271 promise->setProgressRange(0, bytesTotal);
273 promise->setProgressValue(bytesCurrent);
276 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
277 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
280void IBot::sendRequestPrivate(
const QSharedPointer<iRequest> &rquest,
281 const QString &pathToResult,
282 const QSharedPointer<QPromise<QByteArray>> & promise) {
283 auto&& file = QSharedPointer<QFile>::create(pathToResult);
285 if (!file->open(QIODeviceBase::WriteOnly | QIODevice::Truncate)) {
286 qCritical() <<
"Fail to wrote data into " << pathToResult;
290 QNetworkReply* networkReplay = sendRquestImpl(rquest);
291 if (!networkReplay) {
295 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads + 1);
296 connect(networkReplay, &QNetworkReply::finished, [
this, promise, networkReplay, pathToResult](){
298 if (networkReplay->error() == QNetworkReply::NoError) {
299 QByteArray msg = networkReplay->errorString().toLatin1();
300 promise->setException(
HttpException(networkReplay->error(), msg));
301#ifdef QTBOT_PRINT_ERRORS
305 promise->addResult(pathToResult.toUtf8());
308 setCurrentParallelActiveNetworkThreads(_currentParallelActiveNetworkThreads - 1);
311 connect(networkReplay, &QNetworkReply::readyRead, [networkReplay, promise, pathToResult, file](){
312 if (networkReplay->error() == QNetworkReply::NoError) {
313 file->write(networkReplay->readAll());
318 auto && setProggress = [promise](qint64 bytesCurrent, qint64 bytesTotal){
320 if (promise->future().progressMaximum() != bytesTotal)
321 promise->setProgressRange(0, bytesTotal);
323 promise->setProgressValue(bytesCurrent);
326 connect(networkReplay, &QNetworkReply::downloadProgress, setProggress);
327 connect(networkReplay, &QNetworkReply::uploadProgress, setProggress);
336 _processed = newProcessed;
348 if (_notProcessedUpdates.size()) {
349 auto toRemove = std::move(*_notProcessedUpdates.begin());
350 _notProcessedUpdates.erase(_notProcessedUpdates.cbegin());
HttpException(QNetworkReply::NetworkError code, const QByteArray &erroString={})
HttpException constructs the exception with an http error code and an error description.
virtual QString makeUrl(const QSharedPointer< iRequest > &request) const =0
makeUrl This method prepares a prefix url for http requests.
void setName(const QString &newName)
setName This method sets new value for the IBot::name field.
void sigReceiveUpdate(const QSharedPointer< qTbot::iUpdate > &)
sigReceiveUpdate emitted when the bot receives any updates from users.
const QString & name() const
name This is the name of the bot. Usually this field is received from the server after authentication...
unsigned long long totalSentRequests() const
totalSentRequests This is the total count of prepared requests of the bot from the start.
void markUpdateAsUnprocessed(const QSharedPointer< iUpdate > &message)
markMessageAsUnprocessed This method adds the message into the not processed messages store.
void markUpdateAsProcessed(const QSharedPointer< iUpdate > &message)
markMessageAsProcessed This method removes the message from the not processed messages store.
const QByteArray & token() const
token This is the token value for authentication on the remote server (bot).
int parallelActiveNetworkThreads() const
parallelActiveNetworkThreads This method returns the number of parallel active network threads.
virtual void handleIncomeNewUpdate(const QSharedPointer< iUpdate > &)
handleIncomeNewUpdate This method just emits the sigReceiveUpdate signal.
int reqestLimitPerSecond() const
reqestLimitPerSecond This is the request performance limitation. By default it is 20 requests per sec...
void setReqestLimitPerSecond(int newReqestLimitPerSecond)
setReqestLimitPerSecond This method sets a new limitation of the bot performance.
virtual QString defaultFileStorageLocation() const
defaultFileStorageLocation This method returns the default file storage location.
void incomeNewUpdate(const QSharedPointer< iUpdate > &message)
incomeNewUpdate This method saves incoming messages into the store.
void setParallelActiveNetworkThreads(int newParallelActiveNetworkThreads)
setParallelActiveNetworkThreads This method sets the number of parallel active network threads.
QSet< unsigned long long > processed() const
processed This method returns the list of processed messages.
QFuture< QByteArray > sendRequest(const QSharedPointer< iRequest > &rquest)
sendRequest This method sends a custom request to the server.
virtual void setProcessed(const QSet< unsigned long long > &newProcessed)
setProcessed This method sets a new list of processed messages.
void setToken(const QByteArray &newToken)
setToken This is the setter of the IBot::token value.
virtual void logout()
logout This method removes the login token of the bot.
QDateTime startTime() const
startTime This is the time when the bot was started.
IBot()
IBot default constructor. Initializes the network access manager and the request executor.
QSharedPointer< iUpdate > takeNextUnreadUpdate()
takeNextUnreadUpdate This method takes an unread update and marks it as read.
~IBot()
~IBot destructor. Cancels pending requests and frees all network resources.
@ Post
Post general post request.
@ Upload
Upload this is a post request to upload big data to telegram.
@ Get
Get general get request, all request data is sent as a url line.
RequestPriority
The RequestPriority enum This is the list of request execution priorities on the client side.
@ MaxPriorityValue
MaxPriorityValue the maximum value of the priority.
The RequestData class is a simple wrapper of a request object with the path of the response....