diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ed7699d..aa0833a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -69,10 +69,10 @@ jobs: uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Cache Docker layers - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ env.SAFE_ARCH }}-${{ github.sha }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 894075e..8ad72e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if (POLICY CMP0065) cmake_policy(SET CMP0065 NEW) endif() -project(TelegramBotApi VERSION 6.4.1 LANGUAGES CXX) +project(TelegramBotApi VERSION 6.5 LANGUAGES CXX) if (POLICY CMP0069) option(TELEGRAM_BOT_API_ENABLE_LTO "Use \"ON\" to enable Link Time Optimization.") diff --git a/td b/td index 5ee9c73..3179d35 160000 --- a/td +++ b/td @@ -1 +1 @@ -Subproject commit 5ee9c7365b9533d207887e32a9d71645e5d30fd8 +Subproject commit 3179d35694a28267a0b6273fc9b5bdce3b6b1235 diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp index 56da745..013f2af 100644 --- a/telegram-bot-api/Client.cpp +++ b/telegram-bot-api/Client.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -819,10 +819,12 @@ class Client::JsonChat final : public Jsonable { } object("permissions", JsonChatPermissions(permissions)); } - auto everyone_is_administrator = permissions->can_send_messages_ && permissions->can_send_media_messages_ && - permissions->can_send_polls_ && permissions->can_send_other_messages_ && - permissions->can_add_web_page_previews_ && permissions->can_change_info_ && - permissions->can_invite_users_ && permissions->can_pin_messages_; + auto everyone_is_administrator = + permissions->can_send_messages_ && permissions->can_send_audios_ && permissions->can_send_documents_ && + permissions->can_send_photos_ && permissions->can_send_videos_ && permissions->can_send_video_notes_ && + permissions->can_send_voice_notes_ && permissions->can_send_polls_ && + permissions->can_send_other_messages_ && permissions->can_add_web_page_previews_ && + permissions->can_change_info_ && permissions->can_invite_users_ && permissions->can_pin_messages_; object("all_members_are_administrators", td::JsonBool(everyone_is_administrator)); photo = group_info->photo.get(); break; @@ -1825,6 +1827,34 @@ class Client::JsonChatSetMessageAutoDeleteTime final : public Jsonable { const td_api::messageChatSetMessageAutoDeleteTime *chat_set_message_auto_delete_time_; }; +class Client::JsonUserShared final : public Jsonable { + public: + explicit JsonUserShared(const td_api::messageUserShared *user_shared) : user_shared_(user_shared) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("user_id", user_shared_->user_id_); + object("request_id", user_shared_->button_id_); + } + + private: + const td_api::messageUserShared *user_shared_; +}; + +class Client::JsonChatShared final : public Jsonable { + public: + explicit JsonChatShared(const td_api::messageChatShared *chat_shared) : chat_shared_(chat_shared) { + } + void store(JsonValueScope *scope) const { + auto object = scope->enter_object(); + object("chat_id", chat_shared_->chat_id_); + object("request_id", chat_shared_->button_id_); + } + + private: + const td_api::messageChatShared *chat_shared_; +}; + class Client::JsonWebAppInfo final : public Jsonable { public: explicit JsonWebAppInfo(const td::string &url) : url_(url) { @@ -2315,6 +2345,16 @@ void Client::JsonMessage::store(JsonValueScope *scope) const { case td_api::messageBotWriteAccessAllowed::ID: object("write_access_allowed", JsonEmptyObject()); break; + case td_api::messageUserShared::ID: { + auto content = static_cast(message_->content.get()); + object("user_shared", JsonUserShared(content)); + break; + } + case td_api::messageChatShared::ID: { + auto content = static_cast(message_->content.get()); + object("chat_shared", JsonChatShared(content)); + break; + } default: UNREACHABLE(); } @@ -2825,6 +2865,7 @@ class Client::JsonChatJoinRequest final : public Jsonable { auto object = scope->enter_object(); object("chat", JsonChat(update_->chat_id_, false, client_)); object("from", JsonUser(update_->request_->user_id_, client_)); + object("user_chat_id", update_->user_chat_id_); object("date", update_->request_->date_); if (!update_->request_->bio_.empty()) { object("bio", update_->request_->bio_); @@ -4718,7 +4759,11 @@ ServerBotInfo Client::get_bot_info() const { if (user_info != nullptr) { res.username_ = user_info->editable_username; } else if (!was_authorized_) { - res.username_ = ""; + if (logging_out_) { + res.username_ = ""; + } else { + res.username_ = ""; + } } else { res.username_ = ""; } @@ -6000,7 +6045,8 @@ void Client::timeout_expired() { void Client::clear_tqueue() { CHECK(webhook_id_.empty()); auto &tqueue = parameters_->shared_data_->tqueue_; - tqueue->clear(tqueue_id_, 0); + auto deleted_events = tqueue->clear(tqueue_id_, 0); + td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), deleted_events); } bool Client::to_bool(td::MutableSlice value) { @@ -6050,6 +6096,47 @@ td::Result> Client::get_keyboard_butt return make_object(text, make_object(url)); } + if (has_json_object_field(object, "request_user")) { + TRY_RESULT(request_user, get_json_object_field(object, "request_user", JsonValue::Type::Object, false)); + auto &request_user_object = request_user.get_object(); + TRY_RESULT(id, get_json_object_int_field(request_user_object, "request_id", false)); + auto restrict_user_is_bot = has_json_object_field(request_user_object, "user_is_bot"); + TRY_RESULT(user_is_bot, get_json_object_bool_field(request_user_object, "user_is_bot")); + auto restrict_user_is_premium = has_json_object_field(request_user_object, "user_is_premium"); + TRY_RESULT(user_is_premium, get_json_object_bool_field(request_user_object, "user_is_premium")); + return make_object( + text, make_object(id, restrict_user_is_bot, user_is_bot, + restrict_user_is_premium, user_is_premium)); + } + + if (has_json_object_field(object, "request_chat")) { + TRY_RESULT(request_chat, get_json_object_field(object, "request_chat", JsonValue::Type::Object, false)); + auto &request_chat_object = request_chat.get_object(); + TRY_RESULT(id, get_json_object_int_field(request_chat_object, "request_id", false)); + TRY_RESULT(chat_is_channel, get_json_object_bool_field(request_chat_object, "chat_is_channel")); + auto restrict_chat_is_forum = has_json_object_field(request_chat_object, "chat_is_forum"); + TRY_RESULT(chat_is_forum, get_json_object_bool_field(request_chat_object, "chat_is_forum")); + auto restrict_chat_has_username = has_json_object_field(request_chat_object, "chat_has_username"); + TRY_RESULT(chat_has_username, get_json_object_bool_field(request_chat_object, "chat_has_username")); + TRY_RESULT(chat_is_created, get_json_object_bool_field(request_chat_object, "chat_is_created")); + td_api::object_ptr user_administrator_rights; + if (has_json_object_field(request_chat_object, "user_administrator_rights")) { + TRY_RESULT_ASSIGN(user_administrator_rights, get_chat_administrator_rights(get_json_object_field_force( + request_chat_object, "user_administrator_rights"))); + } + td_api::object_ptr bot_administrator_rights; + if (has_json_object_field(request_chat_object, "bot_administrator_rights")) { + TRY_RESULT_ASSIGN(bot_administrator_rights, get_chat_administrator_rights(get_json_object_field_force( + request_chat_object, "bot_administrator_rights"))); + } + TRY_RESULT(bot_is_member, get_json_object_bool_field(request_chat_object, "bot_is_member")); + return make_object( + text, make_object( + id, chat_is_channel, restrict_chat_is_forum, chat_is_forum, restrict_chat_has_username, + chat_has_username, chat_is_created, std::move(user_administrator_rights), + std::move(bot_administrator_rights), bot_is_member)); + } + return make_object(text, nullptr); } if (button.type() == JsonValue::Type::String) { @@ -7551,10 +7638,15 @@ td::Result> Client::get_location(const Quer td::to_double(horizontal_accuracy)); } -td::Result> Client::get_chat_permissions(const Query *query, - bool &allow_legacy) { +td::Result> Client::get_chat_permissions( + const Query *query, bool &allow_legacy, bool use_independent_chat_permissions) { auto can_send_messages = false; - auto can_send_media_messages = false; + auto can_send_audios = false; + auto can_send_documents = false; + auto can_send_photos = false; + auto can_send_videos = false; + auto can_send_video_notes = false; + auto can_send_voice_notes = false; auto can_send_polls = false; auto can_send_other_messages = false; auto can_add_web_page_previews = false; @@ -7580,7 +7672,6 @@ td::Result> Client::get_chat_permiss auto status = [&] { TRY_RESULT_ASSIGN(can_send_messages, get_json_object_bool_field(object, "can_send_messages")); - TRY_RESULT_ASSIGN(can_send_media_messages, get_json_object_bool_field(object, "can_send_media_messages")); TRY_RESULT_ASSIGN(can_send_polls, get_json_object_bool_field(object, "can_send_polls")); TRY_RESULT_ASSIGN(can_send_other_messages, get_json_object_bool_field(object, "can_send_other_messages")); TRY_RESULT_ASSIGN(can_add_web_page_previews, get_json_object_bool_field(object, "can_add_web_page_previews")); @@ -7592,19 +7683,60 @@ td::Result> Client::get_chat_permiss } else { can_manage_topics = can_pin_messages; } + if (has_json_object_field(object, "can_send_audios") || has_json_object_field(object, "can_send_documents") || + has_json_object_field(object, "can_send_photos") || has_json_object_field(object, "can_send_videos") || + has_json_object_field(object, "can_send_video_notes") || + has_json_object_field(object, "can_send_voice_notes")) { + TRY_RESULT_ASSIGN(can_send_audios, get_json_object_bool_field(object, "can_send_audios")); + TRY_RESULT_ASSIGN(can_send_documents, get_json_object_bool_field(object, "can_send_documents")); + TRY_RESULT_ASSIGN(can_send_photos, get_json_object_bool_field(object, "can_send_photos")); + TRY_RESULT_ASSIGN(can_send_videos, get_json_object_bool_field(object, "can_send_videos")); + TRY_RESULT_ASSIGN(can_send_video_notes, get_json_object_bool_field(object, "can_send_video_notes")); + TRY_RESULT_ASSIGN(can_send_voice_notes, get_json_object_bool_field(object, "can_send_voice_notes")); + } else { + TRY_RESULT(can_send_media_messages, get_json_object_bool_field(object, "can_send_media_messages")); + can_send_audios = can_send_media_messages; + can_send_documents = can_send_media_messages; + can_send_photos = can_send_media_messages; + can_send_videos = can_send_media_messages; + can_send_video_notes = can_send_media_messages; + can_send_voice_notes = can_send_media_messages; + if (can_send_media_messages && !use_independent_chat_permissions) { + can_send_messages = true; + } + } return Status::OK(); }(); if (status.is_error()) { return Status::Error(400, PSLICE() << "Can't parse chat permissions: " << status.message()); } + + if ((can_send_other_messages || can_add_web_page_previews) && !use_independent_chat_permissions) { + can_send_audios = true; + can_send_documents = true; + can_send_photos = true; + can_send_videos = true; + can_send_video_notes = true; + can_send_voice_notes = true; + can_send_messages = true; + } + if (can_send_polls && !use_independent_chat_permissions) { + can_send_messages = true; + } } else if (allow_legacy) { allow_legacy = false; can_send_messages = to_bool(query->arg("can_send_messages")); - can_send_media_messages = to_bool(query->arg("can_send_media_messages")); + bool can_send_media_messages = to_bool(query->arg("can_send_media_messages")); can_send_other_messages = to_bool(query->arg("can_send_other_messages")); can_add_web_page_previews = to_bool(query->arg("can_add_web_page_previews")); + if ((can_send_other_messages || can_add_web_page_previews) && !use_independent_chat_permissions) { + can_send_media_messages = true; + } + if (can_send_media_messages && !use_independent_chat_permissions) { + can_send_messages = true; + } if (can_send_messages && can_send_media_messages && can_send_other_messages && can_add_web_page_previews) { // legacy unrestrict @@ -7617,14 +7749,19 @@ td::Result> Client::get_chat_permiss query->has_arg("can_send_other_messages") || query->has_arg("can_add_web_page_previews")) { allow_legacy = true; } + + can_send_audios = can_send_media_messages; + can_send_documents = can_send_media_messages; + can_send_photos = can_send_media_messages; + can_send_videos = can_send_media_messages; + can_send_video_notes = can_send_media_messages; + can_send_voice_notes = can_send_media_messages; } - if (can_send_other_messages || can_add_web_page_previews) { - can_send_media_messages = true; - } - return make_object(can_send_messages, can_send_media_messages, can_send_polls, - can_send_other_messages, can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages, can_manage_topics); + return make_object(can_send_messages, can_send_audios, can_send_documents, can_send_photos, + can_send_videos, can_send_video_notes, can_send_voice_notes, + can_send_polls, can_send_other_messages, can_add_web_page_previews, + can_change_info, can_invite_users, can_pin_messages, can_manage_topics); } td::Result> Client::get_input_media(const Query *query, @@ -9123,7 +9260,8 @@ td::Status Client::process_set_chat_title_query(PromisedQueryPtr &query) { td::Status Client::process_set_chat_permissions_query(PromisedQueryPtr &query) { auto chat_id = query->arg("chat_id"); bool allow_legacy = false; - TRY_RESULT(permissions, get_chat_permissions(query.get(), allow_legacy)); + auto use_independent_chat_permissions = to_bool(query->arg("use_independent_chat_permissions")); + TRY_RESULT(permissions, get_chat_permissions(query.get(), allow_legacy, use_independent_chat_permissions)); CHECK(!allow_legacy); check_chat(chat_id, AccessRights::Write, std::move(query), @@ -9575,7 +9713,8 @@ td::Status Client::process_restrict_chat_member_query(PromisedQueryPtr &query) { TRY_RESULT(user_id, get_user_id(query.get())); int32 until_date = get_integer_arg(query.get(), "until_date", 0); bool allow_legacy = true; - TRY_RESULT(permissions, get_chat_permissions(query.get(), allow_legacy)); + auto use_independent_chat_permissions = to_bool(query->arg("use_independent_chat_permissions")); + TRY_RESULT(permissions, get_chat_permissions(query.get(), allow_legacy, use_independent_chat_permissions)); check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id, until_date, is_legacy = allow_legacy, permissions = std::move(permissions)]( @@ -10303,10 +10442,10 @@ td::Status Client::process_create_chat_query(PromisedQueryPtr &query) { auto description = query->arg("description"); auto message_auto_delete_time = get_integer_arg(query.get(), "message_auto_delete_time", 0); if (chat_type == "supergroup") { - send_request(make_object(title.str(), false, description.str(), nullptr, message_auto_delete_time, false), + send_request(make_object(title.str(), false, false, description.str(), nullptr, message_auto_delete_time, false), td::make_unique(this, std::move(query))); } else if (chat_type == "channel") { - send_request(make_object(title.str(), true, description.str(), nullptr, message_auto_delete_time, false), + send_request(make_object(title.str(), false, true, description.str(), nullptr, message_auto_delete_time, false), td::make_unique(this, std::move(query))); } else if (chat_type == "group") { TRY_RESULT(initial_members, get_int_array_arg(query.get(), "user_ids")) @@ -10920,7 +11059,8 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu LOG(DEBUG) << "Queue head = " << tqueue->get_head(tqueue_id_) << ", queue tail = " << tqueue->get_tail(tqueue_id_); if (offset < 0) { - tqueue->clear(tqueue_id_, -offset); + auto deleted_events = tqueue->clear(tqueue_id_, -offset); + td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), deleted_events); } if (offset <= 0) { offset = tqueue->get_head(tqueue_id_).value(); @@ -11375,8 +11515,17 @@ void Client::json_store_administrator_rights(td::JsonObjectScope &object, const } void Client::json_store_permissions(td::JsonObjectScope &object, const td_api::chatPermissions *permissions) { + bool can_send_media_messages = permissions->can_send_audios_ || permissions->can_send_documents_ || + permissions->can_send_photos_ || permissions->can_send_videos_ || + permissions->can_send_video_notes_ || permissions->can_send_voice_notes_; object("can_send_messages", td::JsonBool(permissions->can_send_messages_)); - object("can_send_media_messages", td::JsonBool(permissions->can_send_media_messages_)); + object("can_send_media_messages", td::JsonBool(can_send_media_messages)); + object("can_send_audios", td::JsonBool(permissions->can_send_audios_)); + object("can_send_documents", td::JsonBool(permissions->can_send_documents_)); + object("can_send_photos", td::JsonBool(permissions->can_send_photos_)); + object("can_send_videos", td::JsonBool(permissions->can_send_videos_)); + object("can_send_video_notes", td::JsonBool(permissions->can_send_video_notes_)); + object("can_send_voice_notes", td::JsonBool(permissions->can_send_voice_notes_)); object("can_send_polls", td::JsonBool(permissions->can_send_polls_)); object("can_send_other_messages", td::JsonBool(permissions->can_send_other_messages_)); object("can_add_web_page_previews", td::JsonBool(permissions->can_add_web_page_previews_)); diff --git a/telegram-bot-api/Client.h b/telegram-bot-api/Client.h index 169e5b4..8088e61 100644 --- a/telegram-bot-api/Client.h +++ b/telegram-bot-api/Client.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -173,6 +173,8 @@ class Client final : public WebhookActor::Callback { class JsonVideoChatEnded; class JsonInviteVideoChatParticipants; class JsonChatSetMessageAutoDeleteTime; + class JsonUserShared; + class JsonChatShared; class JsonUpdateTypes; class JsonWebhookInfo; class JsonStickerSet; @@ -484,7 +486,8 @@ class Client final : public WebhookActor::Callback { static td::Result> get_location(const Query *query); - static td::Result> get_chat_permissions(const Query *query, bool &allow_legacy); + static td::Result> get_chat_permissions(const Query *query, bool &allow_legacy, + bool use_independent_chat_permissions); td::Result> get_input_media(const Query *query, td::JsonValue &&input_media, bool for_album) const; diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp index 67f06db..6815302 100644 --- a/telegram-bot-api/ClientManager.cpp +++ b/telegram-bot-api/ClientManager.cpp @@ -221,6 +221,39 @@ bool ClientManager::check_flood_limits(PromisedQueryPtr &query, bool is_user_log return true; } +ClientManager::TopClients ClientManager::get_top_clients(std::size_t max_count, td::Slice token_filter) { + auto now = td::Time::now(); + TopClients result; + td::vector> top_client_ids; + for (auto id : clients_.ids()) { + auto *client_info = clients_.get(id); + CHECK(client_info); + + if (client_info->stat_.is_active(now)) { + result.active_count++; + } + + if (!td::begins_with(client_info->token_, token_filter)) { + continue; + } + + auto score = static_cast(client_info->stat_.get_score(now) * -1e9); + if (score == 0 && top_client_ids.size() >= max_count) { + continue; + } + top_client_ids.emplace_back(score, id); + } + if (top_client_ids.size() < max_count) { + max_count = top_client_ids.size(); + } + std::partial_sort(top_client_ids.begin(), top_client_ids.begin() + max_count, top_client_ids.end()); + result.top_client_ids.reserve(max_count); + for (std::size_t i = 0; i < max_count; i++) { + result.top_client_ids.push_back(top_client_ids[i].second); + } + return result; +} + void ClientManager::get_stats(td::Promise promise, td::vector> args, bool as_json) { @@ -266,32 +299,7 @@ void ClientManager::get_stats(td::Promise promise, } auto now = td::Time::now(); - td::int32 active_bot_count = 0; - td::vector> top_bot_ids; - size_t max_bots = 50; - for (auto id : clients_.ids()) { - auto *client_info = clients_.get(id); - CHECK(client_info); - - if (client_info->stat_.is_active(now)) { - active_bot_count++; - } - - if (!td::begins_with(client_info->token_, id_filter)) { - continue; - } - - auto score = static_cast(client_info->stat_.get_score(now) * -1e9); - if (score == 0 && top_bot_ids.size() >= max_bots) { - continue; - } - top_bot_ids.emplace_back(score, id); - } - if (top_bot_ids.size() < max_bots) { - max_bots = top_bot_ids.size(); - } - std::partial_sort(top_bot_ids.begin(), top_bot_ids.begin() + max_bots, top_bot_ids.end()); - top_bot_ids.resize(max_bots); + auto top_clients = get_top_clients(50, id_filter); if(!as_json) { sb << stat_.get_description() << '\n'; @@ -308,9 +316,9 @@ void ClientManager::get_stats(td::Promise promise, sb << "bot_count\t" << clients_.size() << '\n'; } if(as_json) { - jb_root("active_bot_count", td::JsonInt(active_bot_count)); + jb_root("active_bot_count", td::JsonInt(top_clients.active_count)); } else { - sb << "active_bot_count\t" << active_bot_count << '\n'; + sb << "active_bot_count\t" << top_clients.active_count << '\n'; } auto r_mem_stat = td::mem_stat(); if (r_mem_stat.is_ok()) { @@ -364,8 +372,8 @@ void ClientManager::get_stats(td::Promise promise, if(as_json) { td::vector bots; - for (std::pair top_bot_id : top_bot_ids) { - auto client_info = clients_.get(top_bot_id.second); + for (auto top_client_id : top_clients.top_client_ids) { + auto client_info = clients_.get(top_client_id); CHECK(client_info); ServerBotInfo bot_info = client_info->client_.get_actor_unsafe()->get_bot_info(); auto active_request_count = client_info->stat_.get_active_request_count(); @@ -373,15 +381,15 @@ void ClientManager::get_stats(td::Promise promise, auto active_file_upload_count = client_info->stat_.get_active_file_upload_count(); auto stats = client_info->stat_.as_json_ready_vector(now); JsonStatsBotAdvanced bot( - std::move(top_bot_id), std::move(bot_info), active_request_count, active_file_upload_bytes, active_file_upload_count, std::move(stats), parameters_->stats_hide_sensible_data_, now + std::move(top_client_id), std::move(bot_info), active_request_count, active_file_upload_bytes, active_file_upload_count, std::move(stats), parameters_->stats_hide_sensible_data_, now ); bots.push_back(bot); } auto bot_count = bots.size(); jb_root("bots", JsonStatsBots(std::move(bots), bot_count > 100)); } else { - for (auto top_bot_id : top_bot_ids) { - auto *client_info = clients_.get(top_bot_id.second); + for (auto top_client_id : top_clients.top_client_ids) { + auto *client_info = clients_.get(top_client_id); CHECK(client_info); auto bot_info = client_info->client_.get_actor_unsafe()->get_bot_info(); auto active_request_count = client_info->stat_.get_active_request_count(); @@ -525,7 +533,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool td::vector containers; auto add_string = [&containers](td::Slice str) { containers.emplace_back(str); - return containers.back().as_slice(); + return containers.back().as_mutable_slice(); }; token = add_string(token); @@ -631,6 +639,37 @@ void ClientManager::dump_statistics() { } td::dump_pending_network_queries(*parameters_->net_query_stats_); + + auto now = td::Time::now(); + auto top_clients = get_top_clients(10, {}); + for (auto top_client_id : top_clients.top_client_ids) { + auto *client_info = clients_.get(top_client_id); + CHECK(client_info); + + auto bot_info = client_info->client_.get_actor_unsafe()->get_bot_info(); + td::string update_count; + td::string request_count; + auto replace_tabs = [](td::string &str) { + for (auto &c : str) { + if (c == '\t') { + c = ' '; + } + } + }; + auto stats = client_info->stat_.as_vector(now); + for (auto &stat : stats) { + if (stat.key_ == "update_count") { + replace_tabs(stat.value_); + update_count = std::move(stat.value_); + } + if (stat.key_ == "request_count") { + replace_tabs(stat.value_); + request_count = std::move(stat.value_); + } + } + LOG(WARNING) << td::tag("id", bot_info.id_) << td::tag("update_count", update_count) + << td::tag("request_count", request_count); + } } void ClientManager::raw_event(const td::Event::Raw &event) { diff --git a/telegram-bot-api/ClientManager.h b/telegram-bot-api/ClientManager.h index d8a2a40..ff96f72 100644 --- a/telegram-bot-api/ClientManager.h +++ b/telegram-bot-api/ClientManager.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -79,13 +79,19 @@ class ClientManager final : public td::Actor { td::int64 tqueue_deleted_events_ = 0; td::int64 last_tqueue_deleted_events_ = 0; - static constexpr double WATCHDOG_TIMEOUT = 0.5; + static constexpr double WATCHDOG_TIMEOUT = 0.25; static td::int64 get_tqueue_id(td::int64 user_id, bool is_test_dc); static PromisedQueryPtr get_webhook_restore_query(td::Slice token, bool is_user, td::Slice webhook_info, std::shared_ptr shared_data); + struct TopClients { + td::int32 active_count = 0; + td::vector top_client_ids; + }; + TopClients get_top_clients(std::size_t max_count, td::Slice token_filter); + void start_up() final; void raw_event(const td::Event::Raw &event) final; void timeout_expired() final; diff --git a/telegram-bot-api/ClientParameters.h b/telegram-bot-api/ClientParameters.h index 7b71ad5..1de2489 100644 --- a/telegram-bot-api/ClientParameters.h +++ b/telegram-bot-api/ClientParameters.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/HttpConnection.cpp b/telegram-bot-api/HttpConnection.cpp index aa902f2..fd8166a 100644 --- a/telegram-bot-api/HttpConnection.cpp +++ b/telegram-bot-api/HttpConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/HttpConnection.h b/telegram-bot-api/HttpConnection.h index d0d245c..0e3b39c 100644 --- a/telegram-bot-api/HttpConnection.h +++ b/telegram-bot-api/HttpConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/HttpServer.h b/telegram-bot-api/HttpServer.h index 9699835..d5f0008 100644 --- a/telegram-bot-api/HttpServer.h +++ b/telegram-bot-api/HttpServer.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/HttpStatConnection.cpp b/telegram-bot-api/HttpStatConnection.cpp index 23cd293..e6e678e 100644 --- a/telegram-bot-api/HttpStatConnection.cpp +++ b/telegram-bot-api/HttpStatConnection.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/HttpStatConnection.h b/telegram-bot-api/HttpStatConnection.h index 013db83..a249aea 100644 --- a/telegram-bot-api/HttpStatConnection.h +++ b/telegram-bot-api/HttpStatConnection.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/Query.cpp b/telegram-bot-api/Query.cpp index f017e6a..9429564 100644 --- a/telegram-bot-api/Query.cpp +++ b/telegram-bot-api/Query.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/Query.h b/telegram-bot-api/Query.h index 6c360ba..18cf010 100644 --- a/telegram-bot-api/Query.h +++ b/telegram-bot-api/Query.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -117,6 +117,8 @@ class Query final : public td::ListNode { if (!empty()) { shared_data_->query_list_size_.fetch_sub(1, std::memory_order_relaxed); } + td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), container_, args_, + headers_, files_, answer_); } } diff --git a/telegram-bot-api/Stats.cpp b/telegram-bot-api/Stats.cpp index c9bc343..014568c 100644 --- a/telegram-bot-api/Stats.cpp +++ b/telegram-bot-api/Stats.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/Stats.h b/telegram-bot-api/Stats.h index 89c3300..f3ed265 100644 --- a/telegram-bot-api/Stats.h +++ b/telegram-bot-api/Stats.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/StatsJson.h b/telegram-bot-api/StatsJson.h index 92531da..a655ee3 100644 --- a/telegram-bot-api/StatsJson.h +++ b/telegram-bot-api/StatsJson.h @@ -123,16 +123,15 @@ class JsonStatsCpu : public td::Jsonable { class JsonStatsBot : public td::Jsonable { public: - explicit JsonStatsBot(std::pair score_id_pair) : score_id_pair_(std::move(score_id_pair)) { + explicit JsonStatsBot(td::uint64 client_id) : client_id_(client_id) { } void store(td::JsonValueScope *scope) const { auto object = scope->enter_object(); - object("score", td::JsonLong(score_id_pair_.first)); - object("internal_id", td::JsonLong(score_id_pair_.second)); + object("client_id", td::JsonLong(client_id_)); } protected: - const std::pair score_id_pair_; + const td::uint64 client_id_; }; class JsonStatsBotStatDouble : public td::Jsonable { @@ -198,7 +197,7 @@ class JsonStatsBotStats : public td::Jsonable { class JsonStatsBotAdvanced : public JsonStatsBot { public: - explicit JsonStatsBotAdvanced(std::pair score_id_pair, + explicit JsonStatsBotAdvanced(td::uint64 client_id, ServerBotInfo bot, td::int64 active_request_count, td::int64 active_file_upload_bytes, @@ -206,7 +205,7 @@ class JsonStatsBotAdvanced : public JsonStatsBot { td::vector stats, const bool hide_sensible_data, const double now) - : JsonStatsBot(std::move(score_id_pair)), bot_(std::move(bot)), active_request_count_(active_request_count), + : JsonStatsBot(client_id), bot_(std::move(bot)), active_request_count_(active_request_count), active_file_upload_bytes_(active_file_upload_bytes), active_file_upload_count_(active_file_upload_count), stats_(std::move(stats)), hide_sensible_data_(hide_sensible_data), now_(now) { } @@ -214,8 +213,7 @@ class JsonStatsBotAdvanced : public JsonStatsBot { auto object = scope->enter_object(); object("id", td::JsonLong(td::to_integer(bot_.id_))); object("uptime", now_ - bot_.start_time_); - object("score", td::JsonLong(score_id_pair_.first)); - object("internal_id", td::JsonLong(score_id_pair_.second)); + object("client_id", td::JsonLong(client_id_)); if (!hide_sensible_data_) { object("token", td::JsonString(bot_.token_)); } diff --git a/telegram-bot-api/Watchdog.cpp b/telegram-bot-api/Watchdog.cpp index 8b8105e..1bb15e6 100644 --- a/telegram-bot-api/Watchdog.cpp +++ b/telegram-bot-api/Watchdog.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/Watchdog.h b/telegram-bot-api/Watchdog.h index 5ab0115..89ac3c6 100644 --- a/telegram-bot-api/Watchdog.h +++ b/telegram-bot-api/Watchdog.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp index 7701605..af97634 100644 --- a/telegram-bot-api/WebhookActor.cpp +++ b/telegram-bot-api/WebhookActor.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -504,7 +504,8 @@ void WebhookActor::on_update_error(td::TQueue::EventId event_id, td::Slice error int next_delay = update.delay_; int next_effective_delay = retry_after; if (retry_after == 0 && update.fail_count_ > 0) { - next_delay = td::min(WEBHOOK_MAX_RESEND_TIMEOUT, next_delay * 2); + auto max_timeout = td::Random::fast(WEBHOOK_MAX_RESEND_TIMEOUT, WEBHOOK_MAX_RESEND_TIMEOUT * 2); + next_delay = td::min(max_timeout, next_delay * 2); next_effective_delay = next_delay; } if (parameters_->shared_data_->get_unix_time(now) + next_effective_delay > update.expires_at_) { @@ -589,6 +590,11 @@ void WebhookActor::send_updates() { } void WebhookActor::handle(td::unique_ptr response) { + SCOPE_EXIT { + bool dummy = false; + td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), response, dummy); + }; + auto connection_id = get_link_token(); if (response) { VLOG(webhook) << "Got response from connection " << connection_id; @@ -686,7 +692,7 @@ void WebhookActor::start_up() { next_ip_address_resolve_time_ = last_success_time_ = td::Time::now() - 3600; - active_new_connection_flood_.add_limit(1, 20); + active_new_connection_flood_.add_limit(0.5, 10); pending_new_connection_flood_.add_limit(2, 1); diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h index ab3b8af..926a914 100644 --- a/telegram-bot-api/WebhookActor.h +++ b/telegram-bot-api/WebhookActor.h @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp index 7b3f55a..363101f 100644 --- a/telegram-bot-api/telegram-bot-api.cpp +++ b/telegram-bot-api/telegram-bot-api.cpp @@ -1,5 +1,5 @@ // -// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -165,7 +165,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.4.1"; + parameters->version_ = "6.5"; parameters->shared_data_ = shared_data; parameters->start_time_ = start_time; auto net_query_stats = td::create_net_query_stats(); @@ -304,7 +304,7 @@ int main(int argc, char *argv[]) { options.add_checked_option('\0', "file-expiration-time", PSLICE() << "downloaded files expire after this amount of seconds of not being used (defaults to " << parameters->file_expiration_timeout_seconds_ << ")", td::OptionParser::parse_integer(parameters->file_expiration_timeout_seconds_)); - + options.add_checked_option('\0', "proxy", "HTTP proxy server for outgoing webhook requests in the format http://host:port", [&](td::Slice address) { @@ -474,7 +474,7 @@ int main(int argc, char *argv[]) { // LOG(WARNING) << "Bot API server with commit " << td::GitInfo::commit() << ' ' // << (td::GitInfo::is_dirty() ? "(dirty)" : "") << " started"; - LOG(WARNING) << "Bot API " << parameters->version_ << " server started"; + LOG(WARNING) << "TDLight Bot API " << parameters->version_ << " server started"; // +3 threads for Td // one thread for ClientManager and all Clients @@ -514,7 +514,7 @@ int main(int argc, char *argv[]) { .release(); } - constexpr double WATCHDOG_TIMEOUT = 0.5; + constexpr double WATCHDOG_TIMEOUT = 0.25; auto watchdog_id = sched.create_actor_unsafe(thread_count - 2, "Watchdog", td::this_thread::get_id(), WATCHDOG_TIMEOUT);