Initial commit

This commit is contained in:
Moritz Sternemann
2015-12-29 16:44:16 +01:00
commit 3663fd38ce
41 changed files with 2095 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
### Qt ###
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib
# Qt-es
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
qrc_*.cpp
ui_*.h
Makefile*
*-build-*
# QtCreator
*.autosave
#QtCtreator Qml
*.qmlproject.user
*.qmlproject.user.*
**/bin

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Moritz Sternemann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
QtTelegramBot.pri Normal file
View File

@ -0,0 +1,43 @@
INCLUDEPATH += $$PWD
QT += core network
SOURCES += \
$$PWD/qttelegrambot.cpp \
$$PWD/networking.cpp \
$$PWD/types/message.cpp \
$$PWD/types/update.cpp \
$$PWD/types/chat.cpp \
$$PWD/types/user.cpp \
$$PWD/types/document.cpp \
$$PWD/types/photosize.cpp \
$$PWD/types/audio.cpp \
$$PWD/types/sticker.cpp \
$$PWD/types/video.cpp \
$$PWD/types/voice.cpp \
$$PWD/types/contact.cpp \
$$PWD/types/location.cpp
HEADERS += \
$$PWD/qttelegrambot.h \
$$PWD/networking.h \
$$PWD/types/message.h \
$$PWD/types/update.h \
$$PWD/types/chat.h \
$$PWD/types/user.h \
$$PWD/types/file.h \
$$PWD/types/document.h \
$$PWD/types/photosize.h \
$$PWD/types/audio.h \
$$PWD/types/sticker.h \
$$PWD/types/video.h \
$$PWD/types/voice.h \
$$PWD/types/contact.h \
$$PWD/types/location.h \
$$PWD/types/reply/genericreply.h \
$$PWD/types/reply/replykeyboardmarkup.h \
$$PWD/types/reply/replykeyboardhide.h \
$$PWD/types/reply/forcereply.h
OTHER_FILES += \
$$PWD/README.md

27
README.md Normal file
View File

