fix conflict

This commit is contained in:
Giuseppe Marino 2021-06-28 13:41:22 +02:00
commit a1510e45bb
No known key found for this signature in database
GPG Key ID: 2BC70C5463357449
14 changed files with 432 additions and 263 deletions

View File

@ -1,6 +1,12 @@
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)

View File

@ -121,7 +121,7 @@ function onLoad(initial) {
function onOsChanged(initial) {
var os = document.getElementById('osSelect').value;
if (os.includes('Choose ')) {
if (history.state != '') {
if (history.state !== '' && history.state !== null) {
history.pushState('', '', 'build.html');
}

2
td

@ -1 +1 @@
Subproject commit 49fd5cbd874601da8a9ff0083894d7104a4e8680
Subproject commit 1ea79d273976c22655742d4031ebbe62dba6cd7c

View File

@ -25,6 +25,7 @@
#include "td/utils/port/Stat.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Span.h"
#include "td/utils/StackAllocator.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;
Slice real_error_message = error_message;
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;
}
if (error_code == 400) {
@ -197,6 +202,7 @@ bool Client::init_methods() {
methods_.emplace("getme", &Client::process_get_me_query);
methods_.emplace("getmycommands", &Client::process_get_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("sendmessage", &Client::process_send_message_query);
methods_.emplace("sendanimation", &Client::process_send_animation_query);
@ -336,7 +342,7 @@ class Client::JsonDatedFile : public Jsonable {
class Client::JsonDatedFiles : public Jsonable {
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) {
}
void store(JsonValueScope *scope) const {
@ -347,7 +353,7 @@ class Client::JsonDatedFiles : public Jsonable {
}
private:
const td::vector<td_api::object_ptr<td_api::datedFile>> &files_;
const td::vector<object_ptr<td_api::datedFile>> &files_;
const Client *client_;
};
@ -1592,7 +1598,7 @@ class Client::JsonInlineKeyboardButton : public Jsonable {
case td_api::inlineKeyboardButtonTypeCallbackWithPassword::ID: {
auto data = get_callback_data(button_->type_);
if (!td::check_utf8(data)) {
object("callback_data", td::JsonRawString(data));
object("callback_data", "INVALID");
} else {
object("callback_data", data);
}
@ -2398,6 +2404,7 @@ class Client::JsonChatMembers : public Jsonable {
continue;
}
auto user_id = static_cast<const td_api::messageSenderUser *>(member->member_id_.get())->user_id_;
/*
bool is_member_bot = member->bot_info_ != nullptr;
if (!is_member_bot) {
// bot info may be unknown
@ -2406,7 +2413,7 @@ class Client::JsonChatMembers : public Jsonable {
is_member_bot = true;
}
}
/*
if (is_member_bot && user_id != client_->my_id_) {
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));
}
CHECK(result->get_id() == td_api::userFullInfo::ID);
auto user_full_info = move_object_as<td_api::userFullInfo>(result);
td::vector<object_ptr<td_api::botCommand>> commands;
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()); }),
CHECK(result->get_id() == td_api::botCommands::ID);
auto bot_commands = move_object_as<td_api::botCommands>(result);
answer_query(td::json_array(bot_commands->commands_, [](auto &command) { return JsonBotCommand(command.get()); }),
std::move(query_));
}
@ -3960,18 +3963,14 @@ void Client::start_up() {
CHECK(r_absolute_dir.is_ok());
absolute_dir_ = r_absolute_dir.move_as_ok();
auto suff = bot_token_with_dc_ + TD_DIR_SLASH;
#if TD_PORT_WINDOWS
for (auto &c : suff) {
if (c == ':') {
c = '~';
if (!parameters_->allow_colon_in_filenames_) {
for (auto &c : suff) {
if (c == ':') {
c = '~';
}
}
}
#endif
dir_ = td::string(".") + TD_DIR_SLASH + suff;
if (absolute_dir_.back() != TD_DIR_SLASH) {
absolute_dir_ += TD_DIR_SLASH;
}
absolute_dir_ += suff;
dir_ = parameters_->working_directory_ + suff;
class TdCallback : public td::TdCallback {
public:
@ -3995,10 +3994,7 @@ void Client::start_up() {
void Client::send(PromisedQueryPtr query) {
if (!query->is_internal()) {
send_closure(
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());
query->set_stat_actor(stat_actor_);
}
cmd_queue_.emplace(std::move(query));
loop();
@ -4174,6 +4170,10 @@ void Client::check_chat_access(int64 chat_id, AccessRights access_rights, const
break;
}
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);
CHECK(group_info != nullptr);
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),
std::move(parameters));
} else {
LOG(WARNING) << "Group chat " << chat_info->group_id << " with " << group_info->member_count
<< " members and title \"" << chat_info->title << "\" is deactivated";
return fail_query(400, "Bad Request: group chat was deactivated", std::move(query));
return fail_query(403, "Forbidden: the group chat was deleted", std::move(query));
}
}
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)));
}
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>
void Client::check_remote_file_id(td::string file_id, PromisedQueryPtr query, OnSuccess on_success) {
if (file_id.empty()) {
@ -4577,6 +4605,8 @@ void Client::on_update_authorization_state() {
case td_api::authorizationStateWaitEncryptionKey::ID:
return send_request(make_object<td_api::checkDatabaseEncryptionKey>(), std::make_unique<TdOnInitCallback>(this));
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_) {
waiting_for_auth_input_ = true;
return loop();
@ -5009,8 +5039,8 @@ void Client::on_closed() {
td::ActorId<Client> parent_;
void start_up() override {
CHECK(!dir_.empty());
CHECK(dir_[0] == '.');
CHECK(dir_.size() >= 24);
CHECK(dir_.back() == TD_DIR_SLASH);
td::rmrf(dir_).ignore();
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::vector<td::vector<object_ptr<td_api::keyboardButton>>> rows;
td::vector<td::vector<object_ptr<td_api::inlineKeyboardButton>>> inline_rows;
Slice input_field_placeholder;
bool resize = false;
bool one_time = 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");
}
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;
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()) {
result = make_object<td_api::replyMarkupInlineKeyboard>(std::move(inline_rows));
} else if (remove) {
result = make_object<td_api::replyMarkupRemoveKeyboard>(is_personal);
} 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) {
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");
}
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) {
if (value.type() != JsonValue::Type::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,
int32 max_value) {
auto s_arg = query->arg(field_name);
if (s_arg.empty()) {
return default_value;
}
return td::clamp(td::to_integer<int32>(s_arg), min_value, max_value);
auto value = s_arg.empty() ? default_value : td::to_integer<int32>(s_arg);
return td::clamp(value, min_value, max_value);
}
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) {
send_request(make_object<td_api::getUserFullInfo>(my_id_),
std::make_unique<TdOnGetMyCommandsCallback>(std::move(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::getCommands>(std::move(scope), language_code),
std::make_unique<TdOnGetMyCommandsCallback>(std::move(query)));
});
return Status::OK();
}
td::Status Client::process_set_my_commands_query(PromisedQueryPtr &query) {
CHECK_IS_BOT();
TRY_RESULT(bot_commands, get_bot_commands(query.get()));
send_request(make_object<td_api::setCommands>(std::move(bot_commands)),
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
TRY_RESULT(scope, get_bot_command_scope(query.get()));
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();
}
@ -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"));
auto message_id = get_message_id(query.get());
bool replace_caption = query->has_arg("caption");
td_api::object_ptr<td_api::formattedText> caption;
object_ptr<td_api::formattedText> caption;
if (replace_caption) {
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");
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) {
get_chat_member(chat_id, user_id, std::move(query),
[this, chat_type = get_chat_type(chat_id)](td_api::object_ptr<td_api::chatMember> &&chat_member,
PromisedQueryPtr query) {
answer_query(JsonChatMember(chat_member.get(), chat_type, this), std::move(query));
});
});
check_chat(chat_id, AccessRights::ReadMembers, std::move(query),
[this, user_id](int64 chat_id, PromisedQueryPtr query) {
get_chat_member(chat_id, user_id, std::move(query),
[this, chat_type = get_chat_type(chat_id)](object_ptr<td_api::chatMember> &&chat_member,
PromisedQueryPtr query) {
answer_query(JsonChatMember(chat_member.get(), chat_type, this), std::move(query));
});
});
return Status::OK();
}
td::Status Client::process_get_chat_administrators_query(PromisedQueryPtr &query) {
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);
CHECK(chat_info != nullptr);
switch (chat_info->type) {
case ChatInfo::Type::Private:
return fail_query(400, "Bad Request: there are no administrators in the private chat", std::move(query));
case ChatInfo::Type::Group: {
auto group_info = get_group_info(chat_info->group_id);
CHECK(group_info != nullptr);
case ChatInfo::Type::Group:
return send_request(make_object<td_api::getBasicGroupFullInfo>(chat_info->group_id),
std::make_unique<TdOnGetGroupMembersCallback>(this, true, std::move(query)));
}
case ChatInfo::Type::Supergroup:
return send_request(
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) {
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);
CHECK(chat_info != nullptr);
switch (chat_info->type) {
@ -7937,9 +8058,6 @@ td::Status Client::process_get_chat_member_count_query(PromisedQueryPtr &query)
case ChatInfo::Type::Group: {
auto group_info = get_group_info(chat_info->group_id);
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));
}
case ChatInfo::Type::Supergroup:
@ -8016,8 +8134,8 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) {
get_chat_member(
chat_id, user_id, std::move(query),
[this, chat_id, user_id, status = std::move(status)](
td_api::object_ptr<td_api::chatMember> &&chat_member, PromisedQueryPtr query) mutable {
[this, chat_id, user_id, status = std::move(status)](object_ptr<td_api::chatMember> &&chat_member,
PromisedQueryPtr query) mutable {
if (chat_member->status_->get_id() == td_api::chatMemberStatusAdministrator::ID) {
auto administrator =
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(
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) {
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(
chat_id, user_id, std::move(query),
[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) {
auto restricted =
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) {
get_chat_member(chat_id, user_id, std::move(query),
[this, chat_id, user_id](td_api::object_ptr<td_api::chatMember> &&chat_member,
PromisedQueryPtr query) {
if (chat_member->status_->get_id() != td_api::chatMemberStatusBanned::ID) {
return answer_query(td::JsonTrue(), std::move(query));
}
get_chat_member(
chat_id, user_id, std::move(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) {
return answer_query(td::JsonTrue(), std::move(query));
}
send_request(make_object<td_api::setChatMemberStatus>(
chat_id, td_api::make_object<td_api::messageSenderUser>(user_id),
make_object<td_api::chatMemberStatusLeft>()),
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
});
send_request(make_object<td_api::setChatMemberStatus>(
chat_id, td_api::make_object<td_api::messageSenderUser>(user_id),
make_object<td_api::chatMemberStatusLeft>()),
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
});
} else {
check_user_no_fail(user_id, std::move(query), [this, chat_id, user_id](PromisedQueryPtr query) {
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),
[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)));
});
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),
[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,
std::move(stickers)),
std::move(stickers), PSTRING() << "bot" << my_id_),
std::make_unique<TdOnReturnStickerSetCallback>(this, false, std::move(query)));
});
return Status::OK();
@ -8317,7 +8436,7 @@ td::Status Client::process_set_webhook_query(PromisedQueryPtr &query) {
}
auto now = td::Time::now_cached();
if (!new_url.empty()) {
if (!new_url.empty() && !query->is_internal()) {
if (now < next_allowed_set_webhook_time_) {
query->set_retry_after_error(1);
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_;
file_download_listeners_[file_id].push_back(std::move(query));
if (file->local_->is_downloading_completed_) {
Slice relative_path = td::PathView::relative(file->local_->path_, absolute_dir_, true);
Slice relative_path = td::PathView::relative(file->local_->path_, dir_, true);
if (!relative_path.empty()) {
auto r_stat = td::stat(file->local_->path_);
if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ == file->size_) {
@ -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_));
}
} 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)) {
object("file_path", relative_path);
}

View File

@ -237,8 +237,9 @@ class Client : public WebhookActor::Callback {
struct UserInfo;
struct ChatInfo;
struct BotCommandScope;
enum class AccessRights { Read, Edit, Write };
enum class AccessRights { Read, ReadMembers, Edit, Write };
template <class OnSuccess>
class TdOnCheckUserCallback;
@ -288,6 +289,9 @@ class Client : public WebhookActor::Callback {
void enable_internet_connection(PromisedQueryPtr query);
template <class OnSuccess>
void check_bot_command_scope(BotCommandScope &&scope, PromisedQueryPtr query, OnSuccess on_success);
template <class OnSuccess>
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);
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<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"));
int64 extract_yet_unsent_message_query_id(int64 chat_id, int64 message_id, bool *is_reply_to_message_deleted);
// start custom helper methods
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_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_send_message_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;
td::string dir_;
td::string absolute_dir_;
td::ActorOwn<td::ClientActor> td_client_;
td::ActorContext context_;
std::queue<PromisedQueryPtr> cmd_queue_;

View File

@ -31,6 +31,7 @@
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/StringBuilder.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 *client_info = clients_.get(id);
auto stat_actor = client_info->stat_.actor_id(&client_info->stat_);
auto client_id = td::create_actor<Client>(
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_, std::move(stat_actor));
client_info->client_ =
td::create_actor<Client>(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_));
auto method = query->method();
if (method != "deletewebhook" && method != "setwebhook") {
auto webhook_info = parameters_->shared_data_->webhook_db_->get(bot_token_with_dc);
if (!webhook_info.empty()) {
send_closure(client_id, &Client::send,
get_webhook_restore_query(bot_token_with_dc, query->is_user(), webhook_info, parameters_->shared_data_));
send_closure(client_info->client_, &Client::send,
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);
}
auto *client_info = clients_.get(id_it->second);
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
send_closure(clients_.get(id_it->second)->client_, &Client::send,
std::move(query)); // will send 429 if the client is already closed
}
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::int64 loaded_event_count = 0;
binlog
->init("tqueue.binlog",
->init(parameters_->working_directory_ + "tqueue.binlog",
[&](const td::BinlogEvent &event) {
if (tqueue_binlog->replay(event, *tqueue).is_error()) {
failed_to_replay_log_event_ids.push_back(event.id_);
@ -436,7 +432,8 @@ void ClientManager::start_up() {
// init webhook_db and user_db
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();
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");
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<td::HttpFile>(), std::move(shared_data), td::IPAddress());
query->set_internal(true);
td::vector<td::HttpFile>(), std::move(shared_data), td::IPAddress(), true);
return PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
}

View File

@ -55,6 +55,9 @@ struct SharedData {
};
struct ClientParameters {
td::string working_directory_;
bool allow_colon_in_filenames_ = true;
bool local_mode_ = false;
bool allow_http_ = false;
bool use_relative_path_ = false;

View File

@ -14,6 +14,7 @@
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/Parser.h"
#include "td/utils/SliceBuilder.h"
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,
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::FutureActor<td::unique_ptr<Query>> future;

View File

@ -15,6 +15,7 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Time.h"
#include <functional>

View File

@ -14,6 +14,7 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Time.h"
#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,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
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)
, shared_data_(shared_data)
, peer_address_(peer_address)
@ -36,7 +37,8 @@ Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_u
, method_(method)
, args_(std::move(args))
, headers_(std::move(headers))
, files_(std::move(files)) {
, files_(std::move(files))
, is_internal_(is_internal) {
if (method_.empty()) {
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); });
}
void Query::set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
stat_actor_ = stat_actor;
send_request_stat();
}
void Query::set_ok(td::BufferSlice result) {
CHECK(state_ == State::Query);
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;
}
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()) {
return;
}
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

View File

@ -76,17 +76,6 @@ class Query : public td::ListNode {
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() {
return answer_;
}
@ -109,26 +98,14 @@ class Query : public td::ListNode {
return state_ != State::Query;
}
bool is_error() const {
return state_ == State::Error;
}
bool is_ok() const {
return state_ == State::OK;
}
bool is_internal() const {
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,
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
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 &operator=(const Query &) = delete;
Query(Query &&) = delete;
@ -143,10 +120,7 @@ class Query : public td::ListNode {
return start_timestamp_;
}
void set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
stat_actor_ = stat_actor;
}
void send_response_stat();
void set_stat_actor(td::ActorId<BotStatActor> stat_actor);
private:
State state_;
@ -170,6 +144,21 @@ class Query : public td::ListNode {
td::BufferSlice answer_;
int http_status_code_ = 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);

View File

@ -7,8 +7,8 @@
#include "telegram-bot-api/Stats.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/StringBuilder.h"
namespace telegram_bot_api {

View File

@ -28,6 +28,7 @@
#include "td/utils/port/SocketFd.h"
#include "td/utils/Random.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Span.h"
#include "td/utils/Time.h"
@ -165,7 +166,7 @@ td::Status WebhookActor::create_connection() {
Callback &operator=(Callback &&) = delete;
~Callback() {
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 {
@ -585,10 +586,10 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
if (!method.empty() && method != "deletewebhook" && method != "setwebhook" && method != "close" &&
method != "logout" && !td::begins_with(method, "get")) {
VLOG(webhook) << "Receive request " << method << " in response to webhook";
auto query =
std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false, td::MutableSlice(),
std::move(response->args_), std::move(response->headers_),
std::move(response->files_), parameters_->shared_data_, response->peer_address_);
auto query = std::make_unique<Query>(std::move(response->container_), td::MutableSlice(), false, false,
td::MutableSlice(), std::move(response->args_),
std::move(response->headers_), std::move(response->files_),
parameters_->shared_data_, response->peer_address_, false);
auto promised_query =
PromisedQueryPtr(query.release(), PromiseDeleter(td::PromiseActor<td::unique_ptr<Query>>()));
send_closure(callback_, &Callback::send, std::move(promised_query));

View File

@ -24,8 +24,8 @@
#include "td/actor/ConcurrentScheduler.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/CombinedLog.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/ExitGuard.h"
@ -36,15 +36,19 @@
#include "td/utils/MemoryLog.h"
#include "td/utils/misc.h"
#include "td/utils/OptionParser.h"
#include "td/utils/PathView.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/path.h"
#include "td/utils/port/rlimit.h"
#include "td/utils/port/signals.h"
#include "td/utils/port/stacktrace.h"
#include "td/utils/port/Stat.h"
#include "td/utils/port/user.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/Time.h"
#include "td/utils/TsLog.h"
#include "memprof/memprof.h"
@ -56,10 +60,10 @@
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) {
need_rotate_log.clear();
static void after_log_rotation_signal_handler(int sig) {
need_reopen_log.clear();
}
static std::atomic_flag need_quit;
@ -106,11 +110,65 @@ static void sigsegv_signal_handler(int signum, void *addr) {
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[]) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL));
td::ExitGuard exit_guard;
need_rotate_log.test_and_set();
need_reopen_log.test_and_set();
need_quit.test_and_set();
need_change_verbosity_level.test_and_set();
need_dump_log.test_and_set();
@ -118,7 +176,7 @@ int main(int argc, char *argv[]) {
td::Stacktrace::init();
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::Pipe).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 shared_data = std::make_shared<SharedData>();
auto parameters = std::make_unique<ClientParameters>();
parameters->version_ = "5.2";
parameters->version_ = "5.3";
parameters->shared_data_ = shared_data;
parameters->start_time_ = start_time;
auto net_query_stats = td::create_net_query_stats();
@ -142,6 +200,7 @@ int main(int argc, char *argv[]) {
td::OptionParser options;
bool need_print_usage = false;
bool need_print_version = false;
int http_port = 8081;
int http_stat_port = 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 memory_verbosity_level = VERBOSITY_NAME(INFO);
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 username;
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_description("Telegram Bot API server");
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; });
options.add_option('\0', "no-file-limit", "disable the file limits",
[&] { parameters->no_file_limit_ = true; });
@ -288,77 +348,17 @@ int main(int argc, char *argv[]) {
LOG(PLAIN) << options;
return 0;
}
if (need_print_version) {
LOG(PLAIN) << "Bot API " << parameters->version_;
return 0;
}
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;
return 1;
}
class CombineLog : public td::LogInterface {
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;
td::CombinedLog log;
log.set_first(td::default_log_interface);
log.set_second(&memory_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: ");
}
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 (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: ");
}
{ // 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 (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: ");
log.set_first(&ts_log);
}
@ -392,11 +442,13 @@ int main(int argc, char *argv[]) {
return td::Status::OK();
}();
if (init_status.is_error()) {
LOG(PLAIN) << init_status.error();
LOG(PLAIN) << init_status.error().message();
LOG(PLAIN) << options;
return 1;
}
parameters->working_directory_ = std::move(working_directory);
if (parameters->default_max_webhook_connections_ <= 0) {
parameters->default_max_webhook_connections_ = parameters->local_mode_ ? 100 : 40;
}
@ -458,8 +510,8 @@ int main(int argc, char *argv[]) {
while (true) {
sched.run_main(next_cron_time - td::Time::now());
if (!need_rotate_log.test_and_set()) {
td::log_interface->rotate();
if (!need_reopen_log.test_and_set()) {
td::log_interface->after_rotation();
}
if (!need_quit.test_and_set()) {
@ -468,7 +520,8 @@ int main(int argc, char *argv[]) {
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;
auto guard = sched.get_main_guard();
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()) {
print_log();
dump_statistics(shared_data, net_query_stats);
}
double now = td::Time::now();
@ -525,48 +579,7 @@ int main(int argc, char *argv[]) {
if (now > last_dump_time + 300.0) {
last_dump_time = now;
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", 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);
dump_statistics(shared_data, net_query_stats);
}
}