diff --git a/CMakeLists.txt b/CMakeLists.txt
index 76ef36aa6..45d5df6ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
-project(TelegramBotApi VERSION 6.1 LANGUAGES CXX)
+project(TelegramBotApi VERSION 6.2 LANGUAGES CXX)
if (POLICY CMP0069)
option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.")
diff --git a/build.html b/build.html
index d24505d8e..6bb77c87e 100644
--- a/build.html
+++ b/build.html
@@ -181,7 +181,7 @@
-
Choose an operating system, on which you want to use the Telegram Bot API server:
+
Choose an operating system on which you want to use the Telegram Bot API server:
-
Choose a Linux distro, on which you want to use the Telegram Bot API server:
+
Choose a Linux distro on which you want to use the Telegram Bot API server:
@@ -493,6 +494,8 @@
return '-6.0';
case 'Ubuntu 20':
return '-10';
+ case 'Ubuntu 22':
+ return '-14';
default:
return ''; // use default version
}
@@ -534,6 +537,7 @@
case 'Ubuntu 16':
case 'Ubuntu 18':
case 'Ubuntu 20':
+ case 'Ubuntu 22':
if (linux_distro.includes('Debian') && !use_root) {
commands.push('su -');
}
@@ -550,7 +554,7 @@
}
if (use_clang) {
packages += ' clang' + getClangVersionSuffix() + ' libc++-dev';
- if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
+ if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22') {
packages += ' libc++abi-dev';
}
} else {
diff --git a/td b/td
index b393215d6..d9cfcf88f 160000
--- a/td
+++ b/td
@@ -1 +1 @@
-Subproject commit b393215d6671863b6baf2a589d343cff9474f6ba
+Subproject commit d9cfcf88fe4ad06dae1716ce8f66bbeb7f9491d9
diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp
index 85204f656..1effe2a15 100644
--- a/telegram-bot-api/Client.cpp
+++ b/telegram-bot-api/Client.cpp
@@ -10,7 +10,7 @@
#include "td/db/TQueue.h"
-#include "td/actor/PromiseFuture.h"
+#include "td/actor/MultiPromise.h"
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
@@ -22,7 +22,6 @@
#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/port/path.h"
-#include "td/utils/port/Stat.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Span.h"
@@ -207,6 +206,12 @@ Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_us
CHECK(is_inited);
}
+Client::~Client() {
+ td::Scheduler::instance()->destroy_on_scheduler(get_file_gc_scheduler_id(), messages_, users_, groups_, supergroups_,
+ chats_, reply_message_ids_, yet_unsent_reply_message_ids_,
+ sticker_set_names_);
+}
+
bool Client::init_methods() {
methods_.emplace("getme", &Client::process_get_me_query);
methods_.emplace("getmycommands", &Client::process_get_my_commands_query);
@@ -285,6 +290,7 @@ bool Client::init_methods() {
methods_.emplace("approvechatjoinrequest", &Client::process_approve_chat_join_request_query);
methods_.emplace("declinechatjoinrequest", &Client::process_decline_chat_join_request_query);
methods_.emplace("getstickerset", &Client::process_get_sticker_set_query);
+ methods_.emplace("getcustomemojistickers", &Client::process_get_custom_emoji_stickers_query);
methods_.emplace("uploadstickerfile", &Client::process_upload_sticker_file_query);
methods_.emplace("createnewstickerset", &Client::process_create_new_sticker_set_query);
methods_.emplace("addstickertoset", &Client::process_add_sticker_to_set_query);
@@ -526,6 +532,12 @@ class Client::JsonEntity final : public Jsonable {
object("user", JsonUser(entity->user_id_, client_));
break;
}
+ case td_api::textEntityTypeCustomEmoji::ID: {
+ auto entity = static_cast
(entity_->type_.get());
+ object("type", "custom_emoji");
+ object("custom_emoji_id", td::to_string(entity->custom_emoji_id_));
+ break;
+ }
default:
UNREACHABLE();
}
@@ -738,6 +750,9 @@ class Client::JsonChat final : public Jsonable {
if (user_info->has_private_forwards) {
object("has_private_forwards", td::JsonTrue());
}
+ if (user_info->has_restricted_voice_and_video_messages) {
+ object("has_restricted_voice_and_video_messages", td::JsonTrue());
+ }
}
photo = user_info->photo.get();
break;
@@ -1117,14 +1132,20 @@ class Client::JsonSticker final : public Jsonable {
if (!set_name.empty()) {
object("set_name", set_name);
}
- auto type = sticker_->type_->get_id();
- object("is_animated", td::JsonBool(type == td_api::stickerTypeAnimated::ID));
- object("is_video", td::JsonBool(type == td_api::stickerTypeVideo::ID));
- if (type == td_api::stickerTypeMask::ID) {
- const auto &mask_position = static_cast(sticker_->type_.get())->mask_position_;
- if (mask_position != nullptr) {
- object("mask_position", JsonMaskPosition(mask_position.get()));
- }
+
+ auto format = sticker_->format_->get_id();
+ object("is_animated", td::JsonBool(format == td_api::stickerFormatTgs::ID));
+ object("is_video", td::JsonBool(format == td_api::stickerFormatWebm::ID));
+
+ object("type", Client::get_sticker_type(sticker_->type_));
+
+ if (sticker_->custom_emoji_id_ != 0) {
+ object("custom_emoji_id", td::to_string(sticker_->custom_emoji_id_));
+ }
+
+ const auto &mask_position = sticker_->mask_position_;
+ if (mask_position != nullptr) {
+ object("mask_position", JsonMaskPosition(mask_position.get()));
}
if (sticker_->premium_animation_ != nullptr) {
object("premium_animation", JsonFile(sticker_->premium_animation_.get(), client_, false));
@@ -2136,6 +2157,8 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
object("web_app_data", JsonWebAppData(content));
break;
}
+ case td_api::messageGiftedPremium::ID:
+ break;
default:
UNREACHABLE();
}
@@ -2751,10 +2774,15 @@ class Client::JsonStickerSet final : public Jsonable {
if (sticker_set_->thumbnail_ != nullptr) {
client_->json_store_thumbnail(object, sticker_set_->thumbnail_.get());
}
- auto type = sticker_set_->sticker_type_->get_id();
- object("is_animated", td::JsonBool(type == td_api::stickerTypeAnimated::ID));
- object("is_video", td::JsonBool(type == td_api::stickerTypeVideo::ID));
- object("contains_masks", td::JsonBool(type == td_api::stickerTypeMask::ID));
+
+ auto format = sticker_set_->sticker_format_->get_id();
+ object("is_animated", td::JsonBool(format == td_api::stickerFormatTgs::ID));
+ object("is_video", td::JsonBool(format == td_api::stickerFormatWebm::ID));
+
+ auto type = Client::get_sticker_type(sticker_set_->sticker_type_);
+ object("sticker_type", type);
+ object("contains_masks", td::JsonBool(type == "mask"));
+
object("stickers", JsonStickers(sticker_set_->stickers_, client_));
}
@@ -4075,6 +4103,68 @@ class Client::TdOnReturnStickerSetCallback final : public TdQueryCallback {
PromisedQueryPtr query_;
};
+class Client::TdOnGetStickerSetPromiseCallback final : public TdQueryCallback {
+ public:
+ TdOnGetStickerSetPromiseCallback(Client *client, td::Promise &&promise)
+ : client_(client), promise_(std::move(promise)) {
+ }
+
+ void on_result(object_ptr result) final {
+ if (result->get_id() == td_api::error::ID) {
+ auto error = move_object_as(result);
+ return promise_.set_error(Status::Error(error->code_, error->message_));
+ }
+
+ CHECK(result->get_id() == td_api::stickerSet::ID);
+ auto sticker_set = move_object_as(result);
+ client_->on_get_sticker_set_name(sticker_set->id_, sticker_set->name_);
+ promise_.set_value(td::Unit());
+ }
+
+ private:
+ Client *client_;
+ td::Promise promise_;
+};
+
+class Client::TdOnGetStickersCallback final : public TdQueryCallback {
+ public:
+ TdOnGetStickersCallback(Client *client, PromisedQueryPtr query) : client_(client), query_(std::move(query)) {
+ }
+
+ void on_result(object_ptr result) final {
+ if (result->get_id() == td_api::error::ID) {
+ return fail_query_with_error(std::move(query_), move_object_as(result));
+ }
+
+ CHECK(result->get_id() == td_api::stickers::ID);
+ auto stickers = move_object_as(result);
+ td::FlatHashSet sticker_set_ids;
+ for (const auto &sticker : stickers->stickers_) {
+ if (sticker->set_id_ != 0 && client_->get_sticker_set_name(sticker->set_id_).empty()) {
+ sticker_set_ids.insert(sticker->set_id_);
+ }
+ }
+
+ td::MultiPromiseActorSafe mpas("GetStickerSetsMultiPromiseActor");
+ mpas.add_promise(td::PromiseCreator::lambda([actor_id = client_->actor_id(client_), stickers = std::move(stickers),
+ query = std::move(query_)](td::Unit) mutable {
+ send_closure(actor_id, &Client::return_stickers, std::move(stickers), std::move(query));
+ }));
+ mpas.set_ignore_errors(true);
+
+ auto lock = mpas.get_promise();
+ for (auto sticker_set_id : sticker_set_ids) {
+ client_->send_request(make_object(sticker_set_id),
+ td::make_unique(client_, mpas.get_promise()));
+ }
+ lock.set_value(td::Unit());
+ }
+
+ private:
+ Client *client_;
+ PromisedQueryPtr query_;
+};
+
class Client::TdOnSendCustomRequestCallback final : public TdQueryCallback {
public:
explicit TdOnSendCustomRequestCallback(PromisedQueryPtr query) : query_(std::move(query)) {
@@ -5308,6 +5398,8 @@ void Client::on_update(object_ptr result) {
set_user_bio(user_id, std::move(full_info->bio_->text_));
}
set_user_has_private_forwards(user_id, full_info->has_private_forwards_);
+ set_user_has_restricted_voice_and_video_messages(user_id,
+ full_info->has_restricted_voice_and_video_note_messages_);
break;
}
case td_api::updateUserStatus::ID: {
@@ -5532,30 +5624,13 @@ void Client::on_closed() {
parameters_->shared_data_->webhook_db_->erase(bot_token_with_dc_);
parameters_->shared_data_->user_db_->erase(bot_token_with_dc_);
- class RmWorker final : public td::Actor {
- public:
- RmWorker(td::string dir, td::ActorId parent) : dir_(std::move(dir)), parent_(std::move(parent)) {
- }
-
- private:
- td::string dir_;
- td::ActorId parent_;
-
- void start_up() final {
- CHECK(dir_.size() >= 24);
- CHECK(dir_.back() == TD_DIR_SLASH);
- td::rmrf(dir_).ignore();
- stop();
- }
- void tear_down() final {
- send_closure(parent_, &Client::finish_closing);
- }
- };
- // NB: the same scheduler as for database in Td
- auto current_scheduler_id = td::Scheduler::instance()->sched_id();
- auto scheduler_count = td::Scheduler::instance()->sched_count();
- auto scheduler_id = td::min(current_scheduler_id + 1, scheduler_count - 1);
- td::create_actor_on_scheduler("RmWorker", scheduler_id, dir_, actor_id(this)).release();
+ td::Scheduler::instance()->run_on_scheduler(get_file_gc_scheduler_id(),
+ [actor_id = actor_id(this), dir = dir_](td::Unit) {
+ CHECK(dir.size() >= 24);
+ CHECK(dir.back() == TD_DIR_SLASH);
+ td::rmrf(dir).ignore();
+ send_closure(actor_id, &Client::finish_closing);
+ });
return;
}
@@ -5575,6 +5650,20 @@ void Client::timeout_expired() {
stop();
}
+td::int32 Client::get_database_scheduler_id() {
+ // the same scheduler as for database in Td
+ auto current_scheduler_id = td::Scheduler::instance()->sched_id();
+ auto scheduler_count = td::Scheduler::instance()->sched_count();
+ return td::min(current_scheduler_id + 1, scheduler_count - 1);
+}
+
+td::int32 Client::get_file_gc_scheduler_id() {
+ // the same scheduler as for file GC in Td
+ auto current_scheduler_id = td::Scheduler::instance()->sched_id();
+ auto scheduler_count = td::Scheduler::instance()->sched_count();
+ return td::min(current_scheduler_id + 2, scheduler_count - 1);
+}
+
void Client::clear_tqueue() {
CHECK(webhook_id_.empty());
auto &tqueue = parameters_->shared_data_->tqueue_;
@@ -6818,27 +6907,23 @@ td::Result> Client::get_mask_position(c
return r_mask_position.move_as_ok();
}
-td::Result>> Client::get_input_stickers(const Query *query,
- bool is_masks) const {
+td::Result>> Client::get_input_stickers(const Query *query) const {
auto emojis = query->arg("emojis");
auto sticker = get_input_file(query, "png_sticker");
- object_ptr sticker_type;
+ object_ptr sticker_format;
+ object_ptr mask_position;
if (sticker != nullptr) {
- if (is_masks) {
- TRY_RESULT(mask_position, get_mask_position(query, "mask_position"));
- sticker_type = make_object(std::move(mask_position));
- } else {
- sticker_type = make_object();
- }
+ sticker_format = make_object();
+ TRY_RESULT_ASSIGN(mask_position, get_mask_position(query, "mask_position"));
} else {
sticker = get_input_file(query, "tgs_sticker", true);
if (sticker != nullptr) {
- sticker_type = make_object();
+ sticker_format = make_object();
} else {
sticker = get_input_file(query, "webm_sticker", true);
if (sticker != nullptr) {
- sticker_type = make_object();
+ sticker_format = make_object();
} else {
if (!query->arg("tgs_sticker").empty()) {
return Status::Error(400, "Bad Request: animated sticker must be uploaded as an InputFile");
@@ -6852,7 +6937,8 @@ td::Result>> Client::get_inp
}
td::vector> stickers;
- stickers.push_back(make_object(std::move(sticker), emojis.str(), std::move(sticker_type)));
+ stickers.push_back(make_object(std::move(sticker), emojis.str(), std::move(sticker_format),
+ std::move(mask_position)));
return std::move(stickers);
}
@@ -7028,6 +7114,10 @@ td::Result> Client::get_text_entity_t
TRY_RESULT(user_id, get_json_object_long_field(user.get_object(), "id", false));
return make_object(user_id);
}
+ if (type == "custom_emoji") {
+ TRY_RESULT(custom_emoji_id, get_json_object_long_field(object, "custom_emoji_id", false));
+ return make_object(custom_emoji_id);
+ }
if (type == "mention" || type == "hashtag" || type == "cashtag" || type == "bot_command" || type == "url" ||
type == "email" || type == "phone_number" || type == "bank_card_number") {
return nullptr;
@@ -9111,18 +9201,48 @@ td::Status Client::process_get_sticker_set_query(PromisedQueryPtr &query) {
return Status::OK();
}
+td::Status Client::process_get_custom_emoji_stickers_query(PromisedQueryPtr &query) {
+ TRY_RESULT(custom_emoji_ids_json, get_required_string_arg(query.get(), "custom_emoji_ids"));
+
+ LOG(INFO) << "Parsing JSON object: " << custom_emoji_ids_json;
+ auto r_value = json_decode(custom_emoji_ids_json);
+ if (r_value.is_error()) {
+ return Status::Error(400, "Can't parse custom emoji identifiers JSON object");
+ }
+ auto value = r_value.move_as_ok();
+ if (value.type() != JsonValue::Type::Array) {
+ return Status::Error(400, "Expected an Array of custom emoji identifiers");
+ }
+
+ td::vector custom_emoji_ids;
+ for (auto &custom_emoji_id : value.get_array()) {
+ if (custom_emoji_id.type() != JsonValue::Type::String) {
+ return Status::Error(400, "Custom emoji identifier must be of type String");
+ }
+ auto parsed_id = td::to_integer_safe(custom_emoji_id.get_string());
+ if (parsed_id.is_error()) {
+ return Status::Error(400, "Invalid custom emoji identifier specified");
+ }
+ custom_emoji_ids.push_back(parsed_id.ok());
+ }
+
+ send_request(make_object(std::move(custom_emoji_ids)),
+ td::make_unique(this, std::move(query)));
+ return Status::OK();
+}
+
td::Status Client::process_upload_sticker_file_query(PromisedQueryPtr &query) {
CHECK_IS_BOT();
TRY_RESULT(user_id, get_user_id(query.get()));
auto png_sticker = get_input_file(query.get(), "png_sticker");
- check_user(user_id, std::move(query),
- [this, user_id, png_sticker = std::move(png_sticker)](PromisedQueryPtr query) mutable {
- send_request(make_object(
- user_id, make_object(std::move(png_sticker), "",
- make_object())),
- td::make_unique(this, std::move(query)));
- });
+ check_user(
+ user_id, std::move(query), [this, user_id, png_sticker = std::move(png_sticker)](PromisedQueryPtr query) mutable {
+ send_request(make_object(
+ user_id, make_object(std::move(png_sticker), "",
+ make_object(), nullptr)),
+ td::make_unique(this, std::move(query)));
+ });
return Status::OK();
}
@@ -9131,14 +9251,20 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query)
TRY_RESULT(user_id, get_user_id(query.get()));
auto name = query->arg("name");
auto title = query->arg("title");
- auto is_masks = to_bool(query->arg("contains_masks"));
- TRY_RESULT(stickers, get_input_stickers(query.get(), is_masks));
+ TRY_RESULT(stickers, get_input_stickers(query.get()));
+
+ TRY_RESULT(sticker_type, get_sticker_type(query->arg("sticker_type")));
+ if (to_bool(query->arg("contains_masks"))) {
+ sticker_type = make_object();
+ }
check_user(user_id, std::move(query),
- [this, user_id, title, name, stickers = std::move(stickers)](PromisedQueryPtr query) mutable {
- send_request(make_object(user_id, title.str(), name.str(),
- std::move(stickers), PSTRING() << "bot" << my_id_),
- td::make_unique(this, false, std::move(query)));
+ [this, user_id, title, name, sticker_type = std::move(sticker_type),
+ stickers = std::move(stickers)](PromisedQueryPtr query) mutable {
+ send_request(
+ make_object(user_id, title.str(), name.str(), std::move(sticker_type),
+ std::move(stickers), PSTRING() << "bot" << my_id_),
+ td::make_unique(this, false, std::move(query)));
});
return Status::OK();
}
@@ -9147,7 +9273,7 @@ td::Status Client::process_add_sticker_to_set_query(PromisedQueryPtr &query) {
CHECK_IS_BOT();
TRY_RESULT(user_id, get_user_id(query.get()));
auto name = query->arg("name");
- TRY_RESULT(stickers, get_input_stickers(query.get(), true));
+ TRY_RESULT(stickers, get_input_stickers(query.get()));
CHECK(!stickers.empty());
check_user(user_id, std::move(query),
@@ -9818,16 +9944,6 @@ void Client::do_get_file(object_ptr file, PromisedQueryPtr query)
auto file_id = file->id_;
file_download_listeners_[file_id].push_back(std::move(query));
- if (file->local_->is_downloading_completed_) {
- Slice relative_path = td::PathView::relative(file->local_->path_, dir_, true);
- if (!relative_path.empty()) {
- auto r_stat = td::stat(file->local_->path_);
- if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ == file->size_) {
- return on_file_download(file_id, std::move(file));
- }
- }
- }
-
send_request(make_object(file_id, 1, 0, 0, false),
td::make_unique(this, file_id));
}
@@ -9854,6 +9970,10 @@ void Client::on_file_download(int32 file_id, td::Result
}
}
+void Client::return_stickers(object_ptr stickers, PromisedQueryPtr query) {
+ answer_query(JsonStickers(stickers->stickers_, this), std::move(query));
+}
+
void Client::webhook_verified(td::string cached_ip_address) {
if (get_link_token() != webhook_generation_) {
return;
@@ -9916,14 +10036,25 @@ void Client::webhook_error(Status status) {
}
void Client::webhook_closed(Status status) {
+ if (has_webhook_certificate_) {
+ td::Scheduler::instance()->run_on_scheduler(get_database_scheduler_id(),
+ [actor_id = actor_id(this), path = get_webhook_certificate_path(),
+ status = std::move(status)](td::Unit) mutable {
+ LOG(INFO) << "Unlink certificate " << path;
+ td::unlink(path).ignore();
+ send_closure(actor_id, &Client::on_webhook_closed, std::move(status));
+ });
+ return;
+ }
+ on_webhook_closed(std::move(status));
+}
+
+void Client::on_webhook_closed(Status status) {
LOG(WARNING) << "Webhook closed: " << status
<< ", webhook_query_type = " << (webhook_query_type_ == WebhookQueryType::Verify ? "verify" : "change");
webhook_id_.release();
webhook_url_ = td::string();
- if (has_webhook_certificate_) {
- td::unlink(get_webhook_certificate_path()).ignore();
- has_webhook_certificate_ = false;
- }
+ has_webhook_certificate_ = false;
webhook_max_connections_ = 0;
webhook_ip_address_ = td::string();
webhook_fix_ip_address_ = false;
@@ -10216,7 +10347,7 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu
CHECK(total_size >= updates.size());
total_size -= updates.size();
- bool need_warning = false;
+ bool need_warning = total_size > 0 && (query->start_timestamp() - previous_get_updates_finish_time_ > 10.0);
if (total_size <= MIN_PENDING_UPDATES_WARNING / 2) {
if (last_pending_update_count_ > MIN_PENDING_UPDATES_WARNING) {
need_warning = true;
@@ -10228,7 +10359,7 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu
last_pending_update_count_ *= 2;
}
}
- if (need_warning) {
+ if (need_warning && previous_get_updates_finish_time_ > 0) {
LOG(WARNING) << "Found " << updates.size() << " updates out of " << (total_size + updates.size())
<< " after last getUpdates call " << (query->start_timestamp() - previous_get_updates_finish_time_)
<< " seconds ago in " << (td::Time::now() - query->start_timestamp()) << " seconds";
@@ -10351,6 +10482,11 @@ void Client::set_user_has_private_forwards(int64 user_id, bool has_private_forwa
add_user_info(user_id)->has_private_forwards = has_private_forwards;
}
+void Client::set_user_has_restricted_voice_and_video_messages(int64 user_id,
+ bool has_restricted_voice_and_video_messages) {
+ add_user_info(user_id)->has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages;
+}
+
void Client::set_user_status(int64 user_id, object_ptr &&status) {
add_user_info(user_id)->status = std::move(status);
}
@@ -11113,6 +11249,8 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptrreply_to_message_id = reply_to_message_id;
}
+td::Slice Client::get_sticker_type(const object_ptr &type) {
+ CHECK(type != nullptr);
+ switch (type->get_id()) {
+ case td_api::stickerTypeRegular::ID:
+ return Slice("regular");
+ case td_api::stickerTypeMask::ID:
+ return Slice("mask");
+ case td_api::stickerTypeCustomEmoji::ID:
+ return Slice("custom_emoji");
+ default:
+ UNREACHABLE();
+ return Slice();
+ }
+}
+
+td::Result> Client::get_sticker_type(Slice type) {
+ if (type.empty() || type == "regular") {
+ return make_object();
+ }
+ if (type == "mask") {
+ return make_object();
+ }
+ if (type == "custom_emoji") {
+ return make_object();
+ }
+ return Status::Error(400, "Unsupported sticker type specified");
+}
+
td::CSlice Client::get_callback_data(const object_ptr &type) {
CHECK(type != nullptr);
switch (type->get_id()) {
diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h
index dd7d9886a..92bc67408 100644
--- a/telegram-bot-api/Client.h
+++ b/telegram-bot-api/Client.h
@@ -16,7 +16,6 @@
#include "td/net/HttpFile.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/actor/SignalSlot.h"
#include "td/utils/common.h"
@@ -24,6 +23,7 @@
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/JsonBuilder.h"
+#include "td/utils/Promise.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@@ -42,6 +42,11 @@ class Client final : public WebhookActor::Callback {
public:
Client(td::ActorShared<> parent, const td::string &bot_token, bool is_user, bool is_test_dc, td::int64 tqueue_id,
std::shared_ptr parameters, td::ActorId stat_actor);
+ Client(const Client &) = delete;
+ Client &operator=(const Client &) = delete;
+ Client(Client &&) = delete;
+ Client &operator=(Client &&) = delete;
+ ~Client();
void send(PromisedQueryPtr query) final;
@@ -212,6 +217,8 @@ class Client final : public WebhookActor::Callback {
class TdOnAnswerWebAppQueryCallback;
class TdOnReturnFileCallback;
class TdOnReturnStickerSetCallback;
+ class TdOnGetStickerSetPromiseCallback;
+ class TdOnGetStickersCallback;
class TdOnDownloadFileCallback;
class TdOnCancelDownloadFileCallback;
class TdOnSendCustomRequestCallback;
@@ -346,6 +353,10 @@ class Client final : public WebhookActor::Callback {
void on_closed();
void finish_closing();
+ static int32 get_database_scheduler_id();
+
+ static int32 get_file_gc_scheduler_id();
+
void clear_tqueue();
bool allow_update_before_authorization(const td_api::Object *update) const;
@@ -432,7 +443,7 @@ class Client final : public WebhookActor::Callback {
static object_ptr mask_index_to_point(int32 index);
- td::Result>> get_input_stickers(const Query *query, bool is_masks) const;
+ td::Result>> get_input_stickers(const Query *query) const;
static td::Result get_passport_element_hash(Slice encoded_hash);
@@ -599,6 +610,7 @@ class Client final : public WebhookActor::Callback {
Status process_approve_chat_join_request_query(PromisedQueryPtr &query);
Status process_decline_chat_join_request_query(PromisedQueryPtr &query);
Status process_get_sticker_set_query(PromisedQueryPtr &query);
+ Status process_get_custom_emoji_stickers_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);
@@ -663,6 +675,8 @@ class Client final : public WebhookActor::Callback {
void save_webhook() const;
td::string get_webhook_certificate_path() const;
+ void on_webhook_closed(Status status);
+
void do_send_message(object_ptr input_message_content, PromisedQueryPtr query);
int64 get_send_message_query_id(PromisedQueryPtr query, bool is_multisend);
@@ -674,6 +688,8 @@ class Client final : public WebhookActor::Callback {
bool is_file_being_downloaded(int32 file_id) const;
void on_file_download(int32 file_id, td::Result> r_file);
+ void return_stickers(object_ptr stickers, PromisedQueryPtr query);
+
void fix_reply_markup_bot_user_ids(object_ptr &reply_markup) const;
void fix_inline_query_results_bot_user_ids(td::vector> &results) const;
@@ -726,6 +742,7 @@ class Client final : public WebhookActor::Callback {
bool can_read_all_group_messages = false;
bool is_inline_bot = false;
bool has_private_forwards = false;
+ bool has_restricted_voice_and_video_messages = false;
bool is_premium = false;
bool added_to_attachment_menu = false;
};
@@ -733,7 +750,10 @@ class Client final : public WebhookActor::Callback {
void set_user_photo(int64 user_id, object_ptr &&photo);
void set_user_bio(int64 user_id, td::string &&bio);
void set_user_has_private_forwards(int64 user_id, bool has_private_forwards);
+ void set_user_has_restricted_voice_and_video_messages(int64 user_id, bool has_restricted_voice_and_video_messages);
+
void set_user_status(int64 user_id, object_ptr &&status);
+
UserInfo *add_user_info(int64 user_id);
const UserInfo *get_user_info(int64 user_id) const;
@@ -851,6 +871,10 @@ class Client final : public WebhookActor::Callback {
void set_message_reply_to_message_id(MessageInfo *message_info, int64 reply_to_message_id);
+ static Slice get_sticker_type(const object_ptr &type);
+
+ static td::Result> get_sticker_type(Slice type);
+
static td::CSlice get_callback_data(const object_ptr &type);
static bool are_equal_inline_keyboard_buttons(const td_api::inlineKeyboardButton *lhs,
diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp
index 6350a3373..a7bfa9548 100644
--- a/telegram-bot-api/ClientManager.cpp
+++ b/telegram-bot-api/ClientManager.cpp
@@ -99,8 +99,8 @@ void ClientManager::send(PromisedQueryPtr query) {
return;
}
- auto tqueue_id = get_tqueue_id(r_user_id.ok(), query->is_test_dc());
- if (active_client_count_.find(tqueue_id) != active_client_count_.end()) {
+ auto tqueue_id = get_tqueue_id(user_id, query->is_test_dc());
+ if (active_client_count_.count(tqueue_id) != 0) {
// return query->set_retry_after_error(1);
}
@@ -163,7 +163,7 @@ void ClientManager::user_login(PromisedQueryPtr query) {
parameters_, std::move(stat_actor));
clients_.get(id)->client_ = std::move(client_id);
- auto id_it = token_to_id_.end();
+ auto id_it = token_to_id_.find(user_token);
std::tie(id_it, std::ignore) = token_to_id_.emplace(user_token, id);
send_closure(client_info->client_, &Client::send, std::move(query)); // will send 429 if the client is already closed
@@ -216,7 +216,7 @@ bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_log
return true;
}
-void ClientManager::get_stats(td::PromiseActor promise,
+void ClientManager::get_stats(td::Promise promise,
td::vector> args,
bool as_json) {
if (close_flag_) {
@@ -544,7 +544,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool
auto query = td::make_unique(std::move(containers), token, is_user, is_test_dc, method, std::move(args),
td::vector>(),
td::vector(), std::move(shared_data), td::IPAddress(), true);
- return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor>()));
+ return PromisedQueryPtr(query.release(), PromiseDeleter(td::Promise>()));
}
void ClientManager::raw_event(const td::Event::Raw &event) {
diff --git a/telegram-bot-api/ClientManager.h b/telegram-bot-api/ClientManager.h
index ec1771e2f..1fc2aebe7 100644
--- a/telegram-bot-api/ClientManager.h
+++ b/telegram-bot-api/ClientManager.h
@@ -11,13 +11,13 @@
#include "telegram-bot-api/Stats.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FloodControlFast.h"
+#include "td/utils/Promise.h"
#include "td/utils/Slice.h"
#include
@@ -46,7 +46,7 @@ class ClientManager final : public td::Actor {
bool check_flood_limits(PromisedQueryPtr &query, bool is_user_login=false);
- void get_stats(td::PromiseActor promise, td::vector> args, bool as_json);
+ void get_stats(td::Promise promise, td::vector> args, bool as_json);
void close(td::Promise &&promise);
diff --git a/telegram-bot-api/HttpConnection.cpp b/telegram-bot-api/HttpConnection.cpp
index 73265b1eb..6c2c4e623 100644
--- a/telegram-bot-api/HttpConnection.cpp
+++ b/telegram-bot-api/HttpConnection.cpp
@@ -14,6 +14,7 @@
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/Parser.h"
+#include "td/utils/Promise.h"
#include "td/utils/SliceBuilder.h"
namespace telegram_bot_api {
@@ -63,26 +64,21 @@ void HttpConnection::handle(td::unique_ptr http_query,
std::move(http_query->args_), std::move(http_query->headers_),
std::move(http_query->files_), shared_data_, http_query->peer_address_, false);
- td::PromiseActor> promise;
- td::FutureActor> future;
- td::init_promise_future(&promise, &future);
- future.set_event(td::EventCreator::yield(actor_id()));
+ auto promise = td::PromiseCreator::lambda([actor_id = actor_id(this)](td::Result> r_query) {
+ send_closure(actor_id, &HttpConnection::on_query_finished, std::move(r_query));
+ });
auto promised_query = PromisedQueryPtr(query.release(), PromiseDeleter(std::move(promise)));
if (is_login) {
send_closure(client_manager_, &ClientManager::user_login, std::move(promised_query));
} else {
send_closure(client_manager_, &ClientManager::send, std::move(promised_query));
}
- result_ = std::move(future);
}
-void HttpConnection::wakeup() {
- if (result_.empty()) {
- return;
- }
- LOG_CHECK(result_.is_ok()) << result_.move_as_error();
+void HttpConnection::on_query_finished(td::Result> r_query) {
+ LOG_CHECK(r_query.is_ok()) << r_query.error();
- auto query = result_.move_as_ok();
+ auto query = r_query.move_as_ok();
send_response(query->http_status_code(), std::move(query->answer()), query->retry_after());
}
diff --git a/telegram-bot-api/HttpConnection.h b/telegram-bot-api/HttpConnection.h
index d63124470..d0d245cad 100644
--- a/telegram-bot-api/HttpConnection.h
+++ b/telegram-bot-api/HttpConnection.h
@@ -13,10 +13,10 @@
#include "td/net/HttpQuery.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
#include
@@ -32,10 +32,7 @@ class HttpConnection final : public td::HttpInboundConnection::Callback {
void handle(td::unique_ptr http_query, td::ActorOwn connection) final;
- void wakeup() final;
-
private:
- td::FutureActor> result_;
td::ActorId client_manager_;
td::ActorOwn connection_;
std::shared_ptr shared_data_;
@@ -45,6 +42,8 @@ class HttpConnection final : public td::HttpInboundConnection::Callback {
stop();
}
+ void on_query_finished(td::Result> r_query);
+
void send_response(int http_status_code, td::BufferSlice &&content, int retry_after);
void send_http_error(int http_status_code, td::Slice description);
diff --git a/telegram-bot-api/HttpStatConnection.cpp b/telegram-bot-api/HttpStatConnection.cpp
index 922f80e1a..02506cbf7 100644
--- a/telegram-bot-api/HttpStatConnection.cpp
+++ b/telegram-bot-api/HttpStatConnection.cpp
@@ -9,7 +9,7 @@
#include "td/net/HttpHeaderCreator.h"
#include "td/utils/common.h"
-#include "td/utils/logging.h"
+#include "td/utils/Promise.h"
namespace telegram_bot_api {
@@ -17,25 +17,23 @@ void HttpStatConnection::handle(td::unique_ptr http_query,
td::ActorOwn connection) {
CHECK(connection_->empty());
connection_ = std::move(connection);
-
- td::PromiseActor promise;
- td::FutureActor future;
- init_promise_future(&promise, &future);
- future.set_event(td::EventCreator::yield(actor_id()));
- LOG(DEBUG) << "SEND";
td::Parser url_path_parser(http_query->url_path_);
as_json_ = url_path_parser.try_skip("/json");
+
+ auto promise = td::PromiseCreator::lambda([actor_id = actor_id(this)](td::Result result) {
+ send_closure(actor_id, &HttpStatConnection::on_result, std::move(result));
+ });
send_closure(client_manager_, &ClientManager::get_stats, std::move(promise), http_query->get_args(), as_json_);
- result_ = std::move(future);
}
-void HttpStatConnection::wakeup() {
- if (result_.empty()) {
+void HttpStatConnection::on_result(td::Result result) {
+ if (result.is_error()) {
+ send_closure(connection_.release(), &td::HttpInboundConnection::write_error,
+ td::Status::Error(500, "Internal Server Error: closing"));
return;
}
- LOG_CHECK(result_.is_ok()) << result_.move_as_error();
- auto content = result_.move_as_ok();
+ auto content = result.move_as_ok();
td::HttpHeaderCreator hc;
hc.init_status_line(200);
hc.set_keep_alive();
diff --git a/telegram-bot-api/HttpStatConnection.h b/telegram-bot-api/HttpStatConnection.h
index f5cb5c176..013db83ce 100644
--- a/telegram-bot-api/HttpStatConnection.h
+++ b/telegram-bot-api/HttpStatConnection.h
@@ -12,9 +12,9 @@
#include "td/net/HttpQuery.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
+#include "td/utils/Status.h"
namespace telegram_bot_api {
@@ -22,16 +22,16 @@ class HttpStatConnection final : public td::HttpInboundConnection::Callback {
public:
explicit HttpStatConnection(td::ActorId client_manager) : client_manager_(client_manager) {
}
- void handle(td::unique_ptr http_query, td::ActorOwn connection) final;
- void wakeup() final;
+ void handle(td::unique_ptr http_query, td::ActorOwn connection) final;
private:
bool as_json_;
- td::FutureActor result_;
td::ActorId client_manager_;
td::ActorOwn connection_;
+ void on_result(td::Result result);
+
void hangup() final {
connection_.release();
stop();
diff --git a/telegram-bot-api/Query.h b/telegram-bot-api/Query.h
index 43aecbba8..7df19fc87 100644
--- a/telegram-bot-api/Query.h
+++ b/telegram-bot-api/Query.h
@@ -11,7 +11,6 @@
#include "td/net/HttpFile.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
@@ -19,6 +18,7 @@
#include "td/utils/JsonBuilder.h"
#include "td/utils/List.h"
#include "td/utils/port/IPAddress.h"
+#include "td/utils/Promise.h"
#include "td/utils/Slice.h"
#include "td/utils/StringBuilder.h"
@@ -231,7 +231,7 @@ class JsonQueryError final : public td::Jsonable {
class PromiseDeleter {
public:
- explicit PromiseDeleter(td::PromiseActor> &&promise) : promise_(std::move(promise)) {
+ explicit PromiseDeleter(td::Promise> &&promise) : promise_(std::move(promise)) {
}
PromiseDeleter() = default;
PromiseDeleter(const PromiseDeleter &) = delete;
@@ -240,7 +240,7 @@ class PromiseDeleter {
PromiseDeleter &operator=(PromiseDeleter &&) = default;
void operator()(Query *raw_ptr) {
td::unique_ptr query(raw_ptr); // now I cannot forget to delete this pointer
- if (!promise_.empty_promise()) {
+ if (promise_) {
if (!query->is_ready()) {
query->set_retry_after_error(5);
}
@@ -249,11 +249,11 @@ class PromiseDeleter {
}
}
~PromiseDeleter() {
- CHECK(promise_.empty());
+ CHECK(!promise_);
}
private:
- td::PromiseActor> promise_;
+ td::Promise> promise_;
};
using PromisedQueryPtr = std::unique_ptr;
diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp
index d6cd7febc..e6b772700 100644
--- a/telegram-bot-api/WebhookActor.cpp
+++ b/telegram-bot-api/WebhookActor.cpp
@@ -15,7 +15,6 @@
#include "td/net/TransparentProxy.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
@@ -26,6 +25,7 @@
#include "td/utils/misc.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/SocketFd.h"
+#include "td/utils/Promise.h"
#include "td/utils/Random.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/SliceBuilder.h"
@@ -82,7 +82,7 @@ void WebhookActor::relax_wakeup_at(double wakeup_at, const char *source) {
}
void WebhookActor::resolve_ip_address() {
- if (fix_ip_address_) {
+ if (fix_ip_address_ || is_ip_address_being_resolved_) {
return;
}
if (td::Time::now() < next_ip_address_resolve_time_) {
@@ -90,43 +90,42 @@ void WebhookActor::resolve_ip_address() {
return;
}
- bool future_created = false;
- if (future_ip_address_.empty()) {
- td::PromiseActor promise;
- init_promise_future(&promise, &future_ip_address_);
- future_created = true;
- send_closure(parameters_->get_host_by_name_actor_id_, &td::GetHostByNameActor::run, url_.host_, url_.port_, false,
- td::PromiseCreator::from_promise_actor(std::move(promise)));
- }
+ is_ip_address_being_resolved_ = true;
+ auto promise = td::PromiseCreator::lambda([actor_id = actor_id(this)](td::Result r_ip_address) {
+ send_closure(actor_id, &WebhookActor::on_resolved_ip_address, std::move(r_ip_address));
+ });
+ send_closure(parameters_->get_host_by_name_actor_id_, &td::GetHostByNameActor::run, url_.host_, url_.port_, false,
+ std::move(promise));
+}
- if (future_ip_address_.is_ready()) {
- next_ip_address_resolve_time_ =
- td::Time::now() + IP_ADDRESS_CACHE_TIME + td::Random::fast(0, IP_ADDRESS_CACHE_TIME / 10);
- relax_wakeup_at(next_ip_address_resolve_time_, "resolve_ip_address");
+void WebhookActor::on_resolved_ip_address(td::Result r_ip_address) {
+ CHECK(is_ip_address_being_resolved_);
+ is_ip_address_being_resolved_ = false;
- auto r_ip_address = future_ip_address_.move_as_result();
- if (r_ip_address.is_error()) {
- CHECK(!(r_ip_address.error() == td::Status::Error::HANGUP_ERROR_CODE>()));
- return on_error(r_ip_address.move_as_error());
- }
- auto new_ip_address = r_ip_address.move_as_ok();
- if (!check_ip_address(new_ip_address)) {
- return on_error(td::Status::Error(PSLICE() << "IP address " << new_ip_address.get_ip_str() << " is reserved"));
- }
- if (!(ip_address_ == new_ip_address)) {
- VLOG(webhook) << "IP address has changed: " << ip_address_ << " --> " << new_ip_address;
- ip_address_ = new_ip_address;
- ip_generation_++;
- if (was_checked_) {
- on_webhook_verified();
- }
- }
- VLOG(webhook) << "IP address was verified";
- } else {
- if (future_created) {
- future_ip_address_.set_event(td::EventCreator::yield(actor_id()));
+ next_ip_address_resolve_time_ =
+ td::Time::now() + IP_ADDRESS_CACHE_TIME + td::Random::fast(0, IP_ADDRESS_CACHE_TIME / 10);
+ relax_wakeup_at(next_ip_address_resolve_time_, "on_resolved_ip_address");
+
+ SCOPE_EXIT {
+ loop();
+ };
+
+ if (r_ip_address.is_error()) {
+ return on_error(r_ip_address.move_as_error());
+ }
+ auto new_ip_address = r_ip_address.move_as_ok();
+ if (!check_ip_address(new_ip_address)) {
+ return on_error(td::Status::Error(PSLICE() << "IP address " << new_ip_address.get_ip_str() << " is reserved"));
+ }
+ if (!(ip_address_ == new_ip_address)) {
+ VLOG(webhook) << "IP address has changed: " << ip_address_ << " --> " << new_ip_address;
+ ip_address_ = new_ip_address;
+ ip_generation_++;
+ if (was_checked_) {
+ on_webhook_verified();
}
}
+ VLOG(webhook) << "IP address was verified";
}
td::Status WebhookActor::create_connection() {
@@ -602,11 +601,10 @@ void WebhookActor::handle(td::unique_ptr response) {
method != "logout" && !td::begins_with(method, "get")) {
VLOG(webhook) << "Receive request " << method << " in response to webhook";
auto query = td::make_unique(std::move(response->container_), td::MutableSlice(), false, false,
- td::MutableSlice(), std::move(response->args_),
- std::move(response->headers_), std::move(response->files_),
- parameters_->shared_data_, response->peer_address_, false);
- auto promised_query =
- PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor>()));
+ td::MutableSlice(), std::move(response->args_),
+ std::move(response->headers_), std::move(response->files_),
+ parameters_->shared_data_, response->peer_address_, false);
+ auto promised_query = PromisedQueryPtr(query.release(), PromiseDeleter(td::Promise>()));
send_closure(callback_, &Callback::send, std::move(promised_query));
}
first_error_410_time_ = 0;
diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h
index d5e2499f6..28f598993 100644
--- a/telegram-bot-api/WebhookActor.h
+++ b/telegram-bot-api/WebhookActor.h
@@ -15,7 +15,6 @@
#include "td/net/SslStream.h"
#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/BufferedFd.h"
#include "td/utils/common.h"
@@ -137,7 +136,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
td::IPAddress ip_address_;
td::int32 ip_generation_ = 0;
double next_ip_address_resolve_time_ = 0;
- td::FutureActor future_ip_address_;
+ bool is_ip_address_being_resolved_ = false;
class Connection final : public td::ListNode {
public:
@@ -175,6 +174,7 @@ class WebhookActor final : public td::HttpOutboundConnection::Callback {
void relax_wakeup_at(double wakeup_at, const char *source);
void resolve_ip_address();
+ void on_resolved_ip_address(td::Result r_ip_address);
td::Result create_ssl_stream();
td::Status create_connection() TD_WARN_UNUSED_RESULT;
diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp
index 8c159a95a..4c72ae7ee 100644
--- a/telegram-bot-api/telegram-bot-api.cpp
+++ b/telegram-bot-api/telegram-bot-api.cpp
@@ -22,7 +22,6 @@
#include "td/actor/actor.h"
#include "td/actor/ConcurrentScheduler.h"
-#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
#include "td/utils/CombinedLog.h"
@@ -45,6 +44,7 @@
#include "td/utils/port/stacktrace.h"
#include "td/utils/port/Stat.h"
#include "td/utils/port/user.h"
+#include "td/utils/Promise.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
@@ -200,7 +200,7 @@ int main(int argc, char *argv[]) {
auto start_time = td::Time::now();
auto shared_data = std::make_shared();
auto parameters = std::make_unique();
- parameters->version_ = "6.1";
+ parameters->version_ = "6.2";
parameters->shared_data_ = shared_data;
parameters->start_time_ = start_time;
auto net_query_stats = td::create_net_query_stats();