@ -0,0 +1,27 @@
## Introduction
Qt5 library for the Telegram Bot API ([https://core.telegram.org/bots/api]()).
It can be used to interact with the API directly and/or configured to automatically notify you on updates using Qts signals and slots.
You need a bot token to make API calls. Text the [@botfather](https://telegram.me/BotFather) to create a new bot and obtain a token.
## Usage
Create a lib directory in your project directory and cd into it:
mkdir lib && cd lib
Clone the repo to your projects source directory:
git clone https://github.com/iMoritz/QtTelegramBot.git
Alternatively you can add QtTelegramBot as a git submodule to always stay up-to-date:
git submodule add https://github.com/iMoritz/QtTelegramBot.git
In your project file (*project*.pro) add this include:
include($$PWD/lib/QtTelegramBot/QtTelegramBot.pri)
Now you can use the Bot classes using:
#include "qttelegrambot.h"
Everything in *QtTelegramBot* is in the `Telegram` namespace.
## Examples
Examples can be found in the `examples` directory. You can build them in QtCreator or using the command line.

73
examples/echo/.gitignore vendored Normal file
View File

@ -0,0 +1,73 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

12
examples/echo/echo.pro Normal file
View File

@ -0,0 +1,12 @@
QT += core
QT -= gui
TARGET = echo
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
include(../../QtTelegramBot.pri)

27
examples/echo/main.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <QCoreApplication>
#include <QDebug>
#include "qttelegrambot.h"
#define TOKEN "YOUR BOT TOKEN"
Telegram::Bot *bot;
void newMessage(Telegram::Message message)
{
qDebug() << "new message:" << message;
if (bot && message.type == Telegram::Message::TextType) {
bot->sendMessage(message.from.id, message.string);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
bot = new Telegram::Bot(TOKEN, false, 500, 4);
QObject::connect(bot, &Telegram::Bot::message, &newMessage);
qDebug() << "Started Telegram Bot";
return a.exec();
}

3
examples/examples.pro Normal file
View File

@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += \
echo

157
networking.cpp Normal file
View File

@ -0,0 +1,157 @@
#include "networking.h"
using namespace Telegram;
Networking::Networking(QString token, QObject *parent) :
QObject(parent),
m_nam(new QNetworkAccessManager(this)),
m_token(token)
{
}
Networking::~Networking()
{
delete m_nam;
}
QByteArray Networking::request(QString endpoint, ParameterList params, Networking::Method method)
{
if (endpoint.isEmpty()) {
qWarning("Cannot do request without endpoint");
return QByteArray();
}
if (m_token.isEmpty()) {
qWarning("Cannot do request without a Telegram Bot Token");
return QByteArray();
}
QNetworkRequest req;
QUrl url = buildUrl(endpoint);
req.setUrl(url);
#ifdef DEBUG
qDebug("HTTP request: %s", qUtf8Printable(req.url().toString()));
#endif
QEventLoop loop;
QNetworkReply *reply;
if (method == GET) {
url.setQuery(parameterListToString(params));
req.setUrl(url);
reply = m_nam->get(req);
} else if (method == POST) {
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
reply = m_nam->post(req, parameterListToString(params));
} else if (method == UPLOAD) {
QByteArray boundary = generateMultipartBoundary(params);
req.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=" + boundary);
QByteArray requestData = generateMultipartFormData(params, boundary);
req.setHeader(QNetworkRequest::ContentLengthHeader, requestData.length());
reply = m_nam->post(req, requestData);
} else {
qCritical("No valid method!");
reply = NULL;
}
if (reply == NULL) {
qWarning("Reply is NULL");
delete reply;
return QByteArray();
}
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) {
qCritical("%s", qPrintable(QString("[%1] %2").arg(reply->error()).arg(reply->errorString())));
delete reply;
return QByteArray();
}
QByteArray ret = reply->readAll();
delete reply;
return ret;
}
QUrl Networking::buildUrl(QString endpoint)
{
QUrl url = QUrl();
url.setScheme("https");
url.setHost(API_HOST);
url.setPath("/bot" + m_token + endpoint);
return url;
}
QByteArray Networking::parameterListToString(ParameterList list)
{
QByteArray ret;
ParameterList::iterator i = list.begin();
while (i != list.end()) {
ret.append(i.key() + "=" + i.value().value + "&");
++i;
}
ret = ret.left(ret.length() - 1);
return ret;
}
QByteArray Networking::generateMultipartBoundary(ParameterList list)
{
// Generates a boundary that is not existent in the data
QByteArray result;
srand((unsigned int) time(NULL));
ParameterList::iterator i = list.begin();
while (i != list.end()) {
if (i.value().isFile) {
while (result.isEmpty() || i.value().value.contains(result)) {
result.append(generateRandomString(4));
}
}
++i;
}
return result;
}
QByteArray Networking::generateMultipartFormData(ParameterList list, QByteArray boundary)
{
QByteArray result;
ParameterList::iterator i = list.begin();
while (i != list.end()) {
HttpParameter param = i.value();
result.append("--" + boundary + "\r\n");
result.append("Content-Disposition: form-data; name=\"" + i.key());
if (param.isFile) {
result.append("\"; filename=\"" + param.filename);
}
result.append("\"\r\n");
if (param.isFile) {
result.append("Content-Type: " + param.mimeType + "\r\n");
}
result.append("\r\n");
result.append(param.value);
result.append("\r\n");
++i;
}
result.append("--" + boundary + "--");
return result;
}
QString Networking::generateRandomString(int length)
{
static const std::string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890-=[]\\;',./!@#$%^&*()_+{}|:\"<>?`~");
static const size_t charsLen = chars.length();
QString result;
for (int i = 0; i < length; ++i) {
result += QChar(chars[rand() % charsLen]);
}
return result;
}

69
networking.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef NETWORKING_H
#define NETWORKING_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#define API_HOST "api.telegram.org"
#define ENDPOINT_GET_ME "/getMe"
#define ENDPOINT_SEND_MESSAGE "/sendMessage"
#define ENDPOINT_FORWARD_MESSAGE "/forwardMessage"
#define ENDPOINT_SEND_PHOTO "/sendPhoto"
#define ENDPOINT_SEND_AUDIO "/sendAudio"
#define ENDPOINT_SEND_DOCUMENT "/sendDocument"
#define ENDPOINT_SEND_STICKER "/sendSticker"
#define ENDPOINT_SEND_VIDEO "/sendVideo"
#define ENDPOINT_SEND_VOICE "/sendVoice"
#define ENDPOINT_SEND_LOCATION "/sendLocation"
#define ENDPOINT_SEND_CHAT_ACTION "/sendChatAction"
#define ENDPOINT_GET_USER_PROFILE_PHOTOS "/getUserProfilePhotos"
#define ENDPOINT_GET_UPDATES "/getUpdates"
#define ENDPOINT_SET_WEBHOOK "/setWebhook"
#define ENDPOINT_GET_FILE "/getFile"
namespace Telegram {
class HttpParameter {
public:
HttpParameter() {}
HttpParameter(QVariant value, bool isFile = false, QString mimeType = "text/plain", QString filename = "") :
value(value.toByteArray()), isFile(isFile), mimeType(mimeType), filename(filename) {}
QByteArray value;
bool isFile;
QString mimeType;
QString filename;
};
typedef QMap<QString, HttpParameter> ParameterList;
class Networking : public QObject
{
Q_OBJECT
public:
Networking(QString token, QObject *parent = 0);
~Networking();
enum Method { GET, POST, UPLOAD };
QByteArray request(QString endpoint, ParameterList params, Method method);
private:
QNetworkAccessManager *m_nam;
QString m_token;
QUrl buildUrl(QString endpoint);
QByteArray parameterListToString(ParameterList list);
QByteArray generateMultipartBoundary(ParameterList list);
QByteArray generateMultipartFormData(ParameterList list, QByteArray boundary);
QString generateRandomString(int length);
};
}
#endif // NETWORKING_H

402
qttelegrambot.cpp Normal file
View File

@ -0,0 +1,402 @@
#include "qttelegrambot.h"
using namespace Telegram;
Bot::Bot(QString token, bool updates, quint32 updateInterval, quint32 pollingTimeout, QObject *parent) :
QObject(parent),
m_net(new Networking(token)),
m_internalUpdateTimer(new QTimer(this)),
m_updateInterval(updateInterval),
m_pollingTimeout(pollingTimeout)
{
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
if (updates) {
m_internalUpdateTimer->setSingleShot(true);
connect(m_internalUpdateTimer, &QTimer::timeout, this, &Bot::internalGetUpdates);
internalGetUpdates();
}
}
Bot::~Bot()
{
delete m_net;
}
User Bot::getMe()
{
QJsonObject json = this->jsonObjectFromByteArray(
m_net->request(ENDPOINT_GET_ME, ParameterList(), Networking::GET));
User ret;
ret.id = json.value("id").toInt();
ret.firstname = json.value("first_name").toString();
ret.lastname = json.value("last_name").toString();
ret.username = json.value("username").toString();
if (ret.id == 0 || ret.firstname.isEmpty()) {
qCritical("%s", qPrintable("Got invalid user in " + QString(ENDPOINT_GET_ME)));
return User();
}
return ret;
}
bool Bot::sendMessage(QVariant chatId, QString text, bool markdown, bool disableWebPagePreview, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
if (markdown) params.insert("parse_mode", HttpParameter("Markdown"));
if (disableWebPagePreview) params.insert("disable_web_page_preview", HttpParameter(disableWebPagePreview));
return this->_sendPayload(chatId, text, ParameterList(), replyToMessageId, replyMarkup, "text", ENDPOINT_SEND_MESSAGE);
}
bool Bot::forwardMessage(QVariant chatId, quint32 fromChatId, quint32 messageId)
{
if (chatId.type() != QVariant::String && chatId.type() != QVariant::Int) {
qCritical("Please provide a QString or int as chatId");
return false;
}
ParameterList params;
params.insert("chat_id", HttpParameter(chatId));
params.insert("from_chat_id", HttpParameter(fromChatId));
params.insert("message_id", HttpParameter(messageId));
bool success = this->responseOk(m_net->request(ENDPOINT_FORWARD_MESSAGE, params, Networking::POST));
return success;
}
bool Bot::sendPhoto(QVariant chatId, QFile *file, QString caption, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
if (!caption.isEmpty()) params.insert("caption", HttpParameter(caption));
return this->_sendPayload(chatId, file, params, replyToMessageId, replyMarkup, "photo", ENDPOINT_SEND_PHOTO);
}
bool Bot::sendPhoto(QVariant chatId, QString fileId, QString caption, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
if (!caption.isEmpty()) params.insert("caption", HttpParameter(caption));
return this->_sendPayload(chatId, fileId, params, replyToMessageId, replyMarkup, "photo", ENDPOINT_SEND_PHOTO);
}
bool Bot::sendAudio(QVariant chatId, QFile *file, qint64 duration, QString performer, QString title, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
if (duration >= 0) params.insert("duration", HttpParameter(duration));
if (!performer.isEmpty()) params.insert("performer", HttpParameter(performer));
if (!title.isEmpty()) params.insert("title", HttpParameter(title));
return this->_sendPayload(chatId, file, params, replyToMessageId, replyMarkup, "audio", ENDPOINT_SEND_AUDIO);
}
bool Bot::sendAudio(QVariant chatId, QString fileId, qint64 duration, QString performer, QString title, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
if (duration >= 0) params.insert("duration", HttpParameter(duration));
if (!performer.isEmpty()) params.insert("performer", HttpParameter(performer));
if (!title.isEmpty()) params.insert("title", HttpParameter(title));
return this->_sendPayload(chatId, fileId, params, replyToMessageId, replyMarkup, "audio", ENDPOINT_SEND_AUDIO);
}
bool Bot::sendDocument(QVariant chatId, QFile *file, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
return this->_sendPayload(chatId, file, ParameterList(), replyToMessageId, replyMarkup, "document", ENDPOINT_SEND_DOCUMENT);
}
bool Bot::sendDocument(QVariant chatId, QString fileId, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
return this->_sendPayload(chatId, fileId, ParameterList(), replyToMessageId, replyMarkup, "document", ENDPOINT_SEND_DOCUMENT);
}
bool Bot::sendSticker(QVariant chatId, QFile *file, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
return this->_sendPayload(chatId, file, ParameterList(), replyToMessageId, replyMarkup, "sticker", ENDPOINT_SEND_STICKER);
}
bool Bot::sendSticker(QVariant chatId, QString fileId, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
return this->_sendPayload(chatId, fileId, ParameterList(), replyToMessageId, replyMarkup, "sticker", ENDPOINT_SEND_STICKER);
}
bool Bot::sendVideo(QVariant chatId, QFile *file, qint64 duration, QString caption, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
params.insert("duration", HttpParameter(duration));
params.insert("caption", HttpParameter(caption));
return this->_sendPayload(chatId, file, params, replyToMessageId, replyMarkup, "video", ENDPOINT_SEND_VIDEO);
}
bool Bot::sendVideo(QVariant chatId, QString fileId, qint64 duration, QString caption, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
params.insert("duration", HttpParameter(duration));
params.insert("caption", HttpParameter(caption));
return this->_sendPayload(chatId, fileId, params, replyToMessageId, replyMarkup, "video", ENDPOINT_SEND_VIDEO);
}
bool Bot::sendVoice(QVariant chatId, QFile *file, qint64 duration, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
params.insert("duration", HttpParameter(duration));
return this->_sendPayload(chatId, file, params, replyToMessageId, replyMarkup, "voice", ENDPOINT_SEND_VOICE);
}
bool Bot::sendVoice(QVariant chatId, QString fileId, qint64 duration, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
ParameterList params;
params.insert("duration", HttpParameter(duration));
return this->_sendPayload(chatId, fileId, params, replyToMessageId, replyMarkup, "voice", ENDPOINT_SEND_VOICE);
}
bool Bot::sendLocation(QVariant chatId, float latitude, float longitude, qint32 replyToMessageId, const GenericReply &replyMarkup)
{
Q_UNUSED(replyMarkup); // TODO
if (chatId.type() != QVariant::String && chatId.type() != QVariant::Int) {
qCritical("Please provide a QString or int as chatId");
return false;
}
ParameterList params;
params.insert("chat_id", HttpParameter(chatId));
params.insert("latitude", HttpParameter(latitude));
params.insert("longitude", HttpParameter(longitude));
if (replyToMessageId >= 0) params.insert("reply_to_message_id", HttpParameter(replyToMessageId));
bool success = this->responseOk(m_net->request(ENDPOINT_SEND_LOCATION, params, Networking::POST));
return success;
}
bool Bot::sendChatAction(QVariant chatId, Bot::ChatAction action)
{
if (chatId.type() != QVariant::String && chatId.type() != QVariant::Int) {
qCritical("Please provide a QString or int as chatId");
return false;
}
ParameterList params;
params.insert("chat_id", HttpParameter(chatId));
switch (action) {
case Typing:
params.insert("action", HttpParameter("typing"));
break;
case UploadingPhoto:
params.insert("action", HttpParameter("upload_photo"));
break;
case RecordingVideo:
params.insert("action", HttpParameter("record_video"));
break;
case UploadingVideo:
params.insert("action", HttpParameter("upload_video"));
break;
case RecordingAudio:
params.insert("action", HttpParameter("record_audio"));
break;
case UploadingAudio:
params.insert("action", HttpParameter("upload_audio"));
break;
case UploadingDocument:
params.insert("action", HttpParameter("upload_document"));
break;
case FindingLocation:
params.insert("action", HttpParameter("find_location"));
break;
default:
return false;
}
bool success = this->responseOk(m_net->request(ENDPOINT_SEND_CHAT_ACTION, params, Networking::POST));
return success;
}
UserProfilePhotos Bot::getUserProfilePhotos(quint32 userId, qint16 offset, qint8 limit)
{
ParameterList params;
params.insert("user_id", HttpParameter(userId));
if (offset > -1) params.insert("offset", HttpParameter(offset));
if (limit > -1) params.insert("limit", HttpParameter(limit));
QJsonObject json = this->jsonObjectFromByteArray(m_net->request(ENDPOINT_GET_USER_PROFILE_PHOTOS, params, Networking::GET));
UserProfilePhotos ret;
QList<PhotoSize> photo;
foreach (QJsonValue val, json.value("photos").toArray()) {
photo = QList<PhotoSize>();
foreach (QJsonValue p, val.toArray()) {
PhotoSize ps;
ps.fileId = p.toObject().value("file_id").toString();
ps.width = p.toObject().value("width").toInt();
ps.height = p.toObject().value("height").toInt();
if (p.toObject().contains("file_size")) ps.fileSize = p.toObject().value("file_size").toInt();
photo.append(ps);
}
ret.append(photo);
}
return ret;
}
QList<Update> Bot::getUpdates(quint32 timeout, quint32 limit, quint32 offset)
{
ParameterList params;
params.insert("offset", HttpParameter(offset));
params.insert("limit", HttpParameter(limit));
params.insert("timeout", HttpParameter(timeout));
QJsonArray json = this->jsonArrayFromByteArray(m_net->request(ENDPOINT_GET_UPDATES, params, Networking::GET));
QList<Update> ret = QList<Update>();
foreach (QJsonValue value, json) {
ret.append(Update(value.toObject()));
}
return ret;
}
bool Bot::setWebhook(QString url, QFile *certificate)
{
ParameterList params;
params.insert("url", HttpParameter(url));
QMimeDatabase db;
bool openedFile = false;
if (!certificate->isOpen()) {
if (!certificate->open(QFile::ReadOnly)) {
qCritical("Could not open file %s [%s]", qPrintable(certificate->fileName()), qPrintable(certificate->errorString()));
return false;
}
openedFile = true;
}
QByteArray data = certificate->readAll();
if (openedFile) certificate->close();
params.insert("certificate", HttpParameter(data, true, db.mimeTypeForData(data).name(), certificate->fileName()));
bool success = this->responseOk(m_net->request(ENDPOINT_SET_WEBHOOK, params, Networking::UPLOAD));
return success;
}
File Bot::getFile(QString fileId)
{
ParameterList params;
params.insert("file_id", HttpParameter(fileId));
QJsonObject json = this->jsonObjectFromByteArray(m_net->request(ENDPOINT_GET_FILE, params, Networking::GET));
return File(json.value("file_id").toString(), json.value("file_size").toInt(-1), json.value("file_path").toString());
}
bool Bot::_sendPayload(QVariant chatId, QFile *filePayload, ParameterList params, qint32 replyToMessageId, const GenericReply &replyMarkup, QString payloadField, QString endpoint)
{
if (chatId.type() != QVariant::String && chatId.type() != QVariant::Int) {
qCritical("Please provide a QString or int as chatId");
return false;
}
params.insert("chat_id", HttpParameter(chatId));
QMimeDatabase db;
bool openedFile = false;
if (!filePayload->isOpen()) {
if (!filePayload->open(QFile::ReadOnly)) {
qCritical("Could not open file %s [%s]", qPrintable(filePayload->fileName()), qPrintable(filePayload->errorString()));
return false;
}
openedFile = true;
}
QByteArray data = filePayload->readAll();
if (openedFile) filePayload->close();
params.insert(payloadField, HttpParameter(data, true, db.mimeTypeForData(data).name(), filePayload->fileName()));
if (replyToMessageId >= 0) params.insert("reply_to_message_id", HttpParameter(replyToMessageId));
if (replyMarkup.isValid()) params.insert("reply_markup", HttpParameter(replyMarkup.serialize()));
bool success = this->responseOk(m_net->request(endpoint, params, Networking::UPLOAD));
return success;
}
bool Bot::_sendPayload(QVariant chatId, QString textPayload, ParameterList params, qint32 replyToMessageId, const GenericReply &replyMarkup, QString payloadField, QString endpoint)
{
if (chatId.type() != QVariant::String && chatId.type() != QVariant::Int) {
qCritical("Please provide a QString or int as chatId");
return false;
}
params.insert("chat_id", HttpParameter(chatId));
params.insert(payloadField, HttpParameter(textPayload));
if (replyToMessageId >= 0) params.insert("reply_to_message_id", HttpParameter(replyToMessageId));
if (replyMarkup.isValid()) params.insert("reply_markup", HttpParameter(replyMarkup.serialize()));
bool success = this->responseOk(m_net->request(endpoint, params, Networking::POST));
return success;
}
QJsonObject Bot::jsonObjectFromByteArray(QByteArray json)
{
QJsonDocument d = QJsonDocument::fromJson(json);
QJsonObject obj = d.object();
if (obj.isEmpty()) {
qCritical("Got an empty response object");
return obj;
}
if (obj.value("ok").toBool() != true) {
qWarning("Result is not Ok");
return obj;
}
return obj.value("result").toObject();
}
QJsonArray Bot::jsonArrayFromByteArray(QByteArray json)
{
QJsonDocument d = QJsonDocument::fromJson(json);
QJsonObject obj = d.object();
if (obj.isEmpty()) {
qCritical("Got an empty response object");
return QJsonArray();
}
if (obj.value("ok").toBool() != true) {
qWarning("Result is not Ok");
return QJsonArray();
}
return obj.value("result").toArray();
}
bool Bot::responseOk(QByteArray json)
{
QJsonDocument d = QJsonDocument::fromJson(json);
QJsonObject obj = d.object();
return (!obj.isEmpty() && obj.value("ok").toBool() == true);
}
void Bot::internalGetUpdates()
{
QList<Update> updates = getUpdates(m_pollingTimeout, 50, m_updateOffset);
foreach (Update u, updates) {
// change updateOffset to u.id to avoid duplicate updates
m_updateOffset = (u.id >= m_updateOffset ? u.id + 1 : m_updateOffset);
emit message(u.message);
}
m_internalUpdateTimer->start(m_updateInterval);
}

309
qttelegrambot.h Normal file
View File

@ -0,0 +1,309 @@
#ifndef QTTELEGRAMBOT_H
#define QTTELEGRAMBOT_H
#include <QObject>
#include <QLoggingCategory>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QHttpPart>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QMimeDatabase>
#include <QTimer>
#include "networking.h"
#include "types/chat.h"
#include "types/update.h"
#include "types/user.h"
#include "types/file.h"
#include "types/message.h"
#include "types/reply/genericreply.h"
#include "types/reply/replykeyboardmarkup.h"
#include "types/reply/replykeyboardhide.h"
#include "types/reply/forcereply.h"
namespace Telegram {
typedef QList<QList<PhotoSize> > UserProfilePhotos;
class Bot : public QObject
{
Q_OBJECT
public:
/**
* Bot constructor
* @param token
* @param updates - enable automatic update polling
* @param updateInterval - interval between update polls in msec
* @param pollingTimeout - timeout in sec
* @param parent
*/
explicit Bot(QString token, bool updates = false, quint32 updateInterval = 1000, quint32 pollingTimeout = 0, QObject *parent = 0);
~Bot();
enum ChatAction { Typing, UploadingPhoto, RecordingVideo, UploadingVideo, RecordingAudio, UploadingAudio, UploadingDocument, FindingLocation };
/**
* Returns basic information about the bot in form of a `User` object.
* @return User Object
* @see https://core.telegram.org/bots/api#getme
*/
User getMe();
/**
* Send text message.
* @param chatId - Unique identifier for the message recipient or @channelname
* @param text - Text of the message to be sent
* @param markdown - Use markdown in message display (only Telegram for Android supports this)
* @param disableWebPagePreview - Disables link previews for links in this message
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendmessage
*/
bool sendMessage(QVariant chatId, QString text, bool markdown = false, bool disableWebPagePreview = false, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Forward messages of any kind.
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fromChatId - Unique identifier for the chat where the original message was sent
* @param messageId - Unique message identifier
* @return success
* @see https://core.telegram.org/bots/api#forwardmessage
*/
bool forwardMessage(QVariant chatId, quint32 fromChatId, quint32 messageId);
/**
* Send a photo
* @param chatId - Unique identifier for the message recipient or @channelname
* @param file - A file to send
* @param caption - Photo caption
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendphoto
*/
bool sendPhoto(QVariant chatId, QFile *file, QString caption = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a photo
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param caption - Photo caption
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendphoto
*/
bool sendPhoto(QVariant chatId, QString fileId, QString caption = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send audio
* @param chatId - Unique identifier for the message recipient or @channelname
* @param file - A file to send
* @param duration - Duration of the audio in seconds
* @param performer - Performer of the audio
* @param title - Track name of the audio
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendaudio
*/
bool sendAudio(QVariant chatId, QFile *file, qint64 duration = -1, QString performer = QString(), QString title = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send audio
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent audio
* @param duration - Duration of the audio in seconds
* @param performer - Performer of the audio
* @param title - Track name of the audio
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendaudio
*/
bool sendAudio(QVariant chatId, QString fileId, qint64 duration = -1, QString performer = QString(), QString title = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a document
* @param chatId - Unique identifier for the message recipient or @channelname
* @param file - A file to send
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#senddocument
*/
bool sendDocument(QVariant chatId, QFile *file, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a document
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#senddocument
*/
bool sendDocument(QVariant chatId, QString fileId, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a sticker
* @param chatId - Unique identifier for the message recipient or @channelname
* @param file - A file to send
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendsticker
*/
bool sendSticker(QVariant chatId, QFile *file, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a sticker
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendsticker
*/
bool sendSticker(QVariant chatId, QString fileId, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a video
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param duration - Duration of sent video in seconds
* @param caption - Video caption
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendvideo
*/
bool sendVideo(QVariant chatId, QFile *file, qint64 duration = -1, QString caption = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a video
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param duration - Duration of sent video in seconds
* @param caption - Video caption
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendvideo
*/
bool sendVideo(QVariant chatId, QString fileId, qint64 duration = -1, QString caption = QString(), qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a voice
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param duration - Duration of sent audio in seconds
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendvoice
*/
bool sendVoice(QVariant chatId, QFile *file, qint64 duration = -1, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a voice
* @param chatId - Unique identifier for the message recipient or @channelname
* @param fileId - Telegram file_id of already sent photo
* @param duration - Duration of sent audio in seconds
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendvoice
*/
bool sendVoice(QVariant chatId, QString fileId, qint64 duration = -1, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Send a location
* @param chatId - Unique identifier for the message recipient or @channelname
* @param latitude - latitude of the location
* @param longitude - longitude of the location
* @param replyToMessageId - If the message is a reply, ID of the original message
* @param replyMarkup - Additional interface options
* @return success
* @see https://core.telegram.org/bots/api#sendlocation
*/
bool sendLocation(QVariant chatId, float latitude, float longitude, qint32 replyToMessageId = -1, const GenericReply &replyMarkup = GenericReply());
/**
* Use this method when you need to tell the user that something is happening on the bot's side.
* @param chatId - Unique identifier for the message recipient or @channelname
* @param action - Type of action to broadcast
* @return success
* @see https://core.telegram.org/bots/api#sendchataction
*/
bool sendChatAction(QVariant chatId, ChatAction action);
/**
* Use this method to get a list of profile pictures for a user.
* @param userId - Unique identifier of the target user
* @param offset - Sequential number of the first photo to be returned.
* @param limit - Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100.
* @return UserProfilePhotos list
* @see Use this method to get a list of profile pictures for a user.
*/
UserProfilePhotos getUserProfilePhotos(quint32 userId, qint16 offset = -1, qint8 limit = -1);
/**
* Use this method to receive incoming updates using long polling
* @param timeout - Timeout in seconds for long polling.
* @param limit - Limits the number of updates to be retrieved.
* @param offset - Identifier of the first update to be returned.
* @return List of Update objects
* @see https://core.telegram.org/bots/api#getupdates
*/
QList<Update> getUpdates(quint32 timeout, quint32 limit, quint32 offset);
/**
* Use this method to specify a url and receive incoming updates via an outgoing webhook.
* @param url - HTTPS url to send updates to. Use an empty string to remove webhook integration
* @param certificate - Upload your public key certificate so that the root certificate in use can be checked.
* @return success
* @see https://core.telegram.org/bots/api#setwebhook
*/
bool setWebhook(QString url, QFile *certificate);
/**
* Use this method to get basic info about a file and prepare it for downloading.
* @param fileId - File identifier to get info about
* @return File object
* @see https://core.telegram.org/bots/api#getfile
*/
File getFile(QString fileId);
private:
Networking *m_net;
bool _sendPayload(QVariant chatId, QFile *filePayload, ParameterList params, qint32 replyToMessageId, const GenericReply &replyMarkup, QString payloadField, QString endpoint);
bool _sendPayload(QVariant chatId, QString textPayload, ParameterList params, qint32 replyToMessageId, const GenericReply &replyMarkup, QString payloadField, QString endpoint);
QJsonObject jsonObjectFromByteArray(QByteArray json);
QJsonArray jsonArrayFromByteArray(QByteArray json);
bool responseOk(QByteArray json);
void internalGetUpdates();
QTimer *m_internalUpdateTimer;
quint32 m_updateInterval;
quint32 m_updateOffset;
quint32 m_pollingTimeout;
signals:
void message(Message message);
};
}
#endif // QTTELEGRAMBOT_H

13
types/audio.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "audio.h"
using namespace Telegram;
Audio::Audio(QJsonObject audio)
{
fileId = audio.value("file_id").toString();
duration = audio.value("duration").toInt();
performer = audio.value("performer").toString();
title = audio.value("title").toString();
mimeType = audio.value("mime_type").toString();
fileSize = audio.value("file_size").toInt();
}

39
types/audio.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef AUDIO_H
#define AUDIO_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class Audio
{
public:
Audio() {}
Audio(QJsonObject audio);
QString fileId;
quint64 duration;
QString performer;
QString title;
QString mimeType;
quint64 fileSize;
};
inline QDebug operator<< (QDebug dbg, const Audio &audio)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Audio(fileId=%1; duration=%2; performer=%3; title=%4; mimeType=%5; fileSize=%6)")
.arg(audio.fileId)
.arg(audio.duration)
.arg(audio.performer)
.arg(audio.title)
.arg(audio.mimeType)
.arg(audio.fileSize));
return dbg.maybeSpace();
}
}
#endif // AUDIO_H

15
types/chat.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "chat.h"
using namespace Telegram;
Chat::Chat(QJsonObject chat)
{
id = chat.value("id").toInt();
QString chatType = chat.value("type").toString();
if (chatType == "private") type = Private;
else if (chatType == "group") type = Group;
else if (chatType == "channel") type = Channel;
username = chat.value("username").toString();
firstname = chat.value("first_name").toString();
lastname = chat.value("last_name").toString();
}

43
types/chat.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef CHAT_H
#define CHAT_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class Chat
{
public:
Chat() {}
Chat(QJsonObject chat);
enum ChatType {
Private, Group, Channel
};
quint32 id;
ChatType type;
QString title;
QString username;
QString firstname;
QString lastname;
};
inline QDebug operator<< (QDebug dbg, const Chat &chat)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Chat(id=%1; type=%2; title=%3; username=%4; firstname=%5; lastname=%6)")
.arg(chat.id)
.arg(chat.type)
.arg(chat.title)
.arg(chat.username)
.arg(chat.firstname)
.arg(chat.lastname));
return dbg.maybeSpace();
}
}
#endif // CHAT_H

11
types/contact.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "contact.h"
using namespace Telegram;
Contact::Contact(QJsonObject contact)
{
phoneNumber = contact.value("phone_number").toString();
firstname = contact.value("first_name").toString();
lastname = contact.value("last_name").toString();
userId = contact.value("user_id").toInt();
}

35
types/contact.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef CONTACT_H
#define CONTACT_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class Contact
{
public:
Contact() {}
Contact(QJsonObject contact);
QString phoneNumber;
QString firstname;
QString lastname;
quint32 userId;
};
inline QDebug operator<< (QDebug dbg, const Contact &contact)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Contact(phoneNumber=%1; firstname=%2; lastname=%3; userId=%4)")
.arg(contact.phoneNumber)
.arg(contact.firstname)
.arg(contact.lastname)
.arg(contact.userId));
return dbg.maybeSpace();
}
}
#endif // CONTACT_H

12
types/document.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "document.h"
using namespace Telegram;
Document::Document(QJsonObject document)
{
fileId = document.value("file_id").toString();
thumb = PhotoSize(document.value("thumb").toObject());
fileName = document.value("file_name").toString();
mimeType = document.value("mime_type").toString();
fileSize = document.value("file_size").toInt();
}

38
types/document.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef DOCUMENT_H
#define DOCUMENT_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
#include "photosize.h"
namespace Telegram {
class Document
{
public:
Document() {}
Document(QJsonObject document);
QString fileId;
PhotoSize thumb;
QString fileName;
QString mimeType;
quint64 fileSize;
};
inline QDebug operator<< (QDebug dbg, const Document &document)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Document(fileId=%1; thumb=%2; fileName=%3; mimeType=%4; fileSize=%5)")
.arg(document.fileId)
.arg("PhotoSize(" + document.thumb.fileId + ")")
.arg(document.fileName)
.arg(document.mimeType)
.arg(document.fileSize));
return dbg.maybeSpace();
}
}
#endif // DOCUMENT_H

32
types/file.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef FILE_H
#define FILE_H
#include <QDebug>
#include <QString>
namespace Telegram {
class File
{
public:
File(QString fileId, qint64 fileSize = -1, QString filePath = QString()) :
fileId(fileId), fileSize(fileSize), filePath(filePath) {}
QString fileId;
qint64 fileSize;
QString filePath;
};
inline QDebug operator<< (QDebug dbg, const File &file)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::File(fileId=%1; fileSize=%2; filePath=%3)")
.arg(file.fileId)
.arg(file.fileSize)
.arg(file.filePath));
return dbg.maybeSpace();
}
}
#endif // FILE_H

9
types/location.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "location.h"
using namespace Telegram;
Location::Location(QJsonObject location)
{
longitude = location.value("longitude").toDouble();
latitude = location.value("latitude").toDouble();
}

31
types/location.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef LOCATION_H
#define LOCATION_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class Location
{
public:
Location() {}
Location(QJsonObject location);
float longitude;
float latitude;
};
inline QDebug operator<< (QDebug dbg, const Location &location)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Location(longitude=%1; latitude=%2)")
.arg(location.longitude)
.arg(location.latitude));
return dbg.maybeSpace();
}
}
#endif // LOCATION_H

