Telegram Bot APi server initial commit.

This commit is contained in:
levlam 2020-11-03 19:34:10 +03:00
commit 74559bab15
26 changed files with 13931 additions and 0 deletions

.clang-format Normal file
View File

@ -0,0 +1,122 @@
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None # All
AllowShortIfStatementsOnASingleLine: Never # WithoutElse
AllowShortLambdasOnASingleLine: Inline # All
AllowShortLoopsOnASingleLine: false # true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: true # false
BreakInheritanceList: BeforeComma # BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true # false
BreakConstructorInitializers: BeforeComma # BeforeColon
# BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120 # 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
IncludeBlocks: Preserve
#IndentCaseBlocks: false
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
# InsertTrailingCommas: None
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# ObjCBinPackProtocolList: Never
# ObjCBlockIndentWidth: 2
# ObjCBreakBeforeNestedBlockParam: true
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false # true
SortIncludes: false # disabled, because we need case insensitive sort
SortUsingDeclarations: false # true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 100 # 8
UseCRLF: false
UseTab: Never

.gitattributes vendored Normal file
View File

@ -0,0 +1,7 @@
* text=auto
*.cpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.h text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.md text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.txt text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.html text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent

.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@

.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "td"]
path = td
url =

CMakeLists.txt Normal file
View File

