Add td_api::sendBusinessMessage.

This commit is contained in:
levlam 2024-03-11 21:27:16 +03:00
parent e717e25a1d
commit 0f98e748a8
7 changed files with 270 additions and 4 deletions

View File

@ -7818,6 +7818,17 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup =
editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok;
//@description Sends a business message; for bots only. Returns the message after it was sent
//@business_connection_id Unique identifier of business connection on behalf of which to send the request
//@chat_id Target chat
//@reply_to Information about the message; pass null if none
//@disable_notification Pass true to disable notification for the message
//@protect_content Pass true if the content of the message must be protected from forwarding and saving; for bots only
//@reply_markup Markup for replying to the message; pass null if none; for bots only
//@input_message_content The content of the message to be sent
sendBusinessMessage business_connection_id:string chat_id:int53 reply_to:InputMessageReplyTo disable_notification:Bool protect_content:Bool reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message;
//@description Checks validness of a name for a quick reply shortcut. Can be called synchronously @name The name of the shortcut; 1-32 characters
checkQuickReplyShortcutName name:string = Ok;

View File

@ -10,7 +10,14 @@
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageCopyOptions.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/MessageSelfDestructType.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/ReplyMarkup.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -18,6 +25,7 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Random.h"
namespace td {
@ -83,6 +91,95 @@ struct BusinessConnectionManager::BusinessConnection {
}
};
struct BusinessConnectionManager::PendingMessage {
BusinessConnectionId business_connection_id_;
DialogId dialog_id_;
MessageInputReplyTo input_reply_to_;
string send_emoji_;
MessageSelfDestructType ttl_;
unique_ptr<MessageContent> content_;
unique_ptr<ReplyMarkup> reply_markup_;
int64 media_album_id_ = 0;
int64 random_id_ = 0;
bool noforwards_ = false;
bool disable_notification_ = false;
bool invert_media_ = false;
bool disable_web_page_preview_ = false;
};
class BusinessConnectionManager::SendBusinessMessageQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::message>> promise_;
unique_ptr<PendingMessage> message_;
public:
explicit SendBusinessMessageQuery(Promise<td_api::object_ptr<td_api::message>> &&promise)
: promise_(std::move(promise)) {
}
void send(unique_ptr<PendingMessage> message) {
message_ = std::move(message);
int32 flags = 0;
if (message_->disable_web_page_preview_) {
flags |= telegram_api::messages_sendMessage::NO_WEBPAGE_MASK;
}
if (message_->disable_notification_) {
flags |= telegram_api::messages_sendMessage::SILENT_MASK;
}
if (message_->noforwards_) {
flags |= telegram_api::messages_sendMessage::NOFORWARDS_MASK;
}
if (message_->invert_media_) {
flags |= telegram_api::messages_sendMessage::INVERT_MEDIA_MASK;
}
auto input_peer = td_->dialog_manager_->get_input_peer_force(message_->dialog_id_);
CHECK(input_peer != nullptr);
auto reply_to = message_->input_reply_to_.get_input_reply_to(td_, MessageId());
if (reply_to != nullptr) {
flags |= telegram_api::messages_sendMessage::REPLY_TO_MASK;
}
const FormattedText *message_text = get_message_content_text(message_->content_.get());
CHECK(message_text != nullptr);
auto entities = get_input_message_entities(td_->contacts_manager_.get(), message_text, "SendBusinessMessageQuery");
if (!entities.empty()) {
flags |= telegram_api::messages_sendMessage::ENTITIES_MASK;
}
if (message_->reply_markup_ != nullptr) {
flags |= telegram_api::messages_sendMessage::REPLY_MARKUP_MASK;
}
send_query(G()->net_query_creator().create_with_prefix(
message_->business_connection_id_.get_invoke_prefix(),
telegram_api::messages_sendMessage(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(reply_to), message_text->text,
message_->random_id_, get_input_reply_markup(td_->contacts_manager_.get(), message_->reply_markup_),
std::move(entities), 0, nullptr, nullptr),
td_->business_connection_manager_->get_business_connection_dc_id(message_->business_connection_id_),
{{message_->dialog_id_}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_sendMessage>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for SendBusinessMessageQuery: " << to_string(ptr);
promise_.set_value(nullptr); // TODO
}
void on_error(Status status) final {
LOG(INFO) << "Receive error for SendBusinessMessageQuery: " << status;
promise_.set_error(std::move(status));
}
};
BusinessConnectionManager::BusinessConnectionManager(Td *td, ActorShared<> parent)
: td_(td), parent_(std::move(parent)) {
}
@ -259,4 +356,119 @@ void BusinessConnectionManager::on_get_business_connection(
}
}
MessageInputReplyTo BusinessConnectionManager::create_business_message_input_reply_to(
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to) {
if (reply_to == nullptr) {
return {};
}
switch (reply_to->get_id()) {
case td_api::inputMessageReplyToStory::ID:
return {};
case td_api::inputMessageReplyToMessage::ID: {
auto reply_to_message = td_api::move_object_as<td_api::inputMessageReplyToMessage>(reply_to);
auto message_id = MessageId(reply_to_message->message_id_);
if (!message_id.is_valid() || !message_id.is_server()) {
return {};
}
if (reply_to_message->chat_id_ != 0) {
return {};
}
FormattedText quote;
int32 quote_position = 0;
if (reply_to_message->quote_ != nullptr) {
int32 ltrim_count = 0;
auto r_quote = get_formatted_text(td_, td_->dialog_manager_->get_my_dialog_id(),
std::move(reply_to_message->quote_->text_), td_->auth_manager_->is_bot(),
true, true, false, &ltrim_count);
if (r_quote.is_ok() && !r_quote.ok().text.empty()) {
quote = r_quote.move_as_ok();
quote_position = reply_to_message->quote_->position_;
if (0 <= quote_position && quote_position <= 1000000) { // some unreasonably big bound
quote_position += ltrim_count;
} else {
quote_position = 0;
}
}
}
return MessageInputReplyTo{message_id, DialogId(), std::move(quote), quote_position};
}
default:
UNREACHABLE();
return {};
}
}
Result<InputMessageContent> BusinessConnectionManager::process_input_message_content(
DialogId dialog_id, td_api::object_ptr<td_api::InputMessageContent> &&input_message_content) {
if (input_message_content == nullptr) {
return Status::Error(400, "Can't send message without content");
}
if (input_message_content->get_id() == td_api::inputMessageForwarded::ID) {
return Status::Error(400, "Can't forward messages as business");
}
return get_input_message_content(dialog_id, std::move(input_message_content), td_, true);
}
unique_ptr<BusinessConnectionManager::PendingMessage> BusinessConnectionManager::create_business_message_to_send(
BusinessConnectionId business_connection_id, DialogId dialog_id, MessageInputReplyTo &&input_reply_to,
bool disable_notification, bool protect_content, unique_ptr<ReplyMarkup> &&reply_markup,
InputMessageContent &&input_content) const {
auto content = dup_message_content(td_, td_->dialog_manager_->get_my_dialog_id(), input_content.content.get(),
MessageContentDupType::Send, MessageCopyOptions());
auto message = make_unique<PendingMessage>();
message->business_connection_id_ = business_connection_id;
message->dialog_id_ = dialog_id;
message->input_reply_to_ = std::move(input_reply_to);
message->noforwards_ = protect_content;
message->content_ = std::move(content);
message->reply_markup_ = std::move(reply_markup);
message->disable_notification_ = disable_notification;
message->invert_media_ = input_content.invert_media;
message->disable_web_page_preview_ = input_content.disable_web_page_preview;
message->ttl_ = input_content.ttl;
message->send_emoji_ = std::move(input_content.emoji);
message->random_id_ = Random::secure_int64();
return message;
}
void BusinessConnectionManager::send_message(BusinessConnectionId business_connection_id, DialogId dialog_id,
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to,
bool disable_notification, bool protect_content,
td_api::object_ptr<td_api::ReplyMarkup> &&reply_markup,
td_api::object_ptr<td_api::InputMessageContent> &&input_message_content,
Promise<td_api::object_ptr<td_api::message>> &&promise) {
TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id, dialog_id));
TRY_RESULT_PROMISE(promise, input_content,
process_input_message_content(dialog_id, std::move(input_message_content)));
auto input_reply_to = create_business_message_input_reply_to(std::move(reply_to));
TRY_RESULT_PROMISE(promise, message_reply_markup,
get_reply_markup(std::move(reply_markup), DialogType::User, td_->auth_manager_->is_bot(), false));
auto message = create_business_message_to_send(std::move(business_connection_id), dialog_id,
std::move(input_reply_to), disable_notification, protect_content,
std::move(message_reply_markup), std::move(input_content));
do_send_message(std::move(message), std::move(promise));
}
void BusinessConnectionManager::do_send_message(unique_ptr<PendingMessage> &&message,
Promise<td_api::object_ptr<td_api::message>> &&promise) {
LOG(INFO) << "Send business message to " << message->dialog_id_;
auto content = message->content_.get();
CHECK(content != nullptr);
auto content_type = content->get_type();
if (content_type == MessageContentType::Text) {
auto input_media = get_message_content_input_media_web_page(td_, content);
if (input_media == nullptr) {
td_->create_handler<SendBusinessMessageQuery>(std::move(promise))->send(std::move(message));
} else {
promise.set_error(Status::Error(400, "Unsupported"));
}
return;
}
promise.set_error(Status::Error(400, "Unsupported"));
}
} // namespace td