116
types/message.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "message.h"
using namespace Telegram;
Message::Message(QJsonObject message)
{
id = message.value("message_id").toInt();
date = QDateTime::fromMSecsSinceEpoch(message.value("date").toInt());
chat = Chat(message.value("chat").toObject());
/**
x audio Audio Optional. Message is an audio file, information about the file
document Document Optional. Message is a general file, information about the file
photo Array of PhotoSize Optional. Message is a photo, available sizes of the photo
sticker Sticker Optional. Message is a sticker, information about the sticker
video Video Optional. Message is a video, information about the video
voice Voice Optional. Message is a voice message, information about the file
caption String Optional. Caption for the photo or video
contact Contact Optional. Message is a shared contact, information about the contact
location Location Optional. Message is a shared location, information about the location
new_chat_participant User Optional. A new member was added to the group, information about them (this member may be bot itself)
left_chat_participant User Optional. A member was removed from the group, information about them (this member may be bot itself)
new_chat_photo Array of PhotoSize Optional. A chat photo was change to this value
delete_chat_photo True Optional. Informs that the chat photo was deleted
group_chat_created True Optional. Informs that the group has been created
*/
if (message.contains("from")) {
from = User(message.value("from").toObject());
}
if (message.contains("forward_from")) {
forwardFrom = User(message.value("forward_from").toObject());
}
if (message.contains("forward_date")) {
forwardDate = QDateTime::fromMSecsSinceEpoch(message.value("forward_date").toInt());
}
if (message.contains("reply_to_message")) {
replyToMessage = new Message(message.value("reply_to_message").toObject());
}
// Parse payload
QJsonObject obj;
if (message.contains("text")) {
string = message.value("text").toString();
type = Message::TextType;
}
if (message.contains("audio")) {
obj = message.value("audio").toObject();
audio = Audio(obj);
type = Message::AudioType;
}
if (message.contains("document")) {
obj = message.value("document").toObject();
document = Document(obj);
type = Message::DocumentType;
}
if (message.contains("photo")) {
foreach (QJsonValue val, message.value("photo").toArray()) {
photo.append(PhotoSize(val.toObject()));
}
type = Message::PhotoType;
}
if (message.contains("sticker")) {
obj = message.value("sticker").toObject();
sticker = Sticker(obj);
type = Message::StickerType;
}
if (message.contains("video")) {
obj = message.value("video").toObject();
video = Video(obj);
type = Message::VideoType;
}
if (message.contains("voice")) {
obj = message.value("voice").toObject();
voice = Voice(obj);
type = Message::VoiceType;
}
if (message.contains("contact")) {
obj = message.value("contact").toObject();
contact = Contact(obj);
type = Message::ContactType;
}
if (message.contains("location")) {
obj = message.value("location").toObject();
location = Location(obj);
type = Message::LocationType;
}
if (message.contains("new_chat_participant")) {
obj = message.value("new_chat_participant").toObject();
user = User(obj);
type = Message::NewChatParticipantType;
}
if (message.contains("left_chat_participant")) {
obj = message.value("left_chat_participant").toObject();
user = User(obj);
type = Message::LeftChatParticipantType;
}
if (message.contains("new_chat_title")) {
string = message.value("new_chat_title").toString();
type = Message::NewChatTitleType;
}
if (message.contains("new_chat_photo")) {
foreach (QJsonValue val, message.value("new_chat_photo").toArray()) {
photo.append(PhotoSize(val.toObject()));
}
type = Message::NewChatPhotoType;
}
if (message.contains("delete_chat_photo")) {
boolean = true;
type = Message::DeleteChatPhotoType;
}
if (message.contains("group_chat_created")) {
boolean = true;
type = Message::GroupChatCreatedType;
}
}

