Add td_api::getMessageStatistics.

GitOrigin-RevId: f5d32ef829b91aa1fea59ea88b665fe39d32642c
This commit is contained in:
levlam 2020-08-01 05:19:54 +03:00
parent 8e77eacc72
commit 53ca6e4f4a
9 changed files with 172 additions and 20 deletions

View File

@ -576,7 +576,7 @@ supergroup id:int32 username:string date:int32 status:ChatMemberStatus member_co
//@can_set_username True, if the chat username can be changed
//@can_set_sticker_set True, if the supergroup sticker set can be changed
//@can_set_location True, if the supergroup location can be changed
//@can_view_statistics True, if the channel statistics is available
//@can_view_statistics True, if the supergroup or channel statistics is available
//@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators
//@sticker_set_id Identifier of the supergroup sticker set; 0 if none
//@location Location to which the supergroup is connected; may be null
@ -660,6 +660,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
//@can_be_forwarded True, if the message can be forwarded
//@can_be_deleted_only_for_self True, if the message can be deleted only for the current user while other users will continue to see it
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
//@can_view_statistics True, if the message statistics is available
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
//@contains_unread_mention True, if the message contains an unread mention for the current user
//@date Point in time (Unix timestamp) when the message was sent
@ -675,7 +676,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
//@content Content of the message
//@reply_markup Reply markup for the message; may be null
message id:int53 sender_user_id:int32 chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo reply_to_message_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
message id:int53 sender_user_id:int32 chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_view_statistics:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo reply_to_message_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
//@description Contains a list of messages @total_count Approximate total count of messages found @messages List of messages; messages may be null
messages total_count:int32 messages:vector<message> = Messages;
@ -1993,7 +1994,7 @@ call id:int32 user_id:int32 is_outgoing:Bool is_video:Bool state:CallState = Cal
//@description Contains settings for the authentication of the user's phone number
//@allow_flash_call Pass true if the authentication code may be sent via flash call to the specified phone number
//@is_current_phone_number Pass true if the authenticated phone number is used on the current device
//@allow_sms_retriever_api For official applications only. True, if the app can use Android SMS Retriever API (requires Google Play Services >= 10.2) to automatically receive the authentication code from the SMS. See https://developers.google.com/identity/sms-retriever/ for more details
//@allow_sms_retriever_api For official applications only. True, if the application can use Android SMS Retriever API (requires Google Play Services >= 10.2) to automatically receive the authentication code from the SMS. See https://developers.google.com/identity/sms-retriever/ for more details
phoneNumberAuthenticationSettings allow_flash_call:Bool is_current_phone_number:Bool allow_sms_retriever_api:Bool = PhoneNumberAuthenticationSettings;
@ -2796,7 +2797,7 @@ networkStatisticsEntryFile file_type:FileType network_type:NetworkType sent_byte
//@sent_bytes Total number of bytes sent @received_bytes Total number of bytes received @duration Total call duration, in seconds
networkStatisticsEntryCall network_type:NetworkType sent_bytes:int53 received_bytes:int53 duration:double = NetworkStatisticsEntry;
//@description A full list of available network statistic entries @since_date Point in time (Unix timestamp) when the app began collecting statistics @entries Network statistics entries
//@description A full list of available network statistic entries @since_date Point in time (Unix timestamp) from which the statistics is collected @entries Network statistics entries
networkStatistics since_date:int32 entries:vector<NetworkStatisticsEntry> = NetworkStatistics;
@ -2960,7 +2961,7 @@ statisticsValue value:double previous_value:double growth_rate_percentage:double
//@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
//@description The graph data to be asynchronously loaded through getStatisticsGraph @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
@ -3032,6 +3033,10 @@ chatStatisticsSupergroup period:dateRange member_count:statisticsValue message_c
chatStatisticsChannel 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<chatStatisticsMessageInteractionInfo> = ChatStatistics;
//@description A detailed statistics about a message @message_interaction_graph A graph containing number of message views and shares
messageStatistics message_interaction_graph:StatisticsGraph = MessageStatistics;
//@class Update @description Contains notifications about data changes
//@description The user authorization state has changed @authorization_state New authorization state
@ -4457,11 +4462,14 @@ reportChat chat_id:int53 reason:ChatReportReason message_ids:vector<int53> = 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 Returns detailed statistics about a chat. Currently this method can be used only for supergroups and channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app
//@description Returns detailed statistics about a chat. Currently this method can be used only for supergroups and channels. Can be used only if SupergroupFullInfo.can_view_statistics == true @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the application
getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics;
//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifier @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 detailed statistics about a message. Can be used only if Message.can_view_statistics == true @chat_id Chat identifier @message_id Message identifier @is_dark Pass true if a dark theme is used by the application
getMessageStatistics chat_id:int53 message_id:int53 is_dark:Bool = MessageStatistics;
//@description Loads asynchronous or zoomed in chat or message statistics graph @chat_id Chat identifier @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise
getStatisticsGraph 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