View File

@ -8,6 +8,7 @@
#include "td/telegram/BusinessConnectionId.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/MessageInputReplyTo.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -21,6 +22,8 @@
namespace td {
struct InputMessageContent;
struct ReplyMarkup;
class Td;
class BusinessConnectionManager final : public Actor {
@ -50,14 +53,36 @@ class BusinessConnectionManager final : public Actor {
void get_business_connection(const BusinessConnectionId &connection_id,
Promise<td_api::object_ptr<td_api::businessConnection>> &&promise);
void send_message(BusinessConnectionId business_connection_id, DialogId dialog_id,
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to, bool disable_notification,
bool protect_content, td_api::object_ptr<td_api::ReplyMarkup> &&reply_markup,
td_api::object_ptr<td_api::InputMessageContent> &&input_message_content,
Promise<td_api::object_ptr<td_api::message>> &&promise);
private:
struct BusinessConnection;
struct PendingMessage;
class SendBusinessMessageQuery;
void tear_down() final;
void on_get_business_connection(const BusinessConnectionId &connection_id,
Result<telegram_api::object_ptr<telegram_api::Updates>> r_updates);
MessageInputReplyTo create_business_message_input_reply_to(
td_api::object_ptr<td_api::InputMessageReplyTo> &&reply_to);
Result<InputMessageContent> process_input_message_content(
DialogId dialog_id, td_api::object_ptr<td_api::InputMessageContent> &&input_message_content);
unique_ptr<PendingMessage> create_business_message_to_send(BusinessConnectionId business_connection_id,
DialogId dialog_id, MessageInputReplyTo &&input_reply_to,
bool disable_notification, bool protect_content,
unique_ptr<ReplyMarkup> &&reply_markup,
InputMessageContent &&input_content) const;
void do_send_message(unique_ptr<PendingMessage> &&message, Promise<td_api::object_ptr<td_api::message>> &&promise);
WaitFreeHashMap<BusinessConnectionId, unique_ptr<BusinessConnection>, BusinessConnectionIdHash> business_connections_;
FlatHashMap<BusinessConnectionId, vector<Promise<td_api::object_ptr<td_api::businessConnection>>>,

View File

@ -24486,15 +24486,16 @@ void MessagesManager::on_text_message_ready_to_send(DialogId dialog_id, MessageI
get_message_flags(m), dialog_id, get_send_message_as_input_peer(m), *get_message_input_reply_to(m),
m->initial_top_thread_message_id, get_message_schedule_date(m),
get_input_reply_markup(td_->contacts_manager_.get(), m->reply_markup),
get_input_message_entities(td_->contacts_manager_.get(), message_text, "do_send_message"), message_text->text,
m->is_copy, random_id, &m->send_query_ref);
get_input_message_entities(td_->contacts_manager_.get(), message_text, "on_text_message_ready_to_send"),
message_text->text, m->is_copy, random_id, &m->send_query_ref);
} else {
td_->create_handler<SendMediaQuery>()->send(
FileId(), FileId(), get_message_flags(m), dialog_id, get_send_message_as_input_peer(m),
*get_message_input_reply_to(m), m->initial_top_thread_message_id, get_message_schedule_date(m),
get_input_reply_markup(td_->contacts_manager_.get(), m->reply_markup),
get_input_message_entities(td_->contacts_manager_.get(), message_text, "do_send_message"), message_text->text,
std::move(input_media), MessageContentType::Text, m->is_copy, random_id, &m->send_query_ref);
get_input_message_entities(td_->contacts_manager_.get(), message_text, "on_text_message_ready_to_send"),
message_text->text, std::move(input_media), MessageContentType::Text, m->is_copy, random_id,
&m->send_query_ref);
}
}
}