79
types/message.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <QDebug>
#include <QString>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonArray>
#include "audio.h"
#include "document.h"
#include "photosize.h"
#include "sticker.h"
#include "video.h"
#include "voice.h"
#include "contact.h"
#include "location.h"
#include "chat.h"
#include "user.h"
namespace Telegram {
class Message
{
public:
Message() {}
Message(QJsonObject message);
/**
* @brief Telegram message events
*/
enum MessageType {
TextType, AudioType, DocumentType, PhotoType, StickerType, VideoType, VoiceType, ContactType,
LocationType, NewChatParticipantType, LeftChatParticipantType, NewChatTitleType,
NewChatPhotoType, DeleteChatPhotoType, GroupChatCreatedType
};
// required
quint32 id;
QDateTime date;
Chat chat;
// optional
User from;
User forwardFrom;
QDateTime forwardDate;
Message *replyToMessage;
MessageType type;
// payload
QString string;
User user;
Audio audio;
Document document;
QList<PhotoSize> photo;
Sticker sticker;
Video video;
Voice voice;
Contact contact;
Location location;
bool boolean;
};
inline QDebug operator<< (QDebug dbg, const Message &message)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Message(id=%1; date=%2; chat=%3; type=%4)")
.arg(message.id)
.arg(message.date.toString("dd.MM.yyyy hh:mm:ss"))
.arg("Chat(" + QString::number(message.chat.id) + ")")
.arg(message.type));
return dbg.maybeSpace();
}
}
#endif // MESSAGE_H

