diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 2ff508b2..fa0c0332 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2734,7 +2734,7 @@ textParseModeHTML = TextParseMode; //@description A SOCKS5 proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty proxyTypeSocks5 username:string password:string = ProxyType; -//@description A HTTP transparent proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty @http_only Pass true, if the proxy supports only HTTP requests and doesn't support transparent TCP connections via HTTP CONNECT method +//@description A HTTP transparent proxy server @username Username for logging in; may be empty @password Password for logging in; may be empty @http_only Pass true if the proxy supports only HTTP requests and doesn't support transparent TCP connections via HTTP CONNECT method proxyTypeHttp username:string password:string http_only:Bool = ProxyType; //@description An MTProto proxy server @secret The proxy's secret in hexadecimal encoding @@ -2762,51 +2762,50 @@ inputStickerStatic sticker:InputFile emojis:string mask_position:maskPosition = inputStickerAnimated sticker:InputFile emojis:string = InputSticker; -//@description chatStatisticsDateRange -//@min_date min_date -//@max_date max_date -chatStatisticsDateRange min_date:int32 max_date:int32 = ChatStatisticsDateRange; - -//@description chatStatisticsAbsoluteValue -//@current current -//@previous previous -chatStatisticsAbsoluteValue current:double previous:double = ChatStatisticsAbsoluteValue; - -//@description chatStatisticsRelativeValue -//@part part -//@total total -chatStatisticsRelativeValue part:double total:double = ChatStatisticsRelativeValue; +//@description Represents a date range @start_date Point in time (Unix timestamp) at which the date range begins @end_date Point in time (Unix timestamp) at which the date range ends +dateRange start_date:int32 end_date:int32 = DateRange; -//@class ChatStatisticsGraph @description ChatStatisticsGraph - -//@description chatStatisticsGraphAsync -//@token token -chatStatisticsGraphAsync token:string = ChatStatisticsGraph; - -//@description chatStatisticsGraphError -//@error_message error_message -chatStatisticsGraphError error_message:string = ChatStatisticsGraph; - -//@description chatStatisticsGraphData -//@json json -//@zoom_token zoom_token -chatStatisticsGraphData json:string zoom_token:string = ChatStatisticsGraph; +//@description A statistics value @value The value @previous_value The value for the previous day @growth_rate_percentage The growth rate of the value, as a percentage +statisticsValue value:double previous_value:double growth_rate_percentage:double = StatisticsValue; -//@description chatStatisticsMessageInteractionCounters -//@message_id message_id -//@views views -//@forwards forwards -chatStatisticsMessageInteractionCounters message_id:int32 views:int32 forwards:int32 = ChatStatisticsMessageInteractionCounters; +//@class StatisticsGraph @description Descrbes a statistics graph + +//@description A graph data @json_data Graph data in JSON format @zoom_token If non-empty, a token which can be used to receive a zoomed in graph +statisticsGraphData json_data:string zoom_token:string = StatisticsGraph; + +//@description The graph data to be asynchronously loaded through getChatStatisticsGraph @token The token to use for data loading +statisticsGraphAsync token:string = StatisticsGraph; + +//@description An error message to be shown to the user instead of the graph @error_message The error message +statisticsGraphError error_message:string = StatisticsGraph; -//@description chatStatistics -//@period period @followers followers @views_per_post views_per_post @shares_per_post shares_per_post @enabled_notifications enabled_notifications -//@growth_graph growth_graph @followers_graph followers_graph @mute_graph mute_graph @top_hours_graph top_hours_graph @interactions_graph interactions_graph -//@iv_interactions_graph iv_interactions_graph @views_by_source_graph views_by_source_graph @new_followers_by_source_graph new_followers_by_source_graph -//@languages_graph languages_graph @recent_message_interactions recent_message_interactions -chatStatistics period:ChatStatisticsDateRange followers:ChatStatisticsAbsoluteValue views_per_post:ChatStatisticsAbsoluteValue shares_per_post:ChatStatisticsAbsoluteValue enabled_notifications:ChatStatisticsRelativeValue growth_graph:ChatStatisticsGraph followers_graph:ChatStatisticsGraph mute_graph:ChatStatisticsGraph top_hours_graph:ChatStatisticsGraph interactions_graph:ChatStatisticsGraph iv_interactions_graph:ChatStatisticsGraph views_by_source_graph:ChatStatisticsGraph new_followers_by_source_graph:ChatStatisticsGraph languages_graph:ChatStatisticsGraph recent_message_interactions:Vector = ChatStatistics; +//@description Contains statistics about interactions with a message +//@message_id Message identifier +//@view_count Number of times the message was viewed +//@forward_count Number of times the message was forwarded +chatStatisticsMessageInteractionCounters message_id:int53 view_count:int32 forward_count:int32 = ChatStatisticsMessageInteractionCounters; + + +//@description A detailed statistics about a chat +//@period A period to which the statistics applies +//@member_count Number of members in the chat +//@mean_view_count Mean number of times the recently sent messages was viewed +//@mean_share_count Mean number of times the recently sent messages was shared +//@enabled_notifications_percentage A percentage of users with enabled notifications for the chat +//@member_count_graph A graph containing number of members in the chat +//@join_graph A graph containing number of members joined and left the chat +//@mute_graph A graph containing number of members muted and unmuted the chat +//@view_count_by_hour_graph A graph containing number of message views in a given hour in the last two weeks +//@view_count_by_source_graph A graph containing number of message views per source +//@join_by_source_graph A graph containing number of new member joins per source +//@language_graph A graph containing number of users viewed chat messages per language +//@message_interaction_graph A graph containing number of chat message views and shares +//@instant_view_interaction_graph A graph containing number of views of associated with the chat instant views +//@recent_message_interactions Detailed statistics about number of views and shares of recently sent messages +chatStatistics period:dateRange member_count:statisticsValue mean_view_count:statisticsValue mean_share_count:statisticsValue enabled_notifications_percentage:double member_count_graph:StatisticsGraph join_graph:StatisticsGraph mute_graph:StatisticsGraph view_count_by_hour_graph:StatisticsGraph view_count_by_source_graph:StatisticsGraph join_by_source_graph:StatisticsGraph language_graph:StatisticsGraph message_interaction_graph:StatisticsGraph instant_view_interaction_graph:StatisticsGraph recent_message_interactions:vector = ChatStatistics; //@class Update @description Contains notifications about data changes @@ -4187,11 +4186,11 @@ reportChat chat_id:int53 reason:ChatReportReason message_ids:vector = Ok; //@description Returns an HTTP URL with the chat statistics. Currently this method of getting the statistics is disabled and can be deleted in the future @chat_id Chat identifier @parameters Parameters from "tg://statsrefresh?params=******" link @is_dark Pass true if a URL with the dark theme must be returned getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl; -//@description Retrieve specified channel statistics @chat_id supergroup id @is_dark is dark +//@description Returns detailed statistics about a chat. Currently this method can be used only for channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics; -//@description Load async stats @token token @x zoom -getChatStatisticsGraph token:string x:int53 = ChatStatisticsGraph; +//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifer @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise +getChatStatisticsGraph chat_id:int53 token:string x:int53 = StatisticsGraph; //@description Returns storage usage statistics. Can be called before authorization @chat_limit The maximum number of chats with the largest storage usage for which separate statistics should be returned. All other chats will be grouped in entries with chat_id == 0. If the chat info database is not used, the chat_limit is ignored and is always set to 0 @@ -4211,7 +4210,7 @@ getDatabaseStatistics = DatabaseStatistics; //@file_types If not empty, only files with the given type(s) are considered. By default, all types except thumbnails, profile photos, stickers and wallpapers are deleted //@chat_ids If not empty, only files from the given chats are considered. Use 0 as chat identifier to delete files not belonging to any chat (e.g., profile photos) //@exclude_chat_ids If not empty, files from the given chats are excluded. Use 0 as chat identifier to exclude all files not belonging to any chat (e.g., profile photos) -//@return_deleted_file_statistics Pass true, if deleted file statistics needs to be returned instead of the whole storage usage statistics. Affects only returned statistics +//@return_deleted_file_statistics Pass true if deleted file statistics needs to be returned instead of the whole storage usage statistics. Affects only returned statistics //@chat_limit Same as in getStorageStatistics. Affects only returned statistics optimizeStorage size:int53 ttl:int32 count:int32 immunity_delay:int32 file_types:vector chat_ids:vector exclude_chat_ids:vector return_deleted_file_statistics:Bool chat_limit:int32 = StorageStatistics; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index de83eff8..fcd68ea2 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 27643bee..13c54fe3 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -2660,21 +2660,22 @@ class GetSupportUserQuery : public Td::ResultHandler { } }; -static tl_object_ptr convert_stats_graph(tl_object_ptr obj) { +tl_object_ptr ContactsManager::convert_stats_graph( + tl_object_ptr obj) { CHECK(obj != nullptr); switch (obj->get_id()) { case telegram_api::statsGraphAsync::ID: { auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->token_)); + return make_tl_object(std::move(graph->token_)); } case telegram_api::statsGraphError::ID: { auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->error_)); + return make_tl_object(std::move(graph->error_)); } case telegram_api::statsGraph::ID: { auto graph = move_tl_object_as(obj); - return make_tl_object(std::move(graph->json_->data_), std::move(graph->zoom_token_)); + return make_tl_object(std::move(graph->json_->data_), std::move(graph->zoom_token_)); } default: UNREACHABLE(); @@ -2682,33 +2683,45 @@ static tl_object_ptr convert_stats_graph(tl_object_ } } -static tl_object_ptr convert_stats_broadcast_stats(tl_object_ptr obj) { +double ContactsManager::get_percentage_value(double part, double total) { + if (total < 1e-6 && total > -1e-6) { + if (part < 1e-6 && part > -1e-6) { + return 0.0; + } + return 100.0; + } + if (part > 1e20) { + return 100.0; + } + return part / total * 100; +} + +tl_object_ptr ContactsManager::convert_stats_absolute_value( + const tl_object_ptr &obj) { + return make_tl_object(obj->current_, obj->previous_, + get_percentage_value(obj->current_ - obj->previous_, obj->previous_)); +} + +tl_object_ptr ContactsManager::convert_broadcast_stats( + tl_object_ptr obj) { CHECK(obj != nullptr); - vector> recent_message_interactions; - recent_message_interactions.reserve(obj->recent_message_interactions_.size()); - for (auto& interaction : obj->recent_message_interactions_) { - recent_message_interactions.push_back( - make_tl_object(interaction->msg_id_, interaction->views_, interaction->forwards_)); - } + auto recent_message_interactions = transform(std::move(obj->recent_message_interactions_), [](auto &&interaction) { + return make_tl_object( + MessageId(ServerMessageId(interaction->msg_id_)).get(), interaction->views_, interaction->forwards_); + }); return make_tl_object( - make_tl_object(obj->period_->min_date_, obj->period_->max_date_), - make_tl_object(obj->followers_->current_, obj->followers_->previous_), - make_tl_object(obj->views_per_post_->current_, obj->views_per_post_->previous_), - make_tl_object(obj->shares_per_post_->current_, obj->shares_per_post_->previous_), - make_tl_object(obj->enabled_notifications_->part_, obj->enabled_notifications_->total_), - convert_stats_graph(std::move(obj->growth_graph_)), - convert_stats_graph(std::move(obj->followers_graph_)), - convert_stats_graph(std::move(obj->mute_graph_)), - convert_stats_graph(std::move(obj->top_hours_graph_)), - convert_stats_graph(std::move(obj->interactions_graph_)), - convert_stats_graph(std::move(obj->iv_interactions_graph_)), + make_tl_object(obj->period_->min_date_, obj->period_->max_date_), + convert_stats_absolute_value(obj->followers_), convert_stats_absolute_value(obj->views_per_post_), + convert_stats_absolute_value(obj->shares_per_post_), + get_percentage_value(obj->enabled_notifications_->part_, obj->enabled_notifications_->total_), + convert_stats_graph(std::move(obj->growth_graph_)), convert_stats_graph(std::move(obj->followers_graph_)), + convert_stats_graph(std::move(obj->mute_graph_)), convert_stats_graph(std::move(obj->top_hours_graph_)), convert_stats_graph(std::move(obj->views_by_source_graph_)), convert_stats_graph(std::move(obj->new_followers_by_source_graph_)), - convert_stats_graph(std::move(obj->languages_graph_)), - std::move(recent_message_interactions) - ); + convert_stats_graph(std::move(obj->languages_graph_)), convert_stats_graph(std::move(obj->interactions_graph_)), + convert_stats_graph(std::move(obj->iv_interactions_graph_)), std::move(recent_message_interactions)); } class GetBroadcastStatsQuery : public Td::ResultHandler { @@ -2716,7 +2729,8 @@ class GetBroadcastStatsQuery : public Td::ResultHandler { ChannelId channel_id_; public: - explicit GetBroadcastStatsQuery(Promise> &&promise) : promise_(std::move(promise)) { + explicit GetBroadcastStatsQuery(Promise> &&promise) + : promise_(std::move(promise)) { } void send(ChannelId channel_id, bool is_dark, DcId dc_id) { @@ -2740,7 +2754,7 @@ class GetBroadcastStatsQuery : public Td::ResultHandler { } auto result = result_ptr.move_as_ok(); - promise_.set_value(convert_stats_broadcast_stats(std::move(result))); + promise_.set_value(ContactsManager::convert_broadcast_stats(std::move(result))); } void on_error(uint64 id, Status status) override { @@ -2749,20 +2763,20 @@ class GetBroadcastStatsQuery : public Td::ResultHandler { } }; -class LoadAyncGraphQuery : public Td::ResultHandler { - Promise> promise_; +class LoadAsyncGraphQuery : public Td::ResultHandler { + Promise> promise_; public: - explicit LoadAyncGraphQuery(Promise> &&promise) : promise_(std::move(promise)) { + explicit LoadAsyncGraphQuery(Promise> &&promise) + : promise_(std::move(promise)) { } - void send(const string& token, int64 x) { + void send(const string &token, int64 x, DcId dc_id) { int32 flags = 0; if (x != 0) { flags |= telegram_api::stats_loadAsyncGraph::X_MASK; } - send_query(G()->net_query_creator().create( - telegram_api::stats_loadAsyncGraph(flags, token, x))); + send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), dc_id)); } void on_result(uint64 id, BufferSlice packet) override { @@ -2772,7 +2786,7 @@ class LoadAyncGraphQuery : public Td::ResultHandler { } auto result = result_ptr.move_as_ok(); - promise_.set_value(convert_stats_graph(std::move(result))); + promise_.set_value(ContactsManager::convert_stats_graph(std::move(result))); } void on_error(uint64 id, Status status) override { @@ -5634,7 +5648,7 @@ void ContactsManager::set_channel_location(DialogId dialog_id, const DialogLocat } if (!dialog_id.is_valid()) { - return promise.set_error(Status::Error(400, "Invalid chat specified")); + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); } if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -5666,7 +5680,7 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow } if (!dialog_id.is_valid()) { - return promise.set_error(Status::Error(400, "Invalid chat specified")); + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); } if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -5691,10 +5705,9 @@ void ContactsManager::set_channel_slow_mode_delay(DialogId dialog_id, int32 slow td_->create_handler(std::move(promise))->send(channel_id, slow_mode_delay); } -void ContactsManager::get_broadcast_stats(DialogId dialog_id, bool is_dark, - Promise> &&promise) { +void ContactsManager::get_channel_statistics_dc_id(DialogId dialog_id, Promise &&promise) { if (!dialog_id.is_valid()) { - return promise.set_error(Status::Error(400, "Invalid chat specified")); + return promise.set_error(Status::Error(400, "Invalid chat identifier specified")); } if (!td_->messages_manager_->have_dialog_force(dialog_id)) { return promise.set_error(Status::Error(400, "Chat not found")); @@ -5713,17 +5726,60 @@ void ContactsManager::get_broadcast_stats(DialogId dialog_id, bool is_dark, return promise.set_error(Status::Error(400, "Chat is not a channel")); } - const ChannelFull* c_full = get_channel_full(channel_id, "get_broadcast_stats"); - if (c_full == nullptr) { - return promise.set_error(Status::Error(400, "Chat info not found")); + auto channel_full = get_channel_full_force(channel_id); + if (channel_full == nullptr) { + return promise.set_error(Status::Error(400, "Chat full info not found")); } - td_->create_handler(std::move(promise))->send(channel_id, is_dark, c_full->stats_dc_id); + if (!channel_full->stats_dc_id.is_exact()) { + return promise.set_error(Status::Error(400, "Chat statistics is not available")); + } + + promise.set_value(DcId(channel_full->stats_dc_id)); } -void ContactsManager::load_async_graph(const string& token, int64 x, Promise> &&promise) -{ - td_->create_handler(std::move(promise))->send(token, x); +void ContactsManager::get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), dialog_id, is_dark, promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &ContactsManager::send_get_broadcast_stats_query, r_dc_id.move_as_ok(), + dialog_id.get_channel_id(), is_dark, std::move(promise)); + }); + get_channel_statistics_dc_id(dialog_id, std::move(dc_id_promise)); +} + +void ContactsManager::send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + td_->create_handler(std::move(promise))->send(channel_id, is_dark, dc_id); +} + +void ContactsManager::load_statistics_graph(DialogId dialog_id, const string &token, int64 x, + Promise> &&promise) { + auto dc_id_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), token, x, promise = std::move(promise)](Result r_dc_id) mutable { + if (r_dc_id.is_error()) { + return promise.set_error(r_dc_id.move_as_error()); + } + send_closure(actor_id, &ContactsManager::send_load_async_graph_query, r_dc_id.move_as_ok(), std::move(token), x, + std::move(promise)); + }); + get_channel_statistics_dc_id(dialog_id, std::move(dc_id_promise)); +} + +void ContactsManager::send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise) { + if (G()->close_flag()) { + return promise.set_error(Status::Error(500, "Request aborted")); + } + + td_->create_handler(std::move(promise))->send(token, x, dc_id); } void ContactsManager::report_channel_spam(ChannelId channel_id, UserId user_id, const vector &message_ids, diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 9cf91934..e4445634 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -357,10 +357,11 @@ class ContactsManager : public Actor { void delete_channel(ChannelId channel_id, Promise &&promise); - void get_broadcast_stats(DialogId dialog_id, bool is_dark, - Promise> &&promise); + void get_channel_statistics(DialogId dialog_id, bool is_dark, + Promise> &&promise); - void load_async_graph(const string& token, int64 x, Promise> &&promise); + void load_statistics_graph(DialogId dialog_id, const string &token, int64 x, + Promise> &&promise); void add_chat_participant(ChatId chat_id, UserId user_id, int32 forward_limit, Promise &&promise); @@ -538,6 +539,16 @@ class ContactsManager : public Actor { void get_current_state(vector> &updates) const; + static tl_object_ptr convert_stats_graph(tl_object_ptr obj); + + static double get_percentage_value(double new_value, double old_value); + + static tl_object_ptr convert_stats_absolute_value( + const tl_object_ptr &obj); + + static tl_object_ptr convert_broadcast_stats( + tl_object_ptr obj); + private: struct User { string first_name; @@ -1327,6 +1338,14 @@ class ContactsManager : public Actor { tl_object_ptr input_check_password, Promise &&promise); + void get_channel_statistics_dc_id(DialogId dialog_id, Promise &&promise); + + void send_get_broadcast_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark, + Promise> &&promise); + + void send_load_async_graph_query(DcId dc_id, string token, int64 x, + Promise> &&promise); + static void on_user_online_timeout_callback(void *contacts_manager_ptr, int64 user_id_long); static void on_channel_unban_timeout_callback(void *contacts_manager_ptr, int64 channel_id_long); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 0b5e3db4..c8008563 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6494,18 +6494,17 @@ void Td::on_request(uint64 id, td_api::getChatStatisticsUrl &request) { std::move(promise)); } -void Td::on_request(uint64 id, td_api::getChatStatistics &request) { +void Td::on_request(uint64 id, const td_api::getChatStatistics &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); - contacts_manager_->get_broadcast_stats(DialogId(request.chat_id_), request.is_dark_, - std::move(promise)); + contacts_manager_->get_channel_statistics(DialogId(request.chat_id_), request.is_dark_, std::move(promise)); } void Td::on_request(uint64 id, td_api::getChatStatisticsGraph &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); CLEAN_INPUT_STRING(request.token_); - contacts_manager_->load_async_graph(request.token_, request.x_, std::move(promise)); + contacts_manager_->load_statistics_graph(DialogId(request.chat_id_), request.token_, request.x_, std::move(promise)); } void Td::on_request(uint64 id, td_api::setChatNotificationSettings &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 43c59aa1..e84c3cdf 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -887,7 +887,7 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::getChatStatisticsUrl &request); - void on_request(uint64 id, td_api::getChatStatistics &request); + void on_request(uint64 id, const td_api::getChatStatistics &request); void on_request(uint64 id, td_api::getChatStatisticsGraph &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index ceff5e9c..117e822c 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3869,7 +3869,23 @@ class CliClient final : public Actor { std::tie(chat_id, args) = split(args); std::tie(parameters, is_dark) = split(args); - send_request(td_api::make_object(as_chat_id(args), parameters, as_bool(is_dark))); + send_request( + td_api::make_object(as_chat_id(chat_id), parameters, as_bool(is_dark))); + } else if (op == "gcst") { + string chat_id; + string is_dark; + std::tie(chat_id, is_dark) = split(args); + + send_request(td_api::make_object(as_chat_id(chat_id), as_bool(is_dark))); + } else if (op == "gcstg") { + string chat_id; + string token; + string x; + std::tie(chat_id, args) = split(args); + std::tie(token, x) = split(args); + + send_request( + td_api::make_object(as_chat_id(chat_id), token, to_integer(x))); } else if (op == "glui" || op == "glu" || op == "glua") { string chat_id; string message_id;