Binary file not shown.

View File

@ -2835,8 +2835,7 @@ class GetMegagroupStatsQuery : public Td::ResultHandler {
return on_error(id, result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
promise_.set_value(td->contacts_manager_->convert_megagroup_stats(std::move(result)));
promise_.set_value(td->contacts_manager_->convert_megagroup_stats(result_ptr.move_as_ok()));
}
void on_error(uint64 id, Status status) override {
@ -2888,6 +2887,51 @@ class GetBroadcastStatsQuery : public Td::ResultHandler {
}
};
tl_object_ptr<td_api::messageStatistics> ContactsManager::convert_message_stats(
tl_object_ptr<telegram_api::stats_messageStats> obj) {
return make_tl_object<td_api::messageStatistics>(convert_stats_graph(std::move(obj->views_graph_)));
}
class GetMessageStatsQuery : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::messageStatistics>> promise_;
ChannelId channel_id_;
public:
explicit GetMessageStatsQuery(Promise<td_api::object_ptr<td_api::messageStatistics>> &&promise)
: promise_(std::move(promise)) {
}
void send(ChannelId channel_id, MessageId message_id, bool is_dark, DcId dc_id) {
channel_id_ = channel_id;
auto input_channel = td->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
int32 flags = 0;
if (is_dark) {
flags |= telegram_api::stats_getMessageStats::DARK_MASK;
}
send_query(G()->net_query_creator().create(
telegram_api::stats_getMessageStats(flags, false /*ignored*/, std::move(input_channel),
message_id.get_server_message_id().get()),
dc_id));
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::stats_getMessageStats>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
promise_.set_value(td->contacts_manager_->convert_message_stats(result_ptr.move_as_ok()));
}
void on_error(uint64 id, Status status) override {
td->contacts_manager_->on_get_channel_error(channel_id_, status, "GetMessageStatsQuery");
promise_.set_error(std::move(status));
}
};
class LoadAsyncGraphQuery : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::StatisticsGraph>> promise_;
@ -6066,6 +6110,56 @@ void ContactsManager::send_get_channel_stats_query(DcId dc_id, ChannelId channel
}
}
bool ContactsManager::can_get_channel_message_statistics(DialogId dialog_id) {
if (dialog_id.get_type() != DialogType::Channel) {
return false;
}
auto channel_id = dialog_id.get_channel_id();
const Channel *c = get_channel(channel_id);
if (c == nullptr || c->is_megagroup) {
return false;
}
auto channel_full = get_channel_full_force(channel_id, "can_get_channel_message_statistics");
if (channel_full == nullptr) {
return c->status.is_administrator();
}
return channel_full->stats_dc_id.is_exact();
}
void ContactsManager::get_channel_message_statistics(FullMessageId full_message_id, bool is_dark,
Promise<td_api::object_ptr<td_api::messageStatistics>> &&promise) {
auto dc_id_promise = PromiseCreator::lambda([actor_id = actor_id(this), full_message_id, is_dark,
promise = std::move(promise)](Result<DcId> 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_channel_message_stats_query, r_dc_id.move_as_ok(),
full_message_id, is_dark, std::move(promise));
});
get_channel_statistics_dc_id(full_message_id.get_dialog_id(), false, std::move(dc_id_promise));
}
void ContactsManager::send_get_channel_message_stats_query(
DcId dc_id, FullMessageId full_message_id, bool is_dark,
Promise<td_api::object_ptr<td_api::messageStatistics>> &&promise) {
if (G()->close_flag()) {
return promise.set_error(Status::Error(500, "Request aborted"));
}
auto dialog_id = full_message_id.get_dialog_id();
if (!td_->messages_manager_->have_message_force(full_message_id, "send_get_channel_message_stats_query")) {
return promise.set_error(Status::Error(400, "Message not found"));
}
if (!td_->messages_manager_->can_get_message_statistics(full_message_id)) {
return promise.set_error(Status::Error(400, "Message statistics is inaccessible"));
}
CHECK(dialog_id.get_type() == DialogType::Channel);
td_->create_handler<GetMessageStatsQuery>(std::move(promise))
->send(dialog_id.get_channel_id(), full_message_id.get_message_id(), is_dark, dc_id);
}
void ContactsManager::load_statistics_graph(DialogId dialog_id, const string &token, int64 x,
Promise<td_api::object_ptr<td_api::StatisticsGraph>> &&promise) {
auto dc_id_promise = PromiseCreator::lambda(

View File

@ -20,6 +20,7 @@
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/Location.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/net/DcId.h"
@ -373,6 +374,11 @@ class ContactsManager : public Actor {
void get_channel_statistics(DialogId dialog_id, bool is_dark,
Promise<td_api::object_ptr<td_api::ChatStatistics>> &&promise);
bool can_get_channel_message_statistics(DialogId dialog_id);
void get_channel_message_statistics(FullMessageId full_message_id, bool is_dark,
Promise<td_api::object_ptr<td_api::messageStatistics>> &&promise);
void load_statistics_graph(DialogId dialog_id, const string &token, int64 x,
Promise<td_api::object_ptr<td_api::StatisticsGraph>> &&promise);
@ -570,6 +576,9 @@ class ContactsManager : public Actor {
static tl_object_ptr<td_api::chatStatisticsChannel> convert_broadcast_stats(
tl_object_ptr<telegram_api::stats_broadcastStats> obj);
static tl_object_ptr<td_api::messageStatistics> convert_message_stats(
tl_object_ptr<telegram_api::stats_messageStats> obj);
private:
struct User {
string first_name;
@ -1426,6 +1435,9 @@ class ContactsManager : public Actor {
void send_get_channel_stats_query(DcId dc_id, ChannelId channel_id, bool is_dark,
Promise<td_api::object_ptr<td_api::ChatStatistics>> &&promise);
void send_get_channel_message_stats_query(DcId dc_id, FullMessageId full_message_id, bool is_dark,
Promise<td_api::object_ptr<td_api::messageStatistics>> &&promise);
void send_load_async_graph_query(DcId dc_id, string token, int64 x,
Promise<td_api::object_ptr<td_api::StatisticsGraph>> &&promise);

View File

@ -8935,6 +8935,22 @@ bool MessagesManager::can_forward_message(DialogId from_dialog_id, const Message
return can_forward_message_content(m->content.get());
}
bool MessagesManager::can_get_message_statistics(FullMessageId full_message_id) {
return can_get_message_statistics(full_message_id.get_dialog_id(),
get_message_force(full_message_id, "can_get_message_statistics"));
}
bool MessagesManager::can_get_message_statistics(DialogId dialog_id, const Message *m) const {
if (td_->auth_manager_->is_bot()) {
return false;
}
if (m == nullptr || m->message_id.is_scheduled() || !m->message_id.is_server() || m->view_count == 0 ||
m->had_forward_info || (m->forward_info != nullptr && m->forward_info->dialog_id.is_valid())) {
return false;
}
return td_->contacts_manager_->can_get_channel_message_statistics(dialog_id);
}
bool MessagesManager::can_delete_channel_message(DialogParticipantStatus status, const Message *m, bool is_bot) {
if (m == nullptr) {
return true;
@ -20027,6 +20043,8 @@ tl_object_ptr<td_api::message> MessagesManager::get_message_object(DialogId dial
auto scheduling_state = is_scheduled ? get_message_scheduling_state_object(m->date) : nullptr;
bool can_be_edited = for_event_log ? false : can_edit_message(dialog_id, m, false, td_->auth_manager_->is_bot());
bool can_be_forwarded = for_event_log ? false : can_forward_message(dialog_id, m);
bool can_get_statistics = for_event_log ? false : can_get_message_statistics(dialog_id, m);
auto via_bot_user_id = td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id");
auto media_album_id = for_event_log ? static_cast<int64>(0) : m->media_album_id;
auto reply_to_message_id = for_event_log ? static_cast<int64>(0) : m->reply_to_message_id.get();
bool contains_unread_mention = for_event_log ? false : m->contains_unread_mention;
@ -20036,11 +20054,10 @@ tl_object_ptr<td_api::message> MessagesManager::get_message_object(DialogId dial
return make_tl_object<td_api::message>(
m->message_id.get(), td_->contacts_manager_->get_user_id_object(m->sender_user_id, "sender_user_id"),
dialog_id.get(), std::move(sending_state), std::move(scheduling_state), is_outgoing, can_be_edited,
can_be_forwarded, can_delete_for_self, can_delete_for_all_users, m->is_channel_post, contains_unread_mention,
date, edit_date, get_message_forward_info_object(m->forward_info),
get_message_interaction_info_object(dialog_id, m), reply_to_message_id, ttl, ttl_expires_in,
td_->contacts_manager_->get_user_id_object(m->via_bot_user_id, "via_bot_user_id"), m->author_signature,
media_album_id, get_restriction_reason_description(m->restriction_reasons),
can_be_forwarded, can_delete_for_self, can_delete_for_all_users, can_get_statistics, m->is_channel_post,
contains_unread_mention, date, edit_date, get_message_forward_info_object(m->forward_info),
get_message_interaction_info_object(dialog_id, m), reply_to_message_id, ttl, ttl_expires_in, via_bot_user_id,
m->author_signature, media_album_id, get_restriction_reason_description(m->restriction_reasons),
get_message_content_object(m->content.get(), td_, live_location_date, m->is_content_secret),
get_reply_markup_object(m->reply_markup));
}

View File

@ -532,6 +532,8 @@ class MessagesManager : public Actor {
vector<DialogId> get_common_dialogs(UserId user_id, DialogId offset_dialog_id, int32 limit, bool force,
Promise<Unit> &&promise);
bool can_get_message_statistics(FullMessageId full_message_id);
bool have_message_force(FullMessageId full_message_id, const char *source);
void get_message(FullMessageId full_message_id, Promise<Unit> &&promise);
@ -1765,6 +1767,8 @@ class MessagesManager : public Actor {
static bool can_forward_message(DialogId from_dialog_id, const Message *m);
bool can_get_message_statistics(DialogId dialog_id, const Message *m) const;
static bool can_delete_channel_message(DialogParticipantStatus status, const Message *m, bool is_bot);
bool can_delete_message(DialogId dialog_id, const Message *m) const;

View File

@ -6728,7 +6728,14 @@ void Td::on_request(uint64 id, const td_api::getChatStatistics &request) {
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) {
void Td::on_request(uint64 id, const td_api::getMessageStatistics &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
contacts_manager_->get_channel_message_statistics({DialogId(request.chat_id_), MessageId(request.message_id_)},
request.is_dark_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::getStatisticsGraph &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
CLEAN_INPUT_STRING(request.token_);

View File

@ -915,7 +915,9 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, const td_api::getChatStatistics &request);
void on_request(uint64 id, td_api::getChatStatisticsGraph &request);
void on_request(uint64 id, const td_api::getMessageStatistics &request);
void on_request(uint64 id, td_api::getStatisticsGraph &request);
void on_request(uint64 id, const td_api::getMapThumbnailFile &request);

View File

@ -4017,15 +4017,23 @@ class CliClient final : public Actor {
std::tie(chat_id, is_dark) = split(args);
send_request(td_api::make_object<td_api::getChatStatistics>(as_chat_id(chat_id), as_bool(is_dark)));
} else if (op == "gcstg") {
} else if (op == "gmst") {
string chat_id;
string message_id;
string is_dark;
std::tie(chat_id, args) = split(args);
std::tie(message_id, is_dark) = split(args);
send_request(td_api::make_object<td_api::getMessageStatistics>(as_chat_id(chat_id), as_message_id(message_id),
as_bool(is_dark)));
} else if (op == "gstg") {
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<td_api::getChatStatisticsGraph>(as_chat_id(chat_id), token, to_integer<int64>(x)));
send_request(td_api::make_object<td_api::getStatisticsGraph>(as_chat_id(chat_id), token, to_integer<int64>(x)));
} else if (op == "hsa" || op == "glu" || op == "glua") {
send_request(td_api::make_object<td_api::hideSuggestedAction>(as_suggested_action(args)));
} else if (op == "glui" || op == "glu" || op == "glua") {