11
types/photosize.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "photosize.h"
using namespace Telegram;
PhotoSize::PhotoSize(QJsonObject photoSize)
{
fileId = photoSize.value("file_id").toString();
width = photoSize.value("width").toInt();
height = photoSize.value("height").toInt();
fileSize = photoSize.value("file_size").toInt();
}

35
types/photosize.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef PHOTOSIZE_H
#define PHOTOSIZE_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class PhotoSize
{
public:
PhotoSize() {}
PhotoSize(QJsonObject photoSize);
QString fileId;
quint16 width;
quint16 height;
quint64 fileSize;
};
inline QDebug operator<< (QDebug dbg, const PhotoSize &photoSize)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::PhotoSize(fileId=%1; width=%2; height=%3; fileSize=%4)")
.arg(photoSize.fileId)
.arg(photoSize.width)
.arg(photoSize.height)
.arg(photoSize.fileSize));
return dbg.maybeSpace();
}
}
#endif // PHOTOSIZE_H

31
types/reply/forcereply.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef FORCEREPLY
#define FORCEREPLY
#include "genericreply.h"
namespace Telegram {
class ForceReply : public GenericReply
{
public:
ForceReply(bool selective = false)
: GenericReply(selective),
forceReply(true) {}
/**
* Shows reply interface to the user, as if they manually selected the bots message and tapped Reply'
*/
const bool forceReply;
virtual QString serialize() const {
QJsonObject o = QJsonObject();
o.insert("force_reply", forceReply);
o.insert("selective", selective);
return serializeJson(o);
}
};
}
#endif // FORCEREPLY

