Files
qad/telegram_test/telegrambotapi.cpp

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)));
}