@ -0,0 +1,80 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TelegramBotApi VERSION 5.0 LANGUAGES CXX)
add_subdirectory(td EXCLUDE_FROM_ALL)
if (POLICY CMP0054)
# do not expand quoted arguments
cmake_policy(SET CMP0054 NEW)
if (POLICY CMP0060)
# link libraries by full path
cmake_policy(SET CMP0060 NEW)
find_package(Threads REQUIRED)
check_cxx_compiler_flag(-no-pie CXX_NO_PIE_FLAG)
elseif (APPLE)
add_executable(telegram-bot-api ${TG_HTTP_CLIENT_SOURCE})
target_include_directories(telegram-bot-api PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(telegram-bot-api PRIVATE memprof tdactor tdcore tddb tdnet tdutils)

LICENSE_1_0.txt Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

90 Normal file
View File

@ -0,0 +1,90 @@
# Telegram Bot API
The Telegram Bot API provides an HTTP API for creating [Telegram Bots](
If you've got any questions about bots or would like to report an issue with your bot, kindly contact us at [@BotSupport]( in Telegram.
Please note that only global Bot API issues that affect all bots are suitable for this repository.
## Table of Contents
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Usage](#usage)
- [Documentation](#documentation)
- [Moving a bot to a local server](#switching)
- [Moving a bot from one local server to another](#moving)
- [License](#license)
<a name="installation"></a>
## Installation
The simplest way to build and install `Telegram Bot API server` is to use our [Telegram Bot API server build instructions generator](
If you do that, you'll only need to choose the target operating system to receive the complete build instructions.
In general, you need to install all `Telegram Bot API server` [dependencies](#dependencies) and compile the source code using CMake:
git clone --recursive
cd telegram-bot-api
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --target install
<a name="dependencies"></a>
## Dependencies
To build and run `Telegram Bot API server` you will need:
* OpenSSL
* zlib
* C++14 compatible compiler (e.g., Clang 3.4+, GCC 4.9+, MSVC 19.0+ (Visual Studio 2015+), Intel C++ Compiler 17+) (build only)
* gperf (build only)
* CMake (3.0.2+, build only)
<a name="usage"></a>
## Usage
Use `telegram-bot-api --help` to receive the list of all available options of the Telegram Bot API server.
The only mandatory options are `--api-id` and `--api-hash`. You must obtain your own `api_id` and `api_hash`
as described in and specify them using the `--api-id` and `--api-hash` options
or the `TELEGRAM_API_ID` and `TELEGRAM_API_HASH` environment variables.
To enable Bot API features not available at ``, such as downloading files without a size limit, specify the option `--local`.
The Telegram Bot API server accepts only HTTP requests, so a TLS termination proxy needs to be used to handle remote HTTPS requests.
By default the Telegram Bot API server is launched on the port 8081, which can be changed using the option `--http-port`.
<a name="documentation"></a>
## Documentation
See [Bots: An introduction for developers]( for a brief description of Telegram Bots and their features.
See the [Telegram Bot API documentation]( for a description of the Bot API interface and a complete list of available classes, methods and updates.
See the [Telegram Bot API server build instructions generator]( for detailed instructions on how to build the Telegram Bot API server.
Subscribe to [@BotNews]( to be the first to know about the latest updates and join the discussion in [@BotTalk](
<a name="switching"></a>
## Moving a bot to a local server
To guarantee that your bot will receive all updates, you must deregister it with the `` server by calling the method [logOut](
After the bot is logged out, you can replace the address to which the bot sends requests with the address of your local server and use it in the usual way.
If the server is launched in `--local` mode, make sure that the bot can correctly handle absolute file paths in response to `getFile` requests.
<a name="moving"></a>
## Moving a bot from one local server to another
If the bot is logged in on more than one server simultaneously, there is no guarantee that it will receive all updates.
To move a bot from one local server to another you can use the method [logOut]( to log out on the old server before switching to the new one.
If you want to avoid losing updates between logging out on the old server and launching on the new server, you can remove the bot's webhook using the method
[deleteWebhook](, then use the method [close]( to close the bot instance.
After the instance is closed, locate the bot's subdirectory in the working directory of the old server by the bot's user ID, move the subdirectory to the working directory of the new server
and continue sending requests to the new server as usual.
<a name="license"></a>
## License
`Telegram Bot API server` source code is licensed under the terms of the Boost Software License. See [LICENSE_1_0.txt]( for more information.

build.html Normal file
View File

@ -0,0 +1,494 @@
<!DOCTYPE html>
<title>Telegram Bot API server build instructions</title>
.hide { display: none; }
div.main { max-width:1200px; margin: auto; font-size: x-large; }
select.large { font-size: large; }
<body onload="onLoad(true)" onpopstate="onLoad(false)">
<div class="main">
<div id="osSelectDiv" class="large" style="text-align:center;">
<p>Choose an operating system, on which you want to use the Telegram Bot API server:</p>
<select id="osSelect" onchange="onOsChanged(false)" autofocus class="large">
<option>Choose an operating system:</option>
<div id="linuxSelectDiv" class="hide" style="text-align:center;">
<p>Choose a Linux distro, on which you want to use the Telegram Bot API server:</p>
<select id="linuxSelect" onchange="onOsChanged(false)" class="large">
<option>Choose a Linux distro:</option>
<option>Debian 8</option>
<option>Debian 9</option>
<option>Debian 10</option>
<option>Ubuntu 14</option>
<option>Ubuntu 16</option>
<option>Ubuntu 18</option>
<option>Ubuntu 20</option>
<div id="buildOptionsDiv" class="hide" style="text-align:center;">
<div id="buildDebugDiv" class="hide">
<label><input type="checkbox" id="buildDebugCheckbox" onchange="onOptionsChanged()"/>Build the debug binary. Debug binaries are much larger and slower than the release one.</label>
<div id="buildInstallLocalDiv" class="hide">
<label><input type="checkbox" id="buildInstallLocalCheckbox" onchange="onOptionsChanged()"/>Install the built Telegram Bot API server to /usr/local instead of placing the files to telegram-bot-api/bin.</label>
<div id="buildCompilerDiv" class="hide">
<span>Choose which compiler you want to use to build the Telegram Bot API server:</span><br>
<label><input type="radio" id="buildCompilerRadioGcc" name="buildCompilerRadio" onchange="onOptionsChanged()" checked/>g++</label>
<label><input type="radio" id="buildCompilerRadioClang" name="buildCompilerRadio" onchange="onOptionsChanged()"/>clang++ (recommended)</label>
<div id="buildShellDiv" class="hide">
<span>Choose which shell application you want to use for building:</span><br>
<label><input type="radio" id="buildShellRadioPowerShell" name="buildShellRadio" onchange="onOptionsChanged()" checked/>PowerShell</label>
<label><input type="radio" id="buildShellRadioBash" name="buildShellRadio" onchange="onOptionsChanged()"/>mintty/Bash</label>
<div id="buildShellBsdDiv" class="hide">
<span>Choose which shell application you want to use for building:</span><br>
<label><input type="radio" id="buildShellBsdRadioCsh" name="buildShellRadioBsd" onchange="onOptionsChanged()" checked/>tcsh/csh</label>
<label><input type="radio" id="buildShellBsdRadioBash" name="buildShellRadioBsd" onchange="onOptionsChanged()"/>Bash</label>
<div id="buildBitnessDiv" class="hide">
<span>Choose for which bitness you want to build the Telegram Bot API server:</span><br>
<label><input type="radio" id="buildBitnessRadio64" name="buildBitnessRadio" onchange="onOptionsChanged()" checked/>64</label>
<label><input type="radio" id="buildBitnessRadio32" name="buildBitnessRadio" onchange="onOptionsChanged()"/>32</label>
<div id="buildRootDiv" class="hide">
<label><input type="checkbox" id="buildRootCheckbox" onchange="onOptionsChanged()"/>Build from root user (unrecommended).</label>
<div id="buildTextDiv" class="hide" style="text-align:center;">
<p id="buildText">Hidden text</p>
<div id="buildCommandsDiv" class="hide" style="text-align:left;">
<p id="buildPre">Hidden text</p>
<code id="buildCommands">Empty commands</code>
function onLoad(initial) {
var url = new URL(window.location.href);
var os = url.searchParams.get('os');
if (!os) {
os = '';
var os_options = document.getElementById('osSelect').options;
for (var i = 0; i < os_options.length; i++) {
os_options[i].selected = os_options[i].text.toLowerCase() === os.toLowerCase();
onOsChanged(initial || !history.state);
function onOsChanged(initial) {
var os = document.getElementById('osSelect').value;
if (os.includes('Choose ')) {
if (history.state != '') {
history.pushState('', '', 'build.html');
document.getElementById('linuxSelectDiv').style.display = 'none';
document.getElementById('buildTextDiv').style.display = 'none';
document.getElementById('buildOptionsDiv').style.display = 'none';
document.getElementById('buildCommandsDiv').style.display = 'none';
if (!initial && history.state !== os) {
history.pushState(os, '', 'build.html?os=' + encodeURIComponent(os));
var os_linux = os.includes('Linux');
if (os_linux) {
document.getElementById('linuxSelectDiv').style.display = 'block';
var linux_distro = document.getElementById('linuxSelect').value;
if (linux_distro.includes('Choose ')) {
document.getElementById('buildTextDiv').style.display = 'none';
document.getElementById('buildOptionsDiv').style.display = 'none';
document.getElementById('buildCommandsDiv').style.display = 'none';
} else {
document.getElementById('linuxSelectDiv').style.display = 'none';
document.getElementById('buildTextDiv').style.display = 'block';
document.getElementById('buildText').innerHTML = 'Here is complete instruction for building Telegram Bot API Server on ' + os + ':';
document.getElementById('buildOptionsDiv').style.display = 'block';
function onOptionsChanged() {
var os = document.getElementById('osSelect').value;
var os_windows = os.includes('Windows');
var os_linux = os.includes('Linux');
var os_mac = os.includes('macOS');
var os_freebsd = os.includes('FreeBSD');
var os_openbsd = os.includes('OpenBSD');
var os_netbsd = os.includes('NetBSD');
var linux_distro = 'none';
if (os_linux) {
linux_distro = document.getElementById('linuxSelect').value;
document.getElementById('buildCommandsDiv').style.display = 'block';
var use_clang = os_freebsd || os_openbsd;
if (os_linux && linux_distro !== 'Alpine') {
document.getElementById('buildCompilerDiv').style.display = 'block';
use_clang = document.getElementById('buildCompilerRadioClang').checked;
} else {
document.getElementById('buildCompilerDiv').style.display = 'none';
var use_root = false;
if ((os_linux && linux_distro !== 'Other') || os_openbsd || os_netbsd) {
use_root = document.getElementById('buildRootCheckbox').checked;
document.getElementById('buildRootDiv').style.display = 'block';
} else {
document.getElementById('buildRootDiv').style.display = 'none';
var use_powershell = false;
var use_cmd = false;
var use_csh = false;
if (os_windows) {
document.getElementById('buildShellDiv').style.display = 'block';
use_powershell = document.getElementById('buildShellRadioPowerShell').checked;
} else {
document.getElementById('buildShellDiv').style.display = 'none';
if (os_freebsd) {
document.getElementById('buildShellBsdDiv').style.display = 'block';
use_csh = document.getElementById('buildShellBsdRadioCsh').checked;
} else {
document.getElementById('buildShellBsdDiv').style.display = 'none';
var use_msvc = os_windows;
var use_vcpkg = os_windows;
var is_debug_build = document.getElementById('buildDebugCheckbox').checked;
document.getElementById('buildDebugDiv').style.display = 'block';
var sudo = 'sudo ';
if (use_root || linux_distro.includes('Debian') || os_freebsd || os_openbsd || os_netbsd) {
sudo = '';
var build_32bit = false;
var build_64bit = false;
if (use_msvc) {
document.getElementById('buildBitnessDiv').style.display = 'block';
build_32bit = document.getElementById('buildBitnessRadio32').checked;
build_64bit = document.getElementById('buildBitnessRadio64').checked;
} else {
document.getElementById('buildBitnessDiv').style.display = 'none';
var local = './';
if (use_cmd) {
local = '.\\';
var install_dir = 'telegram-bot-api/bin';
if (!os_windows) {
document.getElementById('buildInstallLocalDiv').style.display = 'block';
if (document.getElementById('buildInstallLocalCheckbox').checked) {
install_dir = '/usr/local';
} else {
document.getElementById('buildInstallLocalDiv').style.display = 'none';
var pre_text = [];
if (os_windows) {
pre_text.push('Note that Windows Subsystem for Linux (WSL) and Cygwin are not Windows environments, so you need to use instructions for Linux for them instead.');
pre_text.push('Download and install <a href="">Microsoft Visual Studio</a>. Enable C++ support while installing.');
pre_text.push('Download and install <a href="">CMake</a>; choose "Add CMake to the system PATH" option while installing.');
pre_text.push('Download and install <a href="">Git</a>.');
pre_text.push('Download and install <a href="">gperf</a>. Add the path to gperf.exe to the PATH environment variable.');
if (os_linux && linux_distro === 'Other') {
var compiler = use_clang ? 'clang++ >= 3.4' : 'g++ >= 4.9.2';
pre_text.push('Install Git, ' + compiler + ', make, CMake >= 3.0.2, OpenSSL-dev, zlib-dev, gperf using your package manager.');
if (os_freebsd) {
pre_text.push('Note that the following instruction is for FreeBSD 11.');
pre_text.push('Note that the following calls to <code>pkg</code> needs to be run as <code>root</code>.');
if (os_openbsd) {
pre_text.push('Note that the following instruction is for OpenBSD 6.7 and default KSH shell.');
pre_text.push('Note that building requires a lot of memory, so you may need to increase allowed per-process memory usage in /etc/login.conf or build from root.');
if (os_netbsd) {
pre_text.push('Note that the following instruction is for NetBSD 8.0 and default SH shell.');
var terminal_name = (function () {
if (os_windows) {
return use_powershell ? 'PowerShell' : 'mintty/Bash';
if (os_mac) {
return 'Terminal';
if (os_openbsd) {
return 'ksh';
if (os_netbsd) {
return 'sh';
if (use_csh) {
return 'tcsh/csh';
return 'Bash';
if (os_windows) {
pre_text.push('Close and re-open ' + terminal_name + ' if the PATH environment variable was changed.');
pre_text.push('Run these commands in ' + terminal_name + ' to build Bot API server and to install it to ' + install_dir + ':');
document.getElementById('buildPre').innerHTML = '<ul><li>' + pre_text.join('</li><li>') + '</li></ul>';
document.getElementById('buildPre').style.display = 'block';
if (install_dir !== '/usr/local') {
install_dir = '..';
function getClangVersionSuffix() {
switch (linux_distro) {
case 'Ubuntu 14':
return '-3.9';
case 'Ubuntu 18':
return '-6.0';
case 'Ubuntu 20':
return '-10';
return ''; // use default version
var commands = [];
if (os_mac) {
commands.push('xcode-select --install');
commands.push('/usr/bin/ruby -e "$(curl -fsSL"');
commands.push('brew install gperf cmake openssl');
} else if (os_linux && linux_distro !== 'Other') {
switch (linux_distro) {
case 'Alpine':
commands.push(sudo + 'apk update');
commands.push(sudo + 'apk upgrade');
var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf cmake';
commands.push(sudo + 'apk add --update ' + packages);
case 'Debian 8':
case 'Debian 9':
case 'Debian 10':
case 'Ubuntu 14':
case 'Ubuntu 16':
case 'Ubuntu 18':
case 'Ubuntu 20':
if (linux_distro.includes('Debian') && !use_root) {
commands.push('su -');
if (linux_distro === 'Ubuntu 14' && !use_clang) {
commands.push(sudo + 'add-apt-repository ppa:ubuntu-toolchain-r/test');
commands.push(sudo + 'apt-get update');
commands.push(sudo + 'apt-get upgrade');
var packages = 'make git zlib1g-dev libssl-dev gperf';
if (linux_distro === 'Ubuntu 14') {
packages += ' cmake3';
} else {
packages += ' cmake';
if (use_clang) {
packages += ' clang' + getClangVersionSuffix() + ' libc++-dev';
if (linux_distro === 'Debian 10' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
packages += ' libc++abi-dev';
} else {
packages += ' g++';
if (linux_distro === 'Ubuntu 14') {
packages += '-4.9';
commands.push(sudo + 'apt-get install ' + packages);
if (linux_distro.includes('Debian') && !use_root) {
} else if (os_freebsd) {
commands.push(sudo + 'pkg upgrade');
var packages = 'git gperf cmake';
commands.push(sudo + 'pkg install ' + packages);
} else if (os_openbsd) {
if (!use_root) {
commands.push('su -');
var packages = 'git gperf cmake';
commands.push('pkg_add -z ' + packages);
if (!use_root) {
} else if (os_netbsd) {
if (!use_root) {
commands.push('su -');
commands.push('export PKG_PATH=');
var packages = 'git gperf cmake openssl gcc5-libs';
commands.push('pkg_add ' + packages);
if (!use_root) {
commands.push('git clone --recursive');
commands.push('cd telegram-bot-api');
if (use_vcpkg) {
commands.push('git clone');
commands.push('cd vcpkg');
commands.push(local + 'bootstrap-vcpkg.bat');
if (build_64bit) {
commands.push(local + 'vcpkg.exe install openssl:x64-windows zlib:x64-windows');
} else {
commands.push(local + 'vcpkg.exe install openssl:x86-windows zlib:x86-windows');
commands.push('cd ..');
function getBacicCmakeInitOptions() {
var options = [];
if (!use_msvc) {
options.push('-DCMAKE_BUILD_TYPE=' + (is_debug_build ? 'Debug' : 'Release'));
if (use_msvc) {
if (build_64bit) {
options.push('-A x64');
} else {
options.push('-A Win32');
return options;
commands.push(use_powershell ? 'Remove-Item build -Force -Recurse -ErrorAction SilentlyContinue' : 'rm -rf build');
commands.push('mkdir build');
commands.push('cd build');
if (!use_msvc) {
var c_flags = [];
var cxx_flags = [];
if (build_32bit) {
} else if (build_64bit) {
if (os_linux) {
if (use_clang) {
} else {
if (c_flags.length) {
commands.push('export CFLAGS="' + c_flags.join(' ') + '"');
if (cxx_flags.length) {
commands.push('export CXXFLAGS="' + cxx_flags.join(' ') + '"');
cmake_init_options = getBacicCmakeInitOptions();
if (os_mac) {
cmake_init_options.push('-DCMAKE_INSTALL_PREFIX:PATH=' + install_dir);
if (use_vcpkg) {
function getCmakeInitCommand(options) {
var prefix = '';
if (os_linux) {
if (use_clang) {
var clang_version_suffix = getClangVersionSuffix();
prefix = 'CC=/usr/bin/clang' + clang_version_suffix + ' CXX=/usr/bin/clang++' + clang_version_suffix + ' ';
} else if (linux_distro === 'Ubuntu 14') {
prefix = 'CC=/usr/bin/gcc-4.9 CXX=/usr/bin/g++-4.9 ';
return prefix + 'cmake ' + options.join(' ') + ' ..';
let build_command = 'cmake --build . --target install';
if (use_msvc) {
if (!is_debug_build) {
commands.push(build_command + ' --config Release');
if (is_debug_build) {
commands.push(build_command + ' --config Debug');
} else {
commands.push('cd ../..');
if (install_dir !== '/usr/local') {
install_dir = 'telegram-bot-api';
commands.push((use_powershell ? 'dir ' : 'ls -l ') + install_dir + '/bin/telegram-bot-api*');
document.getElementById('buildCommands').innerHTML = '<ul><li>' + commands.join('</li><li>') + '</li></ul>';

td Submodule

@ -0,0 +1 @@
Subproject commit c9a70fcd49757084a0c3124e610e92bf0586894b

telegram-bot-api/Client.cpp Normal file

File diff suppressed because it is too large Load Diff

telegram-bot-api/Client.h Normal file
View File

@ -0,0 +1,935 @@
// Copyright Aliaksei Levin (, Arseny Smirnov ( 2014-2020
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
#pragma once
#include "telegram-bot-api/Query.h"
#include "telegram-bot-api/Stats.h"
#include "telegram-bot-api/WebhookActor.h"
#include "td/telegram/ClientActor.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/SignalSlot.h"
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <functional>
#include <limits>
#include <memory>
#include <queue>
#include <unordered_map>
#include <unordered_set>
namespace telegram_bot_api {
struct ClientParameters;
namespace td_api = td::td_api;
class Client : public WebhookActor::Callback {
Client(td::ActorShared<> parent, const td::string &bot_token, bool is_test_dc, td::int64 tqueue_id,
std::shared_ptr<const ClientParameters> parameters, td::ActorId<BotStatActor> stat_actor);
void start_up() override;
void send(PromisedQueryPtr query) override;
void close();
// for stats
ServerBotInfo get_bot_info() const;
using int32 = td::int32;
using int64 = td::int64;
using Slice = td::Slice;
using Status = td::Status;
template <class T>
using object_ptr = td_api::object_ptr<T>;
static constexpr bool USE_MESSAGE_DATABASE = false;
static constexpr int32 MAX_CERTIFICATE_FILE_SIZE = 3 << 20;
static constexpr int32 MAX_DOWNLOAD_FILE_SIZE = 20 << 20;
static constexpr int32 MESSAGES_CACHE_TIME = 3600;
static constexpr std::size_t MIN_PENDING_UPDATES_WARNING = 200;
static constexpr int64 GREAT_MINDS_SET_ID = 1842540969984001;
static constexpr Slice GREAT_MINDS_SET_NAME = "TelegramGreatMinds";
static constexpr int32 MASK_POINTS_SIZE = 4;
static constexpr Slice MASK_POINTS[MASK_POINTS_SIZE] = {"forehead", "eyes", "mouth", "chin"};
static constexpr int32 MAX_LENGTH = 10000; // max width or height
static constexpr int32 MAX_DURATION = 24 * 60 * 60;
static constexpr int LOGGING_OUT_ERROR_CODE = 401;
static constexpr Slice LOGGING_OUT_ERROR_DESCRIPTION = "Unauthorized";
static constexpr int CLOSING_ERROR_CODE = 500;
static constexpr Slice CLOSING_ERROR_DESCRIPTION = "Internal Server Error: restart";
class JsonFile;
class JsonDatedFile;
class JsonDatedFiles;
class JsonUser;
class JsonUsers;
class JsonChatPermissions;
class JsonChatPhotoInfo;
class JsonChatLocation;
class JsonChat;
class JsonMessageSender;
class JsonAnimation;
class JsonAudio;
class JsonDocument;
class JsonPhotoSize;
class JsonPhoto;
class JsonChatPhoto;
class JsonThumbnail;
class JsonMaskPosition;
class JsonSticker;
class JsonStickers;
class JsonVideo;
class JsonVideoNote;
class JsonVoiceNote;
class JsonContact;
class JsonDice;
class JsonGame;
class JsonInvoice;
class JsonLocation;
class JsonVenue;
class JsonPollOption;
class JsonPoll;
class JsonPollAnswer;
class JsonEntity;
class JsonVectorEntities;
class JsonCallbackGame;
class JsonInlineKeyboardButton;
class JsonInlineKeyboard;
class JsonReplyMarkup;
class JsonMessage;
class JsonMessages;
class JsonDeletedMessage;
class JsonMessageId;
class JsonInlineQuery;
class JsonChosenInlineResult;
class JsonCallbackQuery;
class JsonInlineCallbackQuery;
class JsonShippingQuery;
class JsonPreCheckoutQuery;
class JsonBotCommand;
class JsonChatPhotos;
class JsonChatMember;
class JsonChatMembers;
class JsonGameHighScore;
class JsonAddress;
class JsonOrderInfo;
class JsonSuccessfulPaymentBot;
class JsonEncryptedPassportElement;
class JsonEncryptedCredentials;
class JsonPassportData;
class JsonProximityAlertTriggered;
class JsonUpdateTypes;
class JsonWebhookInfo;
class JsonStickerSet;
class JsonCustomJson;
class TdOnOkCallback;
class TdOnAuthorizationCallback;
class TdOnInitCallback;
class TdOnGetUserProfilePhotosCallback;
class TdOnSendMessageCallback;
class TdOnSendMessageAlbumCallback;
class TdOnDeleteFailedToSendMessageCallback;
class TdOnEditMessageCallback;
class TdOnEditInlineMessageCallback;
class TdOnStopPollCallback;
class TdOnOkQueryCallback;
class TdOnGetReplyMessageCallback;
class TdOnGetEditedMessageCallback;
class TdOnGetCallbackQueryMessageCallback;
class TdOnGetStickerSetCallback;
class TdOnGetMyCommandsCallback;
class TdOnGetChatFullInfoCallback;
class TdOnGetChatStickerSetCallback;
class TdOnGetChatPinnedMessageCallback;
class TdOnGetChatPinnedMessageToUnpinCallback;
class TdOnGetGroupMembersCallback;
class TdOnGetSupergroupMembersCallback;
class TdOnGetSupergroupMembersCountCallback;
class TdOnGenerateChatInviteLinkCallback;
class TdOnGetGameHighScoresCallback;
class TdOnReturnFileCallback;
class TdOnReturnStickerSetCallback;
class TdOnDownloadFileCallback;
class TdOnCancelDownloadFileCallback;
class TdOnSendCustomRequestCallback;
void on_get_reply_message(int64 chat_id, object_ptr<td_api::message> reply_to_message);
void on_get_edited_message(object_ptr<td_api::message> edited_message);
void on_get_callback_query_message(object_ptr<td_api::message> message, int32 user_id, int state);
void on_get_sticker_set(int64 set_id, int32 new_callback_query_user_id, int64 new_message_chat_id,
object_ptr<td_api::stickerSet> sticker_set);
void on_get_sticker_set_name(int64 set_id, const td::string &name);
class TdQueryCallback {
virtual void on_result(object_ptr<td_api::Object> result) = 0;
TdQueryCallback() = default;
TdQueryCallback(const TdQueryCallback &) = delete;
TdQueryCallback &operator=(const TdQueryCallback &) = delete;
TdQueryCallback(TdQueryCallback &&) = delete;
TdQueryCallback &operator=(TdQueryCallback &&) = delete;
virtual ~TdQueryCallback() = default;
struct UserInfo;
struct ChatInfo;
enum class AccessRights { Read, Edit, Write };
template <class OnSuccess>
class TdOnCheckUserCallback;
template <class OnSuccess>
class TdOnCheckUserNoFailCallback;
template <class OnSuccess>
class TdOnCheckChatCallback;
template <class OnSuccess>
class TdOnCheckMessageCallback;
template <class OnSuccess>
class TdOnCheckRemoteFileIdCallback;
template <class OnSuccess>
class TdOnGetChatMemberCallback;
template <class OnSuccess>
class TdOnSearchStickerSetCallback;
class TdOnResolveBotUsernameCallback;
template <class OnSuccess>
void check_user(int32 user_id, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void check_user_no_fail(int32 user_id, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
static void check_user_read_access(const UserInfo *user_info, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void check_chat_access(int64 chat_id, AccessRights access_rights, const ChatInfo *chat_info, PromisedQueryPtr query,
OnSuccess on_success) const;
template <class OnSuccess>
void check_chat(Slice chat_id_str, AccessRights access_rights, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void check_message(Slice chat_id_str, int64 message_id, bool allow_empty, AccessRights access_rights,
Slice message_type, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void resolve_sticker_set(const td::string &sticker_set_name, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void resolve_reply_markup_bot_usernames(object_ptr<td_api::ReplyMarkup> reply_markup, PromisedQueryPtr query,
OnSuccess on_success);
template <class OnSuccess>
void resolve_inline_query_results_bot_usernames(td::vector<object_ptr<td_api::InputInlineQueryResult>> results,
PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
void get_chat_member(int64 chat_id, int32 user_id, PromisedQueryPtr query, OnSuccess on_success);
void send_request(object_ptr<td_api::Function> &&f, std::unique_ptr<TdQueryCallback> handler);
void do_send_request(object_ptr<td_api::Function> &&f, std::unique_ptr<TdQueryCallback> handler);
static object_ptr<td_api::Object> execute(object_ptr<td_api::Function> &&f);
void on_update(object_ptr<td_api::Object> result);
void on_result(td::uint64 id, object_ptr<td_api::Object> result);
void on_update_authorization_state();
void log_out();
void on_closed();
void finish_closing();
void clear_tqueue();
bool allow_update_before_authorization(const td_api::Object *update) const;
void update_shared_unix_time_difference();
void on_update_file(object_ptr<td_api::file> file);
static bool to_bool(td::MutableSlice value);
static td::Result<object_ptr<td_api::keyboardButton>> get_keyboard_button(td::JsonValue &button);
td::Result<object_ptr<td_api::inlineKeyboardButton>> get_inline_keyboard_button(td::JsonValue &button);
td::Result<object_ptr<td_api::ReplyMarkup>> get_reply_markup(const Query *query);
td::Result<object_ptr<td_api::ReplyMarkup>> get_reply_markup(td::JsonValue &&value);
static td::Result<object_ptr<td_api::labeledPricePart>> get_labeled_price_part(td::JsonValue &value);
static td::Result<td::vector<object_ptr<td_api::labeledPricePart>>> get_labeled_price_parts(td::JsonValue &value);
static td::Result<object_ptr<td_api::shippingOption>> get_shipping_option(td::JsonValue &option);
static td::Result<td::vector<object_ptr<td_api::shippingOption>>> get_shipping_options(const Query *query);
static td::Result<td::vector<object_ptr<td_api::shippingOption>>> get_shipping_options(td::JsonValue &&value);
static td::Result<object_ptr<td_api::InputMessageContent>> get_input_message_content(
td::JsonValue &input_message_content, bool is_input_message_content_required);
static object_ptr<td_api::ChatAction> get_chat_action(const Query *query);
static td::string get_local_file_path(Slice file_uri);
object_ptr<td_api::InputFile> get_input_file(const Query *query, Slice field_name, bool force_file = false) const;
object_ptr<td_api::InputFile> get_input_file(const Query *query, Slice field_name, Slice file_id,
bool force_file) const;
object_ptr<td_api::inputThumbnail> get_input_thumbnail(const Query *query, Slice field_name) const;
td::Result<object_ptr<td_api::InputInlineQueryResult>> get_inline_query_result(td::JsonValue &&value);
td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results(const Query *query);
td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results(td::JsonValue &&value);
static td::Result<object_ptr<td_api::botCommand>> get_bot_command(td::JsonValue &&value);
static td::Result<td::vector<object_ptr<td_api::botCommand>>> get_bot_commands(const Query *query);
static td::Result<object_ptr<td_api::maskPosition>> get_mask_position(const Query *query, Slice field_name);
static td::Result<object_ptr<td_api::maskPosition>> get_mask_position(td::JsonValue &&value);
static int32 mask_point_to_index(const object_ptr<td_api::MaskPoint> &mask_point);
static object_ptr<td_api::MaskPoint> mask_index_to_point(int32 index);
td::Result<td::vector<object_ptr<td_api::InputSticker>>> get_input_stickers(const Query *query) const;
static td::Result<td::string> get_passport_element_hash(Slice encoded_hash);
static td::Result<object_ptr<td_api::InputPassportElementErrorSource>> get_passport_element_error_source(
td::JsonObject &object);
static td::Result<object_ptr<td_api::inputPassportElementError>> get_passport_element_error(td::JsonValue &&value);
static td::Result<td::vector<object_ptr<td_api::inputPassportElementError>>> get_passport_element_errors(
const Query *query);
static td::Result<object_ptr<td_api::formattedText>> get_caption(const Query *query);
static td::Result<object_ptr<td_api::TextEntityType>> get_text_entity_type(td::JsonObject &object);
static td::Result<object_ptr<td_api::textEntity>> get_text_entity(td::JsonValue &&value);
static td::Result<object_ptr<td_api::formattedText>> get_formatted_text(td::string text, td::string parse_mode,
td::JsonValue &&input_entities);
static td::Result<object_ptr<td_api::inputMessageText>> get_input_message_text(const Query *query);
static td::Result<object_ptr<td_api::inputMessageText>> get_input_message_text(td::string text,
bool disable_web_page_preview,
td::string parse_mode,
td::JsonValue &&input_entities);
static td::Result<object_ptr<td_api::location>> get_location(const Query *query);
static td::Result<object_ptr<td_api::chatPermissions>> get_chat_permissions(const Query *query, bool &allow_legacy);
td::Result<object_ptr<td_api::InputMessageContent>> get_input_media(const Query *query, td::JsonValue &&input_media,
bool for_album) const;
td::Result<object_ptr<td_api::InputMessageContent>> get_input_media(const Query *query, Slice field_name,
bool for_album) const;
td::Result<td::vector<object_ptr<td_api::InputMessageContent>>> get_input_message_contents(const Query *query,
Slice field_name) const;
td::Result<td::vector<object_ptr<td_api::InputMessageContent>>> get_input_message_contents(
const Query *query, td::JsonValue &&value) const;
static object_ptr<td_api::messageSendOptions> get_message_send_options(bool disable_notification);
static td::Result<td::vector<td::string>> get_poll_options(const Query *query);
static int32 get_integer_arg(const Query *query, Slice field_name, int32 default_value,
int32 min_value = std::numeric_limits<int32>::min(),
int32 max_value = std::numeric_limits<int32>::max());
static td::Result<td::MutableSlice> get_required_string_arg(const Query *query, Slice field_name);
static int64 get_message_id(const Query *query, Slice field_name = "message_id");
static td::Result<Slice> get_inline_message_id(const Query *query, Slice field_name = "inline_message_id");
static td::Result<int32> get_user_id(const Query *query, Slice field_name = "user_id");
int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, bool *is_reply_to_message_deleted);
void on_message_send_succeeded(object_ptr<td_api::message> &&message, int64 old_message_id);
void on_message_send_failed(int64 chat_id, int64 old_message_id, int64 new_message_id, Status result);
static bool init_methods();
void on_cmd(PromisedQueryPtr query);
Status process_get_me_query(PromisedQueryPtr &query);
Status process_get_my_commands_query(PromisedQueryPtr &query);
Status process_set_my_commands_query(PromisedQueryPtr &query);
Status process_get_user_profile_photos_query(PromisedQueryPtr &query);
Status process_send_message_query(PromisedQueryPtr &query);
Status process_send_animation_query(PromisedQueryPtr &query);
Status process_send_audio_query(PromisedQueryPtr &query);
Status process_send_dice_query(PromisedQueryPtr &query);
Status process_send_document_query(PromisedQueryPtr &query);
Status process_send_photo_query(PromisedQueryPtr &query);
Status process_send_sticker_query(PromisedQueryPtr &query);
Status process_send_video_query(PromisedQueryPtr &query);
Status process_send_video_note_query(PromisedQueryPtr &query);
Status process_send_voice_query(PromisedQueryPtr &query);
Status process_send_game_query(PromisedQueryPtr &query);
Status process_send_invoice_query(PromisedQueryPtr &query);
Status process_send_location_query(PromisedQueryPtr &query);
Status process_send_venue_query(PromisedQueryPtr &query);
Status process_send_contact_query(PromisedQueryPtr &query);
Status process_send_poll_query(PromisedQueryPtr &query);
Status process_stop_poll_query(PromisedQueryPtr &query);
Status process_copy_message_query(PromisedQueryPtr &query);
Status process_forward_message_query(PromisedQueryPtr &query);
Status process_send_media_group_query(PromisedQueryPtr &query);
Status process_send_chat_action_query(PromisedQueryPtr &query);
Status process_edit_message_text_query(PromisedQueryPtr &query);
Status process_edit_message_live_location_query(PromisedQueryPtr &query);
Status process_edit_message_media_query(PromisedQueryPtr &query);
Status process_edit_message_caption_query(PromisedQueryPtr &query);
Status process_edit_message_reply_markup_query(PromisedQueryPtr &query);
Status process_delete_message_query(PromisedQueryPtr &query);
Status process_set_game_score_query(PromisedQueryPtr &query);
Status process_get_game_high_scores_query(PromisedQueryPtr &query);
Status process_answer_inline_query_query(PromisedQueryPtr &query);
Status process_answer_callback_query_query(PromisedQueryPtr &query);
Status process_answer_shipping_query_query(PromisedQueryPtr &query);
Status process_answer_pre_checkout_query_query(PromisedQueryPtr &query);
Status process_export_chat_invite_link_query(PromisedQueryPtr &query);
Status process_get_chat_query(PromisedQueryPtr &query);
Status process_set_chat_photo_query(PromisedQueryPtr &query);
Status process_delete_chat_photo_query(PromisedQueryPtr &query);
Status process_set_chat_title_query(PromisedQueryPtr &query);
Status process_set_chat_permissions_query(PromisedQueryPtr &query);
Status process_set_chat_description_query(PromisedQueryPtr &query);
Status process_pin_chat_message_query(PromisedQueryPtr &query);
Status process_unpin_chat_message_query(PromisedQueryPtr &query);
Status process_unpin_all_chat_messages_query(PromisedQueryPtr &query);
Status process_set_chat_sticker_set_query(PromisedQueryPtr &query);
Status process_delete_chat_sticker_set_query(PromisedQueryPtr &query);
Status process_get_chat_member_query(PromisedQueryPtr &query);
Status process_get_chat_administrators_query(PromisedQueryPtr &query);
Status process_get_chat_member_count_query(PromisedQueryPtr &query);
Status process_leave_chat_query(PromisedQueryPtr &query);
Status process_promote_chat_member_query(PromisedQueryPtr &query);
Status process_set_chat_administrator_custom_title_query(PromisedQueryPtr &query);
Status process_ban_chat_member_query(PromisedQueryPtr &query);
Status process_restrict_chat_member_query(PromisedQueryPtr &query);
Status process_unban_chat_member_query(PromisedQueryPtr &query);
Status process_get_sticker_set_query(PromisedQueryPtr &query);
Status process_upload_sticker_file_query(PromisedQueryPtr &query);
Status process_create_new_sticker_set_query(PromisedQueryPtr &query);
Status process_add_sticker_to_set_query(PromisedQueryPtr &query);
Status process_set_sticker_set_thumb_query(PromisedQueryPtr &query);
Status process_set_sticker_position_in_set_query(PromisedQueryPtr &query);
Status process_delete_sticker_from_set_query(PromisedQueryPtr &query);
Status process_set_passport_data_errors_query(PromisedQueryPtr &query);
Status process_send_custom_request_query(PromisedQueryPtr &query);
Status process_answer_custom_query_query(PromisedQueryPtr &query);
Status process_get_updates_query(PromisedQueryPtr &query);
Status process_set_webhook_query(PromisedQueryPtr &query);
Status process_get_webhook_info_query(PromisedQueryPtr &query);
Status process_get_file_query(PromisedQueryPtr &query);
void webhook_verified(td::string cached_ip_address) override;
void webhook_success() override;
void webhook_error(Status status) override;
void webhook_closed(Status status) override;
void hangup_shared() override;
int32 get_webhook_max_connections(const Query *query) const;
static bool get_webhook_fix_ip_address(const Query *query);
void do_set_webhook(PromisedQueryPtr query, bool was_deleted);
void save_webhook() const;
td::string get_webhook_certificate_path() const;
void do_send_message(object_ptr<td_api::InputMessageContent> input_message_content, PromisedQueryPtr query);
int64 get_send_message_query_id(PromisedQueryPtr query, bool is_multisend);
void on_sent_message(object_ptr<td_api::message> &&message, int64 query_id);
void do_get_file(object_ptr<td_api::file> file, PromisedQueryPtr query);
bool is_file_being_downloaded(int32 file_id) const;
void on_file_download(int32 file_id, td::Result<object_ptr<td_api::file>> r_file);
void fix_reply_markup_bot_user_ids(object_ptr<td_api::ReplyMarkup> &reply_markup) const;
void fix_inline_query_results_bot_user_ids(td::vector<object_ptr<td_api::InputInlineQueryResult>> &results) const;
void resolve_bot_usernames(PromisedQueryPtr query, td::Promise<PromisedQueryPtr> on_success);
void on_resolve_bot_username(const td::string &username, int32 user_id);
void abort_long_poll(bool from_set_webhook);
void fail_query_conflict(Slice message, PromisedQueryPtr &&query);
static void fail_query_with_error(PromisedQueryPtr query, int32 error_code, Slice error_message,
Slice default_message = Slice());
static void fail_query_with_error(PromisedQueryPtr &&query, object_ptr<td_api::error> error,
Slice default_message = Slice());
class JsonUpdates;
void do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQueryPtr query);
void long_poll_wakeup(bool force_flag);
void raw_event(const td::Event::Raw &event) override;
void loop() override;
void timeout_expired() override;
struct UserInfo {
enum class Type { Regular, Deleted, Bot, Unknown };
Type type = Type::Unknown;
td::string first_name;
td::string last_name;
td::string username;
td::string language_code;
td::string bio;
bool have_access = false;
bool can_join_groups = false;
bool can_read_all_group_messages = false;
bool is_inline_bot = false;
static void add_user(std::unordered_map<int32, UserInfo> &users, object_ptr<td_api::user> &&user);
void set_user_bio(int32 user_id, td::string &&bio);
const UserInfo *get_user_info(int32 user_id) const;
struct GroupInfo {
td::string description;
td::string invite_link;
int32 member_count = 0;
bool left = false;
bool kicked = false;
bool is_active = false;
int32 upgraded_to_supergroup_id = 0;
static void add_group(std::unordered_map<int32, GroupInfo> &groups, object_ptr<td_api::basicGroup> &&group);
void set_group_description(int32 group_id, td::string &&descripton);
void set_group_invite_link(int32 group_id, td::string &&invite_link);
const GroupInfo *get_group_info(int32 group_id) const;
struct SupergroupInfo {
td::string username;
td::string description;
td::string invite_link;
int64 sticker_set_id = 0;
int32 date = 0;
int32 slow_mode_delay = 0;
int64 linked_chat_id = 0;
object_ptr<td_api::chatLocation> location;
object_ptr<td_api::ChatMemberStatus> status;
bool is_supergroup = false;
bool can_set_sticker_set = false;
bool has_location = false;
static void add_supergroup(std::unordered_map<int32, SupergroupInfo> &supergroups,
object_ptr<td_api::supergroup> &&supergroup);
void set_supergroup_description(int32 supergroup_id, td::string &&descripton);
void set_supergroup_invite_link(int32 supergroup_id, td::string &&invite_link);
void set_supergroup_sticker_set_id(int32 supergroup_id, int64 sticker_set_id);
void set_supergroup_can_set_sticker_set(int32 supergroup_id, bool can_set_sticker_set);
void set_supergroup_slow_mode_delay(int32 supergroup_id, int32 slow_mode_delay);
void set_supergroup_linked_chat_id(int32 supergroup_id, int64 linked_chat_id);
void set_supergroup_location(int32 supergroup_id, object_ptr<td_api::chatLocation> location);
const SupergroupInfo *get_supergroup_info(int32 supergroup_id) const;
struct ChatInfo {
enum class Type { Private, Group, Supergroup, Unknown };
Type type = Type::Unknown;
td::string title;
object_ptr<td_api::chatPhotoInfo> photo;
object_ptr<td_api::chatPermissions> permissions;
union {
int32 user_id;
int32 group_id;
int32 supergroup_id;
ChatInfo *add_chat(int64 chat_id);
const ChatInfo *get_chat(int64 chat_id) const;
enum class ChatType { Private, Group, Supergroup, Channel, Unknown };
ChatType get_chat_type(int64 chat_id) const;
td::string get_chat_description(int64 chat_id) const;
struct MessageInfo {
mutable double access_time = 1e20;
mutable const MessageInfo *lru_next = nullptr;
mutable const MessageInfo *lru_prev = nullptr;
int64 id = 0;
int32 sender_user_id = 0;
int64 sender_chat_id = 0;
int64 chat_id = 0;
int32 date = 0;
int32 edit_date = 0;
int64 initial_chat_id = 0;
int32 initial_sender_user_id = 0;
int64 initial_sender_chat_id = 0;
int32 initial_send_date = 0;
int64 initial_message_id = 0;
td::string initial_author_signature;
td::string initial_sender_name;
td::string author_signature;
int64 reply_to_message_id = 0;
int64 media_album_id = 0;
int32 via_bot_user_id = 0;
object_ptr<td_api::MessageContent> content;
object_ptr<td_api::ReplyMarkup> reply_markup;
mutable bool is_reply_to_message_deleted = false;
mutable bool is_content_changed = false;
static int64 &get_reply_to_message_id(object_ptr<td_api::message> &message);
void set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id);
static td::CSlice get_callback_data(const object_ptr<td_api::InlineKeyboardButtonType> &type);
static bool are_equal_inline_keyboard_buttons(const td_api::inlineKeyboardButton *lhs,
const td_api::inlineKeyboardButton *rhs);
static bool are_equal_inline_keyboards(const td_api::replyMarkupInlineKeyboard *lhs,
const td_api::replyMarkupInlineKeyboard *rhs);
void set_message_reply_markup(MessageInfo *message_info, object_ptr<td_api::ReplyMarkup> &&reply_markup);
static int64 get_sticker_set_id(const object_ptr<td_api::MessageContent> &content);
bool have_sticker_set_name(int64 sticker_set_id) const;
Slice get_sticker_set_name(int64 sticker_set_id) const;
int32 choose_added_member_id(const td_api::messageChatAddMembers *message_add_members) const;
bool need_skip_update_message(int64 chat_id, const object_ptr<td_api::message> &message, bool is_edited) const;
void json_store_file(td::JsonObjectScope &object, const td_api::file *file, bool with_path = false) const;
void json_store_thumbnail(td::JsonObjectScope &object, const td_api::thumbnail *thumbnail) const;
static void json_store_callback_query_payload(td::JsonObjectScope &object,
const td_api::CallbackQueryPayload *payload);
static void json_store_permissions(td::JsonObjectScope &object, const td_api::chatPermissions *permissions);
void remove_replies_to_message(int64 chat_id, int64 reply_to_message_id, bool only_from_cache);
void delete_message(int64 chat_id, int64 message_id, bool only_from_cache);
static void delete_messages_lru(void *client_void);
void schedule_next_delete_messages_lru();
void update_message_lru(const MessageInfo *message_info) const;
void add_new_message(object_ptr<td_api::message> &&message, bool is_edited);
void process_new_message_queue(int64 chat_id);
struct FullMessageId {
int64 chat_id;
int64 message_id;
FullMessageId() : chat_id(0), message_id(0) {
FullMessageId(int64 chat_id, int64 message_id) : chat_id(chat_id), message_id(message_id) {
bool operator==(const FullMessageId &other) const {
return chat_id == other.chat_id && message_id == other.message_id;
struct FullMessageIdHash {
std::size_t operator()(FullMessageId full_message_id) const {