View File

@ -5789,6 +5789,15 @@ void Td::on_request(uint64 id, td_api::editMessageSchedulingState &request) {
std::move(request.scheduling_state_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::sendBusinessMessage &request) {
CHECK_IS_BOT();
CREATE_REQUEST_PROMISE();
business_connection_manager_->send_message(
BusinessConnectionId(std::move(request.business_connection_id_)), DialogId(request.chat_id_),
std::move(request.reply_to_), request.disable_notification_, request.protect_content_,
std::move(request.reply_markup_), std::move(request.input_message_content_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::loadQuickReplyShortcuts &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();

View File

@ -876,6 +876,8 @@ class Td final : public Actor {
void on_request(uint64 id, td_api::editMessageSchedulingState &request);
void on_request(uint64 id, td_api::sendBusinessMessage &request);
void on_request(uint64 id, const td_api::loadQuickReplyShortcuts &request);
void on_request(uint64 id, const td_api::setQuickReplyShortcutName &request);

View File

@ -2343,6 +2343,12 @@ class CliClient final : public Actor {
void send_message(int64 chat_id, td_api::object_ptr<td_api::InputMessageContent> &&input_message_content,
bool disable_notification = false, bool from_background = false) {
if (!business_connection_id_.empty()) {
send_request(td_api::make_object<td_api::sendBusinessMessage>(
business_connection_id_, chat_id, get_input_message_reply_to(), disable_notification, rand_bool(), nullptr,
std::move(input_message_content)));
return;
}
auto id = send_request(td_api::make_object<td_api::sendMessage>(
chat_id, message_thread_id_, get_input_message_reply_to(),
td_api::make_object<td_api::messageSendOptions>(disable_notification, from_background, true, true,