tdlight-telegram-bot-api/telegram-bot-api/Client.cpp
2024-09-09 00:34:57 +02:00

16147 lines
673 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// 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)
//
#include "telegram-bot-api/Client.h"
#include "telegram-bot-api/ClientParameters.h"
#include "td/db/TQueue.h"
#include "td/actor/MultiPromise.h"
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
#include "td/utils/base64.h"
#include "td/utils/emoji.h"
#include "td/utils/filesystem.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/port/path.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"
#include "td/utils/StringBuilder.h"
#include "td/utils/Time.h"
#include "td/utils/utf8.h"
#include <cstdlib>
#include <climits>
#define CHECK_IS_BOT() \
if (is_user_) { \
return td::Status::Error(BOT_ONLY_ERROR_CODE, BOT_ONLY_ERROR_DESCRIPTION); \
}
#define CHECK_IS_USER() \
if (!is_user_) { \
return td::Status::Error(USER_ONLY_ERROR_CODE, USER_ONLY_ERROR_DESCRIPTION); \
}
#define CHECK_USER_REPLY_MARKUP() \
if (reply_markup != nullptr && is_user_) { \
return td::Status::Error(BOT_ONLY_ERROR_CODE, BOT_ONLY_ERROR_DESCRIPTION); \
}
namespace telegram_bot_api {
using td_api::make_object;
using td_api::move_object_as;
Client::Client(td::ActorShared<> parent, const td::string &bot_token, bool is_user, bool is_test_dc, int64 tqueue_id,
std::shared_ptr<const ClientParameters> parameters, td::ActorId<BotStatActor> stat_actor)
: parent_(std::move(parent))
, bot_token_(bot_token)
, bot_token_id_("<unknown>")
, is_user_(is_user)
, is_test_dc_(is_test_dc)
, tqueue_id_(tqueue_id)
, parameters_(std::move(parameters))
, stat_actor_(std::move(stat_actor)) {
static auto is_inited = init_methods();
CHECK(is_inited);
}
Client::~Client() {
td::Scheduler::instance()->destroy_on_scheduler(SharedData::get_file_gc_scheduler_id(), messages_, users_, groups_,
supergroups_, chats_, sticker_set_names_);
}
int Client::get_retry_after_time(td::Slice error_message) {
td::Slice prefix = "Too Many Requests: retry after ";
if (td::begins_with(error_message, prefix)) {
auto r_retry_after = td::to_integer_safe<int>(error_message.substr(prefix.size()));
if (r_retry_after.is_ok() && r_retry_after.ok() > 0) {
return r_retry_after.ok();
}
}
return 0;
}
void Client::fail_query_with_error(PromisedQueryPtr query, int32 error_code, td::Slice error_message,
td::Slice default_message) {
if (error_code == 429) {
auto retry_after_time = get_retry_after_time(error_message);
if (retry_after_time > 0) {
return query->set_retry_after_error(retry_after_time);
}
LOG(ERROR) << "Wrong error message: " << error_message << " from " << *query;
return fail_query(500, error_message, std::move(query));
}
int32 real_error_code = error_code;
td::Slice real_error_message = error_message;
if (error_code < 400 || error_code == 404) {
if (error_code < 200) {
LOG(ERROR) << "Receive error \"" << real_error_message << "\" with code " << error_code << " from " << *query;
}
error_code = 400;
} else if (error_code == 403) {
bool is_server_error = true;
for (auto c : error_message) {
if (c == '_' || ('A' <= c && c <= 'Z') || td::is_digit(c)) {
continue;
}
is_server_error = false;
break;
}
if (is_server_error) {
error_code = 400;
}
}
if (error_code == 400) {
if (!default_message.empty()) {
error_message = default_message;
}
if (error_message == "MESSAGE_NOT_MODIFIED") {
error_message = td::Slice(
"message is not modified: specified new message content and reply markup are exactly the same as a current "
"content and reply markup of the message");
} else if (error_message == "WC_CONVERT_URL_INVALID" || error_message == "EXTERNAL_URL_INVALID") {
error_message = "Wrong HTTP URL specified";
} else if (error_message == "WEBPAGE_CURL_FAILED") {
error_message = "Failed to get HTTP URL content";
} else if (error_message == "WEBPAGE_MEDIA_EMPTY") {
error_message = "Wrong type of the web page content";
} else if (error_message == "MEDIA_GROUPED_INVALID") {
error_message = "Can't use the media of the specified type in the album";
} else if (error_message == "REPLY_MARKUP_TOO_LONG") {
error_message = td::Slice("reply markup is too long");
} else if (error_message == "INPUT_USER_DEACTIVATED") {
error_code = 403;
error_message = td::Slice("Forbidden: user is deactivated");
} else if (error_message == "USER_IS_BLOCKED") {
error_code = 403;
error_message = td::Slice("bot was blocked by the user");
} else if (error_message == "USER_ADMIN_INVALID") {
error_code = 400;
error_message = td::Slice("user is an administrator of the chat");
} else if (error_message == "File generation failed") {
error_code = 400;
error_message = td::Slice("can't upload file by URL");
} else if (error_message == "CHAT_ABOUT_NOT_MODIFIED") {
error_code = 400;
error_message = td::Slice("chat description is not modified");
} else if (error_message == "PACK_SHORT_NAME_INVALID") {
error_code = 400;
error_message = td::Slice("invalid sticker set name is specified");
} else if (error_message == "PACK_SHORT_NAME_OCCUPIED") {
error_code = 400;
error_message = td::Slice("sticker set name is already occupied");
} else if (error_message == "STICKER_EMOJI_INVALID") {
error_code = 400;
error_message = td::Slice("invalid sticker emojis");
} else if (error_message == "QUERY_ID_INVALID") {
error_code = 400;
error_message = td::Slice("query is too old and response timeout expired or query ID is invalid");
} else if (error_message == "MESSAGE_DELETE_FORBIDDEN") {
error_code = 400;
error_message = td::Slice("message can't be deleted");
}
}
td::Slice prefix;
switch (error_code) {
case 400:
prefix = td::Slice("Bad Request");
break;
case 401:
prefix = td::Slice("Unauthorized");
break;
case 403:
prefix = td::Slice("Forbidden");
break;
case 405:
prefix = td::Slice("Method Not Allowed");
break;
case 500:
prefix = td::Slice("Internal Server Error");
if (real_error_message != td::Slice("Request aborted")) {
LOG(ERROR) << "Receive Internal Server Error \"" << real_error_message << "\" from " << *query;
}
break;
default:
LOG(ERROR) << "Unsupported error " << real_error_code << ": " << real_error_message << " from " << *query;
return fail_query(400, PSLICE() << "Bad Request: " << error_message, std::move(query));
}
if (td::begins_with(error_message, prefix)) {
return fail_query(error_code, error_message, std::move(query));
} else {
td::string error_str = prefix.str();
if (error_message.empty()) {
LOG(ERROR) << "Empty error message with code " << real_error_code << " from " << *query;
} else {
error_str += ": ";
if (error_message.size() >= 2u &&
(error_message[1] == '_' || ('A' <= error_message[1] && error_message[1] <= 'Z'))) {
error_str += error_message.str();
} else {
error_str += td::to_lower(error_message[0]);
error_str += error_message.substr(1).str();
}
}
return fail_query(error_code, error_str, std::move(query));
}
}
void Client::fail_query_with_error(PromisedQueryPtr &&query, object_ptr<td_api::error> error,
td::Slice default_message) {
fail_query_with_error(std::move(query), error->code_, error->message_, default_message);
}
bool Client::is_special_error_code(int32 error_code) {
return error_code == 401 || error_code == 429 || error_code >= 500;
}
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("getmydefaultadministratorrights", &Client::process_get_my_default_administrator_rights_query);
methods_.emplace("setmydefaultadministratorrights", &Client::process_set_my_default_administrator_rights_query);
methods_.emplace("getmyname", &Client::process_get_my_name_query);
methods_.emplace("setmyname", &Client::process_set_my_name_query);
methods_.emplace("getmydescription", &Client::process_get_my_description_query);
methods_.emplace("setmydescription", &Client::process_set_my_description_query);
methods_.emplace("getmyshortdescription", &Client::process_get_my_short_description_query);
methods_.emplace("setmyshortdescription", &Client::process_set_my_short_description_query);
methods_.emplace("getchatmenubutton", &Client::process_get_chat_menu_button_query);
methods_.emplace("setchatmenubutton", &Client::process_set_chat_menu_button_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);
methods_.emplace("sendaudio", &Client::process_send_audio_query);
methods_.emplace("senddice", &Client::process_send_dice_query);
methods_.emplace("senddocument", &Client::process_send_document_query);
methods_.emplace("sendphoto", &Client::process_send_photo_query);
methods_.emplace("sendsticker", &Client::process_send_sticker_query);
methods_.emplace("sendvideo", &Client::process_send_video_query);
methods_.emplace("sendvideonote", &Client::process_send_video_note_query);
methods_.emplace("sendvoice", &Client::process_send_voice_query);
methods_.emplace("sendpaidmedia", &Client::process_send_paid_media_query);
methods_.emplace("sendgame", &Client::process_send_game_query);
methods_.emplace("sendinvoice", &Client::process_send_invoice_query);
methods_.emplace("sendlocation", &Client::process_send_location_query);
methods_.emplace("sendvenue", &Client::process_send_venue_query);
methods_.emplace("sendcontact", &Client::process_send_contact_query);
methods_.emplace("sendpoll", &Client::process_send_poll_query);
methods_.emplace("stoppoll", &Client::process_stop_poll_query);
methods_.emplace("copymessage", &Client::process_copy_message_query);
methods_.emplace("copymessages", &Client::process_copy_messages_query);
methods_.emplace("forwardmessage", &Client::process_forward_message_query);
methods_.emplace("forwardmessages", &Client::process_forward_messages_query);
methods_.emplace("sendmediagroup", &Client::process_send_media_group_query);
methods_.emplace("sendchataction", &Client::process_send_chat_action_query);
methods_.emplace("setmessagereaction", &Client::process_set_message_reaction_query);
methods_.emplace("editmessagetext", &Client::process_edit_message_text_query);
methods_.emplace("editmessagelivelocation", &Client::process_edit_message_live_location_query);
methods_.emplace("stopmessagelivelocation", &Client::process_edit_message_live_location_query);
methods_.emplace("editmessagemedia", &Client::process_edit_message_media_query);
methods_.emplace("editmessagecaption", &Client::process_edit_message_caption_query);
methods_.emplace("editmessagereplymarkup", &Client::process_edit_message_reply_markup_query);
methods_.emplace("deletemessage", &Client::process_delete_message_query);
methods_.emplace("deletemessages", &Client::process_delete_messages_query);
methods_.emplace("createinvoicelink", &Client::process_create_invoice_link_query);
methods_.emplace("getstartransactions", &Client::process_get_star_transactions_query);
methods_.emplace("refundstarpayment", &Client::process_refund_star_payment_query);
methods_.emplace("setgamescore", &Client::process_set_game_score_query);
methods_.emplace("getgamehighscores", &Client::process_get_game_high_scores_query);
methods_.emplace("answerwebappquery", &Client::process_answer_web_app_query_query);
methods_.emplace("answerinlinequery", &Client::process_answer_inline_query_query);
methods_.emplace("answercallbackquery", &Client::process_answer_callback_query_query);
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("createchatsubscriptioninvitelink", &Client::process_create_chat_subscription_invite_link_query);
methods_.emplace("editchatinvitelink", &Client::process_edit_chat_invite_link_query);
methods_.emplace("editchatsubscriptioninvitelink", &Client::process_edit_chat_subscription_invite_link_query);
methods_.emplace("revokechatinvitelink", &Client::process_revoke_chat_invite_link_query);
methods_.emplace("getbusinessconnection", &Client::process_get_business_connection_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);
methods_.emplace("setchattitle", &Client::process_set_chat_title_query);
methods_.emplace("setchatpermissions", &Client::process_set_chat_permissions_query);
methods_.emplace("setchatdescription", &Client::process_set_chat_description_query);
methods_.emplace("pinchatmessage", &Client::process_pin_chat_message_query);
methods_.emplace("unpinchatmessage", &Client::process_unpin_chat_message_query);
methods_.emplace("unpinallchatmessages", &Client::process_unpin_all_chat_messages_query);
methods_.emplace("setchatstickerset", &Client::process_set_chat_sticker_set_query);
methods_.emplace("deletechatstickerset", &Client::process_delete_chat_sticker_set_query);
methods_.emplace("getforumtopiciconstickers", &Client::process_get_forum_topic_icon_stickers_query);
methods_.emplace("createforumtopic", &Client::process_create_forum_topic_query);
methods_.emplace("editforumtopic", &Client::process_edit_forum_topic_query);
methods_.emplace("closeforumtopic", &Client::process_close_forum_topic_query);
methods_.emplace("reopenforumtopic", &Client::process_reopen_forum_topic_query);
methods_.emplace("deleteforumtopic", &Client::process_delete_forum_topic_query);
methods_.emplace("unpinallforumtopicmessages", &Client::process_unpin_all_forum_topic_messages_query);
methods_.emplace("editgeneralforumtopic", &Client::process_edit_general_forum_topic_query);
methods_.emplace("closegeneralforumtopic", &Client::process_close_general_forum_topic_query);
methods_.emplace("reopengeneralforumtopic", &Client::process_reopen_general_forum_topic_query);
methods_.emplace("hidegeneralforumtopic", &Client::process_hide_general_forum_topic_query);
methods_.emplace("unhidegeneralforumtopic", &Client::process_unhide_general_forum_topic_query);
methods_.emplace("unpinallgeneralforumtopicmessages", &Client::process_unpin_all_general_forum_topic_messages_query);
methods_.emplace("getchatmember", &Client::process_get_chat_member_query);
methods_.emplace("getchatadministrators", &Client::process_get_chat_administrators_query);
methods_.emplace("getchatmembercount", &Client::process_get_chat_member_count_query);
methods_.emplace("getchatmemberscount", &Client::process_get_chat_member_count_query);
methods_.emplace("leavechat", &Client::process_leave_chat_query);
methods_.emplace("promotechatmember", &Client::process_promote_chat_member_query);
methods_.emplace("setchatadministratorcustomtitle", &Client::process_set_chat_administrator_custom_title_query);
methods_.emplace("banchatmember", &Client::process_ban_chat_member_query);
methods_.emplace("kickchatmember", &Client::process_ban_chat_member_query);
methods_.emplace("restrictchatmember", &Client::process_restrict_chat_member_query);
methods_.emplace("unbanchatmember", &Client::process_unban_chat_member_query);
methods_.emplace("banchatsenderchat", &Client::process_ban_chat_sender_chat_query);
methods_.emplace("unbanchatsenderchat", &Client::process_unban_chat_sender_chat_query);
methods_.emplace("approvechatjoinrequest", &Client::process_approve_chat_join_request_query);
methods_.emplace("declinechatjoinrequest", &Client::process_decline_chat_join_request_query);
methods_.emplace("getuserchatboosts", &Client::process_get_user_chat_boosts_query);
methods_.emplace("getstickerset", &Client::process_get_sticker_set_query);
methods_.emplace("getcustomemojistickers", &Client::process_get_custom_emoji_stickers_query);
methods_.emplace("uploadstickerfile", &Client::process_upload_sticker_file_query);
methods_.emplace("createnewstickerset", &Client::process_create_new_sticker_set_query);
methods_.emplace("addstickertoset", &Client::process_add_sticker_to_set_query);
methods_.emplace("replacestickerinset", &Client::process_replace_sticker_in_set_query);
methods_.emplace("setstickersettitle", &Client::process_set_sticker_set_title_query);
methods_.emplace("setstickersetthumb", &Client::process_set_sticker_set_thumbnail_query);
methods_.emplace("setstickersetthumbnail", &Client::process_set_sticker_set_thumbnail_query);
methods_.emplace("setcustomemojistickersetthumbnail", &Client::process_set_custom_emoji_sticker_set_thumbnail_query);
methods_.emplace("deletestickerset", &Client::process_delete_sticker_set_query);
methods_.emplace("setstickerpositioninset", &Client::process_set_sticker_position_in_set_query);
methods_.emplace("deletestickerfromset", &Client::process_delete_sticker_from_set_query);
methods_.emplace("setstickeremojilist", &Client::process_set_sticker_emoji_list_query);
methods_.emplace("setstickerkeywords", &Client::process_set_sticker_keywords_query);
methods_.emplace("setstickermaskposition", &Client::process_set_sticker_mask_position_query);
methods_.emplace("setpassportdataerrors", &Client::process_set_passport_data_errors_query);
methods_.emplace("sendcustomrequest", &Client::process_send_custom_request_query);
methods_.emplace("answercustomquery", &Client::process_answer_custom_query_query);
methods_.emplace("getupdates", &Client::process_get_updates_query);
methods_.emplace("setwebhook", &Client::process_set_webhook_query);
methods_.emplace("deletewebhook", &Client::process_set_webhook_query);
methods_.emplace("getwebhookinfo", &Client::process_get_webhook_info_query);
methods_.emplace("getfile", &Client::process_get_file_query);
//custom methods
methods_.emplace("getmessageinfo", &Client::process_get_message_info_query);
methods_.emplace("getparticipants", &Client::process_get_chat_members_query);
methods_.emplace("getchatmembers", &Client::process_get_chat_members_query);
methods_.emplace("togglegroupinvites", &Client::process_toggle_group_invites_query);
methods_.emplace("ping", &Client::process_ping_query);
methods_.emplace("getmemorystats", &Client::process_get_memory_stats_query);
methods_.emplace("getproxies", &Client::process_get_proxies_query);
methods_.emplace("addproxy", &Client::process_add_proxy_query);
methods_.emplace("deleteproxy", &Client::process_delete_proxy_query);
methods_.emplace("enableproxy", &Client::process_enable_proxy_query);
methods_.emplace("disableproxy", &Client::process_disable_proxy_query);
//custom user methods
methods_.emplace("getchats", &Client::process_get_chats_query);
methods_.emplace("getcommonchats", &Client::process_get_common_chats_query);
methods_.emplace("getinactivechats", &Client::process_get_inactive_chats_query);
methods_.emplace("getnearbychats", &Client::process_get_nearby_chats_query);
methods_.emplace("searchpublicchats", &Client::process_search_public_chats_query);
methods_.emplace("votepoll", &Client::process_set_poll_answer_query);
methods_.emplace("joinchat", &Client::process_join_chat_query);
methods_.emplace("addchatmembers", &Client::process_add_chat_member_query);
methods_.emplace("reportchat", &Client::process_report_chat_query);
methods_.emplace("createchat", &Client::process_create_chat_query);
methods_.emplace("searchmessages", &Client::process_search_messages_query);
methods_.emplace("searchchatmessages", &Client::process_search_chat_messages_query);
methods_.emplace("getcallbackqueryanswer", &Client::process_get_callback_query_answer_query);
methods_.emplace("deletechathistory", &Client::process_delete_chat_history_query);
methods_.emplace("getscheduledmessages", &Client::process_get_scheduled_messages_query);
methods_.emplace("editmessagescheduling", &Client::process_edit_message_scheduling_query);
return true;
}
bool Client::is_local_method(td::Slice method) {
return method == "close" || method == "logout" || method == "getme" || method == "getupdates" ||
method == "setwebhook" || method == "deletewebhook" || method == "getwebhookinfo";
}
class Client::JsonEmptyObject final : public td::Jsonable {
public:
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
}
};
class Client::JsonFile final : public td::Jsonable {
public:
JsonFile(const td_api::file *file, const Client *client, bool with_path)
: file_(file), client_(client), with_path_(with_path) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
client_->json_store_file(object, file_, with_path_);
}
private:
const td_api::file *file_;
const Client *client_;
bool with_path_;
};
class Client::JsonDatedFile final : public td::Jsonable {
public:
JsonDatedFile(const td_api::datedFile *file, const Client *client) : file_(file), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
client_->json_store_file(object, file_->file_.get());
object("file_date", file_->date_);
}
private:
const td_api::datedFile *file_;
const Client *client_;
};
class Client::JsonDatedFiles final : public td::Jsonable {
public:
JsonDatedFiles(const td::vector<object_ptr<td_api::datedFile>> &files, const Client *client)
: files_(files), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &file : files_) {
array << JsonDatedFile(file.get(), client_);
}
}
private:
const td::vector<object_ptr<td_api::datedFile>> &files_;
const Client *client_;
};
class Client::JsonUser final : public td::Jsonable {
public:
JsonUser(int64 user_id, const Client *client, bool full_bot_info = false)
: user_id_(user_id), client_(client), full_bot_info_(full_bot_info) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
auto user_info = client_->get_user_info(user_id_);
object("id", user_id_);
bool is_bot = user_info != nullptr && user_info->type == UserInfo::Type::Bot;
object("is_bot", td::JsonBool(is_bot));
bool is_deleted = user_info != nullptr && user_info->type == UserInfo::Type::Deleted;
object("is_deleted", td::JsonBool(is_deleted));
object("first_name", user_info == nullptr ? "" : user_info->first_name);
if (user_info != nullptr && !user_info->last_name.empty()) {
object("last_name", user_info->last_name);
}
if (user_info != nullptr && !user_info->active_usernames.empty()) {
object("username", user_info->active_usernames[0]);
}
if (user_info != nullptr && !user_info->language_code.empty()) {
object("language_code", user_info->language_code);
}
if (user_info != nullptr && user_info->is_premium) {
object("is_premium", td::JsonTrue());
}
if (user_info != nullptr && user_info->added_to_attachment_menu) {
object("added_to_attachment_menu", td::JsonTrue());
}
// start custom properties impl
if (user_info != nullptr && user_info->is_verified) {
object("is_verified", td::JsonBool(user_info->is_verified));
}
if (user_info != nullptr && user_info->is_scam) {
object("is_scam", td::JsonBool(user_info->is_scam));
}
if (user_info != nullptr) {
json_store_user_status(object, user_info->status.get());
}
//end custom properties impl
if (is_bot && full_bot_info_) {
object("can_join_groups", td::JsonBool(user_info->can_join_groups));
object("can_read_all_group_messages", td::JsonBool(user_info->can_read_all_group_messages));
object("supports_inline_queries", td::JsonBool(user_info->is_inline_bot));
object("can_connect_to_business", td::JsonBool(user_info->can_connect_to_business));
object("has_main_web_app", td::JsonBool(user_info->has_main_web_app));
}
}
private:
int64 user_id_;
const Client *client_;
bool full_bot_info_;
};
class Client::JsonUsers final : public td::Jsonable {
public:
JsonUsers(const td::vector<int64> &user_ids, const Client *client) : user_ids_(user_ids), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &user_id : user_ids_) {
array << JsonUser(user_id, client_);
}
}
private:
const td::vector<int64> &user_ids_;
const Client *client_;
};
class Client::JsonEntity final : public td::Jsonable {
public:
JsonEntity(const td_api::textEntity *entity, const Client *client) : entity_(entity), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("offset", entity_->offset_);
object("length", entity_->length_);
switch (entity_->type_->get_id()) {
case td_api::textEntityTypeMention::ID:
object("type", "mention");
break;
case td_api::textEntityTypeHashtag::ID:
object("type", "hashtag");
break;
case td_api::textEntityTypeCashtag::ID:
object("type", "cashtag");
break;
case td_api::textEntityTypeBotCommand::ID:
object("type", "bot_command");
break;
case td_api::textEntityTypeUrl::ID:
object("type", "url");
break;
case td_api::textEntityTypeEmailAddress::ID:
object("type", "email");
break;
case td_api::textEntityTypePhoneNumber::ID:
object("type", "phone_number");
break;
case td_api::textEntityTypeBankCardNumber::ID:
object("type", "bank_card_number");
break;
case td_api::textEntityTypeBold::ID:
object("type", "bold");
break;
case td_api::textEntityTypeItalic::ID:
object("type", "italic");
break;
case td_api::textEntityTypeUnderline::ID:
object("type", "underline");
break;
case td_api::textEntityTypeStrikethrough::ID:
object("type", "strikethrough");
break;
case td_api::textEntityTypeSpoiler::ID:
object("type", "spoiler");
break;
case td_api::textEntityTypeCode::ID:
object("type", "code");
break;
case td_api::textEntityTypePre::ID:
object("type", "pre");
break;
case td_api::textEntityTypePreCode::ID: {
auto entity = static_cast<const td_api::textEntityTypePreCode *>(entity_->type_.get());
object("type", "pre");
object("language", entity->language_);
break;
}
case td_api::textEntityTypeTextUrl::ID: {
auto entity = static_cast<const td_api::textEntityTypeTextUrl *>(entity_->type_.get());
object("type", "text_link");
object("url", entity->url_);
break;
}
case td_api::textEntityTypeMentionName::ID: {
auto entity = static_cast<const td_api::textEntityTypeMentionName *>(entity_->type_.get());
object("type", "text_mention");
object("user", JsonUser(entity->user_id_, client_));
break;
}
case td_api::textEntityTypeCustomEmoji::ID: {
auto entity = static_cast<const td_api::textEntityTypeCustomEmoji *>(entity_->type_.get());
object("type", "custom_emoji");
object("custom_emoji_id", td::to_string(entity->custom_emoji_id_));
break;
}
case td_api::textEntityTypeBlockQuote::ID:
object("type", "blockquote");
break;
case td_api::textEntityTypeExpandableBlockQuote::ID:
object("type", "expandable_blockquote");
break;
default:
UNREACHABLE();
}
}
private:
const td_api::textEntity *entity_;
const Client *client_;
};
class Client::JsonVectorEntities final : public td::Jsonable {
public:
JsonVectorEntities(const td::vector<object_ptr<td_api::textEntity>> &entities, const Client *client)
: entities_(entities), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &entity : entities_) {
auto entity_type = entity->type_->get_id();
if (entity_type != td_api::textEntityTypeBankCardNumber::ID &&
entity_type != td_api::textEntityTypeMediaTimestamp::ID) {
array << JsonEntity(entity.get(), client_);
}
}
}
private:
const td::vector<object_ptr<td_api::textEntity>> &entities_;
const Client *client_;
};
class Client::JsonMaskPosition final : public td::Jsonable {
public:
explicit JsonMaskPosition(const td_api::maskPosition *mask_position) : mask_position_(mask_position) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("point", Client::MASK_POINTS[Client::mask_point_to_index(mask_position_->point_)]);
object("x_shift", mask_position_->x_shift_);
object("y_shift", mask_position_->y_shift_);
object("scale", mask_position_->scale_);
}
private:
const td_api::maskPosition *mask_position_;
};
class Client::JsonSticker final : public td::Jsonable {
public:
JsonSticker(const td_api::sticker *sticker, const Client *client) : sticker_(sticker), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("width", sticker_->width_);
object("height", sticker_->height_);
if (!sticker_->emoji_.empty()) {
object("emoji", sticker_->emoji_);
}
auto set_name = client_->get_sticker_set_name(sticker_->set_id_);
if (!set_name.empty()) {
object("set_name", set_name);
}
auto format = sticker_->format_->get_id();
object("is_animated", td::JsonBool(format == td_api::stickerFormatTgs::ID));
object("is_video", td::JsonBool(format == td_api::stickerFormatWebm::ID));
switch (sticker_->full_type_->get_id()) {
case td_api::stickerFullTypeRegular::ID: {
auto full_type = static_cast<const td_api::stickerFullTypeRegular *>(sticker_->full_type_.get());
object("type", Client::get_sticker_type(make_object<td_api::stickerTypeRegular>()));
if (full_type->premium_animation_ != nullptr) {
object("premium_animation", JsonFile(full_type->premium_animation_.get(), client_, false));
}
break;
}
case td_api::stickerFullTypeMask::ID: {
auto full_type = static_cast<const td_api::stickerFullTypeMask *>(sticker_->full_type_.get());
object("type", Client::get_sticker_type(make_object<td_api::stickerTypeMask>()));
if (full_type->mask_position_ != nullptr) {
object("mask_position", JsonMaskPosition(full_type->mask_position_.get()));
}
break;
}
case td_api::stickerFullTypeCustomEmoji::ID: {
auto full_type = static_cast<const td_api::stickerFullTypeCustomEmoji *>(sticker_->full_type_.get());
object("type", Client::get_sticker_type(make_object<td_api::stickerTypeCustomEmoji>()));
if (full_type->custom_emoji_id_ != 0) {
object("custom_emoji_id", td::to_string(full_type->custom_emoji_id_));
}
if (full_type->needs_repainting_) {
object("needs_repainting", td::JsonBool(full_type->needs_repainting_));
}
break;
}
default:
UNREACHABLE();
break;
}
client_->json_store_thumbnail(object, sticker_->thumbnail_.get());
client_->json_store_file(object, sticker_->sticker_.get());
}
private:
const td_api::sticker *sticker_;
const Client *client_;
};
class Client::JsonStickers final : public td::Jsonable {
public:
JsonStickers(const td::vector<object_ptr<td_api::sticker>> &stickers, const Client *client)
: stickers_(stickers), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &sticker : stickers_) {
array << JsonSticker(sticker.get(), client_);
}
}
private:
const td::vector<object_ptr<td_api::sticker>> &stickers_;
const Client *client_;
};
class Client::JsonLocation final : public td::Jsonable {
public:
explicit JsonLocation(const td_api::location *location, double expires_in = 0.0, int32 live_period = 0,
int32 heading = 0, int32 proximity_alert_radius = 0)
: location_(location)
, expires_in_(expires_in)
, live_period_(live_period)
, heading_(heading)
, proximity_alert_radius_(proximity_alert_radius) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("latitude", location_->latitude_);
object("longitude", location_->longitude_);
if (expires_in_ > 0.0) {
object("live_period", live_period_);
if (heading_ > 0) {
object("heading", heading_);
}
if (proximity_alert_radius_ > 0) {
object("proximity_alert_radius", proximity_alert_radius_);
}
}
if (location_->horizontal_accuracy_ > 0) {
object("horizontal_accuracy", location_->horizontal_accuracy_);
}
}
private:
const td_api::location *location_;
double expires_in_;
int32 live_period_;
int32 heading_;
int32 proximity_alert_radius_;
};
class Client::JsonReactionType final : public td::Jsonable {
public:
explicit JsonReactionType(const td_api::ReactionType *reaction_type) : reaction_type_(reaction_type) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
CHECK(reaction_type_ != nullptr);
switch (reaction_type_->get_id()) {
case td_api::reactionTypeEmoji::ID:
object("type", "emoji");
object("emoji", static_cast<const td_api::reactionTypeEmoji *>(reaction_type_)->emoji_);
break;
case td_api::reactionTypeCustomEmoji::ID:
object("type", "custom_emoji");
object("custom_emoji_id",
td::to_string(static_cast<const td_api::reactionTypeCustomEmoji *>(reaction_type_)->custom_emoji_id_));
break;
case td_api::reactionTypePaid::ID:
object("type", "paid");
break;
default:
UNREACHABLE();
}
}
private:
const td_api::ReactionType *reaction_type_;
};
class Client::JsonReactionCount final : public td::Jsonable {
public:
explicit JsonReactionCount(const td_api::messageReaction *message_reaction) : message_reaction_(message_reaction) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("type", JsonReactionType(message_reaction_->type_.get()));
object("total_count", message_reaction_->total_count_);
}
private:
const td_api::messageReaction *message_reaction_;
};
class Client::JsonBirthdate final : public td::Jsonable {
public:
explicit JsonBirthdate(const td_api::birthdate *birthdate) : birthdate_(birthdate) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("day", birthdate_->day_);
object("month", birthdate_->month_);
if (birthdate_->year_ != 0) {
object("year", birthdate_->year_);
}
}
private:
const td_api::birthdate *birthdate_;
};
class Client::JsonBusinessStartPage final : public td::Jsonable {
public:
JsonBusinessStartPage(const td_api::businessStartPage *start_page, const Client *client)
: start_page_(start_page), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (!start_page_->title_.empty()) {
object("title", start_page_->title_);
}
if (!start_page_->message_.empty()) {
object("message", start_page_->message_);
}
if (start_page_->sticker_ != nullptr) {
object("sticker", JsonSticker(start_page_->sticker_.get(), client_));
}
}
private:
const td_api::businessStartPage *start_page_;
const Client *client_;
};
class Client::JsonBusinessLocation final : public td::Jsonable {
public:
explicit JsonBusinessLocation(const td_api::businessLocation *business_location)
: business_location_(business_location) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (business_location_->location_ != nullptr) {
object("location", JsonLocation(business_location_->location_.get()));
}
object("address", business_location_->address_);
}
private:
const td_api::businessLocation *business_location_;
};
class Client::JsonBusinessOpeningHoursInterval final : public td::Jsonable {
public:
explicit JsonBusinessOpeningHoursInterval(const td_api::businessOpeningHoursInterval *opening_hours_interval)
: opening_hours_interval_(opening_hours_interval) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("opening_minute", opening_hours_interval_->start_minute_);
object("closing_minute", opening_hours_interval_->end_minute_);
}
private:
const td_api::businessOpeningHoursInterval *opening_hours_interval_;
};
class Client::JsonBusinessOpeningHours final : public td::Jsonable {
public:
explicit JsonBusinessOpeningHours(const td_api::businessOpeningHours *opening_hours) : opening_hours_(opening_hours) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("opening_hours", td::json_array(opening_hours_->opening_hours_, [](const auto &opening_hours_interval) {
return JsonBusinessOpeningHoursInterval(opening_hours_interval.get());
}));
object("time_zone_name", opening_hours_->time_zone_id_);
}
private:
const td_api::businessOpeningHours *opening_hours_;
};
class Client::JsonChatPermissions final : public td::Jsonable {
public:
explicit JsonChatPermissions(const td_api::chatPermissions *chat_permissions) : chat_permissions_(chat_permissions) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
Client::json_store_permissions(object, chat_permissions_);
}
private:
const td_api::chatPermissions *chat_permissions_;
};
class Client::JsonChatPhotoInfo final : public td::Jsonable {
public:
explicit JsonChatPhotoInfo(const td_api::chatPhotoInfo *chat_photo) : chat_photo_(chat_photo) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("small_file_id", chat_photo_->small_->remote_->id_);
object("small_file_unique_id", chat_photo_->small_->remote_->unique_id_);
object("big_file_id", chat_photo_->big_->remote_->id_);
object("big_file_unique_id", chat_photo_->big_->remote_->unique_id_);
}
private:
const td_api::chatPhotoInfo *chat_photo_;
};
class Client::JsonChatLocation final : public td::Jsonable {
public:
explicit JsonChatLocation(const td_api::chatLocation *chat_location) : chat_location_(chat_location) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("location", JsonLocation(chat_location_->location_.get()));
object("address", chat_location_->address_);
}
private:
const td_api::chatLocation *chat_location_;
};
class Client::JsonChatInviteLink final : public td::Jsonable {
public:
JsonChatInviteLink(const td_api::chatInviteLink *chat_invite_link, const Client *client)
: chat_invite_link_(chat_invite_link), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("invite_link", chat_invite_link_->invite_link_);
if (!chat_invite_link_->name_.empty()) {
object("name", chat_invite_link_->name_);
}
object("creator", JsonUser(chat_invite_link_->creator_user_id_, client_));
if (chat_invite_link_->expiration_date_ != 0) {
object("expire_date", chat_invite_link_->expiration_date_);
}
if (chat_invite_link_->member_limit_ != 0) {
object("member_limit", chat_invite_link_->member_limit_);
}
if (chat_invite_link_->pending_join_request_count_ != 0) {
object("pending_join_request_count", chat_invite_link_->pending_join_request_count_);
}
if (chat_invite_link_->subscription_pricing_ != nullptr) {
object("subscription_period", chat_invite_link_->subscription_pricing_->period_);
object("subscription_price", chat_invite_link_->subscription_pricing_->star_count_);
}
object("creates_join_request", td::JsonBool(chat_invite_link_->creates_join_request_));
object("is_primary", td::JsonBool(chat_invite_link_->is_primary_));
object("is_revoked", td::JsonBool(chat_invite_link_->is_revoked_));
}
private:
const td_api::chatInviteLink *chat_invite_link_;
const Client *client_;
};
class Client::JsonMessage final : public td::Jsonable {
public:
JsonMessage(const MessageInfo *message, bool need_reply, const td::string &source, const Client *client)
: message_(message), need_reply_(need_reply), source_(source), client_(client) {
}
void store(td::JsonValueScope *scope) const;
private:
const MessageInfo *message_;
bool need_reply_;
const td::string &source_;
const Client *client_;
void add_caption(td::JsonObjectScope &object, const object_ptr<td_api::formattedText> &caption,
bool show_caption_above_media) const {
CHECK(caption != nullptr);
if (!caption->text_.empty()) {
object("caption", caption->text_);
if (!caption->entities_.empty()) {
object("caption_entities", JsonVectorEntities(caption->entities_, client_));
}
if (show_caption_above_media) {
object("show_caption_above_media", td::JsonTrue());
}
}
}
void add_media_spoiler(td::JsonObjectScope &object, bool has_spoiler) const {
if (has_spoiler) {
object("has_media_spoiler", td::JsonTrue());
}
}
};
class Client::JsonChat final : public td::Jsonable {
public:
JsonChat(int64 chat_id, const Client *client, bool is_full = false, int64 pinned_message_id = -1, int32 distance = -1)
: chat_id_(chat_id)
, client_(client)
, is_full_(is_full)
, pinned_message_id_(pinned_message_id)
, distance_(distance) {
}
void store(td::JsonValueScope *scope) const {
auto chat_info = client_->get_chat(chat_id_);
CHECK(chat_info != nullptr);
auto object = scope->enter_object();
object("id", chat_id_);
const td_api::chatPhoto *photo = nullptr;
switch (chat_info->type) {
case ChatInfo::Type::Private: {
auto user_info = client_->get_user_info(chat_info->user_id);
CHECK(user_info != nullptr);
object("first_name", user_info->first_name);
if (!user_info->last_name.empty()) {
object("last_name", user_info->last_name);
}
if (!user_info->active_usernames.empty()) {
object("username", user_info->active_usernames[0]);
}
object("type", "private");
// start custom properties impl
if (user_info->is_verified) {
object("is_verified", td::JsonBool(user_info->is_verified));
}
if (user_info->is_scam) {
object("is_scam", td::JsonBool(user_info->is_scam));
}
json_store_user_status(object, user_info->status.get());
// end custom properties impl
if (is_full_) {
if (!user_info->active_usernames.empty()) {
object("active_usernames", td::json_array(user_info->active_usernames,
[](td::Slice username) { return td::JsonString(username); }));
}
if (!user_info->bio.empty()) {
object("bio", user_info->bio);
}
if (user_info->has_private_forwards) {
object("has_private_forwards", td::JsonTrue());
}
if (user_info->has_restricted_voice_and_video_messages) {
object("has_restricted_voice_and_video_messages", td::JsonTrue());
}
if (user_info->business_info != nullptr) {
auto business_info = user_info->business_info.get();
if (business_info->start_page_ != nullptr) {
object("business_intro", JsonBusinessStartPage(business_info->start_page_.get(), client_));
}
if (business_info->location_ != nullptr) {
object("business_location", JsonBusinessLocation(business_info->location_.get()));
}
if (business_info->opening_hours_ != nullptr) {
object("business_opening_hours", JsonBusinessOpeningHours(business_info->opening_hours_.get()));
}
}
if (user_info->birthdate != nullptr) {
object("birthdate", JsonBirthdate(user_info->birthdate.get()));
}
if (user_info->personal_chat_id != 0) {
object("personal_chat", JsonChat(user_info->personal_chat_id, client_));
}
}
photo = user_info->photo.get();
break;
}
case ChatInfo::Type::Group: {
object("title", chat_info->title);
object("type", "group");
const auto *permissions = chat_info->permissions.get();
auto group_info = client_->get_group_info(chat_info->group_id);
CHECK(group_info != nullptr);
if (is_full_) {
if (!group_info->description.empty()) {
object("description", group_info->description);
}
if (!group_info->invite_link.empty()) {
object("invite_link", group_info->invite_link);
}
object("permissions", JsonChatPermissions(permissions));
}
auto everyone_is_administrator =
permissions->can_send_basic_messages_ && permissions->can_send_audios_ &&
permissions->can_send_documents_ && permissions->can_send_photos_ && permissions->can_send_videos_ &&
permissions->can_send_video_notes_ && permissions->can_send_voice_notes_ && permissions->can_send_polls_ &&
permissions->can_send_other_messages_ && permissions->can_add_link_previews_ &&
permissions->can_change_info_ && permissions->can_invite_users_ && permissions->can_pin_messages_;
object("all_members_are_administrators", td::JsonBool(everyone_is_administrator));
photo = group_info->photo.get();
break;
}
case ChatInfo::Type::Supergroup: {
object("title", chat_info->title);
auto supergroup_info = client_->get_supergroup_info(chat_info->supergroup_id);
CHECK(supergroup_info != nullptr);
if (!supergroup_info->active_usernames.empty()) {
object("username", supergroup_info->active_usernames[0]);
}
if (supergroup_info->is_supergroup && supergroup_info->is_forum) {
object("is_forum", td::JsonTrue());
}
if (supergroup_info->is_supergroup) {
object("type", "supergroup");
} else {
object("type", "channel");
}
// start custom properties impl
if (supergroup_info->is_verified) {
object("is_verified", td::JsonBool(supergroup_info->is_verified));
}
if (supergroup_info->is_scam) {
object("is_scam", td::JsonBool(supergroup_info->is_scam));
}
// end custom properties impl
if (is_full_) {
if (!supergroup_info->active_usernames.empty()) {
object("active_usernames", td::json_array(supergroup_info->active_usernames,
[](td::Slice username) { return td::JsonString(username); }));
}
if (!supergroup_info->description.empty()) {
object("description", supergroup_info->description);
}
if (!supergroup_info->invite_link.empty()) {
object("invite_link", supergroup_info->invite_link);
}
if (supergroup_info->sticker_set_id != 0) {
auto sticker_set_name = client_->get_sticker_set_name(supergroup_info->sticker_set_id);
if (!sticker_set_name.empty()) {
object("sticker_set_name", sticker_set_name);
} else {
LOG(ERROR) << "Not found chat sticker set " << supergroup_info->sticker_set_id;
}
}
if (supergroup_info->custom_emoji_sticker_set_id != 0) {
auto sticker_set_name = client_->get_sticker_set_name(supergroup_info->custom_emoji_sticker_set_id);
if (!sticker_set_name.empty()) {
object("custom_emoji_sticker_set_name", sticker_set_name);
} else {
LOG(ERROR) << "Not found chat custom emoji sticker set " << supergroup_info->custom_emoji_sticker_set_id;
}
}
if (supergroup_info->can_set_sticker_set) {
object("can_set_sticker_set", td::JsonTrue());
}
if (supergroup_info->is_all_history_available) {
object("has_visible_history", td::JsonTrue());
}
if (supergroup_info->is_supergroup) {
object("permissions", JsonChatPermissions(chat_info->permissions.get()));
}
if (supergroup_info->is_supergroup && supergroup_info->join_to_send_messages) {
object("join_to_send_messages", td::JsonTrue());
}
if (supergroup_info->is_supergroup && supergroup_info->join_by_request) {
object("join_by_request", td::JsonTrue());
}
if (supergroup_info->is_supergroup && supergroup_info->has_hidden_members) {
object("has_hidden_members", td::JsonTrue());
}
if (supergroup_info->has_aggressive_anti_spam_enabled) {
object("has_aggressive_anti_spam_enabled", td::JsonTrue());
}
if (supergroup_info->slow_mode_delay != 0) {
object("slow_mode_delay", supergroup_info->slow_mode_delay);
}
if (supergroup_info->unrestrict_boost_count != 0) {
object("unrestrict_boost_count", supergroup_info->unrestrict_boost_count);
}
if (supergroup_info->linked_chat_id != 0) {
object("linked_chat_id", supergroup_info->linked_chat_id);
}
if (supergroup_info->location != nullptr) {
object("location", JsonChatLocation(supergroup_info->location.get()));
}
if (supergroup_info->has_paid_media_allowed && !supergroup_info->is_supergroup) {
object("can_send_paid_media", td::JsonTrue());
}
}
photo = supergroup_info->photo.get();
break;
}
case ChatInfo::Type::Unknown:
default:
UNREACHABLE();
}
if (is_full_) {
if (photo != nullptr) {
const td_api::file *small_file = nullptr;
const td_api::file *big_file = nullptr;
for (auto &size : photo->sizes_) {
if (size->type_ == "a") {
small_file = size->photo_.get();
} else if (size->type_ == "c") {
big_file = size->photo_.get();
}
}
if (small_file == nullptr || big_file == nullptr) {
LOG(ERROR) << "Failed to convert chatPhoto to chatPhotoInfo for " << chat_id_ << ": " << to_string(*photo);
} else {
if (chat_info->photo_info == nullptr) {
LOG(ERROR) << "Have chatPhoto without chatPhotoInfo for " << chat_id_;
} else {
if (small_file->remote_->unique_id_ != chat_info->photo_info->small_->remote_->unique_id_ ||
big_file->remote_->unique_id_ != chat_info->photo_info->big_->remote_->unique_id_) {
LOG(ERROR) << "Have different chatPhoto and chatPhotoInfo for " << chat_id_ << ": " << to_string(*photo)
<< ' ' << to_string(chat_info->photo_info);
}
}
}
} else if (chat_info->photo_info != nullptr) {
LOG(ERROR) << "Have chatPhotoInfo without chatPhoto for " << chat_id_;
}
if (chat_info->photo_info != nullptr) {
object("photo", JsonChatPhotoInfo(chat_info->photo_info.get()));
}
if (pinned_message_id_ != 0) {
CHECK(pinned_message_id_ != -1);
const MessageInfo *pinned_message = client_->get_message(chat_id_, pinned_message_id_, true);
if (pinned_message != nullptr) {
object("pinned_message", JsonMessage(pinned_message, false, "pin in JsonChat", client_));
} else {
LOG(INFO) << "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);
}
if (chat_info->emoji_status_custom_emoji_id != 0) {
object("emoji_status_custom_emoji_id", td::to_string(chat_info->emoji_status_custom_emoji_id));
if (chat_info->emoji_status_expiration_date != 0) {
object("emoji_status_expiration_date", chat_info->emoji_status_expiration_date);
}
}
if (chat_info->available_reactions != nullptr) {
object("available_reactions",
td::json_array(chat_info->available_reactions->reactions_,
[](const auto &reaction) { return JsonReactionType(reaction.get()); }));
}
object("max_reaction_count", chat_info->max_reaction_count);
CHECK(chat_info->accent_color_id != -1);
object("accent_color_id", chat_info->accent_color_id);
if (chat_info->background_custom_emoji_id != 0) {
object("background_custom_emoji_id", td::to_string(chat_info->background_custom_emoji_id));
}
if (chat_info->profile_accent_color_id != -1) {
object("profile_accent_color_id", chat_info->profile_accent_color_id);
}
if (chat_info->profile_background_custom_emoji_id != 0) {
object("profile_background_custom_emoji_id", td::to_string(chat_info->profile_background_custom_emoji_id));
}
if (chat_info->has_protected_content) {
object("has_protected_content", td::JsonTrue());
}
}
// start custom properties impl
if (distance_ >= 0) {
object("distance", td::JsonInt(distance_));
}
// end custom properties impl
}
private:
int64 chat_id_;
const Client *client_;
bool is_full_;
int64 pinned_message_id_;
int32 distance_;
};
class Client::JsonInaccessibleMessage final : public td::Jsonable {
public:
JsonInaccessibleMessage(int64 chat_id, int64 message_id, const Client *client)
: chat_id_(chat_id), message_id_(message_id), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("message_id", as_client_message_id(message_id_));
object("chat", JsonChat(chat_id_, client_));
object("date", 0);
}
private:
int64 chat_id_;
int64 message_id_;
const Client *client_;
};
class Client::JsonMessageSender final : public td::Jsonable {
public:
JsonMessageSender(const td_api::MessageSender *sender_id, const Client *client)
: sender_id_(sender_id), client_(client) {
}
void store(td::JsonValueScope *scope) const {
CHECK(sender_id_ != nullptr);
switch (sender_id_->get_id()) {
case td_api::messageSenderUser::ID: {
auto sender_user_id = static_cast<const td_api::messageSenderUser *>(sender_id_)->user_id_;
JsonUser(sender_user_id, client_).store(scope);
break;
}
case td_api::messageSenderChat::ID: {
auto sender_chat_id = static_cast<const td_api::messageSenderChat *>(sender_id_)->chat_id_;
JsonChat(sender_chat_id, client_).store(scope);
break;
}
default:
UNREACHABLE();
}
}
private:
const td_api::MessageSender *sender_id_;
const Client *client_;
};
class Client::JsonMessageOrigin final : public td::Jsonable {
public:
JsonMessageOrigin(const td_api::MessageOrigin *message_origin, int32 initial_send_date, const Client *client)
: message_origin_(message_origin), initial_send_date_(initial_send_date), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
switch (message_origin_->get_id()) {
case td_api::messageOriginUser::ID: {
auto origin = static_cast<const td_api::messageOriginUser *>(message_origin_);
object("type", "user");
object("sender_user", JsonUser(origin->sender_user_id_, client_));
break;
}
case td_api::messageOriginChat::ID: {
auto origin = static_cast<const td_api::messageOriginChat *>(message_origin_);
object("type", "chat");
object("sender_chat", JsonChat(origin->sender_chat_id_, client_));
if (!origin->author_signature_.empty()) {
object("author_signature", origin->author_signature_);
}
break;
}
case td_api::messageOriginHiddenUser::ID: {
auto origin = static_cast<const td_api::messageOriginHiddenUser *>(message_origin_);
object("type", "hidden_user");
if (!origin->sender_name_.empty()) {
object("sender_user_name", origin->sender_name_);
}
break;
}
case td_api::messageOriginChannel::ID: {
auto origin = static_cast<const td_api::messageOriginChannel *>(message_origin_);
object("type", "channel");
object("chat", JsonChat(origin->chat_id_, client_));
object("message_id", as_client_message_id(origin->message_id_));
if (!origin->author_signature_.empty()) {
object("author_signature", origin->author_signature_);
}
break;
}
default:
UNREACHABLE();
}
object("date", initial_send_date_);
}
private:
const td_api::MessageOrigin *message_origin_;
int32 initial_send_date_;
const Client *client_;
};
class Client::JsonMessages final : public td::Jsonable {
public:
explicit JsonMessages(const td::vector<td::string> &messages) : messages_(messages) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &message : messages_) {
array << td::JsonRaw(message);
}
}
private:
const td::vector<td::string> &messages_;
};
class Client::JsonLinkPreviewOptions final : public td::Jsonable {
public:
explicit JsonLinkPreviewOptions(const td_api::linkPreviewOptions *link_preview_options)
: link_preview_options_(link_preview_options) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (link_preview_options_->is_disabled_) {
object("is_disabled", td::JsonTrue());
}
if (!link_preview_options_->url_.empty()) {
object("url", link_preview_options_->url_);
}
if (link_preview_options_->force_small_media_) {
object("prefer_small_media", td::JsonTrue());
}
if (link_preview_options_->force_large_media_) {
object("prefer_large_media", td::JsonTrue());
}
if (link_preview_options_->show_above_text_) {
object("show_above_text", td::JsonTrue());
}
}
private:
const td_api::linkPreviewOptions *link_preview_options_;
};
class Client::JsonAnimation final : public td::Jsonable {
public:
JsonAnimation(const td_api::animation *animation, bool as_document, const Client *client)
: animation_(animation), as_document_(as_document), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (!animation_->file_name_.empty()) {
object("file_name", animation_->file_name_);
}
if (!animation_->mime_type_.empty()) {
object("mime_type", animation_->mime_type_);
}
if (!as_document_) {
object("duration", animation_->duration_);
object("width", animation_->width_);
object("height", animation_->height_);
}
client_->json_store_thumbnail(object, animation_->thumbnail_.get());
client_->json_store_file(object, animation_->animation_.get());
}
private:
const td_api::animation *animation_;
bool as_document_;
const Client *client_;
};
class Client::JsonAudio final : public td::Jsonable {
public:
JsonAudio(const td_api::audio *audio, const Client *client) : audio_(audio), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("duration", audio_->duration_);
if (!audio_->file_name_.empty()) {
object("file_name", audio_->file_name_);
}
if (!audio_->mime_type_.empty()) {
object("mime_type", audio_->mime_type_);
}
if (!audio_->title_.empty()) {
object("title", audio_->title_);
}
if (!audio_->performer_.empty()) {
object("performer", audio_->performer_);
}
client_->json_store_thumbnail(object, audio_->album_cover_thumbnail_.get());
client_->json_store_file(object, audio_->audio_.get());
}
private:
const td_api::audio *audio_;
const Client *client_;
};
class Client::JsonDocument final : public td::Jsonable {
public:
JsonDocument(const td_api::document *document, const Client *client) : document_(document), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (!document_->file_name_.empty()) {
object("file_name", document_->file_name_);
}
if (!document_->mime_type_.empty()) {
object("mime_type", document_->mime_type_);
}
client_->json_store_thumbnail(object, document_->thumbnail_.get());
client_->json_store_file(object, document_->document_.get());
}
private:
const td_api::document *document_;
const Client *client_;
};
class Client::JsonPhotoSize final : public td::Jsonable {
public:
JsonPhotoSize(const td_api::photoSize *photo_size, const Client *client) : photo_size_(photo_size), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
client_->json_store_file(object, photo_size_->photo_.get());
object("width", photo_size_->width_);
object("height", photo_size_->height_);
}
private:
const td_api::photoSize *photo_size_;
const Client *client_;
};
class Client::JsonThumbnail final : public td::Jsonable {
public:
JsonThumbnail(const td_api::thumbnail *thumbnail, const Client *client) : thumbnail_(thumbnail), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
client_->json_store_file(object, thumbnail_->file_.get());
object("width", thumbnail_->width_);
object("height", thumbnail_->height_);
}
private:
const td_api::thumbnail *thumbnail_;
const Client *client_;
};
class Client::JsonPhoto final : public td::Jsonable {
public:
JsonPhoto(const td_api::photo *photo, const Client *client) : photo_(photo), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &photo_size : photo_->sizes_) {
if (photo_size->type_ != "i" && photo_size->type_ != "t" && !photo_size->photo_->remote_->id_.empty()) {
array << JsonPhotoSize(photo_size.get(), client_);
}
}
}
private:
const td_api::photo *photo_;
const Client *client_;
};
class Client::JsonChatPhoto final : public td::Jsonable {
public:
JsonChatPhoto(const td_api::chatPhoto *photo, const Client *client) : photo_(photo), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto array = scope->enter_array();
for (auto &photo_size : photo_->sizes_) {
if (photo_size->type_ != "i" && photo_size->type_ != "t" && !photo_size->photo_->remote_->id_.empty()) {
array << JsonPhotoSize(photo_size.get(), client_);
}
}
}
private:
const td_api::chatPhoto *photo_;
const Client *client_;
};
class Client::JsonVideo final : public td::Jsonable {
public:
JsonVideo(const td_api::video *video, const Client *client) : video_(video), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("duration", video_->duration_);
object("width", video_->width_);
object("height", video_->height_);
if (!video_->file_name_.empty()) {
object("file_name", video_->file_name_);
}
if (!video_->mime_type_.empty()) {
object("mime_type", video_->mime_type_);
}
client_->json_store_thumbnail(object, video_->thumbnail_.get());
client_->json_store_file(object, video_->video_.get());
}
private:
const td_api::video *video_;
const Client *client_;
};
class Client::JsonVideoNote final : public td::Jsonable {
public:
JsonVideoNote(const td_api::videoNote *video_note, const Client *client) : video_note_(video_note), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("duration", video_note_->duration_);
object("length", video_note_->length_);
client_->json_store_thumbnail(object, video_note_->thumbnail_.get());
client_->json_store_file(object, video_note_->video_.get());
}
private:
const td_api::videoNote *video_note_;
const Client *client_;
};
class Client::JsonVoiceNote final : public td::Jsonable {
public:
JsonVoiceNote(const td_api::voiceNote *voice_note, const Client *client) : voice_note_(voice_note), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("duration", voice_note_->duration_);
if (!voice_note_->mime_type_.empty()) {
object("mime_type", voice_note_->mime_type_);
}
client_->json_store_file(object, voice_note_->voice_.get());
}
private:
const td_api::voiceNote *voice_note_;
const Client *client_;
};
class Client::JsonPaidMedia final : public td::Jsonable {
public:
JsonPaidMedia(const td_api::PaidMedia *paid_media, const Client *client) : paid_media_(paid_media), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
switch (paid_media_->get_id()) {
case td_api::paidMediaPreview::ID: {
auto media = static_cast<const td_api::paidMediaPreview *>(paid_media_);
object("type", "preview");
if (media->width_) {
object("width", media->width_);
}
if (media->height_) {
object("height", media->height_);
}
if (media->duration_) {
object("duration", media->duration_);
}
break;
}
case td_api::paidMediaPhoto::ID: {
auto media = static_cast<const td_api::paidMediaPhoto *>(paid_media_);
object("type", "photo");
object("photo", JsonPhoto(media->photo_.get(), client_));
break;
}
case td_api::paidMediaVideo::ID: {
auto media = static_cast<const td_api::paidMediaVideo *>(paid_media_);
object("type", "video");
object("video", JsonVideo(media->video_.get(), client_));
break;
}
case td_api::paidMediaUnsupported::ID:
object("type", "other");
break;
default:
UNREACHABLE();
}
}
private:
const td_api::PaidMedia *paid_media_;
const Client *client_;
};
class Client::JsonPaidMediaInfo final : public td::Jsonable {
public:
JsonPaidMediaInfo(const td_api::messagePaidMedia *paid_media, const Client *client)
: paid_media_(paid_media), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("star_count", paid_media_->star_count_);
object("paid_media", td::json_array(paid_media_->media_, [client = client_](auto &media) {
return JsonPaidMedia(media.get(), client);
}));
}
private:
const td_api::messagePaidMedia *paid_media_;
const Client *client_;
};
class Client::JsonVenue final : public td::Jsonable {
public:
explicit JsonVenue(const td_api::venue *venue) : venue_(venue) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("location", JsonLocation(venue_->location_.get()));
object("title", venue_->title_);
object("address", venue_->address_);
if (venue_->provider_ == "foursquare") {
if (!venue_->id_.empty()) {
object("foursquare_id", venue_->id_);
}
if (!venue_->type_.empty()) {
object("foursquare_type", venue_->type_);
}
}
if (venue_->provider_ == "gplaces") {
if (!venue_->id_.empty()) {
object("google_place_id", venue_->id_);
}
if (!venue_->type_.empty()) {
object("google_place_type", venue_->type_);
}
}
}
private:
const td_api::venue *venue_;
};
class Client::JsonContact final : public td::Jsonable {
public:
explicit JsonContact(const td_api::contact *contact) : contact_(contact) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("phone_number", contact_->phone_number_);
object("first_name", contact_->first_name_);
if (!contact_->last_name_.empty()) {
object("last_name", contact_->last_name_);
}
if (!contact_->vcard_.empty()) {
object("vcard", contact_->vcard_);
}
if (contact_->user_id_) {
object("user_id", contact_->user_id_);
}
}
private:
const td_api::contact *contact_;
};
class Client::JsonDice final : public td::Jsonable {
public:
JsonDice(const td::string &emoji, int32 value) : emoji_(emoji), value_(value) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("emoji", emoji_);
object("value", value_);
}
private:
const td::string &emoji_;
int32 value_;
};
class Client::JsonGame final : public td::Jsonable {
public:
JsonGame(const td_api::game *game, const Client *client) : game_(game), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("title", game_->title_);
if (!game_->text_->text_.empty()) {
object("text", game_->text_->text_);
}
if (!game_->text_->entities_.empty()) {
object("text_entities", JsonVectorEntities(game_->text_->entities_, client_));
}
object("description", game_->description_);
CHECK(game_->photo_ != nullptr);
object("photo", JsonPhoto(game_->photo_.get(), client_));
if (game_->animation_ != nullptr) {
object("animation", JsonAnimation(game_->animation_.get(), false, client_));
}
}
private:
const td_api::game *game_;
const Client *client_;
};
class Client::JsonInvoice final : public td::Jsonable {
public:
explicit JsonInvoice(const td_api::messageInvoice *invoice) : invoice_(invoice) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("title", invoice_->product_info_->title_);
object("description", invoice_->product_info_->description_->text_);
object("start_parameter", invoice_->start_parameter_);
object("currency", invoice_->currency_);
object("total_amount", invoice_->total_amount_);
// skip product_info_->photo
// skip is_test
// skip need_shipping_address
// skip receipt_message_id
}
private:
const td_api::messageInvoice *invoice_;
};
class Client::JsonPollOption final : public td::Jsonable {
public:
JsonPollOption(const td_api::pollOption *option, const Client *client) : option_(option), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("text", option_->text_->text_);
if (!option_->text_->entities_.empty()) {
object("text_entities", JsonVectorEntities(option_->text_->entities_, client_));
}
object("voter_count", option_->voter_count_);
// ignore is_chosen
}
private:
const td_api::pollOption *option_;
const Client *client_;
};
class Client::JsonPoll final : public td::Jsonable {
public:
JsonPoll(const td_api::poll *poll, const Client *client) : poll_(poll), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("id", td::to_string(poll_->id_));
object("question", poll_->question_->text_);
if (!poll_->question_->entities_.empty()) {
object("question_entities", JsonVectorEntities(poll_->question_->entities_, client_));
}
object("options", td::json_array(poll_->options_, [client = client_](auto &option) {
return JsonPollOption(option.get(), client);
}));
object("total_voter_count", poll_->total_voter_count_);
if (poll_->open_period_ != 0 && poll_->close_date_ != 0) {
object("open_period", poll_->open_period_);
object("close_date", poll_->close_date_);
}
object("is_closed", td::JsonBool(poll_->is_closed_));
object("is_anonymous", td::JsonBool(poll_->is_anonymous_));
switch (poll_->type_->get_id()) {
case td_api::pollTypeQuiz::ID: {
object("type", "quiz");
object("allows_multiple_answers", td::JsonFalse());
auto quiz = static_cast<const td_api::pollTypeQuiz *>(poll_->type_.get());
int32 correct_option_id = quiz->correct_option_id_;
if (correct_option_id != -1) {
object("correct_option_id", correct_option_id);
}
auto *explanation = quiz->explanation_.get();
if (!explanation->text_.empty()) {
object("explanation", explanation->text_);
object("explanation_entities", JsonVectorEntities(explanation->entities_, client_));
}
break;
}
case td_api::pollTypeRegular::ID:
object("type", "regular");
object("allows_multiple_answers",
td::JsonBool(static_cast<const td_api::pollTypeRegular *>(poll_->type_.get())->allow_multiple_answers_));
break;
default:
UNREACHABLE();
}
}
private:
const td_api::poll *poll_;
const Client *client_;
};
class Client::JsonPollAnswer final : public td::Jsonable {
public:
JsonPollAnswer(const td_api::updatePollAnswer *poll_answer, const Client *client)
: poll_answer_(poll_answer), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("poll_id", td::to_string(poll_answer_->poll_id_));
switch (poll_answer_->voter_id_->get_id()) {
case td_api::messageSenderUser::ID: {
auto user_id = static_cast<const td_api::messageSenderUser *>(poll_answer_->voter_id_.get())->user_id_;
object("user", JsonUser(user_id, client_));
break;
}
case td_api::messageSenderChat::ID: {
auto voter_chat_id = static_cast<const td_api::messageSenderChat *>(poll_answer_->voter_id_.get())->chat_id_;
object("user", JsonUser(client_->channel_bot_user_id_, client_));
object("voter_chat", JsonChat(voter_chat_id, client_));
break;
}
default:
UNREACHABLE();
}
object("option_ids", td::json_array(poll_answer_->option_ids_, [](int32 option_id) { return option_id; }));
}
private:
const td_api::updatePollAnswer *poll_answer_;
const Client *client_;
};
class Client::JsonStory final : public td::Jsonable {
public:
JsonStory(int64 chat_id, int32 story_id, const Client *client)
: chat_id_(chat_id), story_id_(story_id), client_(client) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("chat", JsonChat(chat_id_, client_));
object("id", story_id_);
}
private:
int64 chat_id_;
int32 story_id_;
const Client *client_;
};
class Client::JsonBackgroundFill final : public td::Jsonable {
public:
explicit JsonBackgroundFill(const td_api::BackgroundFill *background_fill) : background_fill_(background_fill) {
}
void store(td::JsonValueScope *scope) const {
CHECK(background_fill_ != nullptr);
auto object = scope->enter_object();
switch (background_fill_->get_id()) {
case td_api::backgroundFillSolid::ID: {
auto fill = static_cast<const td_api::backgroundFillSolid *>(background_fill_);
object("type", "solid");
object("color", fill->color_);
break;
}
case td_api::backgroundFillGradient::ID: {
auto fill = static_cast<const td_api::backgroundFillGradient *>(background_fill_);
object("type", "gradient");
object("top_color", fill->top_color_);
object("bottom_color", fill->bottom_color_);
object("rotation_angle", fill->rotation_angle_);
break;
}
case td_api::backgroundFillFreeformGradient::ID: {
auto fill = static_cast<const td_api::backgroundFillFreeformGradient *>(background_fill_);
object("type", "freeform_gradient");
object("colors", td::json_array(fill->colors_, [](int32 color) { return color; }));
break;
}
default:
UNREACHABLE();
}
}
private:
const td_api::BackgroundFill *background_fill_;
};
class Client::JsonBackgroundType final : public td::Jsonable {
public:
JsonBackgroundType(const td_api::BackgroundType *background_type, const td_api::document *document,
int32 dark_theme_dimming, const Client *client)
: background_type_(background_type)
, document_(document)
, dark_theme_dimming_(dark_theme_dimming)
, client_(client) {
}
void store(td::JsonValueScope *scope) const {
CHECK(background_type_ != nullptr);
auto object = scope->enter_object();
switch (background_type_->get_id()) {
case td_api::backgroundTypeWallpaper::ID: {
auto type = static_cast<const td_api::backgroundTypeWallpaper *>(background_type_);
object("type", "wallpaper");
CHECK(document_ != nullptr);
object("document", JsonDocument(document_, client_));
object("dark_theme_dimming", dark_theme_dimming_);
if (type->is_blurred_) {
object("is_blurred", td::JsonTrue());
}
if (type->is_moving_) {
object("is_moving", td::JsonTrue());
}
break;
}
case td_api::backgroundTypePattern::ID: {
auto type = static_cast<const td_api::backgroundTypePattern *>(background_type_);
object("type", "pattern");
CHECK(document_ != nullptr);
object("document", JsonDocument(document_, client_));
object("fill", JsonBackgroundFill(type->fill_.get()));
object("intensity", type->intensity_);
if (type->is_inverted_) {
object("is_inverted", td::JsonTrue());
}
if (type->is_moving_) {
object("is_moving", td::JsonTrue());
}
break;
}
case td_api::backgroundTypeFill::ID: {
auto type = static_cast<const td_api::backgroundTypeFill *>(background_type_);
object("type", "fill");
object("fill", JsonBackgroundFill(type->fill_.get()));
object("dark_theme_dimming", dark_theme_dimming_);
break;
}
case td_api::backgroundTypeChatTheme::ID: {
auto type = static_cast<const td_api::backgroundTypeChatTheme *>(background_type_);
object("type", "chat_theme");
object("theme_name", type->theme_name_);
break;
}
default:
UNREACHABLE();
}
}
private:
const td_api::BackgroundType *background_type_;
const td_api::document *document_;
int32 dark_theme_dimming_;
const Client *client_;
};
class Client::JsonChatBackground final : public td::Jsonable {
public:
JsonChatBackground(const td_api::chatBackground *chat_background, const Client *client)
: chat_background_(chat_background), client_(client) {
}
void store(td::JsonValueScope *scope) const {
CHECK(chat_background_ != nullptr);
auto object = scope->enter_object();
auto background = chat_background_->background_.get();
object("type", JsonBackgroundType(background->type_.get(), background->document_.get(),
chat_background_->dark_theme_dimming_, client_));
}
private:
const td_api::chatBackground *chat_background_;
const Client *client_;
};
class Client::JsonForumTopicCreated final : public td::Jsonable {
public:
explicit JsonForumTopicCreated(const td_api::messageForumTopicCreated *forum_topic_created)
: forum_topic_created_(forum_topic_created) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("name", forum_topic_created_->name_);
object("icon_color", forum_topic_created_->icon_->color_);
if (forum_topic_created_->icon_->custom_emoji_id_ != 0) {
object("icon_custom_emoji_id", td::to_string(forum_topic_created_->icon_->custom_emoji_id_));
}
}
private:
const td_api::messageForumTopicCreated *forum_topic_created_;
};
class Client::JsonForumTopicEdited final : public td::Jsonable {
public:
explicit JsonForumTopicEdited(const td_api::messageForumTopicEdited *forum_topic_edited)
: forum_topic_edited_(forum_topic_edited) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (!forum_topic_edited_->name_.empty()) {
object("name", forum_topic_edited_->name_);
}
if (forum_topic_edited_->edit_icon_custom_emoji_id_) {
object("icon_custom_emoji_id", forum_topic_edited_->icon_custom_emoji_id_ == 0
? td::string()
: td::to_string(forum_topic_edited_->icon_custom_emoji_id_));
}
}
private:
const td_api::messageForumTopicEdited *forum_topic_edited_;
};
class Client::JsonForumTopicInfo final : public td::Jsonable {
public:
explicit JsonForumTopicInfo(const td_api::forumTopicInfo *forum_topic_info) : forum_topic_info_(forum_topic_info) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("message_thread_id", as_client_message_id(forum_topic_info_->message_thread_id_));
object("name", forum_topic_info_->name_);
object("icon_color", forum_topic_info_->icon_->color_);
if (forum_topic_info_->icon_->custom_emoji_id_ != 0) {
object("icon_custom_emoji_id", td::to_string(forum_topic_info_->icon_->custom_emoji_id_));
}
}
private:
const td_api::forumTopicInfo *forum_topic_info_;
};
class Client::JsonAddress final : public td::Jsonable {
public:
explicit JsonAddress(const td_api::address *address) : address_(address) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("country_code", address_->country_code_);
object("state", address_->state_);
object("city", address_->city_);
object("street_line1", address_->street_line1_);
object("street_line2", address_->street_line2_);
object("post_code", address_->postal_code_);
}
private:
const td_api::address *address_;
};
class Client::JsonOrderInfo final : public td::Jsonable {
public:
explicit JsonOrderInfo(const td_api::orderInfo *order_info) : order_info_(order_info) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
if (!order_info_->name_.empty()) {
object("name", order_info_->name_);
}
if (!order_info_->phone_number_.empty()) {
object("phone_number", order_info_->phone_number_);
}
if (!order_info_->email_address_.empty()) {
object("email", order_info_->email_address_);
}
if (order_info_->shipping_address_ != nullptr) {
object("shipping_address", JsonAddress(order_info_->shipping_address_.get()));
}
}
private:
const td_api::orderInfo *order_info_;
};
class Client::JsonSuccessfulPaymentBot final : public td::Jsonable {
public:
explicit JsonSuccessfulPaymentBot(const td_api::messagePaymentSuccessfulBot *successful_payment)
: successful_payment_(successful_payment) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("currency", successful_payment_->currency_);
object("total_amount", successful_payment_->total_amount_);
if (!td::check_utf8(successful_payment_->invoice_payload_)) {
LOG(WARNING) << "Receive non-UTF-8 invoice payload";
object("invoice_payload", td::JsonRawString(successful_payment_->invoice_payload_));
} else {
object("invoice_payload", successful_payment_->invoice_payload_);
}
if (!successful_payment_->shipping_option_id_.empty()) {
object("shipping_option_id", successful_payment_->shipping_option_id_);
}
if (successful_payment_->order_info_ != nullptr) {
object("order_info", JsonOrderInfo(successful_payment_->order_info_.get()));
}
object("telegram_payment_charge_id", successful_payment_->telegram_payment_charge_id_);
object("provider_payment_charge_id", successful_payment_->provider_payment_charge_id_);
}
private:
const td_api::messagePaymentSuccessfulBot *successful_payment_;
};
class Client::JsonRefundedPayment final : public td::Jsonable {
public:
explicit JsonRefundedPayment(const td_api::messagePaymentRefunded *refunded_payment)
: refunded_payment_(refunded_payment) {
}
void store(td::JsonValueScope *scope) const {
auto object = scope->enter_object();
object("currency", refunded_payment_->currency_);
object("total_amount", refunded_payment_->total_amount_);
if (!td::check_utf8(refunded_payment_->invoice_payload_)) {
LOG(WARNING) << "Receive non-UTF-8 invoice payload";
object("invoice_payload", td::JsonRawString(refunded_payment_->invoice_payload_));
} else {