View File

@ -0,0 +1,44 @@
#ifndef GENERICREPLY_H
#define GENERICREPLY_H
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
namespace Telegram {
class GenericReply
{
public:
GenericReply() : valid(false) {}
GenericReply(bool selective) : selective(selective), valid(true) {}
/**
* Optional. Use this parameter if you want to show the keyboard to specific users only.
* Targets: 1) users that are @mentioned in the text of the Message object;
* 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
*/
bool selective;
virtual QString serialize() const {
return QString();
}
bool isValid() const {
return valid;
}
private:
bool valid;
protected:
QByteArray serializeJson(QJsonObject o) const {
QJsonDocument d = QJsonDocument(o);
return d.toJson(QJsonDocument::Compact);
}
};
}
#endif // GENERICREPLY_H

View File

@ -0,0 +1,30 @@
#ifndef REPLYKEYBOARDHIDE
#define REPLYKEYBOARDHIDE
#include "genericreply.h"
namespace Telegram {
class ReplyKeyboardHide : public GenericReply
{
public:
ReplyKeyboardHide(bool selective = false)
: GenericReply(selective),
hideKeyboard(true) {}
/**
* Requests clients to hide the custom keyboard
*/
const bool hideKeyboard;
virtual QString serialize() const {
QJsonObject o = QJsonObject();
o.insert("hide_keyboard", hideKeyboard);
o.insert("selective", selective);
return serializeJson(o);
}
};
}
#endif // REPLYKEYBOARDHIDE

