// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026 // // 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 "td/telegram/TranslationManager.h" #include "td/telegram/AccessRights.h" #include "td/telegram/AiComposeTone.hpp" #include "td/telegram/AiComposeToneExample.h" #include "td/telegram/AuthManager.h" #include "td/telegram/Dependencies.h" #include "td/telegram/DialogManager.h" #include "td/telegram/DiffText.h" #include "td/telegram/FileReferenceManager.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserManager.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/logging.h" #include "td/utils/Status.h" namespace td { class TranslateTextQuery final : public Td::ResultHandler { Promise>> promise_; public: explicit TranslateTextQuery(Promise>> &&promise) : promise_(std::move(promise)) { } void send(vector &&texts, MessageFullId message_full_id, const string &to_language_code, const string &tone) { int32 flags = 0; if (!tone.empty()) { flags |= telegram_api::messages_translateText::TONE_MASK; } if (message_full_id.get_message_id().is_valid()) { CHECK(texts.size() == 1u); flags |= telegram_api::messages_translateText::PEER_MASK; auto input_peer = td_->dialog_manager_->get_input_peer(message_full_id.get_dialog_id(), AccessRights::Read); CHECK(input_peer != nullptr); auto message_ids = {message_full_id.get_message_id().get_server_message_id().get()}; send_query(G()->net_query_creator().create(telegram_api::messages_translateText( flags, std::move(input_peer), std::move(message_ids), Auto(), to_language_code, tone))); } else { flags |= telegram_api::messages_translateText::TEXT_MASK; auto input_texts = transform(std::move(texts), [user_manager = td_->user_manager_.get()](FormattedText &&text) { return get_input_text_with_entities(user_manager, std::move(text), "TranslateTextQuery"); }); send_query(G()->net_query_creator().create(telegram_api::messages_translateText( flags, nullptr, vector{}, std::move(input_texts), to_language_code, tone))); } } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 TranslateTextQuery: " << to_string(ptr); promise_.set_value(std::move(ptr->result_)); } void on_error(Status status) final { if (status.message() == "INPUT_TEXT_EMPTY") { vector> result; result.push_back(telegram_api::make_object(string(), Auto())); return promise_.set_value(std::move(result)); } promise_.set_error(std::move(status)); } }; class TranslateRichMessageQuery final : public Td::ResultHandler { Promise>> promise_; vector file_ids_; vector file_references_; vector cover_file_ids_; vector cover_file_references_; RichMessage rich_message_; string to_language_code_; string tone_; public: explicit TranslateRichMessageQuery(Promise>> &&promise) : promise_(std::move(promise)) { } void send(RichMessage &&rich_message, MessageFullId message_full_id, const string &to_language_code, string tone) { int32 flags = 0; if (!tone.empty()) { flags |= telegram_api::messages_translateRichMessage::TONE_MASK; } if (message_full_id.get_message_id().is_valid()) { flags |= telegram_api::messages_translateRichMessage::PEER_MASK; auto input_peer = td_->dialog_manager_->get_input_peer(message_full_id.get_dialog_id(), AccessRights::Read); CHECK(input_peer != nullptr); auto message_ids = {message_full_id.get_message_id().get_server_message_id().get()}; send_query(G()->net_query_creator().create(telegram_api::messages_translateRichMessage( flags, std::move(input_peer), std::move(message_ids), Auto(), to_language_code, tone))); } else { auto input_rich_message = rich_message.get_input_rich_message(td_); if (input_rich_message == nullptr) { return on_error(Status::Error(400, "Invalid rich message specified")); } file_ids_ = rich_message.get_any_file_ids(); cover_file_ids_ = rich_message.get_cover_any_file_ids(); file_references_ = FileManager::extract_file_references(input_rich_message); cover_file_references_ = FileManager::extract_cover_file_references(input_rich_message); rich_message_ = std::move(rich_message); to_language_code_ = to_language_code; tone_ = tone; vector> input_rich_messages; input_rich_messages.push_back(std::move(input_rich_message)); flags |= telegram_api::messages_translateRichMessage::TEXT_MASK; send_query(G()->net_query_creator().create(telegram_api::messages_translateRichMessage( flags, nullptr, vector{}, std::move(input_rich_messages), to_language_code_, tone_))); } } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 TranslateRichMessageQuery: " << to_string(ptr); promise_.set_value(std::move(ptr->result_)); } void on_error(Status status) final { if (status.message() == "INPUT_TEXT_EMPTY") { vector> result; result.push_back(telegram_api::make_object()); return promise_.set_value(std::move(result)); } if (td_->file_reference_manager_->process_file_reference_error( status, file_ids_, file_references_, cover_file_ids_, cover_file_references_, true, [&](size_t pos, FileId file_id) mutable { td_->file_reference_manager_->repair_file_reference( file_id, PromiseCreator::lambda([rich_message = std::move(rich_message_), to_language_code = std::move(to_language_code_), tone = std::move(tone_), promise = std::move(promise_)](Result result) mutable { if (result.is_error()) { return promise.set_error(400, "Failed to find the rich message"); } send_closure(G()->translation_manager(), &TranslationManager::do_translate_rich_message, std::move(rich_message), MessageFullId(), to_language_code, tone, std::move(promise)); })); })) { return; } promise_.set_error(std::move(status)); } }; class GetAiComposeTonesQuery final : public Td::ResultHandler { Promise promise_; public: explicit GetAiComposeTonesQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(int64 hash) { send_query(G()->net_query_creator().create(telegram_api::aicompose_getTones(hash))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 GetAiComposeTonesQuery: " << to_string(ptr); td_->translation_manager_->on_get_ai_compose_tones(std::move(ptr)); promise_.set_value(Unit()); } void on_error(Status status) final { LOG(INFO) << "Receive error for GetAiComposeTonesQuery: " << status; promise_.set_value(Unit()); // ignore the error } }; class CreateToneQuery final : public Td::ResultHandler { Promise> promise_; public: explicit CreateToneQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator) { send_query(G()->net_query_creator().create( telegram_api::aicompose_createTone(0, show_creator, custom_emoji_id.get(), title, prompt))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 CreateToneQuery: " << to_string(ptr); td_->translation_manager_->reload_ai_compose_tones(PromiseCreator::lambda( [promise = std::move(promise_), style = AiComposeTone(std::move(ptr)).get_text_composition_style_object(td_)]( Unit) mutable { promise.set_value(std::move(style)); })); } void on_error(Status status) final { LOG(INFO) << "Receive error for CreateToneQuery: " << status; promise_.set_error(std::move(status)); } }; class UpdateToneQuery final : public Td::ResultHandler { Promise> promise_; public: explicit UpdateToneQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr &&input_tone, const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator) { int32 flags = telegram_api::aicompose_updateTone::DISPLAY_AUTHOR_MASK | telegram_api::aicompose_updateTone::EMOJI_ID_MASK | telegram_api::aicompose_updateTone::TITLE_MASK | telegram_api::aicompose_updateTone::PROMPT_MASK; send_query(G()->net_query_creator().create(telegram_api::aicompose_updateTone( flags, std::move(input_tone), show_creator, custom_emoji_id.get(), title, prompt))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 UpdateToneQuery: " << to_string(ptr); td_->translation_manager_->reload_ai_compose_tones(PromiseCreator::lambda( [promise = std::move(promise_), style = AiComposeTone(std::move(ptr)).get_text_composition_style_object(td_)]( Unit) mutable { promise.set_value(std::move(style)); })); } void on_error(Status status) final { LOG(INFO) << "Receive error for UpdateToneQuery: " << status; promise_.set_error(std::move(status)); } }; class DeleteToneQuery final : public Td::ResultHandler { Promise promise_; public: explicit DeleteToneQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr &&input_tone) { send_query(G()->net_query_creator().create(telegram_api::aicompose_deleteTone(std::move(input_tone)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } td_->translation_manager_->reload_ai_compose_tones(std::move(promise_)); } void on_error(Status status) final { LOG(INFO) << "Receive error for DeleteToneQuery: " << status; promise_.set_error(std::move(status)); } }; class GetToneQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetToneQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr &&input_tone) { send_query(G()->net_query_creator().create(telegram_api::aicompose_getTone(std::move(input_tone)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 GetToneQuery: " << to_string(ptr); switch (ptr->get_id()) { case telegram_api::aicompose_tonesNotModified::ID: break; case telegram_api::aicompose_tones::ID: { auto update = AiComposeTones(td_, telegram_api::move_object_as(ptr)) .get_update_text_composition_styles_object(td_); if (update->styles_.size() == 1u) { return promise_.set_value(std::move(update->styles_[0])); } break; } default: UNREACHABLE(); } LOG(ERROR) << "Receive " << to_string(ptr); promise_.set_value(nullptr); } void on_error(Status status) final { LOG(INFO) << "Receive error for GetToneQuery: " << status; promise_.set_error(std::move(status)); } }; class GetToneExampleQuery final : public Td::ResultHandler { Promise> promise_; public: explicit GetToneExampleQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr &&input_tone, int32 num) { send_query(G()->net_query_creator().create(telegram_api::aicompose_getToneExample(std::move(input_tone), num))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 GetToneExampleQuery: " << to_string(ptr); promise_.set_value(AiComposeToneExample(std::move(ptr)).get_text_composition_style_example_object()); } void on_error(Status status) final { LOG(INFO) << "Receive error for GetToneExampleQuery: " << status; promise_.set_error(std::move(status)); } }; class SaveToneQuery final : public Td::ResultHandler { Promise promise_; public: explicit SaveToneQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(telegram_api::object_ptr &&input_tone, bool unsave) { send_query(G()->net_query_creator().create(telegram_api::aicompose_saveTone(std::move(input_tone), unsave))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } td_->translation_manager_->reload_ai_compose_tones(std::move(promise_)); } void on_error(Status status) final { LOG(INFO) << "Receive error for SaveToneQuery: " << status; promise_.set_error(std::move(status)); } }; class ComposeMessageWithAiQuery final : public Td::ResultHandler { Promise> promise_; bool skip_bot_commands_; int32 max_media_timestamp_; public: explicit ComposeMessageWithAiQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const TranslationManager::InputText &text, const string &translate_to_language_code, telegram_api::object_ptr &&input_tone, bool emojify) { skip_bot_commands_ = text.skip_bot_commands_; max_media_timestamp_ = text.max_media_timestamp_; int32 flags = 0; if (!translate_to_language_code.empty()) { flags |= telegram_api::messages_composeMessageWithAI::TRANSLATE_TO_LANG_MASK; } if (input_tone != nullptr) { flags |= telegram_api::messages_composeMessageWithAI::TONE_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_composeMessageWithAI( flags, false, emojify, get_input_text_with_entities(td_->user_manager_.get(), text.text_, "ComposeMessageWithAiQuery"), translate_to_language_code, std::move(input_tone)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 ComposeMessageWithAiQuery: " << to_string(ptr); auto formatted_text = get_formatted_text(td_->user_manager_.get(), std::move(ptr->result_text_), max_media_timestamp_ == -1, true, "ComposeMessageWithAiQuery"); promise_.set_value( get_formatted_text_object(td_->user_manager_.get(), formatted_text, skip_bot_commands_, max_media_timestamp_)); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; class ComposeRichMessageWithAiQuery final : public Td::ResultHandler { Promise> promise_; vector file_ids_; vector file_references_; vector cover_file_ids_; vector cover_file_references_; TranslationManager::InputRichMessage input_rich_message_; string translate_to_language_code_; bool emojify_ = false; bool proofread_ = false; telegram_api::object_ptr input_tone_; public: explicit ComposeRichMessageWithAiQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(bool has_message, TranslationManager::InputRichMessage &&message, const string &translate_to_language_code, telegram_api::object_ptr &&input_tone, bool emojify, bool proofread) { input_rich_message_ = std::move(message); int32 flags = 0; if (!translate_to_language_code.empty()) { flags |= telegram_api::messages_composeMessageWithAI::TRANSLATE_TO_LANG_MASK; } if (input_tone != nullptr) { flags |= telegram_api::messages_composeMessageWithAI::TONE_MASK; } telegram_api::object_ptr input_rich_message; if (has_message) { flags |= telegram_api::messages_composeRichMessageWithAI::TEXT_MASK; input_rich_message = input_rich_message_.message_.get_input_rich_message(td_); if (input_rich_message == nullptr) { return on_error(Status::Error(400, "Invalid rich message specified")); } file_ids_ = input_rich_message_.message_.get_any_file_ids(); cover_file_ids_ = input_rich_message_.message_.get_cover_any_file_ids(); file_references_ = FileManager::extract_file_references(input_rich_message); cover_file_references_ = FileManager::extract_cover_file_references(input_rich_message); translate_to_language_code_ = translate_to_language_code; input_tone_ = TranslationManager::clone_input_ai_compose_tone(input_tone); emojify_ = emojify; proofread_ = proofread; } send_query(G()->net_query_creator().create(telegram_api::messages_composeRichMessageWithAI( flags, proofread, emojify, std::move(input_rich_message), translate_to_language_code, std::move(input_tone)))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 ComposeRichMessageWithAiQuery: " << to_string(ptr); auto rich_message = RichMessage(td_, std::move(ptr->result_), DialogId()); promise_.set_value(rich_message.get_rich_message_object(td_, input_rich_message_.skip_bot_commands_)); } void on_error(Status status) final { if (td_->file_reference_manager_->process_file_reference_error( status, file_ids_, file_references_, cover_file_ids_, cover_file_references_, true, [&](size_t pos, FileId file_id) mutable { td_->file_reference_manager_->repair_file_reference( file_id, PromiseCreator::lambda([input_rich_message = std::move(input_rich_message_), translate_to_language_code = std::move(translate_to_language_code_), input_tone = std::move(input_tone_), emojify = emojify_, proofread = proofread_, promise = std::move(promise_)](Result result) mutable { if (result.is_error()) { return promise.set_error(400, "Failed to find the rich message"); } send_closure(G()->translation_manager(), &TranslationManager::do_compose_rich_message_with_ai, true, std::move(input_rich_message), translate_to_language_code, std::move(input_tone), emojify, proofread, std::move(promise)); })); })) { return; } promise_.set_error(std::move(status)); } }; class ProofreadMessageWithAiQuery final : public Td::ResultHandler { Promise> promise_; bool skip_bot_commands_; int32 max_media_timestamp_; public: explicit ProofreadMessageWithAiQuery(Promise> &&promise) : promise_(std::move(promise)) { } void send(const TranslationManager::InputText &text) { skip_bot_commands_ = text.skip_bot_commands_; max_media_timestamp_ = text.max_media_timestamp_; send_query(G()->net_query_creator().create(telegram_api::messages_composeMessageWithAI( 0, true, false, get_input_text_with_entities(td_->user_manager_.get(), text.text_, "ProofreadMessageWithAiQuery"), string(), nullptr))); } void on_result(BufferSlice packet) final { auto result_ptr = fetch_result(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 ProofreadMessageWithAiQuery: " << to_string(ptr); auto formatted_text = get_formatted_text(td_->user_manager_.get(), std::move(ptr->result_text_), max_media_timestamp_ == -1, true, "ProofreadMessageWithAiQuery"); auto diff_text = DiffText(std::move(ptr->diff_text_)); promise_.set_value(td_api::make_object( get_formatted_text_object(td_->user_manager_.get(), formatted_text, skip_bot_commands_, max_media_timestamp_), diff_text.get_diff_text_object())); } void on_error(Status status) final { promise_.set_error(std::move(status)); } }; TranslationManager::TranslationManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { } void TranslationManager::start_up() { if (td_->auth_manager_->is_authorized() && !td_->auth_manager_->is_bot()) { auto ai_compose_tones_log_event_string = G()->td_db()->get_binlog_pmc()->get(get_ai_compose_tones_key()); if (!ai_compose_tones_log_event_string.empty()) { if (log_event_parse(ai_compose_tones_, ai_compose_tones_log_event_string).is_error()) { ai_compose_tones_ = {}; } else { Dependencies dependencies; ai_compose_tones_.add_dependencies(dependencies); if (!dependencies.resolve_force(td_, "AiComposeTones")) { ai_compose_tones_ = {}; } } } send_update_text_composition_styles(); if (ai_compose_tones_ == AiComposeTones()) { reload_ai_compose_tones(Auto()); } } } void TranslationManager::tear_down() { parent_.reset(); } void TranslationManager::on_authorization_success() { if (!td_->auth_manager_->is_bot()) { send_update_text_composition_styles(); reload_ai_compose_tones(Auto()); } } Status TranslationManager::check_tone(string &tone) { if (tone == "neutral") { tone.clear(); } else if (!tone.empty() && tone != "formal" && tone != "casual") { return Status::Error(400, "Invalid tone specified"); } return Status::OK(); } telegram_api::object_ptr TranslationManager::clone_input_ai_compose_tone( const telegram_api::object_ptr &input_tone) { if (input_tone == nullptr) { return nullptr; } switch (input_tone->get_id()) { case telegram_api::inputAiComposeToneDefault::ID: { const auto *tone = static_cast(input_tone.get()); return telegram_api::make_object(tone->tone_); } case telegram_api::inputAiComposeToneID::ID: { const auto *tone = static_cast(input_tone.get()); return telegram_api::make_object(tone->id_, tone->access_hash_); } case telegram_api::inputAiComposeToneSlug::ID: { const auto *tone = static_cast(input_tone.get()); return telegram_api::make_object(tone->slug_); } case telegram_api::inputAiComposeToneSingleUse::ID: { const auto *tone = static_cast(input_tone.get()); return telegram_api::make_object(tone->custom_prompt_); } default: UNREACHABLE(); return nullptr; } } Result TranslationManager::get_input_text( td_api::object_ptr &&text) const { if (text == nullptr) { return Status::Error(400, "Text must be non-empty"); } InputText input_text; for (const auto &entity : text->entities_) { if (entity == nullptr || entity->type_ == nullptr) { continue; } switch (entity->type_->get_id()) { case td_api::textEntityTypeBotCommand::ID: input_text.skip_bot_commands_ = false; break; case td_api::textEntityTypeMediaTimestamp::ID: input_text.max_media_timestamp_ = td::max(input_text.max_media_timestamp_, static_cast(entity->type_.get())->media_timestamp_); break; default: // nothing to do break; } } TRY_RESULT(entities, get_message_entities(td_->user_manager_.get(), std::move(text->entities_))); TRY_STATUS(fix_formatted_text(text->text_, entities, true, true, true, true, true, true)); input_text.text_ = FormattedText{std::move(text->text_), std::move(entities)}; return std::move(input_text); } void TranslationManager::translate_text(td_api::object_ptr &&text, const string &to_language_code, string tone, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_text, get_input_text(std::move(text))); translate_text(std::move(input_text), MessageFullId(), to_language_code, std::move(tone), std::move(promise)); } void TranslationManager::translate_text(InputText &&text, MessageFullId message_full_id, const string &to_language_code, string tone, Promise> &&promise) { TRY_STATUS_PROMISE(promise, check_tone(tone)); vector texts; texts.push_back(std::move(text.text_)); auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this), skip_bot_commands = text.skip_bot_commands_, max_media_timestamp = text.max_media_timestamp_, promise = std::move(promise)]( Result>> result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &TranslationManager::on_get_translated_texts, result.move_as_ok(), skip_bot_commands, max_media_timestamp, std::move(promise)); }); td_->create_handler(std::move(query_promise)) ->send(std::move(texts), message_full_id, to_language_code, tone); } void TranslationManager::on_get_translated_texts(vector> texts, bool skip_bot_commands, int32 max_media_timestamp, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (texts.size() != 1u) { if (texts.empty()) { return promise.set_error(500, "Translation failed"); } return promise.set_error(500, "Receive invalid number of results"); } auto formatted_text = get_formatted_text(td_->user_manager_.get(), std::move(texts[0]), max_media_timestamp == -1, true, "on_get_translated_texts"); promise.set_value( get_formatted_text_object(td_->user_manager_.get(), formatted_text, skip_bot_commands, max_media_timestamp)); } Result TranslationManager::get_input_rich_message( td_api::object_ptr &&message) const { TRY_RESULT(rich_message, RichMessage::get_rich_message(td_, DialogId(), std::move(message), false)); InputRichMessage input_rich_message; input_rich_message.skip_bot_commands_ = !rich_message.has_bot_commands(); input_rich_message.message_ = std::move(rich_message); return std::move(input_rich_message); } void TranslationManager::translate_rich_message(td_api::object_ptr &&message, const string &to_language_code, string tone, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_rich_message, get_input_rich_message(std::move(message))); translate_rich_message(std::move(input_rich_message), MessageFullId(), to_language_code, std::move(tone), std::move(promise)); } void TranslationManager::translate_rich_message(InputRichMessage &&input_rich_message, MessageFullId message_full_id, const string &to_language_code, string tone, Promise> &&promise) { TRY_STATUS_PROMISE(promise, check_tone(tone)); auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), skip_bot_commands = input_rich_message.skip_bot_commands_, promise = std::move(promise)]( Result>> result) mutable { if (result.is_error()) { return promise.set_error(result.move_as_error()); } send_closure(actor_id, &TranslationManager::on_get_translated_rich_messages, result.move_as_ok(), skip_bot_commands, std::move(promise)); }); do_translate_rich_message(std::move(input_rich_message.message_), message_full_id, to_language_code, std::move(tone), std::move(query_promise)); } void TranslationManager::do_translate_rich_message( RichMessage &&rich_message, MessageFullId message_full_id, const string &to_language_code, string tone, Promise>> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); td_->create_handler(std::move(promise)) ->send(std::move(rich_message), message_full_id, to_language_code, tone); } void TranslationManager::on_get_translated_rich_messages( vector> rich_messages, bool skip_bot_commands, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); if (rich_messages.size() != 1u) { if (rich_messages.empty()) { return promise.set_error(500, "Translation failed"); } return promise.set_error(500, "Receive invalid number of results"); } auto rich_message = RichMessage(td_, std::move(rich_messages[0]), DialogId()); promise.set_value(rich_message.get_rich_message_object(td_, skip_bot_commands)); } void TranslationManager::compose_message_with_ai(td_api::object_ptr &&text, const string &translate_to_language_code, string tone, bool emojify, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_text, get_input_text(std::move(text))); TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(tone)); td_->create_handler(std::move(promise)) ->send(input_text, translate_to_language_code, std::move(input_tone), emojify); } void TranslationManager::proofread_message_with_ai(td_api::object_ptr &&text, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_text, get_input_text(std::move(text))); td_->create_handler(std::move(promise))->send(input_text); } void TranslationManager::do_compose_rich_message_with_ai( bool has_message, TranslationManager::InputRichMessage &&message, const string &translate_to_language_code, telegram_api::object_ptr &&input_tone, bool emojify, bool proofread, Promise> &&promise) { td_->create_handler(std::move(promise)) ->send(has_message, std::move(message), translate_to_language_code, std::move(input_tone), emojify, proofread); } void TranslationManager::compose_rich_message_with_ai(td_api::object_ptr &&message, const string &translate_to_language_code, string tone, const string &custom_prompt, bool emojify, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_rich_message, get_input_rich_message(std::move(message))); telegram_api::object_ptr input_tone; if (custom_prompt.empty()) { TRY_RESULT_PROMISE_ASSIGN(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(tone)); } else { input_tone = telegram_api::make_object(custom_prompt); } do_compose_rich_message_with_ai(true, std::move(input_rich_message), translate_to_language_code, std::move(input_tone), emojify, false, std::move(promise)); } void TranslationManager::create_rich_message_with_ai(const string &prompt, const string &language_code, bool emojify, Promise> &&promise) { do_compose_rich_message_with_ai(false, InputRichMessage(), language_code, telegram_api::make_object(prompt), emojify, false, std::move(promise)); } void TranslationManager::proofread_rich_message_with_ai(td_api::object_ptr &&message, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_rich_message, get_input_rich_message(std::move(message))); do_compose_rich_message_with_ai(true, std::move(input_rich_message), string(), nullptr, false, true, std::move(promise)); } string TranslationManager::get_ai_compose_tones_key() { return "ai_compose_styles"; } void TranslationManager::reload_ai_compose_tones(Promise &&promise) { if (td_->auth_manager_->is_authorized() && !td_->auth_manager_->is_bot()) { td_->create_handler(std::move(promise))->send(ai_compose_tones_.get_hash()); } } void TranslationManager::on_get_ai_compose_tones(telegram_api::object_ptr &&tones_ptr) { CHECK(tones_ptr != nullptr); if (!td_->auth_manager_->is_authorized()) { return; } switch (tones_ptr->get_id()) { case telegram_api::aicompose_tonesNotModified::ID: break; case telegram_api::aicompose_tones::ID: { auto ai_compose_tones = AiComposeTones(td_, telegram_api::move_object_as(tones_ptr)); if (ai_compose_tones == ai_compose_tones_) { break; } ai_compose_tones_ = std::move(ai_compose_tones); G()->td_db()->get_binlog_pmc()->set(get_ai_compose_tones_key(), log_event_store(ai_compose_tones_).as_slice().str()); send_update_text_composition_styles(); break; } default: UNREACHABLE(); } } void TranslationManager::create_tone(const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator, Promise> &&promise) { td_->user_manager_->get_me([actor_id = actor_id(this), title, custom_emoji_id, prompt, show_creator, promise = std::move(promise)](Unit) mutable { send_closure(actor_id, &TranslationManager::do_create_tone, title, custom_emoji_id, prompt, show_creator, std::move(promise)); }); } void TranslationManager::do_create_tone(const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); td_->create_handler(std::move(promise))->send(title, custom_emoji_id, prompt, show_creator); } void TranslationManager::update_tone(const string &name, const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator, Promise> &&promise) { td_->user_manager_->get_me([actor_id = actor_id(this), name, title, custom_emoji_id, prompt, show_creator, promise = std::move(promise)](Unit) mutable { send_closure(actor_id, &TranslationManager::do_update_tone, name, title, custom_emoji_id, prompt, show_creator, std::move(promise)); }); } void TranslationManager::do_update_tone(const string &name, const string &title, CustomEmojiId custom_emoji_id, const string &prompt, bool show_creator, Promise> &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise)) ->send(std::move(input_tone), title, custom_emoji_id, prompt, show_creator); } void TranslationManager::delete_tone(const string &name, Promise &&promise) { TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise))->send(std::move(input_tone)); } void TranslationManager::search_tone(const string &name, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise))->send(std::move(input_tone)); } void TranslationManager::get_tone_example(const string &name, int32 num, Promise> &&promise) { TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise))->send(std::move(input_tone), num); } void TranslationManager::add_tone(const string &name, Promise &&promise) { TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise))->send(std::move(input_tone), false); } void TranslationManager::remove_tone(const string &name, Promise &&promise) { TRY_RESULT_PROMISE(promise, input_tone, ai_compose_tones_.get_input_ai_compose_tone(name)); td_->create_handler(std::move(promise))->send(std::move(input_tone), true); } void TranslationManager::send_update_text_composition_styles() const { send_closure(G()->td(), &Td::send_update, ai_compose_tones_.get_update_text_composition_styles_object(td_)); } void TranslationManager::get_current_state(vector> &updates) const { if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) { return; } updates.push_back(ai_compose_tones_.get_update_text_composition_styles_object(td_)); } } // namespace td