diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index f02454abd4..e11670673b 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -52,6 +52,112 @@ QString PrepareStoryFileName( + extension; } +std::vector> ButtonRowsFromTL( + const MTPDreplyInlineMarkup &data) { + const auto &list = data.vrows().v; + + if (list.isEmpty()) { + return {}; + } + using Type = HistoryMessageMarkupButton::Type; + auto rows = std::vector>(); + rows.reserve(list.size()); + + for (const auto &tlRow : list) { + auto row = std::vector(); + row.reserve(tlRow.data().vbuttons().v.size()); + for (const auto &button : tlRow.data().vbuttons().v) { + button.match([&](const MTPDkeyboardButton &data) { + row.push_back({ Type::Default, qs(data.vtext()) }); + }, [&](const MTPDkeyboardButtonCallback &data) { + row.push_back({ + (data.is_requires_password() + ? Type::CallbackWithPassword + : Type::Callback), + qs(data.vtext()), + qba(data.vdata()) + }); + }, [&](const MTPDkeyboardButtonRequestGeoLocation &data) { + row.push_back({ Type::RequestLocation, qs(data.vtext()) }); + }, [&](const MTPDkeyboardButtonRequestPhone &data) { + row.push_back({ Type::RequestPhone, qs(data.vtext()) }); + }, [&](const MTPDkeyboardButtonRequestPeer &data) { + row.push_back({ + Type::RequestPeer, + qs(data.vtext()), + QByteArray("unsupported"), + QString(), + int64(data.vbutton_id().v), + }); + }, [&](const MTPDkeyboardButtonUrl &data) { + row.push_back({ + Type::Url, + qs(data.vtext()), + qba(data.vurl()) + }); + }, [&](const MTPDkeyboardButtonSwitchInline &data) { + const auto type = data.is_same_peer() + ? Type::SwitchInlineSame + : Type::SwitchInline; + row.push_back({ type, qs(data.vtext()), qba(data.vquery()) }); + }, [&](const MTPDkeyboardButtonGame &data) { + row.push_back({ Type::Game, qs(data.vtext()) }); + }, [&](const MTPDkeyboardButtonBuy &data) { + row.push_back({ Type::Buy, qs(data.vtext()) }); + }, [&](const MTPDkeyboardButtonUrlAuth &data) { + row.push_back({ + Type::Auth, + qs(data.vtext()), + qba(data.vurl()), + qs(data.vfwd_text().value_or_empty()), + data.vbutton_id().v, + }); + }, [&](const MTPDkeyboardButtonRequestPoll &data) { + const auto quiz = [&] { + if (!data.vquiz()) { + return QByteArray(); + } + return data.vquiz()->match([&](const MTPDboolTrue&) { + return QByteArray(1, 1); + }, [&](const MTPDboolFalse&) { + return QByteArray(1, 0); + }); + }(); + row.push_back({ + Type::RequestPoll, + qs(data.vtext()), + quiz + }); + }, [&](const MTPDkeyboardButtonUserProfile &data) { + row.push_back({ + Type::UserProfile, + qs(data.vtext()), + QByteArray::number(data.vuser_id().v) + }); + }, [&](const MTPDinputKeyboardButtonUrlAuth &data) { + }, [&](const MTPDinputKeyboardButtonUserProfile &data) { + }, [&](const MTPDkeyboardButtonWebView &data) { + row.push_back({ + Type::WebView, + qs(data.vtext()), + data.vurl().v + }); + }, [&](const MTPDkeyboardButtonSimpleWebView &data) { + row.push_back({ + Type::SimpleWebView, + qs(data.vtext()), + data.vurl().v + }); + }); + } + if (!row.empty()) { + rows.push_back(std::move(row)); + } + } + + return rows; +} + } // namespace uint8 PeerColorIndex(BareId bareId) { @@ -1496,6 +1602,14 @@ Message ParseMessage( } context.botId = 0; } + if (const auto replyMarkup = data.vreply_markup()) { + replyMarkup->match([](const MTPDreplyKeyboardMarkup &) { + }, [&](const MTPDreplyInlineMarkup &data) { + result.inlineButtonRows = ButtonRowsFromTL(data); + }, [](const MTPDreplyKeyboardHide &) { + }, [](const MTPDreplyKeyboardForceReply &) { + }); + } result.text = ParseText( data.vmessage(), data.ventities().value_or_empty()); diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 59ffa1c395..272c94f171 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -665,6 +665,33 @@ inline bool operator>=(MessageId a, MessageId b) { return !(a < b); } +struct HistoryMessageMarkupButton { + enum class Type { + Default, + Url, + Callback, + CallbackWithPassword, + RequestPhone, + RequestLocation, + RequestPoll, + RequestPeer, + SwitchInline, + SwitchInlineSame, + Game, + Buy, + Auth, + UserProfile, + WebView, + SimpleWebView, + }; + + Type type; + QString text; + QByteArray data; + QString forwardText; + int64 buttonId = 0; +}; + struct Message { int32 id = 0; TimeId date = 0; @@ -686,6 +713,7 @@ struct Message { Media media; ServiceAction action; bool out = false; + std::vector> inlineButtonRows; File &file(); const File &file() const; diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index f940fea7ae..8439093a88 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -776,6 +776,76 @@ QByteArray SerializeMessage( pushBare("text", SerializeText(context, message.text)); pushBare("text_entities", SerializeText(context, message.text, true)); + if (!message.inlineButtonRows.empty()) { + const auto typeString = []( + const HistoryMessageMarkupButton &entry) -> QByteArray { + using Type = HistoryMessageMarkupButton::Type; + switch (entry.type) { + case Type::Default: return "default"; + case Type::Url: return "url"; + case Type::Callback: return "callback"; + case Type::CallbackWithPassword: return "callback_with_password"; + case Type::RequestPhone: return "request_phone"; + case Type::RequestLocation: return "request_location"; + case Type::RequestPoll: return "request_poll"; + case Type::RequestPeer: return "request_peer"; + case Type::SwitchInline: return "switch_inline"; + case Type::SwitchInlineSame: return "switch_inline_same"; + case Type::Game: return "game"; + case Type::Buy: return "buy"; + case Type::Auth: return "auth"; + case Type::UserProfile: return "user_profile"; + case Type::WebView: return "web_view"; + case Type::SimpleWebView: return "simple_web_view"; + } + Unexpected("Type in HistoryMessageMarkupButton::Type."); + }; + const auto serializeRow = [&]( + const std::vector &row) { + context.nesting.push_back(Context::kArray); + const auto buttons = ranges::views::all( + row + ) | ranges::views::transform([&]( + const HistoryMessageMarkupButton &entry) { + auto pairs = std::vector>(); + pairs.push_back({ + "type", + SerializeString(typeString(entry)), + }); + if (!entry.text.isEmpty()) { + pairs.push_back({ + "text", + SerializeString(entry.text.toUtf8()), + }); + } + if (!entry.data.isEmpty()) { + pairs.push_back({ "data", SerializeString(entry.data) }); + } + if (!entry.forwardText.isEmpty()) { + pairs.push_back({ + "forward_text", + SerializeString(entry.forwardText.toUtf8()), + }); + } + if (entry.buttonId) { + pairs.push_back({ + "button_id", + NumberToString(entry.buttonId), + }); + } + return SerializeObject(context, pairs); + }) | ranges::to_vector; + context.nesting.pop_back(); + return SerializeArray(context, buttons); + }; + context.nesting.push_back(Context::kArray); + const auto rows = ranges::views::all( + message.inlineButtonRows + ) | ranges::views::transform(serializeRow) | ranges::to_vector; + context.nesting.pop_back(); + pushBare("inline_bot_buttons", SerializeArray(context, rows)); + } + return serialized(); }