View File

@ -0,0 +1,54 @@
#ifndef REPLYKEYBOARDMARKUP_H
#define REPLYKEYBOARDMARKUP_H
#include <QString>
#include "genericreply.h"
namespace Telegram {
typedef QList<QStringList> KeyboardMarkup;
class ReplyKeyboardMarkup : public GenericReply
{
public:
ReplyKeyboardMarkup(KeyboardMarkup keyboard, bool resizeKeyboard = false, bool oneTimeKeyboard = false, bool selective = false)
: GenericReply(selective),
keyboard(keyboard),
resizeKeyboard(resizeKeyboard),
oneTimeKeyboard(oneTimeKeyboard) {}
/**
* Array of button rows, each represented by an Array of Strings
*/
KeyboardMarkup keyboard;
/**
* Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons).
* Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
*/
bool resizeKeyboard;
/**
* Optional. Requests clients to hide the keyboard as soon as it's been used.
* Defaults to false.
*/
bool oneTimeKeyboard;
virtual QString serialize() const {
QJsonObject o = QJsonObject();
QJsonArray keyboardMarkup = QJsonArray();
foreach (QStringList list, keyboard) {
keyboardMarkup.append(QJsonArray::fromStringList(list));
}
o.insert("keyboard", keyboardMarkup);
o.insert("resize_keyboard", resizeKeyboard);
o.insert("one_time_keyboard", oneTimeKeyboard);
o.insert("selective", selective);
return serializeJson(o);
}
};
}
#endif // REPLYKEYBOARDMARKUP_H

