diff --git a/CMakeLists.txt b/CMakeLists.txt
index e875fcf..95f1aa8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
-project(TelegramBotApi VERSION 5.3.3 LANGUAGES CXX)
+project(TelegramBotApi VERSION 5.4 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 2b7a006..ad2503b 100644
--- a/build.html
+++ b/build.html
@@ -250,7 +250,7 @@ function onOptionsChanged() {
pre_text.push('Download and install Git.');
}
if (os_linux && linux_distro === 'Other') {
- var compiler = use_clang ? 'clang >= 3.4' : 'g++ >= 4.9.2';
+ var compiler = use_clang ? 'clang >= 3.4, libc++' : '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) {
diff --git a/td b/td
index 0126cec..eb346f5 160000
--- a/td
+++ b/td
@@ -1 +1 @@
-Subproject commit 0126cec2686e3b95cc1b6dfb5676d364da0e091b
+Subproject commit eb346f5573040803d4424049dd2ba8aaa039fa56
diff --git a/telegram-bot-api/Client.cpp b/telegram-bot-api/Client.cpp
index 8edaa7f..563dfa7 100644
--- a/telegram-bot-api/Client.cpp
+++ b/telegram-bot-api/Client.cpp
@@ -75,8 +75,8 @@ void Client::fail_query_with_error(PromisedQueryPtr query, int32 error_code, Sli
}
int32 real_error_code = error_code;
Slice real_error_message = error_message;
- if (error_code < 300 || error_code == 404) {
- if (error_code <= 0) {
+ if (error_code < 400 || error_code == 404) {
+ if (error_code < 200) {
LOG(ERROR) << "Receive error \"" << real_error_message << "\" with code " << error_code << " from " << *query;
}
@@ -278,6 +278,8 @@ bool Client::init_methods() {
methods_.emplace("kickchatmember", &Client::process_ban_chat_member_query);
methods_.emplace("restrictchatmember", &Client::process_restrict_chat_member_query);
methods_.emplace("unbanchatmember", &Client::process_unban_chat_member_query);
+ 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("uploadstickerfile", &Client::process_upload_sticker_file_query);
methods_.emplace("createnewstickerset", &Client::process_create_new_sticker_set_query);
@@ -624,6 +626,9 @@ class Client::JsonChatInviteLink : public Jsonable {
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("invite_link", chat_invite_link_->invite_link_);
+ if (!chat_invite_link_->name_.empty()) {
+ object("name", chat_invite_link_->name_);
+ }
object("creator", JsonUser(chat_invite_link_->creator_user_id_, client_));
if (chat_invite_link_->expire_date_ != 0) {
object("expire_date", chat_invite_link_->expire_date_);
@@ -631,6 +636,10 @@ class Client::JsonChatInviteLink : public Jsonable {
if (chat_invite_link_->member_limit_ != 0) {
object("member_limit", chat_invite_link_->member_limit_);
}
+ if (chat_invite_link_->pending_join_request_count_ != 0) {
+ object("pending_join_request_count", chat_invite_link_->pending_join_request_count_);
+ }
+ object("creates_join_request", td::JsonBool(chat_invite_link_->creates_join_request_));
object("is_primary", td::JsonBool(chat_invite_link_->is_primary_));
object("is_revoked", td::JsonBool(chat_invite_link_->is_revoked_));
}
@@ -1519,54 +1528,54 @@ class Client::JsonProximityAlertTriggered : public Jsonable {
const Client *client_;
};
-class Client::JsonVoiceChatScheduled : public Jsonable {
+class Client::JsonVideoChatScheduled : public Jsonable {
public:
- explicit JsonVoiceChatScheduled(const td_api::messageVoiceChatScheduled *voice_chat_scheduled)
- : voice_chat_scheduled_(voice_chat_scheduled) {
+ explicit JsonVideoChatScheduled(const td_api::messageVideoChatScheduled *video_chat_scheduled)
+ : video_chat_scheduled_(video_chat_scheduled) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
- object("start_date", voice_chat_scheduled_->start_date_);
+ object("start_date", video_chat_scheduled_->start_date_);
}
private:
- const td_api::messageVoiceChatScheduled *voice_chat_scheduled_;
+ const td_api::messageVideoChatScheduled *video_chat_scheduled_;
};
-class Client::JsonVoiceChatStarted : public Jsonable {
+class Client::JsonVideoChatStarted : public Jsonable {
public:
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
}
};
-class Client::JsonVoiceChatEnded : public Jsonable {
+class Client::JsonVideoChatEnded : public Jsonable {
public:
- explicit JsonVoiceChatEnded(const td_api::messageVoiceChatEnded *voice_chat_ended)
- : voice_chat_ended_(voice_chat_ended) {
+ explicit JsonVideoChatEnded(const td_api::messageVideoChatEnded *video_chat_ended)
+ : video_chat_ended_(video_chat_ended) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
- object("duration", voice_chat_ended_->duration_);
+ object("duration", video_chat_ended_->duration_);
}
private:
- const td_api::messageVoiceChatEnded *voice_chat_ended_;
+ const td_api::messageVideoChatEnded *video_chat_ended_;
};
-class Client::JsonInviteVoiceChatParticipants : public Jsonable {
+class Client::JsonInviteVideoChatParticipants : public Jsonable {
public:
- JsonInviteVoiceChatParticipants(const td_api::messageInviteVoiceChatParticipants *invite_voice_chat_participants,
+ JsonInviteVideoChatParticipants(const td_api::messageInviteVideoChatParticipants *invite_video_chat_participants,
const Client *client)
- : invite_voice_chat_participants_(invite_voice_chat_participants), client_(client) {
+ : invite_video_chat_participants_(invite_video_chat_participants), client_(client) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
- object("users", JsonUsers(invite_voice_chat_participants_->user_ids_, client_));
+ object("users", JsonUsers(invite_video_chat_participants_->user_ids_, client_));
}
private:
- const td_api::messageInviteVoiceChatParticipants *invite_voice_chat_participants_;
+ const td_api::messageInviteVideoChatParticipants *invite_video_chat_participants_;
const Client *client_;
};
@@ -1862,6 +1871,14 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
}
break;
}
+ case td_api::messageChatJoinByRequest::ID: {
+ if (message_->sender_user_id > 0) {
+ object("new_chat_participant", JsonUser(message_->sender_user_id, client_));
+ object("new_chat_member", JsonUser(message_->sender_user_id, client_));
+ object("new_chat_members", JsonUsers({message_->sender_user_id}, client_));
+ }
+ break;
+ }
case td_api::messageChatDeleteMember::ID: {
auto message_delete_member = static_cast(message_->content.get());
int64 user_id = message_delete_member->user_id_;
@@ -1959,6 +1976,9 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
break;
case td_api::messageChatSetTheme::ID:
break;
+ case td_api::messageAnimatedEmoji::ID:
+ UNREACHABLE();
+ break;
case td_api::messageWebsiteConnected::ID: {
auto chat = client_->get_chat(message_->chat_id);
if (chat->type != ChatInfo::Type::Private) {
@@ -1984,22 +2004,22 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
object("proximity_alert_triggered", JsonProximityAlertTriggered(content, client_));
break;
}
- case td_api::messageVoiceChatScheduled::ID: {
- auto content = static_cast(message_->content.get());
- object("voice_chat_scheduled", JsonVoiceChatScheduled(content));
+ case td_api::messageVideoChatScheduled::ID: {
+ auto content = static_cast(message_->content.get());
+ object("voice_chat_scheduled", JsonVideoChatScheduled(content));
break;
}
- case td_api::messageVoiceChatStarted::ID:
- object("voice_chat_started", JsonVoiceChatStarted());
+ case td_api::messageVideoChatStarted::ID:
+ object("voice_chat_started", JsonVideoChatStarted());
break;
- case td_api::messageVoiceChatEnded::ID: {
- auto content = static_cast(message_->content.get());
- object("voice_chat_ended", JsonVoiceChatEnded(content));
+ case td_api::messageVideoChatEnded::ID: {
+ auto content = static_cast(message_->content.get());
+ object("voice_chat_ended", JsonVideoChatEnded(content));
break;
}
- case td_api::messageInviteVoiceChatParticipants::ID: {
- auto content = static_cast(message_->content.get());
- object("voice_chat_participants_invited", JsonInviteVoiceChatParticipants(content, client_));
+ case td_api::messageInviteVideoChatParticipants::ID: {
+ auto content = static_cast(message_->content.get());
+ object("voice_chat_participants_invited", JsonInviteVideoChatParticipants(content, client_));
break;
}
default:
@@ -2370,7 +2390,7 @@ class Client::JsonChatMember : public Jsonable {
object("can_pin_messages", td::JsonBool(administrator->can_pin_messages_));
}
object("can_promote_members", td::JsonBool(administrator->can_promote_members_));
- object("can_manage_voice_chats", td::JsonBool(administrator->can_manage_voice_chats_));
+ object("can_manage_voice_chats", td::JsonBool(administrator->can_manage_video_chats_));
if (!administrator->custom_title_.empty()) {
object("custom_title", administrator->custom_title_);
}
@@ -2474,6 +2494,29 @@ class Client::JsonChatMemberUpdated : public Jsonable {
const Client *client_;
};
+class Client::JsonChatJoinRequest : public Jsonable {
+ public:
+ JsonChatJoinRequest(const td_api::updateNewChatJoinRequest *update, const Client *client)
+ : update_(update), client_(client) {
+ }
+ void store(JsonValueScope *scope) const {
+ auto object = scope->enter_object();
+ object("chat", JsonChat(update_->chat_id_, false, client_));
+ object("from", JsonUser(update_->request_->user_id_, client_));
+ object("date", update_->request_->date_);
+ if (!update_->request_->bio_.empty()) {
+ object("bio", update_->request_->bio_);
+ }
+ if (update_->invite_link_ != nullptr) {
+ object("invite_link", JsonChatInviteLink(update_->invite_link_.get(), client_));
+ }
+ }
+
+ private:
+ const td_api::updateNewChatJoinRequest *update_;
+ const Client *client_;
+};
+
class Client::JsonGameHighScore : public Jsonable {
public:
JsonGameHighScore(const td_api::gameHighScore *score, const Client *client) : score_(score), client_(client) {
@@ -4116,7 +4159,7 @@ void Client::raw_event(const td::Event::Raw &event) {
}
void Client::loop() {
- if (logging_out_ || closing_ || was_authorized_ || waiting_for_auth_input_) {
+ if (was_authorized_ || logging_out_ || closing_ || waiting_for_auth_input_) {
while (!cmd_queue_.empty()) {
auto query = std::move(cmd_queue_.front());
cmd_queue_.pop();
@@ -4741,6 +4784,7 @@ void Client::on_update_authorization_state() {
if (!was_authorized_) {
LOG(WARNING) << "Logged in as @" << user_info->username;
was_authorized_ = true;
+ td::send_event(parent_, td::Event::raw(static_cast(this)));
update_shared_unix_time_difference();
if (!pending_updates_.empty()) {
LOG(INFO) << "Process " << pending_updates_.size() << " pending updates";
@@ -4757,15 +4801,21 @@ void Client::on_update_authorization_state() {
if (!logging_out_) {
LOG(WARNING) << "Logging out";
logging_out_ = true;
+ if (was_authorized_ && !closing_) {
+ td::send_event(parent_, td::Event::raw(nullptr));
+ }
}
- break;
+ return loop();
case td_api::authorizationStateClosing::ID:
waiting_for_auth_input_ = false;
if (!closing_) {
LOG(WARNING) << "Closing";
closing_ = true;
+ if (was_authorized_ && !logging_out_) {
+ td::send_event(parent_, td::Event::raw(nullptr));
+ }
}
- break;
+ return loop();
case td_api::authorizationStateClosed::ID:
return on_closed();
default:
@@ -5064,6 +5114,9 @@ void Client::on_update(object_ptr result) {
case td_api::updateChatMember::ID:
add_update_chat_member(move_object_as(result));
break;
+ case td_api::updateNewChatJoinRequest::ID:
+ add_update_chat_join_request(move_object_as(result));
+ break;
default:
// we are not interested in this update
break;
@@ -5121,6 +5174,7 @@ void Client::on_closed() {
}
on_message_send_failed(chat_id, message_id, 0, Status::Error(http_status_code, description));
}
+ CHECK(yet_unsent_message_count_.empty());
while (!pending_bot_resolve_queries_.empty()) {
auto it = pending_bot_resolve_queries_.begin();
@@ -5615,6 +5669,9 @@ td_api::object_ptr Client::get_chat_action(const Query *quer
if (action == "upload_document") {
return make_object();
}
+ if (action == "choose_sticker") {
+ return make_object();
+ }
if (action == "pick_up_location" || action == "find_location") {
return make_object();
}
@@ -6869,6 +6926,13 @@ td::int64 Client::extract_yet_unsent_message_query_id(int64 chat_id, int64 messa
yet_unsent_messages_.erase(yet_unsent_message_it);
+ auto count_it = yet_unsent_message_count_.find(chat_id);
+ CHECK(count_it != yet_unsent_message_count_.end());
+ CHECK(count_it->second > 0);
+ if (--count_it->second == 0) {
+ yet_unsent_message_count_.erase(count_it);
+ }
+
if (reply_to_message_id > 0) {
auto it = yet_unsent_reply_message_ids_.find({chat_id, reply_to_message_id});
CHECK(it != yet_unsent_reply_message_ids_.end());
@@ -7536,6 +7600,10 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) {
auto on_success = [this, disable_notification, input_message_contents = std::move(input_message_contents),
reply_markup = std::move(reply_markup), send_at = std::move(send_at)](
int64 chat_id, int64 reply_to_message_id, PromisedQueryPtr query) mutable {
+ auto it = yet_unsent_message_count_.find(chat_id);
+ if (it != yet_unsent_message_count_.end() && it->second > MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) {
+ return query->set_retry_after_error(60);
+ }
send_request(make_object(chat_id, 0, reply_to_message_id,
get_message_send_options(disable_notification, std::move(send_at)),
std::move(input_message_contents)),
@@ -7906,12 +7974,16 @@ td::Status Client::process_export_chat_invite_link_query(PromisedQueryPtr &query
td::Status Client::process_create_chat_invite_link_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
+ auto name = query->arg("name");
auto expire_date = get_integer_arg(query.get(), "expire_date", 0, 0);
auto member_limit = get_integer_arg(query.get(), "member_limit", 0, 0, 100000);
+ auto creates_join_request = to_bool(query->arg("creates_join_request"));
check_chat(chat_id, AccessRights::Write, std::move(query),
- [this, expire_date, member_limit](int64 chat_id, PromisedQueryPtr query) {
- send_request(make_object(chat_id, expire_date, member_limit),
+ [this, name = name.str(), expire_date, member_limit, creates_join_request](int64 chat_id,
+ PromisedQueryPtr query) {
+ send_request(make_object(chat_id, name, expire_date, member_limit,
+ creates_join_request),
std::make_unique(this, std::move(query)));
});
return Status::OK();
@@ -7920,12 +7992,16 @@ td::Status Client::process_create_chat_invite_link_query(PromisedQueryPtr &query
td::Status Client::process_edit_chat_invite_link_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
auto invite_link = query->arg("invite_link");
+ auto name = query->arg("name");
auto expire_date = get_integer_arg(query.get(), "expire_date", 0, 0);
auto member_limit = get_integer_arg(query.get(), "member_limit", 0, 0, 100000);
+ auto creates_join_request = to_bool(query->arg("creates_join_request"));
check_chat(chat_id, AccessRights::Write, std::move(query),
- [this, invite_link = invite_link.str(), expire_date, member_limit](int64 chat_id, PromisedQueryPtr query) {
- send_request(make_object(chat_id, invite_link, expire_date, member_limit),
+ [this, invite_link = invite_link.str(), name = name.str(), expire_date, member_limit,
+ creates_join_request](int64 chat_id, PromisedQueryPtr query) {
+ send_request(make_object(chat_id, invite_link, name, expire_date,
+ member_limit, creates_join_request),
std::make_unique(this, std::move(query)));
});
return Status::OK();
@@ -8231,11 +8307,11 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) {
auto can_restrict_members = to_bool(query->arg("can_restrict_members"));
auto can_pin_messages = to_bool(query->arg("can_pin_messages"));
auto can_promote_members = to_bool(query->arg("can_promote_members"));
- auto can_manage_voice_chats = to_bool(query->arg("can_manage_voice_chats"));
+ auto can_manage_video_chats = to_bool(query->arg("can_manage_voice_chats"));
auto is_anonymous = to_bool(query->arg("is_anonymous"));
auto status = make_object(
td::string(), true, can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages,
- can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_manage_voice_chats,
+ can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_manage_video_chats,
is_anonymous);
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, user_id, status = std::move(status)](int64 chat_id, PromisedQueryPtr query) mutable {
@@ -8394,6 +8470,32 @@ td::Status Client::process_unban_chat_member_query(PromisedQueryPtr &query) {
return Status::OK();
}
+td::Status Client::process_approve_chat_join_request_query(PromisedQueryPtr &query) {
+ auto chat_id = query->arg("chat_id");
+ TRY_RESULT(user_id, get_user_id(query.get()));
+
+ check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id](int64 chat_id, PromisedQueryPtr query) {
+ check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) {
+ send_request(make_object(chat_id, user_id),
+ std::make_unique(std::move(query)));
+ });
+ });
+ return Status::OK();
+}
+
+td::Status Client::process_decline_chat_join_request_query(PromisedQueryPtr &query) {
+ auto chat_id = query->arg("chat_id");
+ TRY_RESULT(user_id, get_user_id(query.get()));
+
+ check_chat(chat_id, AccessRights::Write, std::move(query), [this, user_id](int64 chat_id, PromisedQueryPtr query) {
+ check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) {
+ send_request(make_object(chat_id, user_id),
+ std::make_unique(std::move(query)));
+ });
+ });
+ return Status::OK();
+}
+
td::Status Client::process_get_sticker_set_query(PromisedQueryPtr &query) {
auto name = query->arg("name");
if (td::trim(to_lower(name)) == to_lower(GREAT_MINDS_SET_NAME)) {
@@ -9335,6 +9437,10 @@ void Client::do_send_message(object_ptr input_messa
auto on_success = [this, disable_notification, input_message_content = std::move(input_message_content),
reply_markup = std::move(reply_markup), send_at = std::move(send_at)](
int64 chat_id, int64 reply_to_message_id, PromisedQueryPtr query) mutable {
+ auto it = yet_unsent_message_count_.find(chat_id);
+ if (it != yet_unsent_message_count_.end() && it->second > MAX_CONCURRENTLY_SENT_CHAT_MESSAGES) {
+ return query->set_retry_after_error(60);
+ }
send_request(make_object(chat_id, 0, reply_to_message_id,
get_message_send_options(disable_notification, std::move(send_at)),
std::move(reply_markup), std::move(input_message_content)),
@@ -9370,6 +9476,7 @@ void Client::on_sent_message(object_ptr &&message, int64 query_
yet_unsent_message.send_message_query_id = query_id;
auto emplace_result = yet_unsent_messages_.emplace(yet_unsent_message_id, yet_unsent_message);
CHECK(emplace_result.second);
+ yet_unsent_message_count_[chat_id]++;
pending_send_message_queries_[query_id].awaited_message_count++;
}
@@ -9848,6 +9955,8 @@ Client::Slice Client::get_update_type_name(UpdateType update_type) {
return Slice("my_chat_member");
case UpdateType::ChatMember:
return Slice("chat_member");
+ case UpdateType::ChatJoinRequest:
+ return Slice("chat_join_request");
default:
UNREACHABLE();
return Slice();
@@ -10136,6 +10245,16 @@ void Client::add_update_chat_member(object_ptr &&updat
}
}
+void Client::add_update_chat_join_request(object_ptr &&update) {
+ CHECK(update != nullptr);
+ CHECK(update->request_ != nullptr);
+ auto left_time = update->request_->date_ + 86400 - get_unix_time();
+ if (left_time > 0) {
+ auto webhook_queue_id = update->chat_id_ + (static_cast(6) << 33);
+ add_update(UpdateType::ChatJoinRequest, JsonChatJoinRequest(update.get(), this), left_time, webhook_queue_id);
+ }
+}
+
td::int64 Client::choose_added_member_id(const td_api::messageChatAddMembers *message_add_members) const {
CHECK(message_add_members != nullptr);
for (auto &member_user_id : message_add_members->member_user_ids_) {
@@ -10158,12 +10277,13 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr &&update);
+ void add_update_chat_join_request(object_ptr &&update);
+
// append only before Size
enum class UpdateType : int32 {
Message,
@@ -917,6 +924,7 @@ class Client : public WebhookActor::Callback {
PollAnswer,
MyChatMember,
ChatMember,
+ ChatJoinRequest,
Size
};
@@ -998,6 +1006,8 @@ class Client : public WebhookActor::Callback {
};
std::unordered_map yet_unsent_messages_;
+ std::unordered_map yet_unsent_message_count_;
+
struct PendingSendMessageQuery {
PromisedQueryPtr query;
bool is_multisend = false;
diff --git a/telegram-bot-api/ClientManager.cpp b/telegram-bot-api/ClientManager.cpp
index 170ac66..c117046 100644
--- a/telegram-bot-api/ClientManager.cpp
+++ b/telegram-bot-api/ClientManager.cpp
@@ -93,12 +93,18 @@ void ClientManager::send(PromisedQueryPtr query) {
if (!check_flood_limits(query)) {
return;
}
- auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), token, td::ActorOwn()});
+
+ 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()) {
+ // return query->set_retry_after_error(1);
+ }
+
+ auto id =
+ clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), token, tqueue_id, td::ActorOwn()});
auto *client_info = clients_.get(id);
- client_info->client_ =
- td::create_actor(PSLICE() << "Client/" << token, actor_shared(this, id), query->token().str(), query->is_user(),
- query->is_test_dc(), get_tqueue_id(r_user_id.ok(), query->is_test_dc()), parameters_,
- client_info->stat_.actor_id(&client_info->stat_));
+ client_info->client_ = td::create_actor(PSLICE() << "Client/" << token, actor_shared(this, id),
+ query->token().str(), query->is_user(), query->is_test_dc(),
+ tqueue_id, parameters_, client_info->stat_.actor_id(&client_info->stat_));
auto method = query->method();
if (method != "deletewebhook" && method != "setwebhook") {
@@ -139,7 +145,12 @@ void ClientManager::user_login(PromisedQueryPtr query) {
long token_hash = std::hash{}(user_token);
- auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), user_token, td::ActorOwn()});
+ auto tqueue_id = get_tqueue_id(token_hash, query->is_test_dc());
+ if (active_client_count_.find(tqueue_id) != active_client_count_.end()) {
+ // return query->set_retry_after_error(1);
+ }
+
+ auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), user_token, tqueue_id, td::ActorOwn()});
auto *client_info = clients_.get(id);
auto stat_actor = client_info->stat_.actor_id(&client_info->stat_);
auto client_id = td::create_actor(PSLICE() << "Client/" << user_token, actor_shared(this, id), user_token,
@@ -270,23 +281,23 @@ void ClientManager::get_stats(td::PromiseActor promise,
}
if(!as_json) {
- sb << stat_.get_description() << "\n";
+ sb << stat_.get_description() << '\n';
}
if (id_filter.empty()) {
if(as_json) {
jb_root("uptime", td::JsonFloat(now - parameters_->start_time_));
} else {
- sb << "uptime\t" << now - parameters_->start_time_ << "\n";
+ sb << "uptime\t" << now - parameters_->start_time_ << '\n';
}
if(as_json) {
jb_root("bot_count", td::JsonLong(clients_.size()));
} else {
- sb << "bot_count\t" << clients_.size() << "\n";
+ sb << "bot_count\t" << clients_.size() << '\n';
}
if(as_json) {
jb_root("active_bot_count", td::JsonInt(active_bot_count));
} else {
- sb << "active_bot_count\t" << active_bot_count << "\n";
+ sb << "active_bot_count\t" << active_bot_count << '\n';
}
auto r_mem_stat = td::mem_stat();
if (r_mem_stat.is_ok()) {
@@ -294,10 +305,10 @@ void ClientManager::get_stats(td::PromiseActor promise,
if(as_json) {
jb_root("memory", JsonStatsMem(mem_stat));
} else {
- sb << "rss\t" << td::format::as_size(mem_stat.resident_size_) << "\n";
- sb << "vm\t" << td::format::as_size(mem_stat.virtual_size_) << "\n";
- sb << "rss_peak\t" << td::format::as_size(mem_stat.resident_size_peak_) << "\n";
- sb << "vm_peak\t" << td::format::as_size(mem_stat.virtual_size_peak_) << "\n";
+ sb << "rss\t" << td::format::as_size(mem_stat.resident_size_) << '\n';
+ sb << "vm\t" << td::format::as_size(mem_stat.virtual_size_) << '\n';
+ sb << "rss_peak\t" << td::format::as_size(mem_stat.resident_size_peak_) << '\n';
+ sb << "vm_peak\t" << td::format::as_size(mem_stat.virtual_size_peak_) << '\n';
}
} else {
if(as_json) {
@@ -314,7 +325,7 @@ void ClientManager::get_stats(td::PromiseActor promise,
} else {
auto cpu_stats = ServerCpuStat::instance().as_vector(td::Time::now());
for (auto &stat : cpu_stats) {
- sb << stat.key_ << "\t" << stat.value_ << "\n";
+ sb << stat.key_ << "\t" << stat.value_ << '\n';
}
}
@@ -324,16 +335,16 @@ void ClientManager::get_stats(td::PromiseActor promise,
jb_root("active_requests", td::JsonLong(parameters_->shared_data_->query_count_.load()));
jb_root("active_network_queries", td::JsonLong(td::get_pending_network_query_count(*parameters_->net_query_stats_)));
} else {
- sb << "buffer_memory\t" << td::format::as_size(td::BufferAllocator::get_buffer_mem()) << "\n";
- sb << "active_webhook_connections\t" << WebhookActor::get_total_connections_count() << "\n";
- sb << "active_requests\t" << parameters_->shared_data_->query_count_.load() << "\n";
- sb << "active_network_queries\t" << td::get_pending_network_query_count(*parameters_->net_query_stats_) << "\n";
+ sb << "buffer_memory\t" << td::format::as_size(td::BufferAllocator::get_buffer_mem()) << '\n';
+ sb << "active_webhook_connections\t" << WebhookActor::get_total_connections_count() << '\n';
+ sb << "active_requests\t" << parameters_->shared_data_->query_count_.load() << '\n';
+ sb << "active_network_queries\t" << td::get_pending_network_query_count(*parameters_->net_query_stats_) << '\n';
}
if(as_json) {
} else {
auto stats = stat_.as_vector(now);
for (auto &stat : stats) {
- sb << stat.key_ << "\t" << stat.value_ << "\n";
+ sb << stat.key_ << "\t" << stat.value_ << '\n';
}
}
}
@@ -358,30 +369,30 @@ void ClientManager::get_stats(td::PromiseActor promise,
CHECK(client_info);
auto bot_info = client_info->client_->get_actor_unsafe()->get_bot_info();
- sb << "\n";
- sb << "id\t" << bot_info.id_ << "\n";
- sb << "uptime\t" << now - bot_info.start_time_ << "\n";
+ sb << '\n';
+ sb << "id\t" << bot_info.id_ << '\n';
+ sb << "uptime\t" << now - bot_info.start_time_ << '\n';
if (!parameters_->stats_hide_sensible_data_) {
- sb << "token\t" << bot_info.token_ << "\n";
+ sb << "token\t" << bot_info.token_ << '\n';
}
- sb << "username\t" << bot_info.username_ << "\n";
+ sb << "username\t" << bot_info.username_ << '\n';
if (!parameters_->stats_hide_sensible_data_) {
- sb << "webhook\t" << bot_info.webhook_ << "\n";
+ sb << "webhook\t" << bot_info.webhook_ << '\n';
} else if (bot_info.webhook_.empty()) {
- sb << "webhook disabled" << "\n";
+ sb << "webhook disabled" << '\n';
} else {
- sb << "webhook enabled" << "\n";
+ sb << "webhook enabled" << '\n';
}
- sb << "has_custom_certificate\t" << bot_info.has_webhook_certificate_ << "\n";
- sb << "head_update_id\t" << bot_info.head_update_id_ << "\n";
- sb << "tail_update_id\t" << bot_info.tail_update_id_ << "\n";
- sb << "pending_update_count\t" << bot_info.pending_update_count_ << "\n";
- sb << "webhook_max_connections\t" << bot_info.webhook_max_connections_ << "\n";
+ sb << "has_custom_certificate\t" << bot_info.has_webhook_certificate_ << '\n';
+ sb << "head_update_id\t" << bot_info.head_update_id_ << '\n';
+ sb << "tail_update_id\t" << bot_info.tail_update_id_ << '\n';
+ sb << "pending_update_count\t" << bot_info.pending_update_count_ << '\n';
+ sb << "webhook_max_connections\t" << bot_info.webhook_max_connections_ << '\n';
auto stats = client_info->stat_.as_vector(now);
for (auto &stat : stats) {
if (stat.key_ == "update_count" || stat.key_ == "request_count") {
- sb << stat.key_ << "/sec\t" << stat.value_ << "\n";
+ sb << stat.key_ << "/sec\t" << stat.value_ << '\n';
}
}
if (sb.is_error()) {
@@ -526,6 +537,21 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool
return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor>()));
}
+void ClientManager::raw_event(const td::Event::Raw &event) {
+ auto id = get_link_token();
+ auto *info = clients_.get(id);
+ CHECK(info != nullptr);
+ auto &value = active_client_count_[info->tqueue_id_];
+ if (event.ptr != nullptr) {
+ value++;
+ } else {
+ CHECK(value > 0);
+ if (--value == 0) {
+ active_client_count_.erase(info->tqueue_id_);
+ }
+ }
+}
+
void ClientManager::hangup_shared() {
auto id = get_link_token();
auto *info = clients_.get(id);
@@ -535,19 +561,23 @@ void ClientManager::hangup_shared() {
clients_.erase(id);
if (close_flag_ && clients_.empty()) {
+ CHECK(active_client_count_.empty());
close_db();
}
}
void ClientManager::close_db() {
LOG(WARNING) << "Closing databases";
- td::MultiPromiseActorSafe mpromise("close binlogs");
- mpromise.add_promise(td::PromiseCreator::lambda(
+ td::MultiPromiseActorSafe mpas("close binlogs");
+ mpas.add_promise(td::PromiseCreator::lambda(
[actor_id = actor_id(this)](td::Unit) { send_closure(actor_id, &ClientManager::finish_close); }));
+ mpas.set_ignore_errors(true);
- parameters_->shared_data_->tqueue_->close(mpromise.get_promise());
- parameters_->shared_data_->webhook_db_->close(mpromise.get_promise());
- parameters_->shared_data_->user_db_->close(mpromise.get_promise());
+ auto lock = mpas.get_promise();
+ parameters_->shared_data_->tqueue_->close(mpas.get_promise());
+ parameters_->shared_data_->webhook_db_->close(mpas.get_promise());
+ parameters_->shared_data_->user_db_->close(mpas.get_promise());
+ lock.set_value(td::Unit());
}
void ClientManager::finish_close() {
diff --git a/telegram-bot-api/ClientManager.h b/telegram-bot-api/ClientManager.h
index efe665b..35b6757 100644
--- a/telegram-bot-api/ClientManager.h
+++ b/telegram-bot-api/ClientManager.h
@@ -55,6 +55,7 @@ class ClientManager final : public td::Actor {
public:
BotStatActor stat_;
td::string token_;
+ td::int64 tqueue_id_;
td::ActorOwn client_;
};
td::Container clients_;
@@ -65,6 +66,7 @@ class ClientManager final : public td::Actor {
std::unordered_map token_to_id_;
std::unordered_map flood_controls_;
+ std::unordered_map active_client_count_;
bool close_flag_ = false;
td::vector> close_promises_;
@@ -75,6 +77,7 @@ class ClientManager final : public td::Actor {
std::shared_ptr shared_data);
void start_up() override;
+ void raw_event(const td::Event::Raw &event) override;
void hangup_shared() override;
void close_db();
void finish_close();
diff --git a/telegram-bot-api/HttpServer.h b/telegram-bot-api/HttpServer.h
index 976836c..b500bab 100644
--- a/telegram-bot-api/HttpServer.h
+++ b/telegram-bot-api/HttpServer.h
@@ -11,6 +11,7 @@
#include "td/net/HttpInboundConnection.h"
#include "td/net/TcpListener.h"
+#include "td/utils/BufferedFd.h"
#include "td/utils/FloodControlFast.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
@@ -64,8 +65,8 @@ class HttpServer : public td::TcpListener::Callback {
if (scheduler_id > 0) {
scheduler_id--;
}
- td::create_actor("HttpInboundConnection", std::move(fd), 0, 20, 500, creator_(),
- scheduler_id)
+ td::create_actor("HttpInboundConnection", td::BufferedFd(std::move(fd)), 0,
+ 20, 500, creator_(), scheduler_id)
.release();
}
diff --git a/telegram-bot-api/WebhookActor.cpp b/telegram-bot-api/WebhookActor.cpp
index 33ca271..1f991b6 100644
--- a/telegram-bot-api/WebhookActor.cpp
+++ b/telegram-bot-api/WebhookActor.cpp
@@ -156,7 +156,7 @@ td::Status WebhookActor::create_connection() {
public:
Callback(td::ActorId actor, td::int64 id) : actor_(actor), id_(id) {
}
- void set_result(td::Result result) override {
+ void set_result(td::Result> result) override {
send_closure(std::move(actor_), &WebhookActor::on_socket_ready_async, std::move(result), id_);
CHECK(actor_.empty());
}
@@ -195,7 +195,7 @@ td::Status WebhookActor::create_connection() {
on_error(r_fd.move_as_error());
return error;
}
- return create_connection(r_fd.move_as_ok());
+ return create_connection(td::BufferedFd(r_fd.move_as_ok()));
}
td::Result WebhookActor::create_ssl_stream() {
@@ -215,7 +215,7 @@ td::Result WebhookActor::create_ssl_stream() {
return r_ssl_stream.move_as_ok();
}
-td::Status WebhookActor::create_connection(td::SocketFd fd) {
+td::Status WebhookActor::create_connection(td::BufferedFd fd) {
TRY_RESULT(ssl_stream, create_ssl_stream());
auto id = connections_.create(Connection());
@@ -237,7 +237,7 @@ td::Status WebhookActor::create_connection(td::SocketFd fd) {
return td::Status::OK();
}
-void WebhookActor::on_socket_ready_async(td::Result r_fd, td::int64 id) {
+void WebhookActor::on_socket_ready_async(td::Result> r_fd, td::int64 id) {
pending_sockets_.erase(id);
if (r_fd.is_ok()) {
VLOG(webhook) << "Socket " << id << " is ready";
diff --git a/telegram-bot-api/WebhookActor.h b/telegram-bot-api/WebhookActor.h
index 49de882..fa98ddb 100644
--- a/telegram-bot-api/WebhookActor.h
+++ b/telegram-bot-api/WebhookActor.h
@@ -17,6 +17,7 @@
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
+#include "td/utils/BufferedFd.h"
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/FloodControlFast.h"
@@ -159,7 +160,7 @@ class WebhookActor : public td::HttpOutboundConnection::Callback {
}
};
td::Container> pending_sockets_;
- td::vector ready_sockets_;
+ td::vector> ready_sockets_;
td::int32 max_connections_ = 0;
td::Container connections_;
@@ -176,8 +177,8 @@ class WebhookActor : public td::HttpOutboundConnection::Callback {
td::Result create_ssl_stream();
td::Status create_connection() TD_WARN_UNUSED_RESULT;
- td::Status create_connection(td::SocketFd fd) TD_WARN_UNUSED_RESULT;
- void on_socket_ready_async(td::Result r_fd, td::int64 id);
+ td::Status create_connection(td::BufferedFd fd) TD_WARN_UNUSED_RESULT;
+ void on_socket_ready_async(td::Result> r_fd, td::int64 id);
void create_new_connections();
diff --git a/telegram-bot-api/telegram-bot-api.cpp b/telegram-bot-api/telegram-bot-api.cpp
index 8027854..d087b6e 100644
--- a/telegram-bot-api/telegram-bot-api.cpp
+++ b/telegram-bot-api/telegram-bot-api.cpp
@@ -198,7 +198,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_ = "5.3.3";
+ parameters->version_ = "5.4";
parameters->shared_data_ = shared_data;
parameters->start_time_ = start_time;
auto net_query_stats = td::create_net_query_stats();