Fix conflict

This commit is contained in:
Giuseppe Marino 2021-03-16 18:48:23 +01:00
commit d6a99d8519
No known key found for this signature in database
GPG Key ID: 2BC70C5463357449
20 changed files with 504 additions and 173 deletions

View File

@ -92,7 +92,7 @@ PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PointerAlignment: Right
ReflowComments: false # true
SortIncludes: false # disabled, because we need case insensitive sort
SortUsingDeclarations: false # true

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TelegramBotApi VERSION 5.0.1 LANGUAGES CXX)
project(TelegramBotApi VERSION 5.1 LANGUAGES CXX)
add_subdirectory(td EXCLUDE_FROM_ALL)

2
td

@ -1 +1 @@
Subproject commit ec5614fd43c56ba4d6dd7e6a517bc55116ab45cd
Subproject commit 1ea79d273976c22655742d4031ebbe62dba6cd7c

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -13,6 +13,7 @@
#include "td/db/TQueue.h"
#include "td/utils/algorithm.h"
#include "td/utils/base64.h"
#include "td/utils/filesystem.h"
#include "td/utils/HttpUrl.h"
@ -20,7 +21,6 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/port/path.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Random.h"
@ -145,6 +145,9 @@ void Client::fail_query_with_error(PromisedQueryPtr query, int32 error_code, Sli
break;
case 500:
prefix = Slice("Internal Server Error");
if (real_error_message != Slice("Request aborted")) {
LOG(ERROR) << "Receive Internal Server Error: " << real_error_message;
}
break;
default:
LOG(ERROR) << "Unsupported error " << real_error_code << ": " << real_error_message;
@ -232,6 +235,9 @@ bool Client::init_methods() {
methods_.emplace("answershippingquery", &Client::process_answer_shipping_query_query);
methods_.emplace("answerprecheckoutquery", &Client::process_answer_pre_checkout_query_query);
methods_.emplace("exportchatinvitelink", &Client::process_export_chat_invite_link_query);
methods_.emplace("createchatinvitelink", &Client::process_create_chat_invite_link_query);
methods_.emplace("editchatinvitelink", &Client::process_edit_chat_invite_link_query);
methods_.emplace("revokechatinvitelink", &Client::process_revoke_chat_invite_link_query);
methods_.emplace("getchat", &Client::process_get_chat_query);
methods_.emplace("setchatphoto", &Client::process_set_chat_photo_query);
methods_.emplace("deletechatphoto", &Client::process_delete_chat_photo_query);
@ -586,6 +592,30 @@ class Client::JsonChatLocation : public Jsonable {
const td_api::chatLocation *chat_location_;
};
class Client::JsonChatInviteLink : public Jsonable {
public:
JsonChatInviteLink(const td_api::chatInviteLink *chat_invite_link, const Client *client)
: chat_invite_link_(chat_invite_link), client_(client) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("invite_link", chat_invite_link_->invite_link_);
object("creator", JsonUser(chat_invite_link_->creator_user_id_, client_));
if (chat_invite_link_->expire_date_ != 0) {
object("expire_date", chat_invite_link_->expire_date_);
}
if (chat_invite_link_->member_limit_ != 0) {
object("member_limit", chat_invite_link_->member_limit_);
}
object("is_primary", td::JsonBool(chat_invite_link_->is_primary_));
object("is_revoked", td::JsonBool(chat_invite_link_->is_revoked_));
}
private:
const td_api::chatInviteLink *chat_invite_link_;
const Client *client_;
};
class Client::JsonMessage : public Jsonable {
public:
JsonMessage(const MessageInfo *message, bool need_reply, const td::string &source, const Client *client)
@ -752,6 +782,9 @@ class Client::JsonChat : public Jsonable {
LOG(ERROR) << "Pinned unknown, inaccessible or deleted message " << pinned_message_id_;
}
}
if (chat_info->message_auto_delete_time != 0) {
object("message_auto_delete_time", chat_info->message_auto_delete_time);
}
}
// start custom properties impl
@ -1462,6 +1495,62 @@ class Client::JsonProximityAlertTriggered : public Jsonable {
const Client *client_;
};
class Client::JsonVoiceChatStarted : public Jsonable {
public:
explicit JsonVoiceChatStarted(const td_api::messageVoiceChatStarted *voice_chat_started)
: voice_chat_started_(voice_chat_started) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
}
private:
const td_api::messageVoiceChatStarted *voice_chat_started_;
};
class Client::JsonVoiceChatEnded : public Jsonable {
public:
explicit JsonVoiceChatEnded(const td_api::messageVoiceChatEnded *voice_chat_ended)
: voice_chat_ended_(voice_chat_ended) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("duration", voice_chat_ended_->duration_);
}
private:
const td_api::messageVoiceChatEnded *voice_chat_ended_;
};
class Client::JsonInviteVoiceChatParticipants : public Jsonable {
public:
JsonInviteVoiceChatParticipants(const td_api::messageInviteVoiceChatParticipants *invite_voice_chat_participants,
const Client *client)
: invite_voice_chat_participants_(invite_voice_chat_participants), client_(client) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("users", JsonUsers(invite_voice_chat_participants_->user_ids_, client_));
}
private:
const td_api::messageInviteVoiceChatParticipants *invite_voice_chat_participants_;
const Client *client_;
};
class Client::JsonChatSetTtl : public Jsonable {
public:
explicit JsonChatSetTtl(const td_api::messageChatSetTtl *chat_set_ttl) : chat_set_ttl_(chat_set_ttl) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("message_auto_delete_time", chat_set_ttl_->ttl_);
}
private:
const td_api::messageChatSetTtl *chat_set_ttl_;
};
class Client::JsonCallbackGame : public Jsonable {
public:
void store(JsonValueScope *scope) const {
@ -1821,8 +1910,11 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
break;
case td_api::messageScreenshotTaken::ID:
break;
case td_api::messageChatSetTtl::ID:
case td_api::messageChatSetTtl::ID: {
auto content = static_cast<const td_api::messageChatSetTtl *>(message_->content.get());
object("message_auto_delete_timer_changed", JsonChatSetTtl(content));
break;
}
case td_api::messageUnsupported::ID:
break;
case td_api::messageContactRegistered::ID:
@ -1858,6 +1950,21 @@ void Client::JsonMessage::store(JsonValueScope *scope) const {
object("proximity_alert_triggered", JsonProximityAlertTriggered(content, client_));
break;
}
case td_api::messageVoiceChatStarted::ID: {
auto content = static_cast<const td_api::messageVoiceChatStarted *>(message_->content.get());
object("voice_chat_started", JsonVoiceChatStarted(content));
break;
}
case td_api::messageVoiceChatEnded::ID: {
auto content = static_cast<const td_api::messageVoiceChatEnded *>(message_->content.get());
object("voice_chat_ended", JsonVoiceChatEnded(content));
break;
}
case td_api::messageInviteVoiceChatParticipants::ID: {
auto content = static_cast<const td_api::messageInviteVoiceChatParticipants *>(message_->content.get());
object("voice_chat_participants_invited", JsonInviteVoiceChatParticipants(content, client_));
break;
}
default:
UNREACHABLE();
}
@ -1903,10 +2010,12 @@ class Client::JsonMessageId : public Jsonable {
class Client::JsonInlineQuery : public Jsonable {
public:
JsonInlineQuery(int64 inline_query_id, int32 sender_user_id, const td_api::location *user_location,
const td::string &query, const td::string &offset, const Client *client)
const td_api::ChatType *chat_type, const td::string &query, const td::string &offset,
const Client *client)
: inline_query_id_(inline_query_id)
, sender_user_id_(sender_user_id)
, user_location_(user_location)
, chat_type_(chat_type)
, query_(query)
, offset_(offset)
, client_(client) {
@ -1919,6 +2028,35 @@ class Client::JsonInlineQuery : public Jsonable {
if (user_location_ != nullptr) {
object("location", JsonLocation(user_location_));
}
if (chat_type_ != nullptr) {
auto chat_type = [&] {
switch (chat_type_->get_id()) {
case td_api::chatTypePrivate::ID: {
auto type = static_cast<const td_api::chatTypePrivate *>(chat_type_);
if (type->user_id_ == sender_user_id_) {
return "sender";
}
return "private";
}
case td_api::chatTypeBasicGroup::ID:
return "group";
case td_api::chatTypeSupergroup::ID: {
auto type = static_cast<const td_api::chatTypeSupergroup *>(chat_type_);
if (type->is_channel_) {
return "channel";
} else {
return "supergroup";
}
}
case td_api::chatTypeSecret::ID:
return "";
default:
UNREACHABLE();
return "";
}
}();
object("chat_type", chat_type);
}
object("query", query_);
object("offset", offset_);
}
@ -1927,6 +2065,7 @@ class Client::JsonInlineQuery : public Jsonable {
int64 inline_query_id_;
int32 sender_user_id_;
const td_api::location *user_location_;
const td_api::ChatType *chat_type_;
const td::string &query_;
const td::string &offset_;
const Client *client_;
@ -2166,6 +2305,7 @@ class Client::JsonChatMember : public Jsonable {
case td_api::chatMemberStatusAdministrator::ID: {
auto administrator = static_cast<const td_api::chatMemberStatusAdministrator *>(member_->status_.get());
object("can_be_edited", td::JsonBool(administrator->can_be_edited_));
object("can_manage_chat", td::JsonBool(administrator->can_manage_chat_));
object("can_change_info", td::JsonBool(administrator->can_change_info_));
if (chat_type_ == Client::ChatType::Channel) {
object("can_post_messages", td::JsonBool(administrator->can_post_messages_));
@ -2178,6 +2318,7 @@ class Client::JsonChatMember : public Jsonable {
object("can_pin_messages", td::JsonBool(administrator->can_pin_messages_));
}
object("can_promote_members", td::JsonBool(administrator->can_promote_members_));
object("can_manage_voice_chats", td::JsonBool(administrator->can_manage_voice_chats_));
if (!administrator->custom_title_.empty()) {
object("custom_title", administrator->custom_title_);
}
@ -2252,6 +2393,29 @@ class Client::JsonChatMembers : public Jsonable {
const Client *client_;
};
class Client::JsonChatMemberUpdated : public Jsonable {
public:
JsonChatMemberUpdated(const td_api::updateChatMember *update, const Client *client)
: update_(update), client_(client) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
object("chat", JsonChat(update_->chat_id_, false, client_));
object("from", JsonUser(update_->actor_user_id_, client_));
object("date", update_->date_);
auto chat_type = client_->get_chat_type(update_->chat_id_);
object("old_chat_member", JsonChatMember(update_->old_chat_member_.get(), chat_type, client_));
object("new_chat_member", JsonChatMember(update_->new_chat_member_.get(), chat_type, client_));
if (update_->invite_link_ != nullptr) {
object("invite_link", JsonChatInviteLink(update_->invite_link_.get(), client_));
}
}
private:
const td_api::updateChatMember *update_;
const Client *client_;
};
class Client::JsonGameHighScore : public Jsonable {
public:
JsonGameHighScore(const td_api::gameHighScore *score, const Client *client) : score_(score), client_(client) {
@ -2507,7 +2671,7 @@ class Client::TdOnAuthorizationCallback : public TdQueryCallback {
}
LOG(WARNING) << "Logging out due to " << td::oneline(to_string(error));
client_->log_out();
client_->log_out(error->message_ == "API_ID_INVALID");
} else if (was_ready) {
client_->on_update_authorization_state();
}
@ -3375,9 +3539,9 @@ class Client::TdOnGetSupergroupMembersCountCallback : public TdQueryCallback {
PromisedQueryPtr query_;
};
class Client::TdOnGenerateChatInviteLinkCallback : public TdQueryCallback {
class Client::TdOnReplacePrimaryChatInviteLinkCallback : public TdQueryCallback {
public:
explicit TdOnGenerateChatInviteLinkCallback(PromisedQueryPtr query) : query_(std::move(query)) {
explicit TdOnReplacePrimaryChatInviteLinkCallback(PromisedQueryPtr query) : query_(std::move(query)) {
}
void on_result(object_ptr<td_api::Object> result) override {
@ -3394,6 +3558,33 @@ class Client::TdOnGenerateChatInviteLinkCallback : public TdQueryCallback {
PromisedQueryPtr query_;
};
class Client::TdOnGetChatInviteLinkCallback : public TdQueryCallback {
public:
TdOnGetChatInviteLinkCallback(const Client *client, PromisedQueryPtr query)
: client_(client), query_(std::move(query)) {
}
void on_result(object_ptr<td_api::Object> result) override {
if (result->get_id() == td_api::error::ID) {
return fail_query_with_error(std::move(query_), move_object_as<td_api::error>(result));
}
if (result->get_id() == td_api::chatInviteLink::ID) {
auto invite_link = move_object_as<td_api::chatInviteLink>(result);
return answer_query(JsonChatInviteLink(invite_link.get(), client_), std::move(query_));
} else {
CHECK(result->get_id() == td_api::chatInviteLinks::ID);
auto invite_links = move_object_as<td_api::chatInviteLinks>(result);
CHECK(!invite_links->invite_links_.empty());
return answer_query(JsonChatInviteLink(invite_links->invite_links_[0].get(), client_), std::move(query_));
}
}
private:
const Client *client_;
PromisedQueryPtr query_;
};
class Client::TdOnGetGameHighScoresCallback : public TdQueryCallback {
public:
TdOnGetGameHighScoresCallback(const Client *client, PromisedQueryPtr query)
@ -3658,7 +3849,8 @@ void Client::close() {
}
}
void Client::log_out() {
void Client::log_out(bool is_api_id_invalid) {
is_api_id_invalid_ |= is_api_id_invalid;
if (!td_client_.empty() && !logging_out_ && !closing_) {
do_send_request(make_object<td_api::logOut>(), std::make_unique<TdOnOkCallback>());
}
@ -3704,7 +3896,7 @@ void Client::start_up() {
next_set_webhook_logging_time_ = start_time_;
next_webhook_is_not_modified_warning_time_ = start_time_;
previous_get_updates_start_time_ = start_time_ - 100;
previous_get_updates_finish_time_ = start_time_ - 100;
next_get_updates_conflict_time_ = start_time_ - 100;
sticker_set_names_[GREAT_MINDS_SET_ID] = GREAT_MINDS_SET_NAME.str();
@ -4069,11 +4261,16 @@ void Client::check_message(Slice chat_id_str, int64 message_id, bool allow_empty
check_chat(chat_id_str, access_rights, std::move(query),
[this, message_id, allow_empty, message_type, on_success = std::move(on_success)](
int64 chat_id, PromisedQueryPtr query) mutable {
if (!have_message_access(chat_id)) {
if ((message_id <= 0 && !allow_empty) || !have_message_access(chat_id)) {
return fail_query_with_error(std::move(query), 400, "MESSAGE_NOT_FOUND",
PSLICE() << message_type << " not found");
}
if (message_id <= 0) {
CHECK(allow_empty);
return on_success(chat_id, 0, std::move(query));
}
send_request(make_object<td_api::getMessage>(chat_id, message_id),
std::make_unique<TdOnCheckMessageCallback<OnSuccess>>(
this, chat_id, allow_empty, message_type, std::move(query), std::move(on_success)));
@ -4227,7 +4424,8 @@ void Client::get_chat_member(int64 chat_id, int32 user_id, PromisedQueryPtr quer
void Client::send_request(object_ptr<td_api::Function> &&f, std::unique_ptr<TdQueryCallback> handler) {
if (logging_out_) {
return handler->on_result(make_object<td_api::error>(LOGGING_OUT_ERROR_CODE, LOGGING_OUT_ERROR_DESCRIPTION.str()));
return handler->on_result(
make_object<td_api::error>(LOGGING_OUT_ERROR_CODE, get_logging_out_error_description().str()));
}
if (closing_) {
return handler->on_result(make_object<td_api::error>(CLOSING_ERROR_CODE, CLOSING_ERROR_DESCRIPTION.str()));
@ -4265,7 +4463,7 @@ void Client::on_update_file(object_ptr<td_api::file> file) {
// also includes all 5xx and 429 errors
auto error = Status::Error(400, "Bad Request: wrong file_id or the file is temporarily unavailable");
if (logging_out_) {
error = Status::Error(LOGGING_OUT_ERROR_CODE, LOGGING_OUT_ERROR_DESCRIPTION);
error = Status::Error(LOGGING_OUT_ERROR_CODE, get_logging_out_error_description());
}
if (closing_) {
error = Status::Error(CLOSING_ERROR_CODE, CLOSING_ERROR_DESCRIPTION);
@ -4333,7 +4531,7 @@ void Client::on_update_authorization_state() {
parameters->api_hash_ = parameters_->api_hash_;
parameters->system_language_code_ = "en";
parameters->device_model_ = "server";
parameters->application_version_ = "5.0.1";
parameters->application_version_ = "5.1";
parameters->enable_storage_optimizer_ = true;
parameters->ignore_file_names_ = true;
@ -4393,7 +4591,7 @@ void Client::on_update_authorization_state() {
case td_api::authorizationStateClosed::ID:
return on_closed();
default:
return log_out(); // just in case
return log_out(false); // just in case
}
}
@ -4490,33 +4688,32 @@ void Client::on_update(object_ptr<td_api::Object> result) {
bool need_warning = false;
switch (chat->type_->get_id()) {
case td_api::chatTypePrivate::ID: {
auto info = move_object_as<td_api::chatTypePrivate>(chat->type_);
auto type = move_object_as<td_api::chatTypePrivate>(chat->type_);
chat_info->type = ChatInfo::Type::Private;
auto user_id = info->user_id_;
auto user_id = type->user_id_;
chat_info->user_id = user_id;
need_warning = get_user_info(user_id) == nullptr;
break;
}
case td_api::chatTypeBasicGroup::ID: {
auto info = move_object_as<td_api::chatTypeBasicGroup>(chat->type_);
auto type = move_object_as<td_api::chatTypeBasicGroup>(chat->type_);
chat_info->type = ChatInfo::Type::Group;
auto group_id = info->basic_group_id_;
auto group_id = type->basic_group_id_;
chat_info->group_id = group_id;
need_warning = get_group_info(group_id) == nullptr;
break;
}
case td_api::chatTypeSupergroup::ID: {
auto info = move_object_as<td_api::chatTypeSupergroup>(chat->type_);
auto type = move_object_as<td_api::chatTypeSupergroup>(chat->type_);
chat_info->type = ChatInfo::Type::Supergroup;
auto supergroup_id = info->supergroup_id_;
auto supergroup_id = type->supergroup_id_;
chat_info->supergroup_id = supergroup_id;
need_warning = get_supergroup_info(supergroup_id) == nullptr;
break;
}
case td_api::chatTypeSecret::ID: {
case td_api::chatTypeSecret::ID:
// unsupported
break;
}
default:
UNREACHABLE();
}
@ -4527,6 +4724,7 @@ void Client::on_update(object_ptr<td_api::Object> result) {
chat_info->title = std::move(chat->title_);
chat_info->photo = std::move(chat->photo_);
chat_info->permissions = std::move(chat->permissions_);
chat_info->message_auto_delete_time = chat->message_ttl_setting_;
break;
}
case td_api::updateChatTitle::ID: {
@ -4550,6 +4748,13 @@ void Client::on_update(object_ptr<td_api::Object> result) {
chat_info->permissions = std::move(update->permissions_);
break;
}
case td_api::updateChatMessageTtlSetting::ID: {
auto update = move_object_as<td_api::updateChatMessageTtlSetting>(result);
auto chat_info = add_chat(update->chat_id_);
CHECK(chat_info->type != ChatInfo::Type::Unknown);
chat_info->message_auto_delete_time = update->message_ttl_setting_;
break;
}
case td_api::updateUser::ID: {
auto update = move_object_as<td_api::updateUser>(result);
add_user(users_, std::move(update->user_));
@ -4569,8 +4774,11 @@ void Client::on_update(object_ptr<td_api::Object> result) {
case td_api::updateBasicGroupFullInfo::ID: {
auto update = move_object_as<td_api::updateBasicGroupFullInfo>(result);
auto group_id = update->basic_group_id_;
set_group_description(group_id, std::move(update->basic_group_full_info_->description_));
set_group_invite_link(group_id, std::move(update->basic_group_full_info_->invite_link_));
auto full_info = std::move(update->basic_group_full_info_);
set_group_description(group_id, std::move(full_info->description_));
set_group_invite_link(group_id, full_info->invite_link_ != nullptr
? std::move(full_info->invite_link_->invite_link_)
: td::string());
break;
}
case td_api::updateSupergroup::ID: {
@ -4581,18 +4789,21 @@ void Client::on_update(object_ptr<td_api::Object> result) {
case td_api::updateSupergroupFullInfo::ID: {
auto update = move_object_as<td_api::updateSupergroupFullInfo>(result);
auto supergroup_id = update->supergroup_id_;
set_supergroup_description(supergroup_id, std::move(update->supergroup_full_info_->description_));
set_supergroup_invite_link(supergroup_id, std::move(update->supergroup_full_info_->invite_link_));
set_supergroup_sticker_set_id(supergroup_id, update->supergroup_full_info_->sticker_set_id_);
set_supergroup_can_set_sticker_set(supergroup_id, update->supergroup_full_info_->can_set_sticker_set_);
set_supergroup_slow_mode_delay(supergroup_id, update->supergroup_full_info_->slow_mode_delay_);
set_supergroup_linked_chat_id(supergroup_id, update->supergroup_full_info_->linked_chat_id_);
set_supergroup_location(supergroup_id, std::move(update->supergroup_full_info_->location_));
auto full_info = std::move(update->supergroup_full_info_);
set_supergroup_description(supergroup_id, std::move(full_info->description_));
set_supergroup_invite_link(supergroup_id, full_info->invite_link_ != nullptr
? std::move(full_info->invite_link_->invite_link_)
: td::string());
set_supergroup_sticker_set_id(supergroup_id, full_info->sticker_set_id_);
set_supergroup_can_set_sticker_set(supergroup_id, full_info->can_set_sticker_set_);
set_supergroup_slow_mode_delay(supergroup_id, full_info->slow_mode_delay_);
set_supergroup_linked_chat_id(supergroup_id, full_info->linked_chat_id_);
set_supergroup_location(supergroup_id, std::move(full_info->location_));
break;
}
case td_api::updateOption::ID: {
auto update = move_object_as<td_api::updateOption>(result);
auto name = update->name_;
const td::string &name = update->name_;
if (name == "my_id") {
if (update->value_->get_id() == td_api::optionValueEmpty::ID) {
CHECK(logging_out_);
@ -4646,8 +4857,8 @@ void Client::on_update(object_ptr<td_api::Object> result) {
break;
case td_api::updateNewInlineQuery::ID: {
auto update = move_object_as<td_api::updateNewInlineQuery>(result);
add_new_inline_query(update->id_, update->sender_user_id_, std::move(update->user_location_), update->query_,
update->offset_);
add_new_inline_query(update->id_, update->sender_user_id_, std::move(update->user_location_),
std::move(update->chat_type_), update->query_, update->offset_);
break;
}
case td_api::updateNewChosenInlineResult::ID: {
@ -4674,6 +4885,9 @@ void Client::on_update(object_ptr<td_api::Object> result) {
case td_api::updateNewCustomQuery::ID:
add_new_custom_query(move_object_as<td_api::updateNewCustomQuery>(result));
break;
case td_api::updateChatMember::ID:
add_update_chat_member(move_object_as<td_api::updateChatMember>(result));
break;
default:
// we are not interested in this updates
break;
@ -4693,6 +4907,10 @@ void Client::on_result(td::uint64 id, object_ptr<td_api::Object> result) {
handlers_.erase(id);
}
td::Slice Client::get_logging_out_error_description() const {
return is_api_id_invalid_ ? API_ID_INVALID_ERROR_DESCRIPTION : LOGGING_OUT_ERROR_DESCRIPTION;
}
void Client::on_closed() {
LOG(WARNING) << "Closed";
CHECK(logging_out_ || closing_);
@ -4700,7 +4918,7 @@ void Client::on_closed() {
td_client_.reset();
int http_status_code = logging_out_ ? LOGGING_OUT_ERROR_CODE : CLOSING_ERROR_CODE;
Slice description = logging_out_ ? LOGGING_OUT_ERROR_DESCRIPTION : CLOSING_ERROR_DESCRIPTION;
Slice description = logging_out_ ? get_logging_out_error_description() : CLOSING_ERROR_DESCRIPTION;
if (webhook_set_query_) {
fail_query(http_status_code, description, std::move(webhook_set_query_));
}
@ -5568,7 +5786,7 @@ td::Result<td_api::object_ptr<td_api::InputInlineQueryResult>> Client::get_inlin
TRY_RESULT(sticker_file_id, get_json_object_string_field(object, "sticker_file_id", false));
if (input_message_content == nullptr) {
input_message_content = make_object<td_api::inputMessageSticker>(nullptr, nullptr, 0, 0);
input_message_content = make_object<td_api::inputMessageSticker>(nullptr, nullptr, 0, 0, td::string());
}
return make_object<td_api::inputInlineQueryResultSticker>(id, "", sticker_file_id, 0, 0, std::move(reply_markup),
std::move(input_message_content));
@ -5910,16 +6128,23 @@ td::Result<td::vector<td_api::object_ptr<td_api::inputPassportElementError>>> Cl
return std::move(errors);
}
td::Result<td_api::object_ptr<td_api::formattedText>> Client::get_caption(const Query *query) {
JsonValue entities;
auto r_value = json_decode(query->arg("caption_entities"));
if (r_value.is_ok()) {
entities = r_value.move_as_ok();
} else {
LOG(INFO) << "Can't parse JSON object: " << r_value.error();
JsonValue Client::get_input_entities(const Query *query, Slice field_name) {
auto entities = query->arg(field_name);
if (!entities.empty()) {
auto r_value = json_decode(entities);
if (r_value.is_ok()) {
return r_value.move_as_ok();
}
LOG(INFO) << "Can't parse entities JSON object: " << r_value.error();
}
return get_formatted_text(query->arg("caption").str(), query->arg("parse_mode").str(), std::move(entities));
return JsonValue();
}
td::Result<td_api::object_ptr<td_api::formattedText>> Client::get_caption(const Query *query) {
return get_formatted_text(query->arg("caption").str(), query->arg("parse_mode").str(),
get_input_entities(query, "caption_entities"));
}
td::Result<td_api::object_ptr<td_api::TextEntityType>> Client::get_text_entity_type(td::JsonObject &object) {
@ -6028,16 +6253,8 @@ td::Result<td_api::object_ptr<td_api::formattedText>> Client::get_formatted_text
}
td::Result<td_api::object_ptr<td_api::inputMessageText>> Client::get_input_message_text(const Query *query) {
JsonValue entities;
auto r_value = json_decode(query->arg("entities"));
if (r_value.is_ok()) {
entities = r_value.move_as_ok();
} else {
LOG(INFO) << "Can't parse JSON object: " << r_value.error();
}
return get_input_message_text(query->arg("text").str(), to_bool(query->arg("disable_web_page_preview")),
query->arg("parse_mode").str(), std::move(entities));
query->arg("parse_mode").str(), get_input_entities(query, "entities"));
}
td::Result<td_api::object_ptr<td_api::inputMessageText>> Client::get_input_message_text(td::string text,
@ -6580,10 +6797,10 @@ void Client::on_cmd(PromisedQueryPtr query) {
}
if (logging_out_) {
return fail_query(LOGGING_OUT_ERROR_CODE, LOGGING_OUT_ERROR_DESCRIPTION, std::move(query));
return fail_query(LOGGING_OUT_ERROR_CODE, get_logging_out_error_description(), std::move(query));
}
if (closing_) {
return fail_query(CLOSING_ERROR_CODE, LOGGING_OUT_ERROR_DESCRIPTION, std::move(query));
return fail_query(CLOSING_ERROR_CODE, CLOSING_ERROR_DESCRIPTION, std::move(query));
}
CHECK(was_authorized_);
@ -6708,7 +6925,8 @@ td::Status Client::process_send_sticker_query(PromisedQueryPtr &query) {
if (sticker == nullptr) {
return Status::Error(400, "There is no sticker in the request");
}
do_send_message(make_object<td_api::inputMessageSticker>(std::move(sticker), nullptr, 0, 0), std::move(query));
do_send_message(make_object<td_api::inputMessageSticker>(std::move(sticker), nullptr, 0, 0, td::string()),
std::move(query));
return Status::OK();
}
@ -6873,15 +7091,9 @@ td::Status Client::process_send_poll_query(PromisedQueryPtr &query) {
object_ptr<td_api::PollType> poll_type;
auto type = query->arg("type");
if (type == "quiz") {
JsonValue entities;
auto r_value = json_decode(query->arg("explanation_entities"));
if (r_value.is_ok()) {
entities = r_value.move_as_ok();
} else {
LOG(INFO) << "Can't parse JSON object: " << r_value.error();
}
TRY_RESULT(explanation, get_formatted_text(query->arg("explanation").str(),
query->arg("explanation_parse_mode").str(), std::move(entities)));
TRY_RESULT(explanation,
get_formatted_text(query->arg("explanation").str(), query->arg("explanation_parse_mode").str(),
get_input_entities(query.get(), "explanation_entities")));
poll_type = make_object<td_api::pollTypeQuiz>(get_integer_arg(query.get(), "correct_option_id", -1),
std::move(explanation));
@ -6975,7 +7187,7 @@ td::Status Client::process_send_media_group_query(PromisedQueryPtr &query) {
std::make_unique<TdOnSendMessageAlbumCallback>(this, std::move(query)));
};
check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply,
AccessRights::Write, "reply message", std::move(query), std::move(on_success));
AccessRights::Write, "replied message", std::move(query), std::move(on_success));
});
return Status::OK();
}
@ -7331,12 +7543,51 @@ td::Status Client::process_export_chat_invite_link_query(PromisedQueryPtr &query
auto chat_id = query->arg("chat_id");
check_chat(chat_id, AccessRights::Write, std::move(query), [this](int64 chat_id, PromisedQueryPtr query) {
send_request(make_object<td_api::generateChatInviteLink>(chat_id),
std::make_unique<TdOnGenerateChatInviteLinkCallback>(std::move(query)));
send_request(make_object<td_api::replacePrimaryChatInviteLink>(chat_id),
std::make_unique<TdOnReplacePrimaryChatInviteLinkCallback>(std::move(query)));
});
return Status::OK();
}
td::Status Client::process_create_chat_invite_link_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
auto expire_date = get_integer_arg(query.get(), "expire_date", 0, 0);
auto member_limit = get_integer_arg(query.get(), "member_limit", 0, 0, 100000);
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, expire_date, member_limit](int64 chat_id, PromisedQueryPtr query) {
send_request(make_object<td_api::createChatInviteLink>(chat_id, expire_date, member_limit),
std::make_unique<TdOnGetChatInviteLinkCallback>(this, std::move(query)));
});
return Status::OK();
}
td::Status Client::process_edit_chat_invite_link_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
auto invite_link = query->arg("invite_link");
auto expire_date = get_integer_arg(query.get(), "expire_date", 0, 0);
auto member_limit = get_integer_arg(query.get(), "member_limit", 0, 0, 100000);
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, invite_link = invite_link.str(), expire_date, member_limit](int64 chat_id, PromisedQueryPtr query) {
send_request(make_object<td_api::editChatInviteLink>(chat_id, invite_link, expire_date, member_limit),
std::make_unique<TdOnGetChatInviteLinkCallback>(this, std::move(query)));
});
return Status::OK();
}
td::Status Client::process_revoke_chat_invite_link_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
auto invite_link = query->arg("invite_link");
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, invite_link = invite_link.str()](int64 chat_id, PromisedQueryPtr query) {
send_request(make_object<td_api::revokeChatInviteLink>(chat_id, invite_link),
std::make_unique<TdOnGetChatInviteLinkCallback>(this, std::move(query)));
});
return Status::OK();
}
td::Status Client::process_get_chat_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
@ -7617,6 +7868,7 @@ td::Status Client::process_leave_chat_query(PromisedQueryPtr &query) {
td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
TRY_RESULT(user_id, get_user_id(query.get()));
auto can_manage_chat = to_bool(query->arg("can_manage_chat"));
auto can_change_info = to_bool(query->arg("can_change_info"));
auto can_post_messages = to_bool(query->arg("can_post_messages"));
auto can_edit_messages = to_bool(query->arg("can_edit_messages"));
@ -7625,10 +7877,12 @@ td::Status Client::process_promote_chat_member_query(PromisedQueryPtr &query) {
auto can_restrict_members = to_bool(query->arg("can_restrict_members"));
auto can_pin_messages = to_bool(query->arg("can_pin_messages"));
auto can_promote_members = to_bool(query->arg("can_promote_members"));
auto can_manage_voice_chats = to_bool(query->arg("can_manage_voice_chats"));
auto is_anonymous = to_bool(query->arg("is_anonymous"));
auto status = make_object<td_api::chatMemberStatusAdministrator>(
td::string(), true, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users,
can_restrict_members, can_pin_messages, can_promote_members, is_anonymous);
td::string(), true, can_manage_chat, can_change_info, can_post_messages, can_edit_messages, can_delete_messages,
can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_manage_voice_chats,
is_anonymous);
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, user_id, status = std::move(status)](int64 chat_id, PromisedQueryPtr query) mutable {
auto chat_info = get_chat(chat_id);
@ -7691,13 +7945,14 @@ td::Status Client::process_ban_chat_member_query(PromisedQueryPtr &query) {
auto chat_id = query->arg("chat_id");
TRY_RESULT(user_id, get_user_id(query.get()));
int32 until_date = get_integer_arg(query.get(), "until_date", 0);
auto revoke_messages = to_bool(query->arg("revoke_messages"));
check_chat(chat_id, AccessRights::Write, std::move(query),
[this, user_id, until_date](int64 chat_id, PromisedQueryPtr query) {
[this, user_id, until_date, revoke_messages](int64 chat_id, PromisedQueryPtr query) {
check_user_no_fail(
user_id, std::move(query), [this, chat_id, user_id, until_date](PromisedQueryPtr query) {
send_request(make_object<td_api::setChatMemberStatus>(
chat_id, user_id, make_object<td_api::chatMemberStatusBanned>(until_date)),
user_id, std::move(query),
[this, chat_id, user_id, until_date, revoke_messages](PromisedQueryPtr query) {
send_request(make_object<td_api::banChatMember>(chat_id, user_id, until_date, revoke_messages),
std::make_unique<TdOnOkQueryCallback>(std::move(query)));
});
});
@ -8657,7 +8912,7 @@ void Client::do_send_message(object_ptr<td_api::InputMessageContent> input_messa
std::make_unique<TdOnSendMessageCallback>(this, std::move(query)));
};
check_message(chat_id, reply_to_message_id, reply_to_message_id <= 0 || allow_sending_without_reply,
AccessRights::Write, "reply message", std::move(query), std::move(on_success));
AccessRights::Write, "replied message", std::move(query), std::move(on_success));
});
}
@ -8704,9 +8959,9 @@ void Client::abort_long_poll(bool from_set_webhook) {
void Client::fail_query_conflict(Slice message, PromisedQueryPtr &&query) {
auto now = td::Time::now_cached();
if (now >= previous_get_updates_finish_time_) {
if (now >= next_get_updates_conflict_time_) {
fail_query(409, message, std::move(query));
previous_get_updates_finish_time_ = now + 3.0;
next_get_updates_conflict_time_ = now + 3.0;
} else {
td::create_actor<td::SleepActor>(
"FailQueryConflictSleepActor", 3.0,
@ -8798,7 +9053,8 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu
}
if (need_warning) {
LOG(WARNING) << "Found " << updates.size() << " updates out of " << total_size << " + " << updates.size()
<< " after last getUpdates call at " << previous_get_updates_finish_date_;
<< " after last getUpdates call " << (td::Time::now() - previous_get_updates_finish_time_)
<< " seconds ago";
} else {
LOG(DEBUG) << "Found " << updates.size() << " updates out of " << total_size << " from " << from;
}
@ -8814,7 +9070,7 @@ void Client::do_get_updates(int32 offset, int32 limit, int32 timeout, PromisedQu
long_poll_slot_.set_timeout_at(long_poll_hard_timeout_);
return;
}
previous_get_updates_finish_date_ = td::Clocks::system(); // local time to output it to the log
previous_get_updates_finish_time_ = td::Time::now();
next_bot_updates_warning_time_ = td::Time::now() + BOT_UPDATES_WARNING_DELAY;
if (total_size == updates.size() && was_bot_updates_warning_) {
send_request(make_object<td_api::setBotUpdatesStatus>(0, ""), std::make_unique<TdOnOkCallback>());
@ -8852,10 +9108,10 @@ void Client::long_poll_wakeup(bool force_flag) {
void Client::add_user(std::unordered_map<int32, UserInfo> &users, object_ptr<td_api::user> &&user) {
auto user_info = &users[user->id_];
user_info->first_name = user->first_name_;
user_info->last_name = user->last_name_;
user_info->username = user->username_;
user_info->language_code = user->language_code_;
user_info->first_name = std::move(user->first_name_);
user_info->last_name = std::move(user->last_name_);
user_info->username = std::move(user->username_);
user_info->language_code = std::move(user->language_code_);
// start custom properties
user_info->is_verified = user->is_verified_;
@ -9156,6 +9412,10 @@ Client::Slice Client::get_update_type_name(UpdateType update_type) {
return Slice("poll");
case UpdateType::PollAnswer:
return Slice("poll_answer");
case UpdateType::MyChatMember:
return Slice("my_chat_member");
case UpdateType::ChatMember:
return Slice("chat_member");
default:
UNREACHABLE();
return Slice();
@ -9302,9 +9562,10 @@ void Client::add_update_poll_answer(object_ptr<td_api::updatePollAnswer> &&updat
}
void Client::add_new_inline_query(int64 inline_query_id, int32 sender_user_id, object_ptr<td_api::location> location,
const td::string &query, const td::string &offset) {
object_ptr<td_api::ChatType> chat_type, const td::string &query,
const td::string &offset) {
add_update(UpdateType::InlineQuery,
JsonInlineQuery(inline_query_id, sender_user_id, location.get(), query, offset, this), 30,
JsonInlineQuery(inline_query_id, sender_user_id, location.get(), chat_type.get(), query, offset, this), 30,
sender_user_id + (static_cast<int64>(1) << 33));
}
@ -9426,6 +9687,17 @@ void Client::add_new_custom_query(object_ptr<td_api::updateNewCustomQuery> &&que
add_update(UpdateType::CustomQuery, JsonCustomJson(query->data_), timeout, 0);
}
void Client::add_update_chat_member(object_ptr<td_api::updateChatMember> &&update) {
CHECK(update != nullptr);
auto left_time = update->date_ + 86400 - get_unix_time();
if (left_time > 0) {
bool is_my = (update->old_chat_member_->user_id_ == my_id_);
auto webhook_queue_id = update->chat_id_ + (static_cast<int64>(is_my ? 5 : 6) << 33);
auto update_type = is_my ? UpdateType::MyChatMember : UpdateType::ChatMember;
add_update(update_type, JsonChatMemberUpdated(update.get(), this), left_time, webhook_queue_id);
}
}
td::int32 Client::choose_added_member_id(const td_api::messageChatAddMembers *message_add_members) const {
CHECK(message_add_members != nullptr);
for (auto &member_user_id : message_add_members->member_user_ids_) {
@ -9450,6 +9722,9 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr<td_api::me
case td_api::messageChatDeleteMember::ID:
case td_api::messagePinMessage::ID:
case td_api::messageProximityAlertTriggered::ID:
case td_api::messageVoiceChatStarted::ID:
case td_api::messageVoiceChatEnded::ID:
case td_api::messageInviteVoiceChatParticipants::ID:
// don't skip
break;
default:
@ -9481,9 +9756,15 @@ bool Client::need_skip_update_message(int64 chat_id, const object_ptr<td_api::me
}
}
//if (message->ttl_ > 0) {
// return true;
//}
/*
if (message->ttl_ > 0 && message->ttl_expires_in_ == 0) {
return true;
}
*/
if (message->forward_info_ != nullptr &&
message->forward_info_->origin_->get_id() == td_api::messageForwardOriginMessageImport::ID) {
return true;
}
switch (message->content_->get_id()) {
case td_api::messagePhoto::ID: {
@ -9829,21 +10110,23 @@ void Client::delete_message(int64 chat_id, int64 message_id, bool only_from_cach
auto it = messages_.find({chat_id, message_id});
if (it == messages_.end()) {
if (yet_unsent_messages_.count({chat_id, message_id}) > 0) {
// yet unsent message is deleted, possible only if we are trying to write to inaccessible supergroup
// yet unsent message is deleted, possible only if we are trying to write to inaccessible supergroup or
// sent message was deleted before added to the chat
auto chat_info = get_chat(chat_id);
CHECK(chat_info != nullptr);
Status error;
if (chat_info->type != ChatInfo::Type::Supergroup) {
LOG(ERROR) << "Yet unsent message " << message_id << " is deleted in the chat " << chat_id;
error = Status::Error(403, "Forbidden: bot is not a member of the chat");
} else {
Status error =
Status::Error(500, "Internal Server Error: sent message was immediately deleted and can't be returned");
if (chat_info->type == ChatInfo::Type::Supergroup) {
auto supergroup_info = get_supergroup_info(chat_info->supergroup_id);
CHECK(supergroup_info != nullptr);
if (supergroup_info->is_supergroup) {
error = Status::Error(403, "Forbidden: bot is not a member of the supergroup chat");
} else {
error = Status::Error(403, "Forbidden: bot is not a member of the channel chat");
if (supergroup_info->status->get_id() == td_api::chatMemberStatusBanned::ID ||
supergroup_info->status->get_id() == td_api::chatMemberStatusLeft::ID) {
if (supergroup_info->is_supergroup) {
error = Status::Error(403, "Forbidden: bot is not a member of the supergroup chat");
} else {
error = Status::Error(403, "Forbidden: bot is not a member of the channel chat");
}
}
}
@ -9977,19 +10260,24 @@ Client::FullMessageId Client::add_message(object_ptr<td_api::message> &&message,
case td_api::messageForwardOriginChat::ID: {
auto forward_info = move_object_as<td_api::messageForwardOriginChat>(origin);
message_info->initial_sender_chat_id = forward_info->sender_chat_id_;
message_info->initial_author_signature = forward_info->author_signature_;
message_info->initial_author_signature = std::move(forward_info->author_signature_);
break;
}
case td_api::messageForwardOriginHiddenUser::ID: {
auto forward_info = move_object_as<td_api::messageForwardOriginHiddenUser>(origin);
message_info->initial_sender_name = forward_info->sender_name_;
message_info->initial_sender_name = std::move(forward_info->sender_name_);
break;
}
case td_api::messageForwardOriginChannel::ID: {
auto forward_info = move_object_as<td_api::messageForwardOriginChannel>(origin);
message_info->initial_chat_id = forward_info->chat_id_;
message_info->initial_message_id = forward_info->message_id_;
message_info->initial_author_signature = forward_info->author_signature_;
message_info->initial_author_signature = std::move(forward_info->author_signature_);
break;
}
case td_api::messageForwardOriginMessageImport::ID: {
auto forward_info = move_object_as<td_api::messageForwardOriginMessageImport>(origin);
message_info->initial_sender_name = std::move(forward_info->sender_name_);
break;
}
default:
@ -10220,6 +10508,7 @@ constexpr Client::Slice Client::MASK_POINTS[MASK_POINTS_SIZE];
constexpr int Client::LOGGING_OUT_ERROR_CODE;
constexpr Client::Slice Client::LOGGING_OUT_ERROR_DESCRIPTION;
constexpr Client::Slice Client::API_ID_INVALID_ERROR_DESCRIPTION;
constexpr int Client::CLOSING_ERROR_CODE;
constexpr Client::Slice Client::CLOSING_ERROR_DESCRIPTION;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -78,6 +78,7 @@ class Client : public WebhookActor::Callback {
static constexpr int LOGGING_OUT_ERROR_CODE = 401;
static constexpr Slice LOGGING_OUT_ERROR_DESCRIPTION = "Unauthorized";
static constexpr Slice API_ID_INVALID_ERROR_DESCRIPTION = "Unauthorized: invalid api-id/api-hash";
static constexpr int CLOSING_ERROR_CODE = 500;
static constexpr Slice CLOSING_ERROR_DESCRIPTION = "Internal Server Error: restart";
@ -96,6 +97,7 @@ class Client : public WebhookActor::Callback {
class JsonChatPermissions;
class JsonChatPhotoInfo;
class JsonChatLocation;
class JsonChatInviteLink;
class JsonChat;
class JsonMessageSender;
class JsonAnimation;
@ -140,6 +142,7 @@ class Client : public WebhookActor::Callback {
class JsonChatPhotos;
class JsonChatMember;
class JsonChatMembers;
class JsonChatMemberUpdated;
class JsonGameHighScore;
class JsonAddress;
class JsonOrderInfo;
@ -148,6 +151,10 @@ class Client : public WebhookActor::Callback {
class JsonEncryptedCredentials;
class JsonPassportData;
class JsonProximityAlertTriggered;
class JsonVoiceChatStarted;
class JsonVoiceChatEnded;
class JsonInviteVoiceChatParticipants;
class JsonChatSetTtl;
class JsonUpdateTypes;
class JsonWebhookInfo;
class JsonStickerSet;
@ -185,7 +192,8 @@ class Client : public WebhookActor::Callback {
class TdOnGetGroupMembersCallback;
class TdOnGetSupergroupMembersCallback;
class TdOnGetSupergroupMembersCountCallback;
class TdOnGenerateChatInviteLinkCallback;
class TdOnReplacePrimaryChatInviteLinkCallback;
class TdOnGetChatInviteLinkCallback;
class TdOnGetGameHighScoresCallback;
class TdOnReturnFileCallback;
class TdOnReturnStickerSetCallback;
@ -307,7 +315,8 @@ class Client : public WebhookActor::Callback {
void on_result(td::uint64 id, object_ptr<td_api::Object> result);
void on_update_authorization_state();
void log_out();
void log_out(bool is_api_id_invalid);
Slice get_logging_out_error_description() const;
void on_closed();
void finish_closing();
@ -382,6 +391,8 @@ class Client : public WebhookActor::Callback {
static td::Result<td::vector<object_ptr<td_api::inputPassportElementError>>> get_passport_element_errors(
const Query *query);
static td::JsonValue get_input_entities(const Query *query, Slice field_name);
static td::Result<object_ptr<td_api::formattedText>> get_caption(const Query *query);
static td::Result<object_ptr<td_api::TextEntityType>> get_text_entity_type(td::JsonObject &object);
@ -495,6 +506,9 @@ class Client : public WebhookActor::Callback {
Status process_answer_shipping_query_query(PromisedQueryPtr &query);
Status process_answer_pre_checkout_query_query(PromisedQueryPtr &query);
Status process_export_chat_invite_link_query(PromisedQueryPtr &query);
Status process_create_chat_invite_link_query(PromisedQueryPtr &query);
Status process_edit_chat_invite_link_query(PromisedQueryPtr &query);
Status process_revoke_chat_invite_link_query(PromisedQueryPtr &query);
Status process_get_chat_query(PromisedQueryPtr &query);
Status process_set_chat_photo_query(PromisedQueryPtr &query);
Status process_delete_chat_photo_query(PromisedQueryPtr &query);
@ -688,6 +702,7 @@ class Client : public WebhookActor::Callback {
enum class Type { Private, Group, Supergroup, Unknown };
Type type = Type::Unknown;
td::string title;
int32 message_auto_delete_time = 0;
object_ptr<td_api::chatPhotoInfo> photo;
object_ptr<td_api::chatPermissions> permissions;
union {
@ -832,7 +847,7 @@ class Client : public WebhookActor::Callback {
void add_update_poll_answer(object_ptr<td_api::updatePollAnswer> &&update);
void add_new_inline_query(int64 inline_query_id, int32 sender_user_id, object_ptr<td_api::location> location,
const td::string &query, const td::string &offset);
object_ptr<td_api::ChatType> chat_type, const td::string &query, const td::string &offset);
void add_new_chosen_inline_result(int32 sender_user_id, object_ptr<td_api::location> location,
const td::string &query, const td::string &result_id,
@ -851,6 +866,8 @@ class Client : public WebhookActor::Callback {
void add_new_custom_query(object_ptr<td_api::updateNewCustomQuery> &&query);
void add_update_chat_member(object_ptr<td_api::updateChatMember> &&update);
// append only before Size
enum class UpdateType : int32 {
Message,
@ -866,6 +883,8 @@ class Client : public WebhookActor::Callback {
PreCheckoutQuery,
Poll,
PollAnswer,
MyChatMember,
ChatMember,
Size
};
@ -893,13 +912,15 @@ class Client : public WebhookActor::Callback {
bool have_message_access(int64 chat_id) const;
// by default all 13 update types up to PollAnswer are allowed
static constexpr td::uint32 DEFAULT_ALLOWED_UPDATE_TYPES = ((1 << 13) - 1);
// by default ChatMember updates are disabled
static constexpr td::uint32 DEFAULT_ALLOWED_UPDATE_TYPES =
(1 << static_cast<int32>(UpdateType::Size)) - 1 - (1 << static_cast<int32>(UpdateType::ChatMember));
object_ptr<td_api::AuthorizationState> authorization_state_;
bool was_authorized_ = false;
bool closing_ = false;
bool logging_out_ = false;
bool is_api_id_invalid_ = false;
bool need_close_ = false;
bool clear_tqueue_ = false;
bool waiting_for_auth_input_ = false;
@ -1037,8 +1058,8 @@ class Client : public WebhookActor::Callback {
int32 previous_get_updates_offset_ = -1;
double previous_get_updates_start_time_ = 0;
double previous_get_updates_finish_date_ = 0;
double previous_get_updates_finish_time_ = 0;
double next_get_updates_conflict_time_ = 0;
td::uint64 webhook_generation_ = 1;

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020, Luckydonald (tdlight-telegram-bot-api+code@luckydonald.de) 2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021, Luckydonald (tdlight-telegram-bot-api+code@luckydonald.de) 2020
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -75,7 +75,7 @@ void ClientManager::send(PromisedQueryPtr query) {
}
auto r_user_id = td::to_integer_safe<td::int64>(query->token().substr(0, token.find(':')));
if (r_user_id.is_error() || r_user_id.ok() < 0 || !token_range_(r_user_id.ok())) {
return fail_query(401, "Unauthorized: unallowed token specified", std::move(query));
return fail_query(421, "Misdirected Request: unallowed token specified", std::move(query));
}
if (query->is_test_dc()) {

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -8,8 +8,6 @@
#include "telegram-bot-api/ClientParameters.h"
#include "td/db/TQueue.h"
#include "td/net/GetHostByNameActor.h"
#include "td/net/HttpHeaderCreator.h"
#include "td/net/HttpProxy.h"
@ -26,7 +24,6 @@
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Random.h"
@ -253,7 +250,7 @@ void WebhookActor::on_socket_ready_async(td::Result<td::SocketFd> r_fd, td::int6
}
void WebhookActor::create_new_connections() {
size_t need_connections = queues_.size();
size_t need_connections = queue_updates_.size();
if (need_connections > static_cast<size_t>(max_connections_)) {
need_connections = max_connections_;
}
@ -382,20 +379,18 @@ void WebhookActor::load_updates() {
for (auto &update : updates) {
VLOG(webhook) << "Load update " << update.id;
if (update_map_.find(update.id) != update_map_.end()) {
LOG(ERROR) << "Receive duplicated event from tqueue " << update.id;
LOG(ERROR) << "Receive duplicated event " << update.id << " from tqueue";
continue;
}
auto &dest = update_map_[update.id];
dest.id_ = update.id;
dest.json_ = update.data.str();
dest.state_ = Update::State::Begin;
dest.delay_ = 1;
dest.wakeup_at_ = now;
CHECK(update.expires_at >= unix_time_now);
dest.expires_at_ = update.expires_at;
dest.queue_id_ = update.extra;
tqueue_offset_ = update.id.next().move_as_ok();
begin_updates_n_++;
if (dest.queue_id_ == 0) {
dest.queue_id_ = unique_queue_id_++;
@ -421,9 +416,10 @@ void WebhookActor::load_updates() {
}
}
if (need_warning) {
LOG(WARNING) << "Loaded " << updates.size() << " updates out of " << total_size << ". Have total of "
<< update_map_.size() << " loaded in " << queues_.size() << " queues after last error \""
<< last_error_message_ << "\" at " << last_error_date_;
LOG(WARNING) << "Loaded " << updates.size() << " updates out of " << total_size << ". Have " << update_map_.size()
<< " updates loaded in " << queue_updates_.size() << " queues after last error \""
<< last_error_message_ << "\" " << (last_error_time_ == 0 ? -1 : td::Time::now() - last_error_time_)
<< " seconds ago";
}
if (updates.size() == total_size && last_update_was_successful_) {
@ -439,6 +435,7 @@ void WebhookActor::load_updates() {
void WebhookActor::drop_event(td::TQueue::EventId event_id) {
auto it = update_map_.find(event_id);
CHECK(it != update_map_.end());
auto queue_id = it->second.queue_id_;
update_map_.erase(it);
@ -460,7 +457,12 @@ void WebhookActor::drop_event(td::TQueue::EventId event_id) {
void WebhookActor::on_update_ok(td::TQueue::EventId event_id) {
last_update_was_successful_ = true;
last_success_time_ = td::Time::now();
VLOG(webhook) << "Receive ok for update " << event_id;
auto it = update_map_.find(event_id);
CHECK(it != update_map_.end());
VLOG(webhook) << "Receive ok for update " << event_id << " in " << (last_success_time_ - it->second.last_send_time_)
<< " seconds";
drop_event(event_id);
}
@ -470,6 +472,7 @@ void WebhookActor::on_update_error(td::TQueue::EventId event_id, td::Slice error
double now = td::Time::now();
auto it = update_map_.find(event_id);
CHECK(it != update_map_.end());
const int MAX_RETRY_AFTER = 3600;
retry_after = td::clamp(retry_after, 0, MAX_RETRY_AFTER);
@ -485,14 +488,13 @@ void WebhookActor::on_update_error(td::TQueue::EventId event_id, td::Slice error
return;
}
auto &update = it->second;
update.state_ = Update::State::Begin;
update.delay_ = next_delay;
update.wakeup_at_ = now + next_effective_delay;
update.fail_count_++;
queues_.emplace(update.wakeup_at_, update.queue_id_);
VLOG(webhook) << "Delay update " << event_id << " for " << (update.wakeup_at_ - now) << " seconds because of "
<< error << " after " << update.fail_count_ << " fails";
begin_updates_n_++;
<< error << " after " << update.fail_count_ << " fails received in " << (now - update.last_send_time_)
<< " seconds";
}
td::Status WebhookActor::send_update() {
@ -501,10 +503,11 @@ td::Status WebhookActor::send_update() {
}
if (queues_.empty()) {
return td::Status::Error("No updates");
return td::Status::Error("No pending updates");
}
auto it = queues_.begin();
if (it->wakeup_at > td::Time::now()) {
auto now = td::Time::now();
if (it->wakeup_at > now) {
relax_wakeup_at(it->wakeup_at, "send_update");
return td::Status::Error("No ready updates");
}
@ -514,6 +517,7 @@ td::Status WebhookActor::send_update() {
auto event_id = queue_updates_[queue_id].event_ids.front();
auto &update = update_map_[event_id];
update.last_send_time_ = now;
auto body = td::json_encode<td::BufferSlice>(JsonUpdate(update.id_.value(), update.json_));
@ -533,8 +537,6 @@ td::Status WebhookActor::send_update() {
}
auto &connection = *Connection::from_list_node(ready_connections_.get());
begin_updates_n_--;
update.state_ = Update::State::Send;
connection.event_id_ = update.id_;
VLOG(webhook) << "Send update " << update.id_ << " from queue " << queue_id << " into connection " << connection.id_
@ -548,11 +550,9 @@ td::Status WebhookActor::send_update() {
}
void WebhookActor::send_updates() {
VLOG(webhook) << "Have " << begin_updates_n_ << " pending updates to send";
while (begin_updates_n_ > 0) {
if (send_update().is_error()) {
return;
}
VLOG(webhook) << "Have " << (queues_.size() + update_map_.size() - queue_updates_.size()) << " pending updates in "
<< queues_.size() << " queues to send";
while (send_update().is_ok()) {
}
}
@ -653,7 +653,7 @@ void WebhookActor::handle(td::unique_ptr<td::HttpQuery> response) {
void WebhookActor::start_up() {
max_loaded_updates_ = max_connections_ * 2;
next_ip_address_resolve_time_ = last_success_time_ = td::Time::now();
next_ip_address_resolve_time_ = last_success_time_ = td::Time::now() - 3600;
active_new_connection_flood_.add_limit(1, 10 * max_connections_);
active_new_connection_flood_.add_limit(5, 20 * max_connections_);
@ -749,7 +749,7 @@ void WebhookActor::on_connection_error(td::Status error) {
void WebhookActor::on_webhook_error(td::Slice error) {
if (was_checked_) {
send_closure(callback_, &Callback::webhook_error, td::Status::Error(error));
last_error_date_ = td::Clocks::system(); // local time to output it to the log
last_error_time_ = td::Time::now();
last_error_message_ = error.str();
}
}

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -79,7 +79,7 @@ class WebhookActor : public td::HttpOutboundConnection::Callback {
td::string cert_path_;
std::shared_ptr<const ClientParameters> parameters_;
double last_error_date_ = 0;
double last_error_time_ = 0;
td::string last_error_message_ = "<none>";
bool fix_ip_address_ = false;
@ -94,11 +94,11 @@ class WebhookActor : public td::HttpOutboundConnection::Callback {
td::TQueue::EventId id_;
td::string json_;
td::int32 expires_at_ = 0;
double last_send_time_ = 0;
double wakeup_at_ = 0;
int delay_ = 0;
int fail_count_ = 0;
enum State { Begin, Send } state_ = State::Begin;
td::int64 queue_id_{0};
td::int64 queue_id_ = 0;
};
struct QueueUpdates {
@ -119,8 +119,6 @@ class WebhookActor : public td::HttpOutboundConnection::Callback {
}
};
td::int32 begin_updates_n_ = 0;
td::TQueue::EventId tqueue_offset_;
std::size_t max_loaded_updates_ = 0;
struct EventIdHash {

View File

@ -1,5 +1,5 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@ -24,6 +24,7 @@
#include "td/actor/ConcurrentScheduler.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
@ -145,7 +146,8 @@ int main(int argc, char *argv[]) {
td::string http_ip_address = "0.0.0.0";
td::string http_stat_ip_address = "0.0.0.0";
td::string log_file_path;
int verbosity_level = 0;
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 temporary_directory;
@ -234,7 +236,10 @@ int main(int argc, char *argv[]) {
options.add_option('l', "log", "path to the file where the log will be written",
td::OptionParser::parse_string(log_file_path));
options.add_checked_option('v', "verbosity", "log verbosity level", td::OptionParser::parse_integer(verbosity_level));
options.add_checked_option('v', "verbosity", "log verbosity level",
td::OptionParser::parse_integer(default_verbosity_level));
options.add_checked_option('\0', "memory-verbosity", "memory log verbosity level; defaults to 3",
td::OptionParser::parse_integer(memory_verbosity_level));
options.add_checked_option(
'\0', "log-max-file-size",
PSLICE() << "maximum size of the log file in bytes before it will be auto-rotated (default is "
@ -266,11 +271,17 @@ int main(int argc, char *argv[]) {
return td::Status::OK();
});
options.add_check([&] {
if (verbosity_level < 0) {
if (default_verbosity_level < 0) {
return td::Status::Error("Wrong verbosity level specified");
}
return td::Status::OK();
});
options.add_check([&] {
if (memory_verbosity_level < 0) {
return td::Status::Error("Wrong memory verbosity level specified");
}
return td::Status::OK();
});
auto r_non_options = options.run(argc, argv, 0);
if (need_print_usage) {
LOG(PLAIN) << options;
@ -286,6 +297,9 @@ int main(int argc, char *argv[]) {
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_) {
@ -309,6 +323,14 @@ int main(int argc, char *argv[]) {
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();
@ -380,11 +402,14 @@ int main(int argc, char *argv[]) {
::td::VERBOSITY_NAME(dns_resolver) = VERBOSITY_NAME(WARNING);
int memory_verbosity_level = td::max(VERBOSITY_NAME(INFO), verbosity_level);
SET_VERBOSITY_LEVEL(memory_verbosity_level);
log.set_first_verbosity_level(verbosity_level);
log.set_second_verbosity_level(memory_verbosity_level);
auto set_verbosity_level = [&log, memory_verbosity_level](int new_verbosity_level) {
SET_VERBOSITY_LEVEL(td::max(memory_verbosity_level, new_verbosity_level));
log.set_first_verbosity_level(new_verbosity_level);
};
set_verbosity_level(default_verbosity_level);
// LOG(WARNING) << "Bot API server with commit " << td::GitInfo::commit() << ' '
// << (td::GitInfo::is_dirty() ? "(dirty)" : "") << " started";
LOG(WARNING) << "Bot API server started";
@ -455,20 +480,18 @@ int main(int argc, char *argv[]) {
}
if (!need_change_verbosity_level.test_and_set()) {
auto current_global_verbosity_level = GET_VERBOSITY_LEVEL();
SET_VERBOSITY_LEVEL(256 - current_global_verbosity_level);
verbosity_level = 256 - verbosity_level;
log.set_first_verbosity_level(verbosity_level);
if (log.get_first_verbosity_level() == default_verbosity_level) {
// increase default log verbosity level
set_verbosity_level(100);
} else {
// return back verbosity level
set_verbosity_level(default_verbosity_level);
}
}
auto next_verbosity_level = shared_data->next_verbosity_level_.exchange(-1);
if (next_verbosity_level != -1) {
verbosity_level = next_verbosity_level;
memory_verbosity_level = td::max(VERBOSITY_NAME(INFO), verbosity_level);
SET_VERBOSITY_LEVEL(memory_verbosity_level);
log.set_first_verbosity_level(verbosity_level);
log.set_second_verbosity_level(memory_verbosity_level);
set_verbosity_level(next_verbosity_level);
}
if (!need_dump_log.test_and_set()) {