12
types/sticker.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "sticker.h"
using namespace Telegram;
Sticker::Sticker(QJsonObject sticker)
{
fileId = sticker.value("file_id").toString();
width = sticker.value("width").toInt();
height = sticker.value("height").toInt();
thumb = PhotoSize(sticker.value("thumb").toObject());
fileSize = sticker.value("file_size").toInt();
}

38
types/sticker.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef STICKER_H
#define STICKER_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
#include "photosize.h"
namespace Telegram {
class Sticker
{
public:
Sticker() {}
Sticker(QJsonObject sticker);
QString fileId;
quint16 width;
quint16 height;
PhotoSize thumb;
quint64 fileSize;
};
inline QDebug operator<< (QDebug dbg, const Sticker &sticker)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Sticker(fileId=%1; width=%2; height=%3; thumb=%4; fileSize=%5)")
.arg(sticker.fileId)
.arg(sticker.width)
.arg(sticker.height)
.arg("PhotoSize(" + sticker.thumb.fileId + ")")
.arg(sticker.fileSize));
return dbg.maybeSpace();
}
}
#endif // STICKER_H

9
types/update.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "update.h"
using namespace Telegram;
Update::Update(QJsonObject update)
{
id = update.value("update_id").toInt();
message = Message(update.value("message").toObject());
}

31
types/update.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef UPDATE_H
#define UPDATE_H
#include <QDebug>
#include <QJsonObject>
#include "message.h"
namespace Telegram {
class Update
{
public:
Update() {}
Update(QJsonObject update);
quint32 id;
Message message;
};
inline QDebug operator<< (QDebug dbg, const Update &update)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Update(id=%1; message=%2)")
.arg(update.id)
.arg("Message(" + QString::number(update.message.id) + ")"));
return dbg.maybeSpace();
}
}
#endif // UPDATE_H

11
types/user.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "user.h"
using namespace Telegram;
Telegram::User::User(QJsonObject user)
{
id = user.value("id").toInt();
firstname = user.value("first_name").toString();
lastname = user.value("last_name").toString();
username = user.value("username").toString();
}

35
types/user.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef USER_H
#define USER_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class User
{
public:
User() : id(0), firstname(QString()), lastname(QString()), username(QString()) {}
User(QJsonObject user);
quint32 id;
QString firstname;
QString lastname;
QString username;
};
inline QDebug operator<< (QDebug dbg, const User &user)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::User(id=%1; firstname=%2; lastname=%3; username=%4)")
.arg(user.id)
.arg(user.firstname)
.arg(user.lastname)
.arg(user.username));
return dbg.maybeSpace();
}
}
#endif // USER_H

14
types/video.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "video.h"
using namespace Telegram;
Video::Video(QJsonObject video)
{
fileId = video.value("file_id").toString();
width = video.value("width").toInt();
height = video.value("height").toInt();
duration = video.value("duration").toInt();
thumb = PhotoSize(video.value("thumb").toObject());
mimeType = video.value("mime_type").toString();
fileSize = video.value("file_size").toInt();
}

41
types/video.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef VIDEO_H
#define VIDEO_H
#include <QString>
#include <QJsonObject>
#include "photosize.h"
namespace Telegram {
class Video
{
public:
Video() {}
Video(QJsonObject video);
QString fileId;
quint16 width;
quint16 height;
quint64 duration;
PhotoSize thumb;
QString mimeType;
QString fileSize;
};
inline QDebug operator<< (QDebug dbg, const Video &video)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Video(fileId=%1; width=%2; height=%3; duration=%4; thumb=%5; mimeType=%6; fileSize=%7)")
.arg(video.fileId)
.arg(video.width)
.arg(video.height)
.arg(video.duration)
.arg("PhotoSize(" + video.thumb.fileId + ")")
.arg(video.mimeType)
.arg(video.fileSize));
return dbg.maybeSpace();
}
}
#endif // VIDEO_H

11
types/voice.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "voice.h"
using namespace Telegram;
Voice::Voice(QJsonObject voice)
{
fileId = voice.value("file_id").toString();
duration = voice.value("duration").toInt();
mimeType = voice.value("mime_type").toString();
fileSize = voice.value("file_size").toInt();
}

35
types/voice.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef VOICE_H
#define VOICE_H
#include <QDebug>
#include <QString>
#include <QJsonObject>
namespace Telegram {
class Voice
{
public:
Voice() {}
Voice(QJsonObject voice);
QString fileId;
quint64 duration;
QString mimeType;
quint64 fileSize;
};
inline QDebug operator<< (QDebug dbg, const Voice &voice)
{
dbg.nospace() << qUtf8Printable(QString("Telegram::Voice(fileId=%1; duration=%2; mimeType=%3; fileSize=%4)")
.arg(voice.fileId)
.arg(voice.duration)
.arg(voice.mimeType)
.arg(voice.fileSize));
return dbg.maybeSpace();
}
}
#endif // VOICE_H