tdlight/td/telegram/SponsoredMessageManager.cpp

369 lines
16 KiB
C++
Raw Normal View History

2021-08-24 22:44:47 +02:00
//
2022-12-31 22:28:08 +01:00
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
2021-08-24 22:44:47 +02:00
//
// 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)
//
2021-09-07 14:49:34 +02:00
#include "td/telegram/SponsoredMessageManager.h"
2021-08-24 22:44:47 +02:00
2021-09-01 19:31:39 +02:00
#include "td/telegram/ChannelId.h"
2021-08-24 22:44:47 +02:00
#include "td/telegram/ContactsManager.h"
2021-08-25 21:34:18 +02:00
#include "td/telegram/Global.h"
#include "td/telegram/LinkManager.h"
2021-08-24 22:44:47 +02:00
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessagesManager.h"
2021-08-25 21:34:18 +02:00
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/ServerMessageId.h"
2021-08-24 22:44:47 +02:00
#include "td/telegram/Td.h"
2021-09-01 19:31:39 +02:00
#include "td/telegram/telegram_api.h"
2022-10-12 14:59:58 +02:00
#include "td/telegram/UserId.h"
2021-08-24 22:44:47 +02:00
2021-09-07 16:21:18 +02:00
#include "td/utils/algorithm.h"
2021-09-01 19:31:39 +02:00
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
2021-11-04 10:46:08 +01:00
#include "td/utils/SliceBuilder.h"
2021-09-01 19:31:39 +02:00
#include "td/utils/Status.h"
2021-08-25 21:34:18 +02:00
2021-08-24 22:44:47 +02:00
namespace td {
class GetSponsoredMessagesQuery final : public Td::ResultHandler {
2022-10-07 17:04:00 +02:00
Promise<telegram_api::object_ptr<telegram_api::messages_SponsoredMessages>> promise_;
2021-08-24 22:44:47 +02:00
ChannelId channel_id_;
public:
2021-09-07 16:21:18 +02:00
explicit GetSponsoredMessagesQuery(
2022-10-07 17:04:00 +02:00
Promise<telegram_api::object_ptr<telegram_api::messages_SponsoredMessages>> &&promise)
2021-08-24 22:44:47 +02:00
: promise_(std::move(promise)) {
}
void send(ChannelId channel_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
2021-08-24 22:44:47 +02:00
if (input_channel == nullptr) {
return promise_.set_error(Status::Error(400, "Chat info not found"));
2021-08-24 22:44:47 +02:00
}
send_query(G()->net_query_creator().create(telegram_api::channels_getSponsoredMessages(std::move(input_channel))));
}
void on_result(BufferSlice packet) final {
2021-08-24 22:44:47 +02:00
auto result_ptr = fetch_result<telegram_api::channels_getSponsoredMessages>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
2021-08-24 22:44:47 +02:00
}
2021-09-07 16:21:18 +02:00
promise_.set_value(result_ptr.move_as_ok());
2021-08-24 22:44:47 +02:00
}
void on_error(Status status) final {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "GetSponsoredMessagesQuery");
2021-08-24 22:44:47 +02:00
promise_.set_error(std::move(status));
}
};
2021-08-25 21:34:18 +02:00
class ViewSponsoredMessageQuery final : public Td::ResultHandler {
ChannelId channel_id_;
public:
void send(ChannelId channel_id, const string &message_id) {
channel_id_ = channel_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
2021-08-25 21:34:18 +02:00
if (input_channel == nullptr) {
return;
2021-08-25 21:34:18 +02:00
}
send_query(G()->net_query_creator().create(
telegram_api::channels_viewSponsoredMessage(std::move(input_channel), BufferSlice(message_id))));
}
void on_result(BufferSlice packet) final {
2021-08-25 21:34:18 +02:00
auto result_ptr = fetch_result<telegram_api::channels_viewSponsoredMessage>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
2021-08-25 21:34:18 +02:00
}
}
void on_error(Status status) final {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "ViewSponsoredMessageQuery");
2021-08-25 21:34:18 +02:00
}
};
2021-09-07 16:21:18 +02:00
struct SponsoredMessageManager::SponsoredMessage {
int64 local_id = 0;
bool is_recommended = false;
bool show_dialog_photo = false;
2021-09-07 16:21:18 +02:00
DialogId sponsor_dialog_id;
ServerMessageId server_message_id;
2021-09-07 16:21:18 +02:00
string start_param;
string invite_hash;
2021-09-07 16:21:18 +02:00
unique_ptr<MessageContent> content;
SponsoredMessage(int64 local_id, bool is_recommended, bool show_dialog_photo, DialogId sponsor_dialog_id,
ServerMessageId server_message_id, string start_param, string invite_hash,
unique_ptr<MessageContent> content)
2021-09-10 10:44:50 +02:00
: local_id(local_id)
, is_recommended(is_recommended)
, show_dialog_photo(show_dialog_photo)
2021-09-07 16:21:18 +02:00
, sponsor_dialog_id(sponsor_dialog_id)
, server_message_id(server_message_id)
2021-09-07 16:21:18 +02:00
, start_param(std::move(start_param))
, invite_hash(std::move(invite_hash))
2021-09-07 16:21:18 +02:00
, content(std::move(content)) {
}
};
struct SponsoredMessageManager::DialogSponsoredMessages {
2022-10-21 14:04:56 +02:00
vector<Promise<td_api::object_ptr<td_api::sponsoredMessages>>> promises;
2021-09-07 16:21:18 +02:00
vector<SponsoredMessage> messages;
FlatHashMap<int64, string> message_random_ids;
2022-10-21 14:04:56 +02:00
int32 messages_between = 0;
bool is_premium = false;
2021-09-07 16:21:18 +02:00
};
2021-09-07 14:49:34 +02:00
SponsoredMessageManager::SponsoredMessageManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
2021-09-07 19:27:04 +02:00
delete_cached_sponsored_messages_timeout_.set_callback(on_delete_cached_sponsored_messages_timeout_callback);
delete_cached_sponsored_messages_timeout_.set_callback_data(static_cast<void *>(this));
2021-09-07 14:49:34 +02:00
}
SponsoredMessageManager::~SponsoredMessageManager() = default;
void SponsoredMessageManager::tear_down() {
parent_.reset();
}
2021-09-07 19:27:04 +02:00
void SponsoredMessageManager::on_delete_cached_sponsored_messages_timeout_callback(void *sponsored_message_manager_ptr,
int64 dialog_id_int) {
if (G()->close_flag()) {
return;
}
auto sponsored_message_manager = static_cast<SponsoredMessageManager *>(sponsored_message_manager_ptr);
send_closure_later(sponsored_message_manager->actor_id(sponsored_message_manager),
&SponsoredMessageManager::delete_cached_sponsored_messages, DialogId(dialog_id_int));
}
void SponsoredMessageManager::delete_cached_sponsored_messages(DialogId dialog_id) {
if (G()->close_flag()) {
return;
}
auto it = dialog_sponsored_messages_.find(dialog_id);
if (it != dialog_sponsored_messages_.end() && it->second->promises.empty()) {
dialog_sponsored_messages_.erase(it);
}
2021-09-07 19:27:04 +02:00
}
2021-09-07 16:21:18 +02:00
td_api::object_ptr<td_api::sponsoredMessage> SponsoredMessageManager::get_sponsored_message_object(
DialogId dialog_id, const SponsoredMessage &sponsored_message) const {
td_api::object_ptr<td_api::chatInviteLinkInfo> chat_invite_link_info;
2021-10-08 14:00:13 +02:00
td_api::object_ptr<td_api::InternalLinkType> link;
switch (sponsored_message.sponsor_dialog_id.get_type()) {
case DialogType::User: {
auto user_id = sponsored_message.sponsor_dialog_id.get_user_id();
if (!td_->contacts_manager_->is_user_bot(user_id)) {
break;
}
2022-10-12 20:04:18 +02:00
auto bot_username = td_->contacts_manager_->get_user_first_username(user_id);
2021-10-08 14:00:13 +02:00
if (bot_username.empty()) {
break;
}
link = td_api::make_object<td_api::internalLinkTypeBotStart>(bot_username, sponsored_message.start_param, false);
2021-10-08 14:00:13 +02:00
break;
}
2021-11-27 11:36:17 +01:00
case DialogType::Channel:
if (sponsored_message.server_message_id.is_valid()) {
auto channel_id = sponsored_message.sponsor_dialog_id.get_channel_id();
auto t_me = td_->option_manager_->get_option_string("t_me_url", "https://t.me/");
2021-11-27 11:36:17 +01:00
link = td_api::make_object<td_api::internalLinkTypeMessage>(
PSTRING() << t_me << "c/" << channel_id.get() << '/' << sponsored_message.server_message_id.get());
}
break;
case DialogType::None: {
CHECK(!sponsored_message.invite_hash.empty());
auto invite_link = LinkManager::get_dialog_invite_link(sponsored_message.invite_hash, false);
chat_invite_link_info = td_->contacts_manager_->get_chat_invite_link_info_object(invite_link);
if (chat_invite_link_info == nullptr) {
LOG(ERROR) << "Failed to get invite link info for " << invite_link;
return nullptr;
}
link = td_api::make_object<td_api::internalLinkTypeChatInvite>(
LinkManager::get_dialog_invite_link(sponsored_message.invite_hash, true));
}
2021-10-08 14:00:13 +02:00
default:
break;
}
2021-09-07 16:21:18 +02:00
return td_api::make_object<td_api::sponsoredMessage>(
sponsored_message.local_id, sponsored_message.is_recommended, sponsored_message.sponsor_dialog_id.get(),
std::move(chat_invite_link_info), sponsored_message.show_dialog_photo, std::move(link),
get_message_content_object(sponsored_message.content.get(), td_, dialog_id, 0, false, true, -1));
2021-09-07 16:21:18 +02:00
}
2022-10-21 14:04:56 +02:00
td_api::object_ptr<td_api::sponsoredMessages> SponsoredMessageManager::get_sponsored_messages_object(
2021-09-07 16:21:18 +02:00
DialogId dialog_id, const DialogSponsoredMessages &sponsored_messages) const {
2022-10-21 14:04:56 +02:00
auto messages = transform(sponsored_messages.messages, [this, dialog_id](const SponsoredMessage &message) {
return get_sponsored_message_object(dialog_id, message);
});
return td_api::make_object<td_api::sponsoredMessages>(std::move(messages), sponsored_messages.messages_between);
2021-09-07 16:21:18 +02:00
}
2022-10-21 14:04:56 +02:00
void SponsoredMessageManager::get_dialog_sponsored_messages(
DialogId dialog_id, Promise<td_api::object_ptr<td_api::sponsoredMessages>> &&promise) {
if (!td_->messages_manager_->have_dialog_force(dialog_id, "get_dialog_sponsored_message")) {
2021-08-24 22:44:47 +02:00
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (dialog_id.get_type() != DialogType::Channel) {
2022-10-21 14:04:56 +02:00
return promise.set_value(td_api::make_object<td_api::sponsoredMessages>());
2021-08-24 22:44:47 +02:00
}
2021-09-07 16:21:18 +02:00
auto &messages = dialog_sponsored_messages_[dialog_id];
2021-09-07 19:27:04 +02:00
if (messages != nullptr && messages->promises.empty()) {
if (messages->is_premium == td_->option_manager_->get_option_boolean("is_premium", false)) {
// use cached value
return promise.set_value(get_sponsored_messages_object(dialog_id, *messages));
} else {
// drop cache
messages = nullptr;
delete_cached_sponsored_messages_timeout_.cancel_timeout(dialog_id.get());
}
2021-09-07 19:27:04 +02:00
}
2021-09-07 16:21:18 +02:00
if (messages == nullptr) {
messages = make_unique<DialogSponsoredMessages>();
}
messages->promises.push_back(std::move(promise));
if (messages->promises.size() == 1) {
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this),
2022-10-07 17:04:00 +02:00
dialog_id](Result<telegram_api::object_ptr<telegram_api::messages_SponsoredMessages>> &&result) mutable {
2021-09-07 16:21:18 +02:00
send_closure(actor_id, &SponsoredMessageManager::on_get_dialog_sponsored_messages, dialog_id,
std::move(result));
});
td_->create_handler<GetSponsoredMessagesQuery>(std::move(query_promise))->send(dialog_id.get_channel_id());
}
}
void SponsoredMessageManager::on_get_dialog_sponsored_messages(
2022-10-07 17:04:00 +02:00
DialogId dialog_id, Result<telegram_api::object_ptr<telegram_api::messages_SponsoredMessages>> &&result) {
2023-02-16 11:35:27 +01:00
G()->ignore_result_if_closing(result);
2021-09-07 16:21:18 +02:00
auto &messages = dialog_sponsored_messages_[dialog_id];
CHECK(messages != nullptr);
auto promises = std::move(messages->promises);
reset_to_empty(messages->promises);
2021-09-10 10:44:50 +02:00
CHECK(messages->messages.empty());
CHECK(messages->message_random_ids.empty());
2021-09-07 16:21:18 +02:00
if (result.is_error()) {
2021-09-07 19:27:04 +02:00
dialog_sponsored_messages_.erase(dialog_id);
fail_promises(promises, result.move_as_error());
2021-09-07 16:21:18 +02:00
return;
}
2022-10-07 17:04:00 +02:00
auto sponsored_messages_ptr = result.move_as_ok();
switch (sponsored_messages_ptr->get_id()) {
case telegram_api::messages_sponsoredMessages::ID: {
auto sponsored_messages =
telegram_api::move_object_as<telegram_api::messages_sponsoredMessages>(sponsored_messages_ptr);
td_->contacts_manager_->on_get_users(std::move(sponsored_messages->users_), "on_get_dialog_sponsored_messages");
td_->contacts_manager_->on_get_chats(std::move(sponsored_messages->chats_), "on_get_dialog_sponsored_messages");
for (auto &sponsored_message : sponsored_messages->messages_) {
DialogId sponsor_dialog_id;
ServerMessageId server_message_id;
string invite_hash;
if (sponsored_message->from_id_ != nullptr) {
sponsor_dialog_id = DialogId(sponsored_message->from_id_);
if (!sponsor_dialog_id.is_valid() || !td_->messages_manager_->have_dialog_info_force(sponsor_dialog_id)) {
LOG(ERROR) << "Receive unknown sponsor " << sponsor_dialog_id;
continue;
}
server_message_id = ServerMessageId(sponsored_message->channel_post_);
if (!server_message_id.is_valid() && server_message_id != ServerMessageId()) {
LOG(ERROR) << "Receive invalid channel post in " << to_string(sponsored_message);
server_message_id = ServerMessageId();
}
td_->messages_manager_->force_create_dialog(sponsor_dialog_id, "on_get_dialog_sponsored_messages");
} else if (sponsored_message->chat_invite_ != nullptr && !sponsored_message->chat_invite_hash_.empty()) {
auto invite_link = LinkManager::get_dialog_invite_link(sponsored_message->chat_invite_hash_, false);
if (invite_link.empty()) {
LOG(ERROR) << "Receive invalid invite link hash in " << to_string(sponsored_message);
continue;
}
auto chat_invite = to_string(sponsored_message->chat_invite_);
td_->contacts_manager_->on_get_dialog_invite_link_info(
invite_link, std::move(sponsored_message->chat_invite_), Promise<Unit>());
auto chat_invite_link_info = td_->contacts_manager_->get_chat_invite_link_info_object(invite_link);
if (chat_invite_link_info == nullptr) {
LOG(ERROR) << "Failed to get invite link info from " << chat_invite << " for "
<< to_string(sponsored_message);
continue;
}
invite_hash = std::move(sponsored_message->chat_invite_hash_);
} else {
LOG(ERROR) << "Receive " << to_string(sponsored_message);
continue;
}
auto message_text = get_message_text(td_->contacts_manager_.get(), std::move(sponsored_message->message_),
std::move(sponsored_message->entities_), true, true, 0, false,
"on_get_dialog_sponsored_messages");
int32 ttl = 0;
bool disable_web_page_preview = false;
auto content = get_message_content(td_, std::move(message_text), nullptr, sponsor_dialog_id, true, UserId(),
&ttl, &disable_web_page_preview, "on_get_dialog_sponsored_messages");
if (ttl != 0) {
LOG(ERROR) << "Receive sponsored message with self-destruct time " << ttl;
2022-10-07 17:04:00 +02:00
continue;
}
CHECK(disable_web_page_preview);
current_sponsored_message_id_ = current_sponsored_message_id_.get_next_message_id(MessageType::Local);
if (!current_sponsored_message_id_.is_valid_sponsored()) {
LOG(ERROR) << "Sponsored message ID overflowed";
current_sponsored_message_id_ = MessageId::max().get_next_message_id(MessageType::Local);
CHECK(current_sponsored_message_id_.is_valid_sponsored());
}
auto local_id = current_sponsored_message_id_.get();
CHECK(!current_sponsored_message_id_.is_valid());
CHECK(!current_sponsored_message_id_.is_scheduled());
auto is_inserted =
messages->message_random_ids.emplace(local_id, sponsored_message->random_id_.as_slice().str()).second;
CHECK(is_inserted);
messages->messages.emplace_back(
local_id, sponsored_message->recommended_, sponsored_message->show_peer_photo_, sponsor_dialog_id,
server_message_id, std::move(sponsored_message->start_param_), std::move(invite_hash), std::move(content));
}
2022-10-21 14:04:56 +02:00
messages->messages_between = sponsored_messages->posts_between_;
break;
2021-12-28 15:19:18 +01:00
}
2022-10-07 17:04:00 +02:00
case telegram_api::messages_sponsoredMessagesEmpty::ID:
break;
default:
UNREACHABLE();
2021-09-07 16:21:18 +02:00
}
messages->is_premium = td_->option_manager_->get_option_boolean("is_premium", false);
2021-09-07 16:21:18 +02:00
for (auto &promise : promises) {
2022-10-21 14:04:56 +02:00
promise.set_value(get_sponsored_messages_object(dialog_id, *messages));
2021-09-07 16:21:18 +02:00
}
2021-09-07 19:27:04 +02:00
delete_cached_sponsored_messages_timeout_.set_timeout_in(dialog_id.get(), 300.0);
2021-08-24 22:44:47 +02:00
}
void SponsoredMessageManager::view_sponsored_message(DialogId dialog_id, MessageId sponsored_message_id) {
2021-09-10 10:44:50 +02:00
auto it = dialog_sponsored_messages_.find(dialog_id);
if (it == dialog_sponsored_messages_.end()) {
return;
2021-09-10 10:44:50 +02:00
}
auto random_id_it = it->second->message_random_ids.find(sponsored_message_id.get());
2021-09-10 10:44:50 +02:00
if (random_id_it == it->second->message_random_ids.end()) {
return;
2021-08-25 21:34:18 +02:00
}
2021-09-10 10:44:50 +02:00
auto random_id = std::move(random_id_it->second);
it->second->message_random_ids.erase(random_id_it);
td_->create_handler<ViewSponsoredMessageQuery>()->send(dialog_id.get_channel_id(), random_id);
2021-08-25 21:34:18 +02:00
}
2021-08-24 22:44:47 +02:00
} // namespace td