diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index 9aacf8719..1c78cd13f 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -364,11 +364,10 @@ UserId InlineQueriesManager::get_inline_bot_user_id(int64 query_id) const { return it->second; } -void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_personal, - vector> &&input_results, - int32 cache_time, const string &next_offset, - const string &switch_pm_text, const string &switch_pm_parameter, - Promise &&promise) const { +void InlineQueriesManager::answer_inline_query( + int64 inline_query_id, bool is_personal, vector> &&input_results, + int32 cache_time, const string &next_offset, const string &switch_pm_text, const string &switch_pm_parameter, + Promise &&promise) const { if (!td_->auth_manager_->is_bot()) { return promise.set_error(Status::Error(400, "Method can be used by bots only")); } @@ -390,404 +389,8 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe bool is_gallery = false; bool force_vertical = false; for (auto &input_result : input_results) { - if (input_result == nullptr) { - return promise.set_error(Status::Error(400, "Inline query result must be non-empty")); - } - - string id; - string url; - string type; - string title; - string description; - string thumbnail_url; - string thumbnail_type = "image/jpeg"; - string content_url; - string content_type; - int32 thumbnail_width = 0; - int32 thumbnail_height = 0; - int32 width = 0; - int32 height = 0; - int32 duration = 0; - - FileType file_type = FileType::Temp; - Result> r_inline_message = Status::Error(500, "Uninited"); - switch (input_result->get_id()) { - case td_api::inputInlineQueryResultAnimation::ID: { - auto animation = move_tl_object_as(input_result); - type = "gif"; - id = std::move(animation->id_); - title = std::move(animation->title_); - thumbnail_url = std::move(animation->thumbnail_url_); - if (!animation->thumbnail_mime_type_.empty()) { - thumbnail_type = std::move(animation->thumbnail_mime_type_); - } - content_url = std::move(animation->video_url_); - content_type = std::move(animation->video_mime_type_); - if (content_type != "image/gif" && content_type != "video/mp4") { - return promise.set_error(Status::Error(400, "Wrong animation MIME type specified")); - } - duration = animation->video_duration_; - width = animation->video_width_; - height = animation->video_height_; - is_gallery = true; - - file_type = FileType::Animation; - r_inline_message = get_inline_message(std::move(animation->input_message_content_), - std::move(animation->reply_markup_), td_api::inputMessageAnimation::ID); - break; - } - case td_api::inputInlineQueryResultArticle::ID: { - auto article = move_tl_object_as(input_result); - type = "article"; - id = std::move(article->id_); - if (!article->url_.empty()) { - content_url = std::move(article->url_); - content_type = "text/html"; - if (!article->hide_url_) { - url = content_url; - } - } - title = std::move(article->title_); - description = std::move(article->description_); - thumbnail_url = std::move(article->thumbnail_url_); - if (!thumbnail_url.empty()) { - thumbnail_width = article->thumbnail_width_; - thumbnail_height = article->thumbnail_height_; - } - force_vertical = true; - - r_inline_message = - get_inline_message(std::move(article->input_message_content_), std::move(article->reply_markup_), -1); - break; - } - case td_api::inputInlineQueryResultAudio::ID: { - auto audio = move_tl_object_as(input_result); - type = "audio"; - id = std::move(audio->id_); - title = std::move(audio->title_); - description = std::move(audio->performer_); - content_url = std::move(audio->audio_url_); - content_type = "audio/mpeg"; - duration = audio->audio_duration_; - force_vertical = true; - - file_type = FileType::Audio; - r_inline_message = get_inline_message(std::move(audio->input_message_content_), std::move(audio->reply_markup_), - td_api::inputMessageAudio::ID); - break; - } - case td_api::inputInlineQueryResultContact::ID: { - auto contact = move_tl_object_as(input_result); - if (contact->contact_ == nullptr) { - return promise.set_error(Status::Error(400, "Contact must be non-empty")); - } - type = "contact"; - id = std::move(contact->id_); - string phone_number = trim(contact->contact_->phone_number_); - string first_name = trim(contact->contact_->first_name_); - string last_name = trim(contact->contact_->last_name_); - if (phone_number.empty()) { - return promise.set_error(Status::Error(400, "Field \"phone_number\" must contain a valid phone number")); - } - if (first_name.empty()) { - return promise.set_error(Status::Error(400, "Field \"first_name\" must be non-empty")); - } - if (last_name.empty()) { - title = std::move(first_name); - } else { - title = PSTRING() << first_name << ' ' << last_name; - } - description = std::move(phone_number); - thumbnail_url = std::move(contact->thumbnail_url_); - if (!thumbnail_url.empty()) { - thumbnail_width = contact->thumbnail_width_; - thumbnail_height = contact->thumbnail_height_; - } - force_vertical = true; - - r_inline_message = - get_inline_message(std::move(contact->input_message_content_), std::move(contact->reply_markup_), -1); - break; - } - case td_api::inputInlineQueryResultDocument::ID: { - auto document = move_tl_object_as(input_result); - type = "file"; - id = std::move(document->id_); - title = std::move(document->title_); - description = std::move(document->description_); - thumbnail_url = std::move(document->thumbnail_url_); - content_url = std::move(document->document_url_); - content_type = std::move(document->mime_type_); - thumbnail_width = document->thumbnail_width_; - thumbnail_height = document->thumbnail_height_; - - if (content_url.find('.') != string::npos) { - if (begins_with(content_type, "application/pdf")) { - content_type = "application/pdf"; - } else if (begins_with(content_type, "application/zip")) { - content_type = "application/zip"; - } else { - return promise.set_error(Status::Error(400, "Unallowed document MIME type")); - } - } - - file_type = FileType::Document; - r_inline_message = get_inline_message(std::move(document->input_message_content_), - std::move(document->reply_markup_), td_api::inputMessageDocument::ID); - break; - } - case td_api::inputInlineQueryResultGame::ID: { - auto game = move_tl_object_as(input_result); - auto r_reply_markup = get_reply_markup(std::move(game->reply_markup_), true, true, false, true); - if (r_reply_markup.is_error()) { - return promise.set_error(r_reply_markup.move_as_error()); - } - - auto input_reply_markup = get_input_reply_markup(r_reply_markup.ok()); - int32 flags = 0; - if (input_reply_markup != nullptr) { - flags |= telegram_api::inputBotInlineMessageGame::REPLY_MARKUP_MASK; - } - auto result = make_tl_object( - game->id_, game->game_short_name_, - make_tl_object(flags, std::move(input_reply_markup))); - results.push_back(std::move(result)); - continue; - } - case td_api::inputInlineQueryResultLocation::ID: { - auto location = move_tl_object_as(input_result); - if (location->location_ == nullptr) { - return promise.set_error(Status::Error(400, "Location must be non-empty")); - } - type = "geo"; - id = std::move(location->id_); - title = std::move(location->title_); - description = PSTRING() << location->location_->latitude_ << ' ' << location->location_->longitude_; - thumbnail_url = std::move(location->thumbnail_url_); - // duration = location->live_period_; - if (!thumbnail_url.empty()) { - thumbnail_width = location->thumbnail_width_; - thumbnail_height = location->thumbnail_height_; - } - - r_inline_message = - get_inline_message(std::move(location->input_message_content_), std::move(location->reply_markup_), -1); - break; - } - case td_api::inputInlineQueryResultPhoto::ID: { - auto photo = move_tl_object_as(input_result); - type = "photo"; - id = std::move(photo->id_); - title = std::move(photo->title_); - description = std::move(photo->description_); - thumbnail_url = std::move(photo->thumbnail_url_); - content_url = std::move(photo->photo_url_); - content_type = "image/jpeg"; - width = photo->photo_width_; - height = photo->photo_height_; - is_gallery = true; - - file_type = FileType::Photo; - r_inline_message = get_inline_message(std::move(photo->input_message_content_), std::move(photo->reply_markup_), - td_api::inputMessagePhoto::ID); - break; - } - case td_api::inputInlineQueryResultSticker::ID: { - auto sticker = move_tl_object_as(input_result); - type = "sticker"; - id = std::move(sticker->id_); - thumbnail_url = std::move(sticker->thumbnail_url_); - content_url = std::move(sticker->sticker_url_); - content_type = - "image/webp"; // or "application/x-tgsticker"/"video/webm"; not used for previously uploaded files - width = sticker->sticker_width_; - height = sticker->sticker_height_; - is_gallery = true; - - if (content_url.find('.') != string::npos) { - return promise.set_error(Status::Error(400, "Wrong sticker_file_id specified")); - } - - file_type = FileType::Sticker; - r_inline_message = get_inline_message(std::move(sticker->input_message_content_), - std::move(sticker->reply_markup_), td_api::inputMessageSticker::ID); - break; - } - case td_api::inputInlineQueryResultVenue::ID: { - auto venue = move_tl_object_as(input_result); - if (venue->venue_ == nullptr) { - return promise.set_error(Status::Error(400, "Venue must be non-empty")); - } - type = "venue"; - id = std::move(venue->id_); - title = std::move(venue->venue_->title_); - description = std::move(venue->venue_->address_); - thumbnail_url = std::move(venue->thumbnail_url_); - if (!thumbnail_url.empty()) { - thumbnail_width = venue->thumbnail_width_; - thumbnail_height = venue->thumbnail_height_; - } - - r_inline_message = - get_inline_message(std::move(venue->input_message_content_), std::move(venue->reply_markup_), -1); - break; - } - case td_api::inputInlineQueryResultVideo::ID: { - auto video = move_tl_object_as(input_result); - type = "video"; - id = std::move(video->id_); - title = std::move(video->title_); - description = std::move(video->description_); - thumbnail_url = std::move(video->thumbnail_url_); - content_url = std::move(video->video_url_); - content_type = std::move(video->mime_type_); - width = video->video_width_; - height = video->video_height_; - duration = video->video_duration_; - - if (content_url.find('.') != string::npos) { - if (begins_with(content_type, "video/mp4")) { - content_type = "video/mp4"; - } else if (begins_with(content_type, "text/html")) { - content_type = "text/html"; - } else { - return promise.set_error(Status::Error(400, "Unallowed video MIME type")); - } - } - - file_type = FileType::Video; - r_inline_message = get_inline_message(std::move(video->input_message_content_), std::move(video->reply_markup_), - td_api::inputMessageVideo::ID); - break; - } - case td_api::inputInlineQueryResultVoiceNote::ID: { - auto voice_note = move_tl_object_as(input_result); - type = "voice"; - id = std::move(voice_note->id_); - title = std::move(voice_note->title_); - content_url = std::move(voice_note->voice_note_url_); - content_type = "audio/ogg"; - duration = voice_note->voice_note_duration_; - force_vertical = true; - - file_type = FileType::VoiceNote; - r_inline_message = get_inline_message(std::move(voice_note->input_message_content_), - std::move(voice_note->reply_markup_), td_api::inputMessageVoiceNote::ID); - break; - } - default: - UNREACHABLE(); - break; - } - if (r_inline_message.is_error()) { - return promise.set_error(r_inline_message.move_as_error()); - } - auto inline_message = r_inline_message.move_as_ok(); - if (inline_message->get_id() == telegram_api::inputBotInlineMessageMediaAuto::ID && file_type == FileType::Temp) { - return promise.set_error(Status::Error(400, "Sent message content must be explicitly specified")); - } - - if (duration < 0) { - duration = 0; - } - - int32 flags = 0; - if (!title.empty()) { - flags |= telegram_api::inputBotInlineResult::TITLE_MASK; - if (!clean_input_string(title)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - } - if (!description.empty()) { - flags |= telegram_api::inputBotInlineResult::DESCRIPTION_MASK; - if (!clean_input_string(description)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - } - - if (file_type != FileType::Temp && content_url.find('.') == string::npos) { - auto r_file_id = td_->file_manager_->get_input_file_id( - file_type, make_tl_object(content_url), DialogId(), false, false); - if (r_file_id.is_error()) { - return promise.set_error(Status::Error(400, r_file_id.error().message())); - } - auto file_id = r_file_id.ok(); - FileView file_view = td_->file_manager_->get_file_view(file_id); - CHECK(file_view.has_remote_location()); - if (file_view.is_encrypted()) { - return promise.set_error(Status::Error(400, "Can't send encrypted file")); - } - if (file_view.main_remote_location().is_web()) { - return promise.set_error(Status::Error(400, "Can't send web file")); - } - - if (file_type == FileType::Photo) { - auto result = make_tl_object( - id, type, file_view.main_remote_location().as_input_photo(), std::move(inline_message)); - results.push_back(std::move(result)); - continue; - } - - auto result = make_tl_object( - flags, id, type, title, description, file_view.main_remote_location().as_input_document(), - std::move(inline_message)); - results.push_back(std::move(result)); - continue; - } - - if (!url.empty()) { - flags |= telegram_api::inputBotInlineResult::URL_MASK; - if (!clean_input_string(url)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - } - tl_object_ptr thumbnail; - if (!thumbnail_url.empty()) { - flags |= telegram_api::inputBotInlineResult::THUMB_MASK; - if (!clean_input_string(thumbnail_url)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - vector> attributes; - if (thumbnail_width > 0 && thumbnail_height > 0) { - attributes.push_back( - make_tl_object(thumbnail_width, thumbnail_height)); - } - thumbnail = - make_tl_object(thumbnail_url, 0, thumbnail_type, std::move(attributes)); - } - tl_object_ptr content; - if (!content_url.empty() || !content_type.empty()) { - flags |= telegram_api::inputBotInlineResult::CONTENT_MASK; - if (!clean_input_string(content_url)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - if (!clean_input_string(content_type)) { - return promise.set_error(Status::Error(400, "Strings must be encoded in UTF-8")); - } - - vector> attributes; - if (width > 0 && height > 0) { - if ((duration > 0 || type == "video" || content_type == "video/mp4") && !begins_with(content_type, "image/")) { - attributes.push_back(make_tl_object( - 0, false /*ignored*/, false /*ignored*/, duration, width, height)); - } else { - attributes.push_back(make_tl_object(width, height)); - } - } else if (type == "audio") { - attributes.push_back(make_tl_object( - telegram_api::documentAttributeAudio::TITLE_MASK | telegram_api::documentAttributeAudio::PERFORMER_MASK, - false /*ignored*/, duration, title, description, BufferSlice())); - } else if (type == "voice") { - attributes.push_back(make_tl_object( - telegram_api::documentAttributeAudio::VOICE_MASK, false /*ignored*/, duration, "", "", BufferSlice())); - } - attributes.push_back(make_tl_object(get_url_file_name(content_url))); - - content = make_tl_object(content_url, 0, content_type, std::move(attributes)); - } - - auto result = make_tl_object( - flags, id, type, title, description, url, std::move(thumbnail), std::move(content), std::move(inline_message)); + TRY_RESULT_PROMISE(promise, result, + get_input_bot_inline_result(std::move(input_result), &is_gallery, &force_vertical)); results.push_back(std::move(result)); } @@ -796,6 +399,412 @@ void InlineQueriesManager::answer_inline_query(int64 inline_query_id, bool is_pe switch_pm_text, switch_pm_parameter); } +Result> InlineQueriesManager::get_input_bot_inline_result( + td_api::object_ptr &&result, bool *is_gallery, bool *force_vertical) const { + if (result == nullptr) { + return Status::Error(400, "Inline query result must be non-empty"); + } + + string id; + string url; + string type; + string title; + string description; + string thumbnail_url; + string thumbnail_type = "image/jpeg"; + string content_url; + string content_type; + int32 thumbnail_width = 0; + int32 thumbnail_height = 0; + int32 width = 0; + int32 height = 0; + int32 duration = 0; + + FileType file_type = FileType::Temp; + Result> r_inline_message = Status::Error(500, "Uninited"); + switch (result->get_id()) { + case td_api::inputInlineQueryResultAnimation::ID: { + auto animation = move_tl_object_as(result); + type = "gif"; + id = std::move(animation->id_); + title = std::move(animation->title_); + thumbnail_url = std::move(animation->thumbnail_url_); + if (!animation->thumbnail_mime_type_.empty()) { + thumbnail_type = std::move(animation->thumbnail_mime_type_); + } + content_url = std::move(animation->video_url_); + content_type = std::move(animation->video_mime_type_); + if (content_type != "image/gif" && content_type != "video/mp4") { + return Status::Error(400, "Wrong animation MIME type specified"); + } + duration = animation->video_duration_; + width = animation->video_width_; + height = animation->video_height_; + if (is_gallery != nullptr) { + *is_gallery = true; + } + + file_type = FileType::Animation; + r_inline_message = get_inline_message(std::move(animation->input_message_content_), + std::move(animation->reply_markup_), td_api::inputMessageAnimation::ID); + break; + } + case td_api::inputInlineQueryResultArticle::ID: { + auto article = move_tl_object_as(result); + type = "article"; + id = std::move(article->id_); + if (!article->url_.empty()) { + content_url = std::move(article->url_); + content_type = "text/html"; + if (!article->hide_url_) { + url = content_url; + } + } + title = std::move(article->title_); + description = std::move(article->description_); + thumbnail_url = std::move(article->thumbnail_url_); + if (!thumbnail_url.empty()) { + thumbnail_width = article->thumbnail_width_; + thumbnail_height = article->thumbnail_height_; + } + if (force_vertical != nullptr) { + *force_vertical = true; + } + + r_inline_message = + get_inline_message(std::move(article->input_message_content_), std::move(article->reply_markup_), -1); + break; + } + case td_api::inputInlineQueryResultAudio::ID: { + auto audio = move_tl_object_as(result); + type = "audio"; + id = std::move(audio->id_); + title = std::move(audio->title_); + description = std::move(audio->performer_); + content_url = std::move(audio->audio_url_); + content_type = "audio/mpeg"; + duration = audio->audio_duration_; + if (force_vertical != nullptr) { + *force_vertical = true; + } + + file_type = FileType::Audio; + r_inline_message = get_inline_message(std::move(audio->input_message_content_), std::move(audio->reply_markup_), + td_api::inputMessageAudio::ID); + break; + } + case td_api::inputInlineQueryResultContact::ID: { + auto contact = move_tl_object_as(result); + if (contact->contact_ == nullptr) { + return Status::Error(400, "Contact must be non-empty"); + } + type = "contact"; + id = std::move(contact->id_); + string phone_number = trim(contact->contact_->phone_number_); + string first_name = trim(contact->contact_->first_name_); + string last_name = trim(contact->contact_->last_name_); + if (phone_number.empty()) { + return Status::Error(400, "Field \"phone_number\" must contain a valid phone number"); + } + if (first_name.empty()) { + return Status::Error(400, "Field \"first_name\" must be non-empty"); + } + if (last_name.empty()) { + title = std::move(first_name); + } else { + title = PSTRING() << first_name << ' ' << last_name; + } + description = std::move(phone_number); + thumbnail_url = std::move(contact->thumbnail_url_); + if (!thumbnail_url.empty()) { + thumbnail_width = contact->thumbnail_width_; + thumbnail_height = contact->thumbnail_height_; + } + if (force_vertical != nullptr) { + *force_vertical = true; + } + + r_inline_message = + get_inline_message(std::move(contact->input_message_content_), std::move(contact->reply_markup_), -1); + break; + } + case td_api::inputInlineQueryResultDocument::ID: { + auto document = move_tl_object_as(result); + type = "file"; + id = std::move(document->id_); + title = std::move(document->title_); + description = std::move(document->description_); + thumbnail_url = std::move(document->thumbnail_url_); + content_url = std::move(document->document_url_); + content_type = std::move(document->mime_type_); + thumbnail_width = document->thumbnail_width_; + thumbnail_height = document->thumbnail_height_; + + if (content_url.find('.') != string::npos) { + if (begins_with(content_type, "application/pdf")) { + content_type = "application/pdf"; + } else if (begins_with(content_type, "application/zip")) { + content_type = "application/zip"; + } else { + return Status::Error(400, "Unallowed document MIME type"); + } + } + + file_type = FileType::Document; + r_inline_message = get_inline_message(std::move(document->input_message_content_), + std::move(document->reply_markup_), td_api::inputMessageDocument::ID); + break; + } + case td_api::inputInlineQueryResultGame::ID: { + auto game = move_tl_object_as(result); + auto r_reply_markup = get_reply_markup(std::move(game->reply_markup_), true, true, false, true); + if (r_reply_markup.is_error()) { + return r_reply_markup.move_as_error(); + } + + auto input_reply_markup = get_input_reply_markup(r_reply_markup.ok()); + int32 flags = 0; + if (input_reply_markup != nullptr) { + flags |= telegram_api::inputBotInlineMessageGame::REPLY_MARKUP_MASK; + } + return make_tl_object( + game->id_, game->game_short_name_, + make_tl_object(flags, std::move(input_reply_markup))); + } + case td_api::inputInlineQueryResultLocation::ID: { + auto location = move_tl_object_as(result); + if (location->location_ == nullptr) { + return Status::Error(400, "Location must be non-empty"); + } + type = "geo"; + id = std::move(location->id_); + title = std::move(location->title_); + description = PSTRING() << location->location_->latitude_ << ' ' << location->location_->longitude_; + thumbnail_url = std::move(location->thumbnail_url_); + // duration = location->live_period_; + if (!thumbnail_url.empty()) { + thumbnail_width = location->thumbnail_width_; + thumbnail_height = location->thumbnail_height_; + } + + r_inline_message = + get_inline_message(std::move(location->input_message_content_), std::move(location->reply_markup_), -1); + break; + } + case td_api::inputInlineQueryResultPhoto::ID: { + auto photo = move_tl_object_as(result); + type = "photo"; + id = std::move(photo->id_); + title = std::move(photo->title_); + description = std::move(photo->description_); + thumbnail_url = std::move(photo->thumbnail_url_); + content_url = std::move(photo->photo_url_); + content_type = "image/jpeg"; + width = photo->photo_width_; + height = photo->photo_height_; + if (is_gallery != nullptr) { + *is_gallery = true; + } + + file_type = FileType::Photo; + r_inline_message = get_inline_message(std::move(photo->input_message_content_), std::move(photo->reply_markup_), + td_api::inputMessagePhoto::ID); + break; + } + case td_api::inputInlineQueryResultSticker::ID: { + auto sticker = move_tl_object_as(result); + type = "sticker"; + id = std::move(sticker->id_); + thumbnail_url = std::move(sticker->thumbnail_url_); + content_url = std::move(sticker->sticker_url_); + content_type = "image/webp"; // or "application/x-tgsticker"/"video/webm"; not used for previously uploaded files + width = sticker->sticker_width_; + height = sticker->sticker_height_; + if (is_gallery != nullptr) { + *is_gallery = true; + } + if (content_url.find('.') != string::npos) { + return Status::Error(400, "Wrong sticker_file_id specified"); + } + + file_type = FileType::Sticker; + r_inline_message = get_inline_message(std::move(sticker->input_message_content_), + std::move(sticker->reply_markup_), td_api::inputMessageSticker::ID); + break; + } + case td_api::inputInlineQueryResultVenue::ID: { + auto venue = move_tl_object_as(result); + if (venue->venue_ == nullptr) { + return Status::Error(400, "Venue must be non-empty"); + } + type = "venue"; + id = std::move(venue->id_); + title = std::move(venue->venue_->title_); + description = std::move(venue->venue_->address_); + thumbnail_url = std::move(venue->thumbnail_url_); + if (!thumbnail_url.empty()) { + thumbnail_width = venue->thumbnail_width_; + thumbnail_height = venue->thumbnail_height_; + } + + r_inline_message = + get_inline_message(std::move(venue->input_message_content_), std::move(venue->reply_markup_), -1); + break; + } + case td_api::inputInlineQueryResultVideo::ID: { + auto video = move_tl_object_as(result); + type = "video"; + id = std::move(video->id_); + title = std::move(video->title_); + description = std::move(video->description_); + thumbnail_url = std::move(video->thumbnail_url_); + content_url = std::move(video->video_url_); + content_type = std::move(video->mime_type_); + width = video->video_width_; + height = video->video_height_; + duration = video->video_duration_; + + if (content_url.find('.') != string::npos) { + if (begins_with(content_type, "video/mp4")) { + content_type = "video/mp4"; + } else if (begins_with(content_type, "text/html")) { + content_type = "text/html"; + } else { + return Status::Error(400, "Unallowed video MIME type"); + } + } + + file_type = FileType::Video; + r_inline_message = get_inline_message(std::move(video->input_message_content_), std::move(video->reply_markup_), + td_api::inputMessageVideo::ID); + break; + } + case td_api::inputInlineQueryResultVoiceNote::ID: { + auto voice_note = move_tl_object_as(result); + type = "voice"; + id = std::move(voice_note->id_); + title = std::move(voice_note->title_); + content_url = std::move(voice_note->voice_note_url_); + content_type = "audio/ogg"; + duration = voice_note->voice_note_duration_; + if (force_vertical != nullptr) { + *force_vertical = true; + } + + file_type = FileType::VoiceNote; + r_inline_message = get_inline_message(std::move(voice_note->input_message_content_), + std::move(voice_note->reply_markup_), td_api::inputMessageVoiceNote::ID); + break; + } + default: + UNREACHABLE(); + break; + } + if (r_inline_message.is_error()) { + return r_inline_message.move_as_error(); + } + auto inline_message = r_inline_message.move_as_ok(); + if (inline_message->get_id() == telegram_api::inputBotInlineMessageMediaAuto::ID && file_type == FileType::Temp) { + return Status::Error(400, "Sent message content must be explicitly specified"); + } + + if (duration < 0) { + duration = 0; + } + + int32 flags = 0; + if (!title.empty()) { + flags |= telegram_api::inputBotInlineResult::TITLE_MASK; + if (!clean_input_string(title)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + } + if (!description.empty()) { + flags |= telegram_api::inputBotInlineResult::DESCRIPTION_MASK; + if (!clean_input_string(description)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + } + + if (file_type != FileType::Temp && content_url.find('.') == string::npos) { + auto r_file_id = td_->file_manager_->get_input_file_id( + file_type, make_tl_object(content_url), DialogId(), false, false); + if (r_file_id.is_error()) { + return Status::Error(400, r_file_id.error().message()); + } + auto file_id = r_file_id.ok(); + FileView file_view = td_->file_manager_->get_file_view(file_id); + CHECK(file_view.has_remote_location()); + if (file_view.is_encrypted()) { + return Status::Error(400, "Can't send encrypted file"); + } + if (file_view.main_remote_location().is_web()) { + return Status::Error(400, "Can't send web file"); + } + + if (file_type == FileType::Photo) { + return make_tl_object( + id, type, file_view.main_remote_location().as_input_photo(), std::move(inline_message)); + } + + return make_tl_object( + flags, id, type, title, description, file_view.main_remote_location().as_input_document(), + std::move(inline_message)); + } + + if (!url.empty()) { + flags |= telegram_api::inputBotInlineResult::URL_MASK; + if (!clean_input_string(url)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + } + tl_object_ptr thumbnail; + if (!thumbnail_url.empty()) { + flags |= telegram_api::inputBotInlineResult::THUMB_MASK; + if (!clean_input_string(thumbnail_url)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + vector> attributes; + if (thumbnail_width > 0 && thumbnail_height > 0) { + attributes.push_back(make_tl_object(thumbnail_width, thumbnail_height)); + } + thumbnail = make_tl_object(thumbnail_url, 0, thumbnail_type, std::move(attributes)); + } + tl_object_ptr content; + if (!content_url.empty() || !content_type.empty()) { + flags |= telegram_api::inputBotInlineResult::CONTENT_MASK; + if (!clean_input_string(content_url)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + if (!clean_input_string(content_type)) { + return Status::Error(400, "Strings must be encoded in UTF-8"); + } + + vector> attributes; + if (width > 0 && height > 0) { + if ((duration > 0 || type == "video" || content_type == "video/mp4") && !begins_with(content_type, "image/")) { + attributes.push_back(make_tl_object( + 0, false /*ignored*/, false /*ignored*/, duration, width, height)); + } else { + attributes.push_back(make_tl_object(width, height)); + } + } else if (type == "audio") { + attributes.push_back(make_tl_object( + telegram_api::documentAttributeAudio::TITLE_MASK | telegram_api::documentAttributeAudio::PERFORMER_MASK, + false /*ignored*/, duration, title, description, BufferSlice())); + } else if (type == "voice") { + attributes.push_back(make_tl_object( + telegram_api::documentAttributeAudio::VOICE_MASK, false /*ignored*/, duration, "", "", BufferSlice())); + } + attributes.push_back(make_tl_object(get_url_file_name(content_url))); + + content = make_tl_object(content_url, 0, content_type, std::move(attributes)); + } + + return make_tl_object( + flags, id, type, title, description, url, std::move(thumbnail), std::move(content), std::move(inline_message)); +} + uint64 InlineQueriesManager::send_inline_query(UserId bot_user_id, DialogId dialog_id, Location user_location, const string &query, const string &offset, Promise &&promise) { if (td_->auth_manager_->is_bot()) { diff --git a/td/telegram/InlineQueriesManager.h b/td/telegram/InlineQueriesManager.h index e819932cf..1361750e8 100644 --- a/td/telegram/InlineQueriesManager.h +++ b/td/telegram/InlineQueriesManager.h @@ -43,7 +43,7 @@ class InlineQueriesManager final : public Actor { void after_get_difference(); void answer_inline_query(int64 inline_query_id, bool is_personal, - vector> &&input_results, int32 cache_time, + vector> &&input_results, int32 cache_time, const string &next_offset, const string &switch_pm_text, const string &switch_pm_parameter, Promise &&promise) const; @@ -87,6 +87,9 @@ class InlineQueriesManager final : public Actor { static constexpr int32 BOT_INLINE_MEDIA_RESULT_FLAG_HAS_TITLE = 1 << 2; static constexpr int32 BOT_INLINE_MEDIA_RESULT_FLAG_HAS_DESCRIPTION = 1 << 3; + Result> get_input_bot_inline_result( + td_api::object_ptr &&result, bool *is_gallery, bool *force_vertical) const; + Result> get_inline_message( tl_object_ptr &&input_message_content, tl_object_ptr &&reply_markup_ptr, diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index 84866066a..c3808e8d2 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -13431,7 +13431,7 @@ void MessagesManager::on_message_ttl_expired_impl(Dialog *d, Message *m) { remove_message_notification_id(d, m, true, true); update_message_contains_unread_mention(d, m, false, "on_message_ttl_expired_impl"); remove_message_unread_reactions(d, m, "on_message_ttl_expired_impl"); - unregister_message_reply(d, m); + unregister_message_reply(d->dialog_id, m); m->noforwards = false; m->contains_mention = false; m->reply_to_message_id = MessageId(); @@ -16596,7 +16596,7 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanen ttl_period_unregister_message(d->dialog_id, m); delete_bot_command_message_id(d->dialog_id, m->message_id); unregister_message_content(td_, m->content.get(), {d->dialog_id, m->message_id}, "on_message_deleted"); - unregister_message_reply(d, m); + unregister_message_reply(d->dialog_id, m); if (m->notification_id.is_valid()) { delete_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id); } @@ -16643,7 +16643,7 @@ unique_ptr MessagesManager::do_delete_scheduled_messag cancel_send_deleted_message(d->dialog_id, result.get(), is_permanently_deleted); unregister_message_content(td_, result->content.get(), {d->dialog_id, message_id}, "do_delete_scheduled_message"); - unregister_message_reply(d, m); + unregister_message_reply(d->dialog_id, m); return result; } @@ -27938,25 +27938,25 @@ void MessagesManager::update_message_max_reply_media_timestamp_in_replied_messag } } -void MessagesManager::register_message_reply(const Dialog *d, const Message *m) { +void MessagesManager::register_message_reply(DialogId dialog_id, const Message *m) { if (!m->reply_to_message_id.is_valid() || td_->auth_manager_->is_bot()) { return; } if (has_media_timestamps(get_message_content_text(m->content.get()), 0, std::numeric_limits::max())) { - LOG(INFO) << "Register " << m->message_id << " in " << d->dialog_id << " as reply to " << m->reply_to_message_id; - FullMessageId full_message_id{d->dialog_id, m->reply_to_message_id}; + LOG(INFO) << "Register " << m->message_id << " in " << dialog_id << " as reply to " << m->reply_to_message_id; + FullMessageId full_message_id{dialog_id, m->reply_to_message_id}; bool is_inserted = replied_by_media_timestamp_messages_[full_message_id].insert(m->message_id).second; CHECK(is_inserted); } } -void MessagesManager::reregister_message_reply(const Dialog *d, const Message *m) { +void MessagesManager::reregister_message_reply(DialogId dialog_id, const Message *m) { if (!m->reply_to_message_id.is_valid() || td_->auth_manager_->is_bot()) { return; } - auto it = replied_by_media_timestamp_messages_.find({d->dialog_id, m->reply_to_message_id}); + auto it = replied_by_media_timestamp_messages_.find({dialog_id, m->reply_to_message_id}); bool was_registered = it != replied_by_media_timestamp_messages_.end() && it->second.count(m->message_id) > 0; bool need_register = has_media_timestamps(get_message_content_text(m->content.get()), 0, std::numeric_limits::max()); @@ -27964,21 +27964,21 @@ void MessagesManager::reregister_message_reply(const Dialog *d, const Message *m return; } if (was_registered) { - unregister_message_reply(d, m); + unregister_message_reply(dialog_id, m); } else { - register_message_reply(d, m); + register_message_reply(dialog_id, m); } } -void MessagesManager::unregister_message_reply(const Dialog *d, const Message *m) { - auto it = replied_by_media_timestamp_messages_.find({d->dialog_id, m->reply_to_message_id}); +void MessagesManager::unregister_message_reply(DialogId dialog_id, const Message *m) { + auto it = replied_by_media_timestamp_messages_.find({dialog_id, m->reply_to_message_id}); if (it == replied_by_media_timestamp_messages_.end()) { return; } auto is_deleted = it->second.erase(m->message_id) > 0; if (is_deleted) { - LOG(INFO) << "Unregister " << m->message_id << " in " << d->dialog_id << " as reply to " << m->reply_to_message_id; + LOG(INFO) << "Unregister " << m->message_id << " in " << dialog_id << " as reply to " << m->reply_to_message_id; if (it->second.empty()) { replied_by_media_timestamp_messages_.erase(it); } @@ -30635,7 +30635,7 @@ void MessagesManager::send_update_message_content(const Dialog *d, Message *m, b if (is_message_in_dialog) { delete_bot_command_message_id(d->dialog_id, m->message_id); try_add_bot_command_message_id(d->dialog_id, m); - reregister_message_reply(d, m); + reregister_message_reply(d->dialog_id, m); update_message_max_reply_media_timestamp(d, m, false); // because the message reply can be just registered update_message_max_own_media_timestamp(d, m); } @@ -35059,7 +35059,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq register_message_content(td_, m->content.get(), {dialog_id, m->message_id}, "add_message_to_dialog"); - register_message_reply(d, m); + register_message_reply(dialog_id, m); if (*need_update && m->message_id.is_server() && message_content_type == MessageContentType::PinMessage) { auto pinned_message_id = get_message_content_pinned_message_id(m->content.get()); @@ -35287,7 +35287,7 @@ MessagesManager::Message *MessagesManager::add_scheduled_message_to_dialog(Dialo update_message_max_reply_media_timestamp(d, message.get(), false); update_message_max_own_media_timestamp(d, message.get()); - register_message_reply(d, m); + register_message_reply(dialog_id, m); if (from_update) { update_sent_message_contents(dialog_id, m); @@ -35901,14 +35901,21 @@ bool MessagesManager::update_message(Dialog *d, Message *old_message, unique_ptr // can change message tree and invalidate reference to old_message if (new_message->reply_to_message_id == MessageId() || replace_legacy) { LOG(DEBUG) << "Drop message reply_to_message_id"; - unregister_message_reply(d, old_message); + unregister_message_reply(dialog_id, old_message); old_message->reply_to_message_id = MessageId(); - update_message_max_reply_media_timestamp(d, old_message, true); + update_message_max_reply_media_timestamp(d, old_message, is_message_in_dialog); need_send_update = true; } else if (is_new_available) { - LOG(ERROR) << message_id << " in " << dialog_id << " has changed message it is reply to from " - << old_message->reply_to_message_id << " to " << new_message->reply_to_message_id - << ", message content type is " << old_content_type << '/' << new_content_type; + if (message_id.is_yet_unsent() && old_message->reply_to_message_id == MessageId()) { + CHECK(!is_message_in_dialog); + CHECK(new_message->reply_to_message_id.is_valid()); + CHECK(new_message->reply_to_message_id.is_server()); + old_message->reply_to_message_id = new_message->reply_to_message_id; + } else { + LOG(ERROR) << message_id << " in " << dialog_id << " has changed message it is reply to from " + << old_message->reply_to_message_id << " to " << new_message->reply_to_message_id + << ", message content type is " << old_content_type << '/' << new_content_type; + } } } if (old_message->reply_in_dialog_id != new_message->reply_in_dialog_id) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index ac732b3ca..862f43c08 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -2358,11 +2358,11 @@ class MessagesManager final : public Actor { void update_message_max_reply_media_timestamp_in_replied_messages(DialogId dialog_id, MessageId reply_to_message_id); - void register_message_reply(const Dialog *d, const Message *m); + void register_message_reply(DialogId dialog_id, const Message *m); - void reregister_message_reply(const Dialog *d, const Message *m); + void reregister_message_reply(DialogId dialog_id, const Message *m); - void unregister_message_reply(const Dialog *d, const Message *m); + void unregister_message_reply(DialogId dialog_id, const Message *m); void send_update_new_message(const Dialog *d, const Message *m);