359 lines
9.8 KiB
C++
359 lines
9.8 KiB
C++
#include "telegrambotapi.h"
|
|
//#include <QMessageBox>
|
|
#include <QSslError>
|
|
#include <QDebug>
|
|
#include <QStringList>
|
|
#include <QDateTime>
|
|
#include <QHttpMultiPart>
|
|
#include "json.h"
|
|
|
|
|
|
const char * TelegramBotAPI::send_methods[] = {
|
|
"sendMessage",
|
|
"sendPhoto",
|
|
"sendDocument",
|
|
};
|
|
const char * TelegramBotAPI::param_filename = "__filename";
|
|
|
|
TelegramBotAPI::TelegramBotAPI(QObject *parent) : QObject(parent) {
|
|
is_connected = false;
|
|
wait_server = false;
|
|
has_stop = false;
|
|
qRegisterMetaType<TelegramBotAPI::Message>("TelegramBotAPI::Message");
|
|
qnam = new QNetworkAccessManager();
|
|
// connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(httpFinished(QNetworkReply*)));
|
|
connect(qnam, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
|
|
}
|
|
|
|
|
|
TelegramBotAPI::~TelegramBotAPI() {
|
|
has_stop = true;
|
|
// msleep(60);
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::setBotToken(QString arg) {
|
|
if (m_botToken == arg) {
|
|
checkBot();
|
|
return;
|
|
}
|
|
is_connected = false;
|
|
m_botToken = arg;
|
|
botUrl = "https://api.telegram.org/bot"+m_botToken+"/";
|
|
checkBot();
|
|
}
|
|
|
|
|
|
bool TelegramBotAPI::sendMessage(uint chat_id, TelegramBotAPI::MessageType mtype, QByteArray data, QString filename) {
|
|
if (!is_connected || chat_id == 0 || data.isEmpty()) return false;
|
|
SendQuery sq;
|
|
sq.params["chat_id"] = chat_id;
|
|
sq.method = send_methods[mtype];
|
|
bool ok = false;
|
|
switch (mtype) {
|
|
case Text:
|
|
sq.params["text"] = QString::fromUtf8(data.data(), data.size());
|
|
ok = true;
|
|
break;
|
|
case Photo:
|
|
if (!filename.isEmpty()) {
|
|
sq.params[param_filename] = filename;
|
|
sq.params["photo"] = data;
|
|
ok = true;
|
|
}
|
|
break;
|
|
case Document:
|
|
if (!filename.isEmpty()) {
|
|
sq.params[param_filename] = filename;
|
|
sq.params["document"] = data;
|
|
ok = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (ok) {
|
|
sendmsg_que.enqueue(sq);
|
|
if (wait_server) {
|
|
if (reply->url().toString().split("/").last() == "getUpdates") reply->abort();
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
|
|
//void TelegramBot::run() {
|
|
// while (!has_stop) {
|
|
// mutex.lock();
|
|
// if (qnam == 0) {
|
|
// qnam = new QNetworkAccessManager();
|
|
// connect(qnam, SIGNAL(finished(QNetworkReply*)),
|
|
// this, SLOT(httpFinished(QNetworkReply*)));
|
|
// connect(qnam, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
|
|
// }
|
|
// if (!wait_server) {
|
|
// if (!is_connected) checkBot();
|
|
// updateBot();
|
|
// }
|
|
// mutex.unlock();
|
|
// msleep(50);
|
|
// }
|
|
//}
|
|
|
|
|
|
void TelegramBotAPI::updateBot() {
|
|
if (!is_connected) return;
|
|
QVariantMap vm;
|
|
vm["timeout"] = 50;
|
|
vm["offset"] = last_update;
|
|
sendRequest("getUpdates", vm);
|
|
}
|
|
|
|
|
|
bool TelegramBotAPI::fileDownload() {
|
|
if (download_list.isEmpty()) return false;
|
|
bool ok = false;
|
|
int rm = -1;
|
|
for(int i=0; i<download_list.size(); i++) {
|
|
DownloadFile & df = download_list[i];
|
|
qDebug() << df.id << df.url << df.filename;
|
|
if (df.url.isEmpty()) {
|
|
QVariantMap fm;
|
|
fm["file_id"] = df.id;
|
|
sendRequest("getFile", fm);
|
|
return true;
|
|
} else {
|
|
if (!df.downloaded) {
|
|
QNetworkRequest nr;
|
|
nr.setUrl(df.url);
|
|
reply = qnam->get(nr);
|
|
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
|
|
return true;
|
|
} else {
|
|
File f;
|
|
f.chat_id = df.chat_id;
|
|
f.filename = df.filename;
|
|
f.data = df.data;
|
|
emit newFile(f);
|
|
rm = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (rm >= 0) download_list.removeAt(rm);
|
|
return ok;
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::checkBot() {
|
|
sendRequest("getMe", QVariantMap());
|
|
}
|
|
|
|
|
|
bool TelegramBotAPI::parseHeader(QString json, QVariant & res) {
|
|
bool ok;
|
|
QVariantMap map = QtJson::parse(json, ok).toMap();
|
|
if (!ok) {
|
|
emit errorOccured("parse JSON error");
|
|
return false;
|
|
}
|
|
if (!map.value("ok").toBool()) {
|
|
emit errorOccured("query error");
|
|
return false;
|
|
}
|
|
res = map.value("result");
|
|
return !res.isNull() && res.isValid();
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::parseCheckBot(QString json) {
|
|
QVariant res;
|
|
if (!parseHeader(json, res)) {
|
|
is_connected = false;
|
|
emit botFail();
|
|
return;
|
|
}
|
|
QVariantMap resm = res.toMap();
|
|
m_botID = resm.value("id").toUInt();
|
|
m_botName = resm.value("username").toString();
|
|
is_connected = true;
|
|
last_update = 0;
|
|
emit botOK();
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::parseFile(QString json) {
|
|
QVariant res;
|
|
if (!parseHeader(json, res)) {
|
|
emit errorOccured("can't get file");
|
|
return;
|
|
}
|
|
QVariantMap resm = res.toMap();
|
|
QString fpath = resm["file_path"].toString();
|
|
QString fid = resm["file_id"].toString();
|
|
if (fpath.isEmpty()) return;
|
|
QUrl furl = QUrl("https://api.telegram.org/file/bot"+m_botToken+"/"+fpath);
|
|
qDebug() << "new file :" << furl;
|
|
for (int i=0; i<download_list.size(); i++) {
|
|
if (download_list[i].id == fid) {
|
|
download_list[i].url = furl;
|
|
if (download_list[i].filename.isEmpty())
|
|
download_list[i].filename = fpath.split("/").last();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::parseMessages(QString json) {
|
|
QVariant res;
|
|
if (!parseHeader(json, res)) {
|
|
emit errorOccured("can't update");
|
|
return;
|
|
}
|
|
QVariantList resm = res.toList();
|
|
foreach (QVariant v, resm) {
|
|
QVariantMap mm = v.toMap();
|
|
last_update = mm.value("update_id").toInt();
|
|
Message m = parseMessage(mm.value("message"));
|
|
if (m.id > 0 && !m.text.isEmpty()) {
|
|
emit newMessage(m);
|
|
emit newMessage(m.chat_id, m.text);
|
|
}
|
|
}
|
|
last_update++;
|
|
//qDebug() << "update_id" << last_update;
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::startDownloadFile(uint chat_id, QVariantMap file) {
|
|
if (!file.isEmpty()) {
|
|
DownloadFile df;
|
|
df.downloaded = false;
|
|
df.id = file["file_id"].toString();
|
|
df.chat_id = chat_id;
|
|
df.filename = file["file_name"].toString();
|
|
qDebug() << "new incomming file" << df.id;
|
|
download_list << df;
|
|
}
|
|
}
|
|
|
|
|
|
TelegramBotAPI::Message TelegramBotAPI::parseMessage(QVariant msg) {
|
|
Message ret;
|
|
QVariantMap map = msg.toMap();
|
|
ret.id = map.value("message_id", 0).toUInt();
|
|
QVariantMap chat = map.value("chat").toMap();
|
|
ret.chat_id = chat.value("id", 0).toUInt();
|
|
QVariantMap user = map.value("from").toMap();
|
|
ret.user_id = user.value("id", 0).toUInt();
|
|
ret.user_name = user.value("first_name").toString();
|
|
ret.text = map.value("text").toString();
|
|
ret.time = QDateTime::fromTime_t(map.value("date").toUInt());
|
|
QVariantMap file;
|
|
QVariantList photo = map.value("photo").toList();
|
|
if (!photo.isEmpty()) {
|
|
startDownloadFile(ret.chat_id, photo.last().toMap());
|
|
}
|
|
QVariantMap docum = map.value("document").toMap();
|
|
startDownloadFile(ret.chat_id, docum);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::sslErrors(QNetworkReply*,const QList<QSslError> &errors) {
|
|
QString errorString;
|
|
foreach (const QSslError &error, errors) {
|
|
if (!errorString.isEmpty())
|
|
errorString += ", ";
|
|
errorString += error.errorString();
|
|
}
|
|
emit errorOccured(errorString);
|
|
// if (QMessageBox::warning(0, QString("HTTP"), QString("One or more SSL errors has occurred: %1").arg(errorString),
|
|
// QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Ignore)
|
|
// == QMessageBox::Ignore) {
|
|
// reply->ignoreSslErrors();
|
|
// }
|
|
}
|
|
|
|
|
|
void TelegramBotAPI::httpFinished() {
|
|
// qDebug() << "reply finished";
|
|
// mutex.lock();
|
|
QByteArray ba = reply->readAll();
|
|
QStringList quest_path = reply->url().toString().split("/");
|
|
// qDebug() << "quest list =" << quest_path.size();
|
|
QString quest = quest_path.last();
|
|
reply->deleteLater();
|
|
wait_server = false;
|
|
// mutex.unlock();
|
|
if (quest_path.size() == 5) {
|
|
QString s = QString::fromUtf8(ba.data(), ba.size());
|
|
// qDebug() << quest << s;
|
|
// if (quest.indexOf("?") > 0)
|
|
// quest.resize(quest.indexOf('?'));
|
|
QVariant tmp;
|
|
if (quest == "getMe") parseCheckBot(s);
|
|
if (quest == "getFile") parseFile(s);
|
|
if (quest == "getUpdates") parseMessages(s);
|
|
if (quest == "sendMessage") parseHeader(s, tmp);
|
|
qDebug() << "parse done" << quest << s;
|
|
} else {
|
|
for (int i=0; i<download_list.size(); ++i) {
|
|
if (download_list[i].url == reply->url()) {
|
|
download_list[i].data = ba;
|
|
download_list[i].downloaded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (is_connected) {
|
|
if (!fileDownload()) {
|
|
if (sendmsg_que.isEmpty()) updateBot();
|
|
else {
|
|
SendQuery sq = sendmsg_que.dequeue();
|
|
sendRequest(sq.method, sq.params);
|
|
}
|
|
}
|
|
} else checkBot();
|
|
}
|
|
|
|
|
|
//void TelegramBot::httpFinished(QNetworkReply *) {
|
|
// qDebug() << "get finished";
|
|
//}
|
|
|
|
|
|
void TelegramBotAPI::sendRequest(QString method, QVariantMap params) {
|
|
wait_server = true;
|
|
qDebug() << "startRequest:" << method;
|
|
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
|
|
QString filename = params.take(param_filename).toString();
|
|
QMapIterator<QString, QVariant> it(params);
|
|
while (it.hasNext()) {
|
|
it.next();
|
|
QHttpPart part;
|
|
if (it.value().type() == QVariant::ByteArray) {
|
|
part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; filename=\""+filename+"\"; name=\""+it.key()+"\""));
|
|
qDebug() << "sending file" << double(it.value().toByteArray().size()) / (1024*1024) << "Mb";
|
|
part.setBody(it.value().toByteArray());
|
|
} else {
|
|
part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\""+it.key()+"\""));
|
|
part.setBody(it.value().toString().toUtf8());
|
|
}
|
|
multiPart->append(part);
|
|
}
|
|
if (params.isEmpty()) {
|
|
QHttpPart part;
|
|
part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
|
|
multiPart->append(part);
|
|
}
|
|
QNetworkRequest nr;
|
|
nr.setUrl(QUrl(botUrl + method));
|
|
reply = qnam->post(nr, multiPart);
|
|
multiPart->setParent(reply); // delete the multiPart with the reply
|
|
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
|
|
// connect(reply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
|
|
// connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDataReadProgress(qint64,qint64)));
|
|
}
|