mirror of
https://github.com/tdlight-team/tdlight-telegram-bot-api.git
synced 2024-11-30 07:52:55 +01:00
fix conflict
This commit is contained in:
commit
a1510e45bb
@ -1,6 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||||
|
|
||||||
project(TelegramBotApi VERSION 5.2 LANGUAGES CXX)
|
if (POLICY CMP0065)
|
||||||
|
# do not export symbols from executables
|
||||||
|
# affects compiler checks in project(), so must be set before it
|
||||||
|
cmake_policy(SET CMP0065 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(TelegramBotApi VERSION 5.3 LANGUAGES CXX)
|
||||||
|
|
||||||
add_subdirectory(td EXCLUDE_FROM_ALL)
|
add_subdirectory(td EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ function onLoad(initial) {
|
|||||||
function onOsChanged(initial) {
|
function onOsChanged(initial) {
|
||||||
var os = document.getElementById('osSelect').value;
|
var os = document.getElementById('osSelect').value;
|
||||||
if (os.includes('Choose ')) {
|
if (os.includes('Choose ')) {
|
||||||
if (history.state != '') {
|
if (history.state !== '' && history.state !== null) {
|
||||||
history.pushState('', '', 'build.html');
|
history.pushState('', '', 'build.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
td
2
td
@ -1 +1 @@
|
|||||||
Subproject commit 49fd5cbd874601da8a9ff0083894d7104a4e8680
|
Subproject commit 1ea79d273976c22655742d4031ebbe62dba6cd7c
|
@ -25,6 +25,7 @@
|
|||||||
#include "td/utils/port/Stat.h"
|
#include "td/utils/port/Stat.h"
|
||||||
#include "td/utils/Random.h"
|
#include "td/utils/Random.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/Span.h"
|
#include "td/utils/Span.h"
|
||||||
#include "td/utils/StackAllocator.h"
|
#include "td/utils/StackAllocator.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
@ -75,6 +76,10 @@ void Client::fail_query_with_error(PromisedQueryPtr query, int32 error_code, Sli
|
|||||||
int32 real_error_code = error_code;
|
int32 real_error_code = error_code;
|
||||||
Slice real_error_message = error_message;
|
Slice real_error_message = error_message;
|
||||||
if (error_code < 300 || error_code == 404) {
|
if (error_code < 300 || error_code == 404) {
|
||||||
|
if (error_code <= 0) {
|
||||||
|
LOG(ERROR) << "Receive error \"" << real_error_message << "\" with code " << error_code << " from " << *query;
|
||||||
|
}
|
||||||
|
|
||||||
error_code = 400;
|
error_code = 400;
|
||||||
}
|
}
|
||||||
if (error_code == 400) {
|
if (error_code == 400) {
|
||||||
@ -197,6 +202,7 @@ bool Client::init_methods() {
|
|||||||
methods_.emplace("getme", &Client::process_get_me_query);
|
methods_.emplace("getme", &Client::process_get_me_query);
|
||||||
methods_.emplace("getmycommands", &Client::process_get_my_commands_query);
|
methods_.emplace("getmycommands", &Client::process_get_my_commands_query);
|
||||||
methods_.emplace("setmycommands", &Client::process_set_my_commands_query);
|
methods_.emplace("setmycommands", &Client::process_set_my_commands_query);
|
||||||
|
methods_.emplace("deletemycommands", &Client::process_delete_my_commands_query);
|
||||||
methods_.emplace("getuserprofilephotos", &Client::process_get_user_profile_photos_query);
|
methods_.emplace("getuserprofilephotos", &Client::process_get_user_profile_photos_query);
|
||||||
methods_.emplace("sendmessage", &Client::process_send_message_query);
|
methods_.emplace("sendmessage", &Client::process_send_message_query);
|
||||||
methods_.emplace("sendanimation", &Client::process_send_animation_query);
|
methods_.emplace("sendanimation", &Client::process_send_animation_query);
|
||||||
@ -336,7 +342,7 @@ class Client::JsonDatedFile : public Jsonable {
|
|||||||
|
|
||||||
class Client::JsonDatedFiles : public Jsonable {
|
class Client::JsonDatedFiles : public Jsonable {
|
||||||
public:
|
public:
|
||||||
JsonDatedFiles(const td::vector<td_api::object_ptr<td_api::datedFile>> &files, const Client *client)
|
JsonDatedFiles(const td::vector<object_ptr<td_api::datedFile>> &files, const Client *client)
|
||||||
: files_(files), client_(client) {
|
: files_(files), client_(client) {
|
||||||
}
|
}
|
||||||
void store(JsonValueScope *scope) const {
|
void store(JsonValueScope *scope) const {
|
||||||
@ -347,7 +353,7 @@ class Client::JsonDatedFiles : public Jsonable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const td::vector<td_api::object_ptr<td_api::datedFile>> &files_;
|
const td::vector<object_ptr<td_api::datedFile>> &files_;
|
||||||
const Client *client_;
|
const Client *client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1592,7 +1598,7 @@ class Client::JsonInlineKeyboardButton : public Jsonable {
|
|||||||
case td_api::inlineKeyboardButtonTypeCallbackWithPassword::ID: {
|
case td_api::inlineKeyboardButtonTypeCallbackWithPassword::ID: {
|
||||||
auto data = get_callback_data(button_->type_);
|
auto data = get_callback_data(button_->type_);
|
||||||
if (!td::check_utf8(data)) {
|
if (!td::check_utf8(data)) {
|
||||||
object("callback_data", td::JsonRawString(data));
|
object("callback_data", "INVALID");
|
||||||
} else {
|
} else {
|
||||||
object("callback_data", data);
|
object("callback_data", data);
|
||||||
}
|
}
|
||||||
@ -2398,6 +2404,7 @@ class Client::JsonChatMembers : public Jsonable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto user_id = static_cast<const td_api::messageSenderUser *>(member->member_id_.get())->user_id_;
|
auto user_id = static_cast<const td_api::messageSenderUser *>(member->member_id_.get())->user_id_;
|
||||||
|
/*
|
||||||
bool is_member_bot = member->bot_info_ != nullptr;
|
bool is_member_bot = member->bot_info_ != nullptr;
|
||||||
if (!is_member_bot) {
|
if (!is_member_bot) {
|
||||||
// bot info may be unknown
|
// bot info may be unknown
|
||||||
@ -2406,7 +2413,7 @@ class Client::JsonChatMembers : public Jsonable {
|
|||||||
is_member_bot = true;
|
is_member_bot = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if (is_member_bot && user_id != client_->my_id_) {
|
if (is_member_bot && user_id != client_->my_id_) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3470,13 +3477,9 @@ class Client::TdOnGetMyCommandsCallback : public TdQueryCallback {
|
|||||||
return fail_query_with_error(std::move(query_), move_object_as<td_api::error>(result));
|
return fail_query_with_error(std::move(query_), move_object_as<td_api::error>(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK(result->get_id() == td_api::userFullInfo::ID);
|
CHECK(result->get_id() == td_api::botCommands::ID);
|
||||||
auto user_full_info = move_object_as<td_api::userFullInfo>(result);
|
auto bot_commands = move_object_as<td_api::botCommands>(result);
|
||||||
td::vector<object_ptr<td_api::botCommand>> commands;
|
answer_query(td::json_array(bot_commands->commands_, [](auto &command) { return JsonBotCommand(command.get()); }),
|
||||||
if (user_full_info->bot_info_ != nullptr) {
|
|
||||||
commands = std::move(user_full_info->bot_info_->commands_);
|
|
||||||
}
|
|
||||||
answer_query(td::json_array(commands, [](auto &command) { return JsonBotCommand(command.get()); }),
|
|
||||||
std::move(query_));
|
std::move(query_));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3960,18 +3963,14 @@ void Client::start_up() {
|
|||||||
CHECK(r_absolute_dir.is_ok());
|
CHECK(r_absolute_dir.is_ok());
|
||||||
absolute_dir_ = r_absolute_dir.move_as_ok();
|
absolute_dir_ = r_absolute_dir.move_as_ok();
|
||||||
auto suff = bot_token_with_dc_ + TD_DIR_SLASH;
|
auto suff = bot_token_with_dc_ + TD_DIR_SLASH;
|
||||||
#if TD_PORT_WINDOWS
|
if (!parameters_->allow_colon_in_filenames_) {
|
||||||
for (auto &c : suff) {
|
for (auto &c : suff) {
|
||||||
if (c == ':') {
|
if (c == ':') {
|
||||||
c = '~';
|
c = '~';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
dir_ = parameters_->working_directory_ + suff;
|
||||||
dir_ = td::string(".") + TD_DIR_SLASH + suff;
|
|
||||||
if (absolute_dir_.back() != TD_DIR_SLASH) {
|
|
||||||
absolute_dir_ += TD_DIR_SLASH;
|
|
||||||
}
|
|
||||||
absolute_dir_ += suff;
|
|
||||||
|
|
||||||
class TdCallback : public td::TdCallback {
|
class TdCallback : public td::TdCallback {
|
||||||
public:
|
public:
|
||||||
@ -3995,10 +3994,7 @@ void Client::start_up() {
|
|||||||
|
|
||||||
void Client::send(PromisedQueryPtr query) {
|
void Client::send(PromisedQueryPtr query) {
|
||||||
if (!query->is_internal()) {
|
if (!query->is_internal()) {
|
||||||
send_closure(
|
query->set_stat_actor(stat_actor_);
|
||||||
stat_actor_, &BotStatActor::add_event<ServerBotStat::Request>,
|
|
||||||
ServerBotStat::Request{query->query_size(), query->file_count(), query->files_size(), query->files_max_size()},
|
|
||||||
td::Time::now());
|
|
||||||
}
|
}
|
||||||
cmd_queue_.emplace(std::move(query));
|
cmd_queue_.emplace(std::move(query));
|
||||||
loop();
|
loop();
|
||||||
@ -4174,6 +4170,10 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ChatInfo::Type::Group: {
|
case ChatInfo::Type::Group: {
|
||||||
|
if (access_rights == AccessRights::ReadMembers) { // member list is inaccessible in deactivated groups
|
||||||
|
need_write_access = true;
|
||||||
|
need_edit_access = true;
|
||||||
|
}
|
||||||
auto group_info = get_group_info(chat_info->group_id);
|
auto group_info = get_group_info(chat_info->group_id);
|
||||||
CHECK(group_info != nullptr);
|
CHECK(group_info != nullptr);
|
||||||
if (!group_info->is_active && need_write_access) {
|
if (!group_info->is_active && need_write_access) {
|
||||||
@ -4184,9 +4184,7 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const
|
|||||||
return fail_query(400, "Bad Request: group chat was upgraded to a supergroup chat", std::move(query),
|
return fail_query(400, "Bad Request: group chat was upgraded to a supergroup chat", std::move(query),
|
||||||
std::move(parameters));
|
std::move(parameters));
|
||||||
} else {
|
} else {
|
||||||
LOG(WARNING) << "Group chat " << chat_info->group_id << " with " << group_info->member_count
|
return fail_query(403, "Forbidden: the group chat was deleted", std::move(query));
|
||||||
<< " members and title \"" << chat_info->title << "\" is deactivated";
|
|
||||||
return fail_query(400, "Bad Request: group chat was deactivated", std::move(query));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group_info->is_active && group_info->kicked && need_edit_access) {
|
if (group_info->is_active && group_info->kicked && need_edit_access) {
|
||||||
@ -4247,6 +4245,36 @@ void Client::check_chat(Slice chat_id_str, AccessRights access_rights, PromisedQ
|
|||||||
std::move(on_success)));
|
std::move(on_success)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class OnSuccess>
|
||||||
|
void Client::check_bot_command_scope(BotCommandScope &&scope, PromisedQueryPtr query, OnSuccess on_success) {
|
||||||
|
CHECK(scope.scope_ != nullptr);
|
||||||
|
if (scope.chat_id_.empty()) {
|
||||||
|
on_success(std::move(scope.scope_), std::move(query));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
check_chat(scope.chat_id_, AccessRights::ReadMembers, std::move(query),
|
||||||
|
[this, user_id = scope.user_id_, scope_id = scope.scope_->get_id(), on_success = std::move(on_success)](
|
||||||
|
int64 chat_id, PromisedQueryPtr query) mutable {
|
||||||
|
switch (scope_id) {
|
||||||
|
case td_api::botCommandScopeChat::ID:
|
||||||
|
on_success(make_object<td_api::botCommandScopeChat>(chat_id), std::move(query));
|
||||||
|
break;
|
||||||
|
case td_api::botCommandScopeChatAdministrators::ID:
|
||||||
|
on_success(make_object<td_api::botCommandScopeChatAdministrators>(chat_id), std::move(query));
|
||||||
|
break;
|
||||||
|
case td_api::botCommandScopeChatMember::ID:
|
||||||
|
check_user_no_fail(
|
||||||
|
user_id, std::move(query),
|
||||||
|
[chat_id, user_id, on_success = std::move(on_success)](PromisedQueryPtr query) mutable {
|
||||||
|
on_success(make_object<td_api::botCommandScopeChatMember>(chat_id, user_id), std::move(query));
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <class OnSuccess>
|
template <class OnSuccess>
|
||||||
void Client::check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success) {
|
void Client::check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success) {
|
||||||
if (file_id.empty()) {
|
if (file_id.empty()) {
|
||||||
@ -4577,6 +4605,8 @@ void Client::on_update_authorization_state() {
|
|||||||
case td_api::authorizationStateWaitEncryptionKey::ID:
|
case td_api::authorizationStateWaitEncryptionKey::ID:
|
||||||
return send_request(make_object<td_api::checkDatabaseEncryptionKey>(), std::make_unique<TdOnInitCallback>(this));
|
return send_request(make_object<td_api::checkDatabaseEncryptionKey>(), std::make_unique<TdOnInitCallback>(this));
|
||||||
case td_api::authorizationStateWaitPhoneNumber::ID:
|
case td_api::authorizationStateWaitPhoneNumber::ID:
|
||||||
|
send_request(make_object<td_api::setOption>("online", make_object<td_api::optionValueBoolean>(true)),
|
||||||
|
std::make_unique<TdOnOkCallback>());
|
||||||
if (is_user_) {
|
if (is_user_) {
|
||||||
waiting_for_auth_input_ = true;
|
waiting_for_auth_input_ = true;
|
||||||
return loop();
|
return loop();
|
||||||
@ -5009,8 +5039,8 @@ void Client::on_closed() {
|
|||||||
td::ActorId<Client> parent_;
|
td::ActorId<Client> parent_;
|
||||||
|
|
||||||
void start_up() override {
|
void start_up() override {
|
||||||
CHECK(!dir_.empty());
|
CHECK(dir_.size() >= 24);
|
||||||
CHECK(dir_[0] == '.');
|
CHECK(dir_.back() == TD_DIR_SLASH);
|
||||||
td::rmrf(dir_).ignore();
|
td::rmrf(dir_).ignore();
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
@ -5214,6 +5244,7 @@ td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(con
|
|||||||
td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(JsonValue &&value) {
|
td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(JsonValue &&value) {
|
||||||
td::vector<td::vector<object_ptr<td_api::keyboardButton>>> rows;
|
td::vector<td::vector<object_ptr<td_api::keyboardButton>>> rows;
|
||||||
td::vector<td::vector<object_ptr<td_api::inlineKeyboardButton>>> inline_rows;
|
td::vector<td::vector<object_ptr<td_api::inlineKeyboardButton>>> inline_rows;
|
||||||
|
Slice input_field_placeholder;
|
||||||
bool resize = false;
|
bool resize = false;
|
||||||
bool one_time = false;
|
bool one_time = false;
|
||||||
bool remove = false;
|
bool remove = false;
|
||||||
@ -5289,18 +5320,24 @@ td::Result<td_api::object_ptr<td_api::ReplyMarkup>> Client::get_reply_markup(Jso
|
|||||||
return Status::Error(400, "Field \"force_reply\" of the reply markup must be of the type Boolean");
|
return Status::Error(400, "Field \"force_reply\" of the reply markup must be of the type Boolean");
|
||||||
}
|
}
|
||||||
force_reply = field_value.second.get_boolean();
|
force_reply = field_value.second.get_boolean();
|
||||||
|
} else if (field_value.first == "input_field_placeholder") {
|
||||||
|
if (field_value.second.type() != JsonValue::Type::String) {
|
||||||
|
return Status::Error(400, "Field \"input_field_placeholder\" of the reply markup must be of the type String");
|
||||||
|
}
|
||||||
|
input_field_placeholder = field_value.second.get_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_ptr<td_api::ReplyMarkup> result;
|
object_ptr<td_api::ReplyMarkup> result;
|
||||||
if (!rows.empty()) {
|
if (!rows.empty()) {
|
||||||
result = make_object<td_api::replyMarkupShowKeyboard>(std::move(rows), resize, one_time, is_personal);
|
result = make_object<td_api::replyMarkupShowKeyboard>(std::move(rows), resize, one_time, is_personal,
|
||||||
|
input_field_placeholder.str());
|
||||||
} else if (!inline_rows.empty()) {
|
} else if (!inline_rows.empty()) {
|
||||||
result = make_object<td_api::replyMarkupInlineKeyboard>(std::move(inline_rows));
|
result = make_object<td_api::replyMarkupInlineKeyboard>(std::move(inline_rows));
|
||||||
} else if (remove) {
|
} else if (remove) {
|
||||||
result = make_object<td_api::replyMarkupRemoveKeyboard>(is_personal);
|
result = make_object<td_api::replyMarkupRemoveKeyboard>(is_personal);
|
||||||
} else if (force_reply) {
|
} else if (force_reply) {
|
||||||
result = make_object<td_api::replyMarkupForceReply>(is_personal);
|
result = make_object<td_api::replyMarkupForceReply>(is_personal, input_field_placeholder.str());
|
||||||
}
|
}
|
||||||
if (result == nullptr || result->get_id() != td_api::replyMarkupInlineKeyboard::ID) {
|
if (result == nullptr || result->get_id() != td_api::replyMarkupInlineKeyboard::ID) {
|
||||||
unresolved_bot_usernames_.clear();
|
unresolved_bot_usernames_.clear();
|
||||||
@ -5970,6 +6007,69 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin
|
|||||||
return Status::Error(400, PSLICE() << "type \"" << type << "\" is unsupported for the inline query result");
|
return Status::Error(400, PSLICE() << "type \"" << type << "\" is unsupported for the inline query result");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td::Result<Client::BotCommandScope> Client::get_bot_command_scope(JsonValue &&value) {
|
||||||
|
if (value.type() != JsonValue::Type::Object) {
|
||||||
|
return Status::Error(400, "BotCommandScope must be an Object");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &object = value.get_object();
|
||||||
|
|
||||||
|
TRY_RESULT(type, get_json_object_string_field(object, "type", false));
|
||||||
|
if (type == "default") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeDefault>());
|
||||||
|
}
|
||||||
|
if (type == "all_private_chats") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeAllPrivateChats>());
|
||||||
|
}
|
||||||
|
if (type == "all_group_chats") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeAllGroupChats>());
|
||||||
|
}
|
||||||
|
if (type == "all_chat_administrators") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeAllChatAdministrators>());
|
||||||
|
}
|
||||||
|
if (type != "chat" && type != "chat_administrators" && type != "chat_member") {
|
||||||
|
return Status::Error(400, "Unsupported type specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY_RESULT(chat_id, get_json_object_string_field(object, "chat_id", false));
|
||||||
|
if (chat_id.empty()) {
|
||||||
|
return Status::Error(400, "Empty chat_id specified");
|
||||||
|
}
|
||||||
|
if (type == "chat") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeChat>(0), std::move(chat_id));
|
||||||
|
}
|
||||||
|
if (type == "chat_administrators") {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeChatAdministrators>(0), std::move(chat_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY_RESULT(user_id, get_json_object_int_field(object, "user_id", false));
|
||||||
|
if (user_id <= 0) {
|
||||||
|
return Status::Error(400, "Invalid user_id specified");
|
||||||
|
}
|
||||||
|
CHECK(type == "chat_member");
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeChatMember>(0, user_id), std::move(chat_id), user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Result<Client::BotCommandScope> Client::get_bot_command_scope(const Query *query) {
|
||||||
|
auto scope = query->arg("scope");
|
||||||
|
if (scope.empty()) {
|
||||||
|
return BotCommandScope(make_object<td_api::botCommandScopeDefault>());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Parsing JSON object: " << scope;
|
||||||
|
auto r_value = json_decode(scope);
|
||||||
|
if (r_value.is_error()) {
|
||||||
|
LOG(INFO) << "Can't parse JSON object: " << r_value.error();
|
||||||
|
return Status::Error(400, "Can't parse BotCommandScope JSON object");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r_scope = get_bot_command_scope(r_value.move_as_ok());
|
||||||
|
if (r_scope.is_error()) {
|
||||||
|
return Status::Error(400, PSLICE() << "Can't parse BotCommandScope: " << r_scope.error().message());
|
||||||
|
}
|
||||||
|
return r_scope.move_as_ok();
|
||||||
|
}
|
||||||
|
|
||||||
td::Result<td_api::object_ptr<td_api::botCommand>> Client::get_bot_command(JsonValue &&value) {
|
td::Result<td_api::object_ptr<td_api::botCommand>> Client::get_bot_command(JsonValue &&value) {
|
||||||
if (value.type() != JsonValue::Type::Object) {
|
if (value.type() != JsonValue::Type::Object) {
|
||||||
return Status::Error(400, "expected an Object");
|
return Status::Error(400, "expected an Object");
|
||||||
@ -6604,11 +6704,8 @@ td::Result<td::vector<td::string>> Client::get_poll_options(const Query *query)
|
|||||||
td::int32 Client::get_integer_arg(const Query *query, Slice field_name, int32 default_value, int32 min_value,
|
td::int32 Client::get_integer_arg(const Query *query, Slice field_name, int32 default_value, int32 min_value,
|
||||||
int32 max_value) {
|
int32 max_value) {
|
||||||
auto s_arg = query->arg(field_name);
|
auto s_arg = query->arg(field_name);
|
||||||
if (s_arg.empty()) {
|
auto value = s_arg.empty() ? default_value : td::to_integer<int32>(s_arg);
|
||||||
return default_value;
|
return td::clamp(value, min_value, max_value);
|
||||||
}
|
|
||||||
|
|
||||||
return td::clamp(td::to_integer<int32>(s_arg), min_value, max_value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Result<td::MutableSlice> Client::get_required_string_arg(const Query *query, Slice field_name) {
|
td::Result<td::MutableSlice> Client::get_required_string_arg(const Query *query, Slice field_name) {
|
||||||
@ -6922,16 +7019,42 @@ td::Status Client::process_get_me_query(PromisedQueryPtr &query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
td::Status Client::process_get_my_commands_query(PromisedQueryPtr &query) {
|
td::Status Client::process_get_my_commands_query(PromisedQueryPtr &query) {
|
||||||
send_request(make_object<td_api::getUserFullInfo>(my_id_),
|
TRY_RESULT(scope, get_bot_command_scope(query.get()));
|
||||||
std::make_unique<TdOnGetMyCommandsCallback>(std::move(query)));
|
|
||||||
|
check_bot_command_scope(std::move(scope), std::move(query),
|
||||||
|
[this](object_ptr<td_api::BotCommandScope> &&scope, PromisedQueryPtr query) mutable {
|
||||||
|
auto language_code = query->arg("language_code").str();
|
||||||
|
send_request(make_object<td_api::getCommands>(std::move(scope), language_code),
|
||||||
|
std::make_unique<TdOnGetMyCommandsCallback>(std::move(query)));
|
||||||
|
});
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status Client::process_set_my_commands_query(PromisedQueryPtr &query) {
|
td::Status Client::process_set_my_commands_query(PromisedQueryPtr &query) {
|
||||||
CHECK_IS_BOT();
|
CHECK_IS_BOT();
|
||||||
TRY_RESULT(bot_commands, get_bot_commands(query.get()));
|
TRY_RESULT(bot_commands, get_bot_commands(query.get()));
|
||||||
send_request(make_object<td_api::setCommands>(std::move(bot_commands)),
|
TRY_RESULT(scope, get_bot_command_scope(query.get()));
|
||||||
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
|
||||||
|
check_bot_command_scope(
|
||||||
|
std::move(scope), std::move(query),
|
||||||
|
[this, bot_commands = std::move(bot_commands)](object_ptr<td_api::BotCommandScope> &&scope,
|
||||||
|
PromisedQueryPtr query) mutable {
|
||||||
|
auto language_code = query->arg("language_code").str();
|
||||||
|
send_request(make_object<td_api::setCommands>(std::move(scope), language_code, std::move(bot_commands)),
|
||||||
|
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
||||||
|
});
|
||||||
|
return Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Status Client::process_delete_my_commands_query(PromisedQueryPtr &query) {
|
||||||
|
TRY_RESULT(scope, get_bot_command_scope(query.get()));
|
||||||
|
|
||||||
|
check_bot_command_scope(std::move(scope), std::move(query),
|
||||||
|
[this](object_ptr<td_api::BotCommandScope> &&scope, PromisedQueryPtr query) mutable {
|
||||||
|
auto language_code = query->arg("language_code").str();
|
||||||
|
send_request(make_object<td_api::deleteCommands>(std::move(scope), language_code),
|
||||||
|
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
||||||
|
});
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7258,7 +7381,7 @@ td::Status Client::process_copy_message_query(PromisedQueryPtr &query) {
|
|||||||
TRY_RESULT(from_chat_id, get_required_string_arg(query.get(), "from_chat_id"));
|
TRY_RESULT(from_chat_id, get_required_string_arg(query.get(), "from_chat_id"));
|
||||||
auto message_id = get_message_id(query.get());
|
auto message_id = get_message_id(query.get());
|
||||||
bool replace_caption = query->has_arg("caption");
|
bool replace_caption = query->has_arg("caption");
|
||||||
td_api::object_ptr<td_api::formattedText> caption;
|
object_ptr<td_api::formattedText> caption;
|
||||||
if (replace_caption) {
|
if (replace_caption) {
|
||||||
TRY_RESULT_ASSIGN(caption, get_caption(query.get()));
|
TRY_RESULT_ASSIGN(caption, get_caption(query.get()));
|
||||||
}
|
}
|
||||||
@ -7887,31 +8010,29 @@ td::Status Client::process_get_chat_member_query(PromisedQueryPtr &query) {
|
|||||||
auto chat_id = query->arg("chat_id");
|
auto chat_id = query->arg("chat_id");
|
||||||
TRY_RESULT(user_id, get_user_id(query.get()));
|
TRY_RESULT(user_id, get_user_id(query.get()));
|
||||||
|
|
||||||
check_chat(chat_id, AccessRights::Read, std::move(query), [this, user_id](int64 chat_id, PromisedQueryPtr query) {
|
check_chat(chat_id, AccessRights::ReadMembers, std::move(query),
|
||||||
get_chat_member(chat_id, user_id, std::move(query),
|
[this, user_id](int64 chat_id, PromisedQueryPtr query) {
|
||||||
[this, chat_type = get_chat_type(chat_id)](td_api::object_ptr<td_api::chatMember> &&chat_member,
|
get_chat_member(chat_id, user_id, std::move(query),
|
||||||
PromisedQueryPtr query) {
|
[this, chat_type = get_chat_type(chat_id)](object_ptr<td_api::chatMember> &&chat_member,
|
||||||
answer_query(JsonChatMember(chat_member.get(), chat_type, this), std::move(query));
|
PromisedQueryPtr query) {
|
||||||
});
|
answer_query(JsonChatMember(chat_member.get(), chat_type, this), std::move(query));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status Client::process_get_chat_administrators_query(PromisedQueryPtr &query) {
|
td::Status Client::process_get_chat_administrators_query(PromisedQueryPtr &query) {
|
||||||
auto chat_id = query->arg("chat_id");
|
auto chat_id = query->arg("chat_id");
|
||||||
|
|
||||||
check_chat(chat_id, AccessRights::Read, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) {
|
check_chat(chat_id, AccessRights::ReadMembers, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) {
|
||||||
auto chat_info = get_chat(chat_id);
|
auto chat_info = get_chat(chat_id);
|
||||||
CHECK(chat_info != nullptr);
|
CHECK(chat_info != nullptr);
|
||||||
switch (chat_info->type) {
|
switch (chat_info->type) {
|
||||||
case ChatInfo::Type::Private:
|
case ChatInfo::Type::Private:
|
||||||
return fail_query(400, "Bad Request: there are no administrators in the private chat", std::move(query));
|
return fail_query(400, "Bad Request: there are no administrators in the private chat", std::move(query));
|
||||||
case ChatInfo::Type::Group: {
|
case ChatInfo::Type::Group:
|
||||||
auto group_info = get_group_info(chat_info->group_id);
|
|
||||||
CHECK(group_info != nullptr);
|
|
||||||
return send_request(make_object<td_api::getBasicGroupFullInfo>(chat_info->group_id),
|
return send_request(make_object<td_api::getBasicGroupFullInfo>(chat_info->group_id),
|
||||||
std::make_unique<TdOnGetGroupMembersCallback>(this, true, std::move(query)));
|
std::make_unique<TdOnGetGroupMembersCallback>(this, true, std::move(query)));
|
||||||
}
|
|
||||||
case ChatInfo::Type::Supergroup:
|
case ChatInfo::Type::Supergroup:
|
||||||
return send_request(
|
return send_request(
|
||||||
make_object<td_api::getSupergroupMembers>(
|
make_object<td_api::getSupergroupMembers>(
|
||||||
@ -7928,7 +8049,7 @@ td::Status Client::process_get_chat_administrators_query(PromisedQueryPtr &query
|
|||||||
td::Status Client::process_get_chat_member_count_query(PromisedQueryPtr &query) {
|
td::Status Client::process_get_chat_member_count_query(PromisedQueryPtr &query) {
|
||||||
auto chat_id = query->arg("chat_id");
|
auto chat_id = query->arg("chat_id");
|
||||||
|
|
||||||
check_chat(chat_id, AccessRights::Read, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) {
|
check_chat(chat_id, AccessRights::ReadMembers, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) {
|
||||||
auto chat_info = get_chat(chat_id);
|
auto chat_info = get_chat(chat_id);
|
||||||
CHECK(chat_info != nullptr);
|
CHECK(chat_info != nullptr);
|
||||||
switch (chat_info->type) {
|
switch (chat_info->type) {
|
||||||
@ -7937,9 +8058,6 @@ td::Status Client::process_get_chat_member_count_query(PromisedQueryPtr &query)
|
|||||||
case ChatInfo::Type::Group: {
|
case ChatInfo::Type::Group: {
|
||||||
auto group_info = get_group_info(chat_info->group_id);
|
auto group_info = get_group_info(chat_info->group_id);
|
||||||
CHECK(group_info != nullptr);
|
CHECK(group_info != nullptr);
|
||||||
if (group_info->member_count == 0) {
|
|
||||||
return fail_query(403, "Forbidden: bot is not a member of the group chat", std::move(query));
|
|
||||||
}
|
|
||||||
return answer_query(td::VirtuallyJsonableInt(group_info->member_count), std::move(query));
|
return answer_query(td::VirtuallyJsonableInt(group_info->member_count), std::move(query));
|
||||||
}
|
}
|
||||||
case ChatInfo::Type::Supergroup:
|
case ChatInfo::Type::Supergroup:
|
||||||
@ -8016,8 +8134,8 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) {
|
|||||||
|
|
||||||
get_chat_member(
|
get_chat_member(
|
||||||
chat_id, user_id, std::move(query),
|
chat_id, user_id, std::move(query),
|
||||||
[this, chat_id, user_id, status = std::move(status)](
|
[this, chat_id, user_id, status = std::move(status)](object_ptr<td_api::chatMember> &&chat_member,
|
||||||
td_api::object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) mutable {
|
PromisedQueryPtr query) mutable {
|
||||||
if (chat_member->status_->get_id() == td_api::chatMemberStatusAdministrator::ID) {
|
if (chat_member->status_->get_id() == td_api::chatMemberStatusAdministrator::ID) {
|
||||||
auto administrator =
|
auto administrator =
|
||||||
static_cast<const td_api::chatMemberStatusAdministrator *>(chat_member->status_.get());
|
static_cast<const td_api::chatMemberStatusAdministrator *>(chat_member->status_.get());
|
||||||
@ -8044,7 +8162,7 @@ td::Status Client::process_set_chat_administrator_custom_title_query(PromisedQue
|
|||||||
|
|
||||||
get_chat_member(
|
get_chat_member(
|
||||||
chat_id, user_id, std::move(query),
|
chat_id, user_id, std::move(query),
|
||||||
[this, chat_id, user_id](td_api::object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) {
|
[this, chat_id, user_id](object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) {
|
||||||
if (chat_member->status_->get_id() == td_api::chatMemberStatusCreator::ID) {
|
if (chat_member->status_->get_id() == td_api::chatMemberStatusCreator::ID) {
|
||||||
return fail_query(400, "Bad Request: only creator can edit their custom title", std::move(query));
|
return fail_query(400, "Bad Request: only creator can edit their custom title", std::move(query));
|
||||||
}
|
}
|
||||||
@ -8102,7 +8220,7 @@ td::Status Client::process_restrict_chat_member_query(PromisedQueryPtr &query) {
|
|||||||
get_chat_member(
|
get_chat_member(
|
||||||
chat_id, user_id, std::move(query),
|
chat_id, user_id, std::move(query),
|
||||||
[this, chat_id, user_id, until_date, is_legacy, permissions = std::move(permissions)](
|
[this, chat_id, user_id, until_date, is_legacy, permissions = std::move(permissions)](
|
||||||
td_api::object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) mutable {
|
object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) mutable {
|
||||||
if (is_legacy && chat_member->status_->get_id() == td_api::chatMemberStatusRestricted::ID) {
|
if (is_legacy && chat_member->status_->get_id() == td_api::chatMemberStatusRestricted::ID) {
|
||||||
auto restricted =
|
auto restricted =
|
||||||
static_cast<const td_api::chatMemberStatusRestricted *>(chat_member->status_.get());
|
static_cast<const td_api::chatMemberStatusRestricted *>(chat_member->status_.get());
|
||||||
@ -8138,18 +8256,18 @@ td::Status Client::process_unban_chat_member_query(PromisedQueryPtr &query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (only_if_banned) {
|
if (only_if_banned) {
|
||||||
get_chat_member(chat_id, user_id, std::move(query),
|
get_chat_member(
|
||||||
[this, chat_id, user_id](td_api::object_ptr<td_api::chatMember> &&chat_member,
|
chat_id, user_id, std::move(query),
|
||||||
PromisedQueryPtr query) {
|
[this, chat_id, user_id](object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) {
|
||||||
if (chat_member->status_->get_id() != td_api::chatMemberStatusBanned::ID) {
|
if (chat_member->status_->get_id() != td_api::chatMemberStatusBanned::ID) {
|
||||||
return answer_query(td::JsonTrue(), std::move(query));
|
return answer_query(td::JsonTrue(), std::move(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
send_request(make_object<td_api::setChatMemberStatus>(
|
send_request(make_object<td_api::setChatMemberStatus>(
|
||||||
chat_id, td_api::make_object<td_api::messageSenderUser>(user_id),
|
chat_id, td_api::make_object<td_api::messageSenderUser>(user_id),
|
||||||
make_object<td_api::chatMemberStatusLeft>()),
|
make_object<td_api::chatMemberStatusLeft>()),
|
||||||
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) {
|
check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) {
|
||||||
send_request(make_object<td_api::setChatMemberStatus>(
|
send_request(make_object<td_api::setChatMemberStatus>(
|
||||||
@ -8181,7 +8299,8 @@ td::Status Client::process_upload_sticker_file_query(PromisedQueryPtr &query) {
|
|||||||
|
|
||||||
check_user(user_id, std::move(query),
|
check_user(user_id, std::move(query),
|
||||||
[this, user_id, png_sticker = std::move(png_sticker)](PromisedQueryPtr query) mutable {
|
[this, user_id, png_sticker = std::move(png_sticker)](PromisedQueryPtr query) mutable {
|
||||||
send_request(make_object<td_api::uploadStickerFile>(user_id, std::move(png_sticker)),
|
send_request(make_object<td_api::uploadStickerFile>(
|
||||||
|
user_id, make_object<td_api::inputStickerStatic>(std::move(png_sticker), "", nullptr)),
|
||||||
std::make_unique<TdOnReturnFileCallback>(this, std::move(query)));
|
std::make_unique<TdOnReturnFileCallback>(this, std::move(query)));
|
||||||
});
|
});
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
@ -8198,7 +8317,7 @@ td::Status Client::process_create_new_sticker_set_query(PromisedQueryPtr &query)
|
|||||||
check_user(user_id, std::move(query),
|
check_user(user_id, std::move(query),
|
||||||
[this, user_id, title, name, is_masks, stickers = std::move(stickers)](PromisedQueryPtr query) mutable {
|
[this, user_id, title, name, is_masks, stickers = std::move(stickers)](PromisedQueryPtr query) mutable {
|
||||||
send_request(make_object<td_api::createNewStickerSet>(user_id, title.str(), name.str(), is_masks,
|
send_request(make_object<td_api::createNewStickerSet>(user_id, title.str(), name.str(), is_masks,
|
||||||
std::move(stickers)),
|
std::move(stickers), PSTRING() << "bot" << my_id_),
|
||||||
std::make_unique<TdOnReturnStickerSetCallback>(this, false, std::move(query)));
|
std::make_unique<TdOnReturnStickerSetCallback>(this, false, std::move(query)));
|
||||||
});
|
});
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
@ -8317,7 +8436,7 @@ td::Status Client::process_set_webhook_query(PromisedQueryPtr &query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto now = td::Time::now_cached();
|
auto now = td::Time::now_cached();
|
||||||
if (!new_url.empty()) {
|
if (!new_url.empty() && !query->is_internal()) {
|
||||||
if (now < next_allowed_set_webhook_time_) {
|
if (now < next_allowed_set_webhook_time_) {
|
||||||
query->set_retry_after_error(1);
|
query->set_retry_after_error(1);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
@ -8808,7 +8927,7 @@ void Client::do_get_file(object_ptr<td_api::file> file, PromisedQueryPtr query)
|
|||||||
auto file_id = file->id_;
|
auto file_id = file->id_;
|
||||||
file_download_listeners_[file_id].push_back(std::move(query));
|
file_download_listeners_[file_id].push_back(std::move(query));
|
||||||
if (file->local_->is_downloading_completed_) {
|
if (file->local_->is_downloading_completed_) {
|
||||||
Slice relative_path = td::PathView::relative(file->local_->path_, absolute_dir_, true);
|
Slice relative_path = td::PathView::relative(file->local_->path_, dir_, true);
|
||||||
if (!relative_path.empty()) {
|
if (!relative_path.empty()) {
|
||||||
auto r_stat = td::stat(file->local_->path_);
|
auto r_stat = td::stat(file->local_->path_);
|
||||||
if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ == file->size_) {
|
if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ == file->size_) {
|
||||||
@ -9463,7 +9582,7 @@ void Client::json_store_file(td::JsonObjectScope &object, const td_api::file *fi
|
|||||||
object("file_path", td::JsonRawString(file->local_->path_));
|
object("file_path", td::JsonRawString(file->local_->path_));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Slice relative_path = td::PathView::relative(file->local_->path_, absolute_dir_, true);
|
Slice relative_path = td::PathView::relative(file->local_->path_, dir_, true);
|
||||||
if (!relative_path.empty() && (parameters_->local_mode_ || parameters_->no_file_limit_ || file->local_->downloaded_size_ <= MAX_DOWNLOAD_FILE_SIZE)) {
|
if (!relative_path.empty() && (parameters_->local_mode_ || parameters_->no_file_limit_ || file->local_->downloaded_size_ <= MAX_DOWNLOAD_FILE_SIZE)) {
|
||||||
object("file_path", relative_path);
|
object("file_path", relative_path);
|
||||||
}
|
}
|
||||||
|
@ -237,8 +237,9 @@ class Client : public WebhookActor::Callback {
|
|||||||
|
|
||||||
struct UserInfo;
|
struct UserInfo;
|
||||||
struct ChatInfo;
|
struct ChatInfo;
|
||||||
|
struct BotCommandScope;
|
||||||
|
|
||||||
enum class AccessRights { Read, Edit, Write };
|
enum class AccessRights { Read, ReadMembers, Edit, Write };
|
||||||
|
|
||||||
template <class OnSuccess>
|
template <class OnSuccess>
|
||||||
class TdOnCheckUserCallback;
|
class TdOnCheckUserCallback;
|
||||||
@ -288,6 +289,9 @@ class Client : public WebhookActor::Callback {
|
|||||||
|
|
||||||
void enable_internet_connection(PromisedQueryPtr query);
|
void enable_internet_connection(PromisedQueryPtr query);
|
||||||
|
|
||||||
|
template <class OnSuccess>
|
||||||
|
void check_bot_command_scope(BotCommandScope &&scope, PromisedQueryPtr query, OnSuccess on_success);
|
||||||
|
|
||||||
template <class OnSuccess>
|
template <class OnSuccess>
|
||||||
void check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success);
|
void check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success);
|
||||||
|
|
||||||
@ -370,6 +374,21 @@ class Client : public WebhookActor::Callback {
|
|||||||
|
|
||||||
td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results(td::JsonValue &&value);
|
td::Result<td::vector<object_ptr<td_api::InputInlineQueryResult>>> get_inline_query_results(td::JsonValue &&value);
|
||||||
|
|
||||||
|
struct BotCommandScope {
|
||||||
|
object_ptr<td_api::BotCommandScope> scope_;
|
||||||
|
td::string chat_id_;
|
||||||
|
td::int32 user_id_ = 0;
|
||||||
|
|
||||||
|
explicit BotCommandScope(object_ptr<td_api::BotCommandScope> scope, td::string chat_id = td::string(),
|
||||||
|
td::int32 user_id = 0)
|
||||||
|
: scope_(std::move(scope)), chat_id_(std::move(chat_id)), user_id_(user_id) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static td::Result<BotCommandScope> get_bot_command_scope(const Query *query);
|
||||||
|
|
||||||
|
static td::Result<BotCommandScope> get_bot_command_scope(td::JsonValue &&value);
|
||||||
|
|
||||||
static td::Result<object_ptr<td_api::botCommand>> get_bot_command(td::JsonValue &&value);
|
static td::Result<object_ptr<td_api::botCommand>> get_bot_command(td::JsonValue &&value);
|
||||||
|
|
||||||
static td::Result<td::vector<object_ptr<td_api::botCommand>>> get_bot_commands(const Query *query);
|
static td::Result<td::vector<object_ptr<td_api::botCommand>>> get_bot_commands(const Query *query);
|
||||||
@ -445,7 +464,7 @@ class Client : public WebhookActor::Callback {
|
|||||||
static td::Result<int32> get_user_id(const Query *query, Slice field_name = Slice("user_id"));
|
static td::Result<int32> get_user_id(const Query *query, Slice field_name = Slice("user_id"));
|
||||||
|
|
||||||
int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, bool *is_reply_to_message_deleted);
|
int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, bool *is_reply_to_message_deleted);
|
||||||
|
|
||||||
// start custom helper methods
|
// start custom helper methods
|
||||||
|
|
||||||
static td::Result<object_ptr<td_api::MessageSchedulingState>> get_message_scheduling_state(const Query *query);
|
static td::Result<object_ptr<td_api::MessageSchedulingState>> get_message_scheduling_state(const Query *query);
|
||||||
@ -474,6 +493,7 @@ class Client : public WebhookActor::Callback {
|
|||||||
Status process_get_me_query(PromisedQueryPtr &query);
|
Status process_get_me_query(PromisedQueryPtr &query);
|
||||||
Status process_get_my_commands_query(PromisedQueryPtr &query);
|
Status process_get_my_commands_query(PromisedQueryPtr &query);
|
||||||
Status process_set_my_commands_query(PromisedQueryPtr &query);
|
Status process_set_my_commands_query(PromisedQueryPtr &query);
|
||||||
|
Status process_delete_my_commands_query(PromisedQueryPtr &query);
|
||||||
Status process_get_user_profile_photos_query(PromisedQueryPtr &query);
|
Status process_get_user_profile_photos_query(PromisedQueryPtr &query);
|
||||||
Status process_send_message_query(PromisedQueryPtr &query);
|
Status process_send_message_query(PromisedQueryPtr &query);
|
||||||
Status process_send_animation_query(PromisedQueryPtr &query);
|
Status process_send_animation_query(PromisedQueryPtr &query);
|
||||||
@ -1017,7 +1037,6 @@ class Client : public WebhookActor::Callback {
|
|||||||
int64 current_bot_resolve_query_id_ = 1;
|
int64 current_bot_resolve_query_id_ = 1;
|
||||||
|
|
||||||
td::string dir_;
|
td::string dir_;
|
||||||
td::string absolute_dir_;
|
|
||||||
td::ActorOwn<td::ClientActor> td_client_;
|
td::ActorOwn<td::ClientActor> td_client_;
|
||||||
td::ActorContext context_;
|
td::ActorContext context_;
|
||||||
std::queue<PromisedQueryPtr> cmd_queue_;
|
std::queue<PromisedQueryPtr> cmd_queue_;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "td/utils/port/IPAddress.h"
|
#include "td/utils/port/IPAddress.h"
|
||||||
#include "td/utils/port/Stat.h"
|
#include "td/utils/port/Stat.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/StackAllocator.h"
|
#include "td/utils/StackAllocator.h"
|
||||||
#include "td/utils/StringBuilder.h"
|
#include "td/utils/StringBuilder.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
@ -94,29 +95,24 @@ void ClientManager::send(PromisedQueryPtr query) {
|
|||||||
}
|
}
|
||||||
auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), token, td::ActorOwn<Client>()});
|
auto id = clients_.create(ClientInfo{BotStatActor(stat_.actor_id(&stat_)), token, td::ActorOwn<Client>()});
|
||||||
auto *client_info = clients_.get(id);
|
auto *client_info = clients_.get(id);
|
||||||
auto stat_actor = client_info->stat_.actor_id(&client_info->stat_);
|
client_info->client_ =
|
||||||
auto client_id = td::create_actor<Client>(
|
td::create_actor<Client>(PSLICE() << "Client/" << token, actor_shared(this, id), query->token().str(), query->is_user(),
|
||||||
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_,
|
||||||
query->is_test_dc(), get_tqueue_id(r_user_id.ok(), query->is_test_dc()), parameters_, std::move(stat_actor));
|
client_info->stat_.actor_id(&client_info->stat_));
|
||||||
|
|
||||||
auto method = query->method();
|
auto method = query->method();
|
||||||
if (method != "deletewebhook" && method != "setwebhook") {
|
if (method != "deletewebhook" && method != "setwebhook") {
|
||||||
auto webhook_info = parameters_->shared_data_->webhook_db_->get(bot_token_with_dc);
|
auto webhook_info = parameters_->shared_data_->webhook_db_->get(bot_token_with_dc);
|
||||||
if (!webhook_info.empty()) {
|
if (!webhook_info.empty()) {
|
||||||
send_closure(client_id, &Client::send,
|
send_closure(client_info->client_, &Client::send,
|
||||||
get_webhook_restore_query(bot_token_with_dc, query->is_user(), webhook_info, parameters_->shared_data_));
|
get_webhook_restore_query(bot_token_with_dc, query->is_user() webhook_info, parameters_->shared_data_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clients_.get(id)->client_ = std::move(client_id);
|
|
||||||
std::tie(id_it, std::ignore) = token_to_id_.emplace(token, id);
|
std::tie(id_it, std::ignore) = token_to_id_.emplace(token, id);
|
||||||
}
|
}
|
||||||
auto *client_info = clients_.get(id_it->second);
|
send_closure(clients_.get(id_it->second)->client_, &Client::send,
|
||||||
|
std::move(query)); // will send 429 if the client is already closed
|
||||||
if (!query->is_internal()) {
|
|
||||||
query->set_stat_actor(client_info->stat_.actor_id(&client_info->stat_));
|
|
||||||
}
|
|
||||||
send_closure(client_info->client_, &Client::send, std::move(query)); // will send 429 if the client is already closed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientManager::user_login(PromisedQueryPtr query) {
|
void ClientManager::user_login(PromisedQueryPtr query) {
|
||||||
@ -407,7 +403,7 @@ void ClientManager::start_up() {
|
|||||||
td::vector<td::uint64> failed_to_replay_log_event_ids;
|
td::vector<td::uint64> failed_to_replay_log_event_ids;
|
||||||
td::int64 loaded_event_count = 0;
|
td::int64 loaded_event_count = 0;
|
||||||
binlog
|
binlog
|
||||||
->init("tqueue.binlog",
|
->init(parameters_->working_directory_ + "tqueue.binlog",
|
||||||
[&](const td::BinlogEvent &event) {
|
[&](const td::BinlogEvent &event) {
|
||||||
if (tqueue_binlog->replay(event, *tqueue).is_error()) {
|
if (tqueue_binlog->replay(event, *tqueue).is_error()) {
|
||||||
failed_to_replay_log_event_ids.push_back(event.id_);
|
failed_to_replay_log_event_ids.push_back(event.id_);
|
||||||
@ -436,7 +432,8 @@ void ClientManager::start_up() {
|
|||||||
|
|
||||||
// init webhook_db and user_db
|
// init webhook_db and user_db
|
||||||
auto concurrent_webhook_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
|
auto concurrent_webhook_db = td::make_unique<td::BinlogKeyValue<td::ConcurrentBinlog>>();
|
||||||
auto status = concurrent_webhook_db->init("webhooks_db.binlog", td::DbKey::empty(), scheduler_id);
|
auto status = concurrent_webhook_db->init(parameters_->working_directory_ + "webhooks_db.binlog", td::DbKey::empty(),
|
||||||
|
scheduler_id);
|
||||||
LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status.error();
|
LOG_IF(FATAL, status.is_error()) << "Can't open webhooks_db.binlog " << status.error();
|
||||||
parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db);
|
parameters_->shared_data_->webhook_db_ = std::move(concurrent_webhook_db);
|
||||||
|
|
||||||
@ -510,8 +507,7 @@ PromisedQueryPtr ClientManager::get_webhook_restore_query(td::Slice token, bool
|
|||||||
const auto method = add_string("setwebhook");
|
const auto method = add_string("setwebhook");
|
||||||
auto query = std::make_unique<Query>(std::move(containers), token, is_user, is_test_dc, method, std::move(args),
|
auto query = std::make_unique<Query>(std::move(containers), token, is_user, is_test_dc, method, std::move(args),
|
||||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>>(),
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>>(),
|
||||||
td::vector<td::HttpFile>(), std::move(shared_data), td::IPAddress());
|
td::vector<td::HttpFile>(), std::move(shared_data), td::IPAddress(), true);
|
||||||
query->set_internal(true);
|
|
||||||
return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
|
return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,9 @@ struct SharedData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ClientParameters {
|
struct ClientParameters {
|
||||||
|
td::string working_directory_;
|
||||||
|
bool allow_colon_in_filenames_ = true;
|
||||||
|
|
||||||
bool local_mode_ = false;
|
bool local_mode_ = false;
|
||||||
bool allow_http_ = false;
|
bool allow_http_ = false;
|
||||||
bool use_relative_path_ = false;
|
bool use_relative_path_ = false;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "td/utils/JsonBuilder.h"
|
#include "td/utils/JsonBuilder.h"
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/Parser.h"
|
#include "td/utils/Parser.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
|
|
||||||
namespace telegram_bot_api {
|
namespace telegram_bot_api {
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ void HttpConnection::handle(td::unique_ptr<td::HttpQuery> http_query,
|
|||||||
|
|
||||||
auto query = std::make_unique<Query>(std::move(http_query->container_), token, is_user, is_test_dc, method,
|
auto query = std::make_unique<Query>(std::move(http_query->container_), token, is_user, is_test_dc, method,
|
||||||
std::move(http_query->args_), std::move(http_query->headers_),
|
std::move(http_query->args_), std::move(http_query->headers_),
|
||||||
std::move(http_query->files_), shared_data_, http_query->peer_address_);
|
std::move(http_query->files_), shared_data_, http_query->peer_address_, false);
|
||||||
|
|
||||||
td::PromiseActor<td::unique_ptr<Query>> promise;
|
td::PromiseActor<td::unique_ptr<Query>> promise;
|
||||||
td::FutureActor<td::unique_ptr<Query>> future;
|
td::FutureActor<td::unique_ptr<Query>> future;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "td/utils/format.h"
|
#include "td/utils/format.h"
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/port/SocketFd.h"
|
#include "td/utils/port/SocketFd.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/misc.h"
|
#include "td/utils/misc.h"
|
||||||
#include "td/utils/port/IPAddress.h"
|
#include "td/utils/port/IPAddress.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@ -25,7 +26,7 @@ std::unordered_map<td::string, std::unique_ptr<td::VirtuallyJsonable>> empty_par
|
|||||||
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
||||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
||||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
||||||
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address)
|
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address, bool is_internal)
|
||||||
: state_(State::Query)
|
: state_(State::Query)
|
||||||
, shared_data_(shared_data)
|
, shared_data_(shared_data)
|
||||||
, peer_address_(peer_address)
|
, peer_address_(peer_address)
|
||||||
@ -36,7 +37,8 @@ Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_u
|
|||||||
, method_(method)
|
, method_(method)
|
||||||
, args_(std::move(args))
|
, args_(std::move(args))
|
||||||
, headers_(std::move(headers))
|
, headers_(std::move(headers))
|
||||||
, files_(std::move(files)) {
|
, files_(std::move(files))
|
||||||
|
, is_internal_(is_internal) {
|
||||||
if (method_.empty()) {
|
if (method_.empty()) {
|
||||||
method_ = arg("method");
|
method_ = arg("method");
|
||||||
}
|
}
|
||||||
@ -67,6 +69,11 @@ td::int64 Query::files_max_size() const {
|
|||||||
[](td::int64 acc, const td::HttpFile &file) { return td::max(acc, file.size); });
|
[](td::int64 acc, const td::HttpFile &file) { return td::max(acc, file.size); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Query::set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
|
||||||
|
stat_actor_ = stat_actor;
|
||||||
|
send_request_stat();
|
||||||
|
}
|
||||||
|
|
||||||
void Query::set_ok(td::BufferSlice result) {
|
void Query::set_ok(td::BufferSlice result) {
|
||||||
CHECK(state_ == State::Query);
|
CHECK(state_ == State::Query);
|
||||||
LOG(INFO) << "QUERY: got ok " << td::tag("ptr", this) << td::tag("text", result.as_slice());
|
LOG(INFO) << "QUERY: got ok " << td::tag("ptr", this) << td::tag("text", result.as_slice());
|
||||||
@ -109,12 +116,26 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query) {
|
|||||||
return sb;
|
return sb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Query::send_response_stat() {
|
void Query::send_request_stat() const {
|
||||||
|
if (stat_actor_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Request>,
|
||||||
|
ServerBotStat::Request{query_size(), file_count(), files_size(), files_max_size()}, td::Time::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Query::send_response_stat() const {
|
||||||
|
auto now = td::Time::now();
|
||||||
|
if (now - start_timestamp_ >= 100.0) {
|
||||||
|
LOG(WARNING) << "Answer too old query with code " << http_status_code_ << " and answer size " << answer_.size()
|
||||||
|
<< ": " << *this;
|
||||||
|
}
|
||||||
|
|
||||||
if (stat_actor_.empty()) {
|
if (stat_actor_.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Response>,
|
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Response>,
|
||||||
ServerBotStat::Response{is_ok(), answer().size()}, td::Time::now());
|
ServerBotStat::Response{state_ == State::OK, answer_.size()}, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace telegram_bot_api
|
} // namespace telegram_bot_api
|
||||||
|
@ -76,17 +76,6 @@ class Query : public td::ListNode {
|
|||||||
return peer_address_;
|
return peer_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for stats
|
|
||||||
td::int32 file_count() const {
|
|
||||||
return static_cast<td::int32>(files_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
td::int64 query_size() const;
|
|
||||||
|
|
||||||
td::int64 files_size() const;
|
|
||||||
|
|
||||||
td::int64 files_max_size() const;
|
|
||||||
|
|
||||||
td::BufferSlice &answer() {
|
td::BufferSlice &answer() {
|
||||||
return answer_;
|
return answer_;
|
||||||
}
|
}
|
||||||
@ -109,26 +98,14 @@ class Query : public td::ListNode {
|
|||||||
return state_ != State::Query;
|
return state_ != State::Query;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_error() const {
|
|
||||||
return state_ == State::Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_ok() const {
|
|
||||||
return state_ == State::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_internal() const {
|
bool is_internal() const {
|
||||||
return is_internal_;
|
return is_internal_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_internal(bool is_internal) {
|
|
||||||
is_internal_ = is_internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
||||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
||||||
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
||||||
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address);
|
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_address, bool is_internal);
|
||||||
Query(const Query &) = delete;
|
Query(const Query &) = delete;
|
||||||
Query &operator=(const Query &) = delete;
|
Query &operator=(const Query &) = delete;
|
||||||
Query(Query &&) = delete;
|
Query(Query &&) = delete;
|
||||||
@ -143,10 +120,7 @@ class Query : public td::ListNode {
|
|||||||
return start_timestamp_;
|
return start_timestamp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
|
void set_stat_actor(td::ActorId<BotStatActor> stat_actor);
|
||||||
stat_actor_ = stat_actor;
|
|
||||||
}
|
|
||||||
void send_response_stat();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
State state_;
|
State state_;
|
||||||
@ -170,6 +144,21 @@ class Query : public td::ListNode {
|
|||||||
td::BufferSlice answer_;
|
td::BufferSlice answer_;
|
||||||
int http_status_code_ = 0;
|
int http_status_code_ = 0;
|
||||||
int retry_after_ = 0;
|
int retry_after_ = 0;
|
||||||
|
|
||||||
|
// for stats
|
||||||
|
td::int32 file_count() const {
|
||||||
|
return static_cast<td::int32>(files_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
td::int64 query_size() const;
|
||||||
|
|
||||||
|
td::int64 files_size() const;
|
||||||
|
|
||||||
|
td::int64 files_max_size() const;
|
||||||
|
|
||||||
|
void send_request_stat() const;
|
||||||
|
|
||||||
|
void send_response_stat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query);
|
td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query);
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
#include "telegram-bot-api/Stats.h"
|
#include "telegram-bot-api/Stats.h"
|
||||||
|
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/logging.h"
|
|
||||||
#include "td/utils/port/thread.h"
|
#include "td/utils/port/thread.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/StringBuilder.h"
|
#include "td/utils/StringBuilder.h"
|
||||||
|
|
||||||
namespace telegram_bot_api {
|
namespace telegram_bot_api {
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "td/utils/port/SocketFd.h"
|
#include "td/utils/port/SocketFd.h"
|
||||||
#include "td/utils/Random.h"
|
#include "td/utils/Random.h"
|
||||||
#include "td/utils/ScopeGuard.h"
|
#include "td/utils/ScopeGuard.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/Span.h"
|
#include "td/utils/Span.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
|
|
||||||
@ -165,7 +166,7 @@ td::Status WebhookActor::create_connection() {
|
|||||||
Callback &operator=(Callback &&) = delete;
|
Callback &operator=(Callback &&) = delete;
|
||||||
~Callback() {
|
~Callback() {
|
||||||
if (!actor_.empty()) {
|
if (!actor_.empty()) {
|
||||||
send_closure(std::move(actor_), &WebhookActor::on_socket_ready_async, td::Status::Error("Cancelled"), id_);
|
send_closure(std::move(actor_), &WebhookActor::on_socket_ready_async, td::Status::Error("Canceled"), id_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void on_connected() override {
|
void on_connected() override {
|
||||||
@ -585,10 +586,10 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
|
|||||||
if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" &&
|
if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" &&
|
||||||
method != "logout" && !td::begins_with(method, "get")) {
|
method != "logout" && !td::begins_with(method, "get")) {
|
||||||
VLOG(webhook) << "Receive request " << method << " in response to webhook";
|
VLOG(webhook) << "Receive request " << method << " in response to webhook";
|
||||||
auto query =
|
auto query = std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false,
|
||||||
std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false, td::MutableSlice(),
|
td::MutableSlice(), std::move(response->args_),
|
||||||
std::move(response->args_), std::move(response->headers_),
|
std::move(response->headers_), std::move(response->files_),
|
||||||
std::move(response->files_), parameters_->shared_data_, response->peer_address_);
|
parameters_->shared_data_, response->peer_address_, false);
|
||||||
auto promised_query =
|
auto promised_query =
|
||||||
PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
|
PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
|
||||||
send_closure(callback_, &Callback::send, std::move(promised_query));
|
send_closure(callback_, &Callback::send, std::move(promised_query));
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
#include "td/actor/ConcurrentScheduler.h"
|
#include "td/actor/ConcurrentScheduler.h"
|
||||||
#include "td/actor/PromiseFuture.h"
|
#include "td/actor/PromiseFuture.h"
|
||||||
|
|
||||||
#include "td/utils/algorithm.h"
|
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
|
#include "td/utils/CombinedLog.h"
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/crypto.h"
|
#include "td/utils/crypto.h"
|
||||||
#include "td/utils/ExitGuard.h"
|
#include "td/utils/ExitGuard.h"
|
||||||
@ -36,15 +36,19 @@
|
|||||||
#include "td/utils/MemoryLog.h"
|
#include "td/utils/MemoryLog.h"
|
||||||
#include "td/utils/misc.h"
|
#include "td/utils/misc.h"
|
||||||
#include "td/utils/OptionParser.h"
|
#include "td/utils/OptionParser.h"
|
||||||
|
#include "td/utils/PathView.h"
|
||||||
#include "td/utils/port/IPAddress.h"
|
#include "td/utils/port/IPAddress.h"
|
||||||
#include "td/utils/port/path.h"
|
#include "td/utils/port/path.h"
|
||||||
#include "td/utils/port/rlimit.h"
|
#include "td/utils/port/rlimit.h"
|
||||||
#include "td/utils/port/signals.h"
|
#include "td/utils/port/signals.h"
|
||||||
#include "td/utils/port/stacktrace.h"
|
#include "td/utils/port/stacktrace.h"
|
||||||
|
#include "td/utils/port/Stat.h"
|
||||||
#include "td/utils/port/user.h"
|
#include "td/utils/port/user.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
#include "td/utils/Time.h"
|
#include "td/utils/Time.h"
|
||||||
|
#include "td/utils/TsLog.h"
|
||||||
|
|
||||||
#include "memprof/memprof.h"
|
#include "memprof/memprof.h"
|
||||||
|
|
||||||
@ -56,10 +60,10 @@
|
|||||||
|
|
||||||
namespace telegram_bot_api {
|
namespace telegram_bot_api {
|
||||||
|
|
||||||
static std::atomic_flag need_rotate_log;
|
static std::atomic_flag need_reopen_log;
|
||||||
|
|
||||||
static void rotate_log_signal_handler(int sig) {
|
static void after_log_rotation_signal_handler(int sig) {
|
||||||
need_rotate_log.clear();
|
need_reopen_log.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic_flag need_quit;
|
static std::atomic_flag need_quit;
|
||||||
@ -106,11 +110,65 @@ static void sigsegv_signal_handler(int signum, void *addr) {
|
|||||||
fail_signal_handler(signum);
|
fail_signal_handler(signum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dump_statistics(const std::shared_ptr<SharedData> &shared_data,
|
||||||
|
const std::shared_ptr<td::NetQueryStats> &net_query_stats) {
|
||||||
|
if (is_memprof_on()) {
|
||||||
|
LOG(WARNING) << "Memory dump:";
|
||||||
|
td::vector<AllocInfo> v;
|
||||||
|
dump_alloc([&](const AllocInfo &info) { v.push_back(info); });
|
||||||
|
std::sort(v.begin(), v.end(), [](const AllocInfo &a, const AllocInfo &b) { return a.size > b.size; });
|
||||||
|
size_t total_size = 0;
|
||||||
|
size_t other_size = 0;
|
||||||
|
int count = 0;
|
||||||
|
for (auto &info : v) {
|
||||||
|
if (count++ < 50) {
|
||||||
|
LOG(WARNING) << td::format::as_size(info.size) << td::format::as_array(info.backtrace);
|
||||||
|
} else {
|
||||||
|
other_size += info.size;
|
||||||
|
}
|
||||||
|
total_size += info.size;
|
||||||
|
}
|
||||||
|
LOG(WARNING) << td::tag("other", td::format::as_size(other_size));
|
||||||
|
LOG(WARNING) << td::tag("total size", td::format::as_size(total_size));
|
||||||
|
LOG(WARNING) << td::tag("total traces", get_ht_size());
|
||||||
|
LOG(WARNING) << td::tag("fast_backtrace_success_rate", get_fast_backtrace_success_rate());
|
||||||
|
}
|
||||||
|
auto r_mem_stat = td::mem_stat();
|
||||||
|
if (r_mem_stat.is_ok()) {
|
||||||
|
auto mem_stat = r_mem_stat.move_as_ok();
|
||||||
|
LOG(WARNING) << td::tag("rss", td::format::as_size(mem_stat.resident_size_));
|
||||||
|
LOG(WARNING) << td::tag("vm", td::format::as_size(mem_stat.virtual_size_));
|
||||||
|
LOG(WARNING) << td::tag("rss_peak", td::format::as_size(mem_stat.resident_size_peak_));
|
||||||
|
LOG(WARNING) << td::tag("vm_peak", td::format::as_size(mem_stat.virtual_size_peak_));
|
||||||
|
}
|
||||||
|
LOG(WARNING) << td::tag("buffer_mem", td::format::as_size(td::BufferAllocator::get_buffer_mem()));
|
||||||
|
LOG(WARNING) << td::tag("buffer_slice_size", td::format::as_size(td::BufferAllocator::get_buffer_slice_size()));
|
||||||
|
|
||||||
|
auto query_count = shared_data->query_count_.load();
|
||||||
|
LOG(WARNING) << td::tag("pending queries", query_count);
|
||||||
|
|
||||||
|
td::uint64 i = 0;
|
||||||
|
bool was_gap = false;
|
||||||
|
for (auto end = &shared_data->query_list_, cur = end->prev; cur != end; cur = cur->prev, i++) {
|
||||||
|
if (i < 20 || i > query_count - 20 || i % (query_count / 50 + 1) == 0) {
|
||||||
|
if (was_gap) {
|
||||||
|
LOG(WARNING) << "...";
|
||||||
|
was_gap = false;
|
||||||
|
}
|
||||||
|
LOG(WARNING) << static_cast<const Query &>(*cur);
|
||||||
|
} else {
|
||||||
|
was_gap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td::dump_pending_network_queries(*net_query_stats);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL));
|
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL));
|
||||||
td::ExitGuard exit_guard;
|
td::ExitGuard exit_guard;
|
||||||
|
|
||||||
need_rotate_log.test_and_set();
|
need_reopen_log.test_and_set();
|
||||||
need_quit.test_and_set();
|
need_quit.test_and_set();
|
||||||
need_change_verbosity_level.test_and_set();
|
need_change_verbosity_level.test_and_set();
|
||||||
need_dump_log.test_and_set();
|
need_dump_log.test_and_set();
|
||||||
@ -118,7 +176,7 @@ int main(int argc, char *argv[]) {
|
|||||||
td::Stacktrace::init();
|
td::Stacktrace::init();
|
||||||
|
|
||||||
td::setup_signals_alt_stack().ensure();
|
td::setup_signals_alt_stack().ensure();
|
||||||
td::set_signal_handler(td::SignalType::User, rotate_log_signal_handler).ensure();
|
td::set_signal_handler(td::SignalType::User, after_log_rotation_signal_handler).ensure();
|
||||||
td::ignore_signal(td::SignalType::HangUp).ensure();
|
td::ignore_signal(td::SignalType::HangUp).ensure();
|
||||||
td::ignore_signal(td::SignalType::Pipe).ensure();
|
td::ignore_signal(td::SignalType::Pipe).ensure();
|
||||||
td::set_signal_handler(td::SignalType::Quit, quit_signal_handler).ensure();
|
td::set_signal_handler(td::SignalType::Quit, quit_signal_handler).ensure();
|
||||||
@ -134,7 +192,7 @@ int main(int argc, char *argv[]) {
|
|||||||
auto start_time = td::Time::now();
|
auto start_time = td::Time::now();
|
||||||
auto shared_data = std::make_shared<SharedData>();
|
auto shared_data = std::make_shared<SharedData>();
|
||||||
auto parameters = std::make_unique<ClientParameters>();
|
auto parameters = std::make_unique<ClientParameters>();
|
||||||
parameters->version_ = "5.2";
|
parameters->version_ = "5.3";
|
||||||
parameters->shared_data_ = shared_data;
|
parameters->shared_data_ = shared_data;
|
||||||
parameters->start_time_ = start_time;
|
parameters->start_time_ = start_time;
|
||||||
auto net_query_stats = td::create_net_query_stats();
|
auto net_query_stats = td::create_net_query_stats();
|
||||||
@ -142,6 +200,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
td::OptionParser options;
|
td::OptionParser options;
|
||||||
bool need_print_usage = false;
|
bool need_print_usage = false;
|
||||||
|
bool need_print_version = false;
|
||||||
int http_port = 8081;
|
int http_port = 8081;
|
||||||
int http_stat_port = 0;
|
int http_stat_port = 0;
|
||||||
td::string http_ip_address = "0.0.0.0";
|
td::string http_ip_address = "0.0.0.0";
|
||||||
@ -150,7 +209,7 @@ int main(int argc, char *argv[]) {
|
|||||||
int default_verbosity_level = 0;
|
int default_verbosity_level = 0;
|
||||||
int memory_verbosity_level = VERBOSITY_NAME(INFO);
|
int memory_verbosity_level = VERBOSITY_NAME(INFO);
|
||||||
td::int64 log_max_file_size = 2000000000;
|
td::int64 log_max_file_size = 2000000000;
|
||||||
td::string working_directory;
|
td::string working_directory = PSTRING() << "." << TD_DIR_SLASH;
|
||||||
td::string temporary_directory;
|
td::string temporary_directory;
|
||||||
td::string username;
|
td::string username;
|
||||||
td::string groupname;
|
td::string groupname;
|
||||||
@ -173,7 +232,8 @@ int main(int argc, char *argv[]) {
|
|||||||
options.set_usage(td::Slice(argv[0]), "--api-id=<arg> --api-hash=<arg> [--local] [OPTION]...");
|
options.set_usage(td::Slice(argv[0]), "--api-id=<arg> --api-hash=<arg> [--local] [OPTION]...");
|
||||||
options.set_description("Telegram Bot API server");
|
options.set_description("Telegram Bot API server");
|
||||||
options.add_option('h', "help", "display this help text and exit", [&] { need_print_usage = true; });
|
options.add_option('h', "help", "display this help text and exit", [&] { need_print_usage = true; });
|
||||||
options.add_option('\0', "local", "allow the Bot API server to serve local requests and disables the file limits",
|
options.add_option('\0', "version", "display version number and exit", [&] { need_print_version = true; });
|
||||||
|
options.add_option('\0', "local", "allow the Bot API server to serve local requests",
|
||||||
[&] { parameters->local_mode_ = true; });
|
[&] { parameters->local_mode_ = true; });
|
||||||
options.add_option('\0', "no-file-limit", "disable the file limits",
|
options.add_option('\0', "no-file-limit", "disable the file limits",
|
||||||
[&] { parameters->no_file_limit_ = true; });
|
[&] { parameters->no_file_limit_ = true; });
|
||||||
@ -288,77 +348,17 @@ int main(int argc, char *argv[]) {
|
|||||||
LOG(PLAIN) << options;
|
LOG(PLAIN) << options;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (need_print_version) {
|
||||||
|
LOG(PLAIN) << "Bot API " << parameters->version_;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (r_non_options.is_error()) {
|
if (r_non_options.is_error()) {
|
||||||
LOG(PLAIN) << argv[0] << ": " << r_non_options.error();
|
LOG(PLAIN) << argv[0] << ": " << r_non_options.error().message();
|
||||||
LOG(PLAIN) << options;
|
LOG(PLAIN) << options;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CombineLog : public td::LogInterface {
|
td::CombinedLog log;
|
||||||
public:
|
|
||||||
void append(td::CSlice slice, int log_level) override {
|
|
||||||
if (first_ && log_level <= first_verbosity_level_) {
|
|
||||||
if (log_level == VERBOSITY_NAME(FATAL) && second_ && VERBOSITY_NAME(FATAL) <= second_verbosity_level_) {
|
|
||||||
second_->append(slice, VERBOSITY_NAME(ERROR));
|
|
||||||
}
|
|
||||||
first_->append(slice, log_level);
|
|
||||||
}
|
|
||||||
if (second_ && log_level <= second_verbosity_level_) {
|
|
||||||
second_->append(slice, log_level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_first(LogInterface *first) {
|
|
||||||
first_ = first;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_second(LogInterface *second) {
|
|
||||||
second_ = second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_first_verbosity_level(int verbosity_level) {
|
|
||||||
first_verbosity_level_ = verbosity_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_second_verbosity_level(int verbosity_level) {
|
|
||||||
second_verbosity_level_ = verbosity_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_first_verbosity_level() const {
|
|
||||||
return first_verbosity_level_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_second_verbosity_level() const {
|
|
||||||
return second_verbosity_level_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rotate() override {
|
|
||||||
if (first_) {
|
|
||||||
first_->rotate();
|
|
||||||
}
|
|
||||||
if (second_) {
|
|
||||||
second_->rotate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td::vector<td::string> get_file_paths() override {
|
|
||||||
td::vector<td::string> result;
|
|
||||||
if (first_) {
|
|
||||||
td::append(result, first_->get_file_paths());
|
|
||||||
}
|
|
||||||
if (second_) {
|
|
||||||
td::append(result, second_->get_file_paths());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
LogInterface *first_ = nullptr;
|
|
||||||
int first_verbosity_level_ = VERBOSITY_NAME(FATAL);
|
|
||||||
LogInterface *second_ = nullptr;
|
|
||||||
int second_verbosity_level_ = VERBOSITY_NAME(FATAL);
|
|
||||||
};
|
|
||||||
CombineLog log;
|
|
||||||
log.set_first(td::default_log_interface);
|
log.set_first(td::default_log_interface);
|
||||||
log.set_second(&memory_log);
|
log.set_second(&memory_log);
|
||||||
td::log_interface = &log;
|
td::log_interface = &log;
|
||||||
@ -376,15 +376,65 @@ int main(int argc, char *argv[]) {
|
|||||||
TRY_STATUS_PREFIX(td::change_user(username, groupname), "Can't change effective user: ");
|
TRY_STATUS_PREFIX(td::change_user(username, groupname), "Can't change effective user: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!working_directory.empty()) {
|
{
|
||||||
TRY_STATUS_PREFIX(td::chdir(working_directory), "Can't set working directory: ");
|
TRY_RESULT_PREFIX_ASSIGN(working_directory, td::realpath(working_directory, true),
|
||||||
|
"Invalid working directory specified: ");
|
||||||
|
if (working_directory.empty()) {
|
||||||
|
return td::Status::Error("Working directory can't be empty");
|
||||||
|
}
|
||||||
|
if (working_directory.back() != TD_DIR_SLASH) {
|
||||||
|
working_directory += TD_DIR_SLASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY_STATUS_PREFIX(td::mkpath(working_directory, 0750), "Failed to create working directory: ");
|
||||||
|
|
||||||
|
auto r_temp_file = td::mkstemp(working_directory);
|
||||||
|
if (r_temp_file.is_error()) {
|
||||||
|
return td::Status::Error(PSLICE() << "Can't create files in the directory \"" << working_directory
|
||||||
|
<< "\". Use --dir option to specify a writable working directory");
|
||||||
|
}
|
||||||
|
r_temp_file.ok_ref().first.close();
|
||||||
|
td::unlink(r_temp_file.ok().second).ensure();
|
||||||
|
|
||||||
|
auto r_temp_dir = td::mkdtemp(working_directory, "1:a");
|
||||||
|
if (r_temp_dir.is_error()) {
|
||||||
|
parameters->allow_colon_in_filenames_ = false;
|
||||||
|
r_temp_dir = td::mkdtemp(working_directory, "1~a");
|
||||||
|
if (r_temp_dir.is_error()) {
|
||||||
|
return td::Status::Error(PSLICE() << "Can't create directories in the directory \"" << working_directory
|
||||||
|
<< "\". Use --dir option to specify a writable working directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td::rmdir(r_temp_dir.ok()).ensure();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!temporary_directory.empty()) {
|
if (!temporary_directory.empty()) {
|
||||||
|
if (td::PathView(temporary_directory).is_relative()) {
|
||||||
|
temporary_directory = working_directory + temporary_directory;
|
||||||
|
}
|
||||||
TRY_STATUS_PREFIX(td::set_temporary_dir(temporary_directory), "Can't set temporary directory: ");
|
TRY_STATUS_PREFIX(td::set_temporary_dir(temporary_directory), "Can't set temporary directory: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // check temporary directory
|
||||||
|
auto temp_dir = td::get_temporary_dir();
|
||||||
|
if (temp_dir.empty()) {
|
||||||
|
return td::Status::Error("Can't find directory for temporary files. Use --temp-dir option to specify it");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r_temp_file = td::mkstemp(temp_dir);
|
||||||
|
if (r_temp_file.is_error()) {
|
||||||
|
return td::Status::Error(PSLICE()
|
||||||
|
<< "Can't create files in the directory \"" << temp_dir
|
||||||
|
<< "\". Use --temp-dir option to specify another directory for temporary files");
|
||||||
|
}
|
||||||
|
r_temp_file.ok_ref().first.close();
|
||||||
|
td::unlink(r_temp_file.ok().second).ensure();
|
||||||
|
}
|
||||||
|
|
||||||
if (!log_file_path.empty()) {
|
if (!log_file_path.empty()) {
|
||||||
|
if (td::PathView(log_file_path).is_relative()) {
|
||||||
|
log_file_path = working_directory + log_file_path;
|
||||||
|
}
|
||||||
TRY_STATUS_PREFIX(file_log.init(log_file_path, log_max_file_size), "Can't open log file: ");
|
TRY_STATUS_PREFIX(file_log.init(log_file_path, log_max_file_size), "Can't open log file: ");
|
||||||
log.set_first(&ts_log);
|
log.set_first(&ts_log);
|
||||||
}
|
}
|
||||||
@ -392,11 +442,13 @@ int main(int argc, char *argv[]) {
|
|||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
}();
|
}();
|
||||||
if (init_status.is_error()) {
|
if (init_status.is_error()) {
|
||||||
LOG(PLAIN) << init_status.error();
|
LOG(PLAIN) << init_status.error().message();
|
||||||
LOG(PLAIN) << options;
|
LOG(PLAIN) << options;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameters->working_directory_ = std::move(working_directory);
|
||||||
|
|
||||||
if (parameters->default_max_webhook_connections_ <= 0) {
|
if (parameters->default_max_webhook_connections_ <= 0) {
|
||||||
parameters->default_max_webhook_connections_ = parameters->local_mode_ ? 100 : 40;
|
parameters->default_max_webhook_connections_ = parameters->local_mode_ ? 100 : 40;
|
||||||
}
|
}
|
||||||
@ -458,8 +510,8 @@ int main(int argc, char *argv[]) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
sched.run_main(next_cron_time - td::Time::now());
|
sched.run_main(next_cron_time - td::Time::now());
|
||||||
|
|
||||||
if (!need_rotate_log.test_and_set()) {
|
if (!need_reopen_log.test_and_set()) {
|
||||||
td::log_interface->rotate();
|
td::log_interface->after_rotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!need_quit.test_and_set()) {
|
if (!need_quit.test_and_set()) {
|
||||||
@ -468,7 +520,8 @@ int main(int argc, char *argv[]) {
|
|||||||
std::_Exit(0);
|
std::_Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(WARNING) << "Stopping engine by a signal";
|
LOG(WARNING) << "Stopping engine with uptime " << (td::Time::now() - start_time) << " seconds by a signal";
|
||||||
|
dump_statistics(shared_data, net_query_stats);
|
||||||
close_flag = true;
|
close_flag = true;
|
||||||
auto guard = sched.get_main_guard();
|
auto guard = sched.get_main_guard();
|
||||||
send_closure(client_manager, &ClientManager::close, td::PromiseCreator::lambda([&can_quit](td::Unit) {
|
send_closure(client_manager, &ClientManager::close, td::PromiseCreator::lambda([&can_quit](td::Unit) {
|
||||||
@ -497,6 +550,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if (!need_dump_log.test_and_set()) {
|
if (!need_dump_log.test_and_set()) {
|
||||||
print_log();
|
print_log();
|
||||||
|
dump_statistics(shared_data, net_query_stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
double now = td::Time::now();
|
double now = td::Time::now();
|
||||||
@ -525,48 +579,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if (now > last_dump_time + 300.0) {
|
if (now > last_dump_time + 300.0) {
|
||||||
last_dump_time = now;
|
last_dump_time = now;
|
||||||
if (is_memprof_on()) {
|
dump_statistics(shared_data, net_query_stats);
|
||||||
LOG(WARNING) << "Memory dump:";
|
|
||||||
td::vector<AllocInfo> v;
|
|
||||||
dump_alloc([&](const AllocInfo &info) { v.push_back(info); });
|
|
||||||
std::sort(v.begin(), v.end(), [](const AllocInfo &a, const AllocInfo &b) { return a.size > b.size; });
|
|
||||||
size_t total_size = 0;
|
|
||||||
size_t other_size = 0;
|
|
||||||
int count = 0;
|
|
||||||
for (auto &info : v) {
|
|
||||||
if (count++ < 50) {
|
|
||||||
LOG(WARNING) << td::format::as_size(info.size) << td::format::as_array(info.backtrace);
|
|
||||||
} else {
|
|
||||||
other_size += info.size;
|
|
||||||
}
|
|
||||||
total_size += info.size;
|
|
||||||
}
|
|
||||||
LOG(WARNING) << td::tag("other", td::format::as_size(other_size));
|
|
||||||
LOG(WARNING) << td::tag("total", td::format::as_size(total_size));
|
|
||||||
LOG(WARNING) << td::tag("total traces", get_ht_size());
|
|
||||||
LOG(WARNING) << td::tag("fast_backtrace_success_rate", get_fast_backtrace_success_rate());
|
|
||||||
}
|
|
||||||
LOG(WARNING) << td::tag("buffer_mem", td::format::as_size(td::BufferAllocator::get_buffer_mem()));
|
|
||||||
LOG(WARNING) << td::tag("buffer_slice_size", td::format::as_size(td::BufferAllocator::get_buffer_slice_size()));
|
|
||||||
|
|
||||||
auto query_count = shared_data->query_count_.load();
|
|
||||||
LOG(WARNING) << td::tag("pending queries", query_count);
|
|
||||||
|
|
||||||
td::uint64 i = 0;
|
|
||||||
bool was_gap = false;
|
|
||||||
for (auto end = &shared_data->query_list_, cur = end->prev; cur != end; cur = cur->prev, i++) {
|
|
||||||
if (i < 20 || i > query_count - 20 || i % (query_count / 50 + 1) == 0) {
|
|
||||||
if (was_gap) {
|
|
||||||
LOG(WARNING) << "...";
|
|
||||||
was_gap = false;
|
|
||||||
}
|
|
||||||
LOG(WARNING) << static_cast<Query &>(*cur);
|
|
||||||
} else {
|
|
||||||
was_gap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td::dump_pending_network_queries(*net_query_stats);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user