history_item_reply_markup.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "history/history_item_reply_markup.h"
  8. #include "data/data_session.h"
  9. #include "history/history_item.h"
  10. #include "history/history_item_components.h"
  11. #include "inline_bots/bot_attach_web_view.h"
  12. namespace {
  13. [[nodiscard]] RequestPeerQuery RequestPeerQueryFromTL(
  14. const MTPDkeyboardButtonRequestPeer &query) {
  15. using Type = RequestPeerQuery::Type;
  16. using Restriction = RequestPeerQuery::Restriction;
  17. auto result = RequestPeerQuery();
  18. result.maxQuantity = query.vmax_quantity().v;
  19. const auto restriction = [](const MTPBool *value) {
  20. return !value
  21. ? Restriction::Any
  22. : mtpIsTrue(*value)
  23. ? Restriction::Yes
  24. : Restriction::No;
  25. };
  26. const auto rights = [](const MTPChatAdminRights *value) {
  27. return value ? ChatAdminRightsInfo(*value).flags : ChatAdminRights();
  28. };
  29. query.vpeer_type().match([&](const MTPDrequestPeerTypeUser &data) {
  30. result.type = Type::User;
  31. result.userIsBot = restriction(data.vbot());
  32. result.userIsPremium = restriction(data.vpremium());
  33. }, [&](const MTPDrequestPeerTypeChat &data) {
  34. result.type = Type::Group;
  35. result.amCreator = data.is_creator();
  36. result.isBotParticipant = data.is_bot_participant();
  37. result.groupIsForum = restriction(data.vforum());
  38. result.hasUsername = restriction(data.vhas_username());
  39. result.myRights = rights(data.vuser_admin_rights());
  40. result.botRights = rights(data.vbot_admin_rights());
  41. }, [&](const MTPDrequestPeerTypeBroadcast &data) {
  42. result.type = Type::Broadcast;
  43. result.amCreator = data.is_creator();
  44. result.hasUsername = restriction(data.vhas_username());
  45. result.myRights = rights(data.vuser_admin_rights());
  46. result.botRights = rights(data.vbot_admin_rights());
  47. });
  48. return result;
  49. }
  50. } // namespace
  51. InlineBots::PeerTypes PeerTypesFromMTP(
  52. const MTPvector<MTPInlineQueryPeerType> &types) {
  53. using namespace InlineBots;
  54. auto result = PeerTypes(0);
  55. for (const auto &type : types.v) {
  56. result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
  57. return PeerType::User;
  58. }, [&](const MTPDinlineQueryPeerTypeChat &data) {
  59. return PeerType::Group;
  60. }, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
  61. return PeerType::Group;
  62. }, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
  63. return PeerType::Broadcast;
  64. }, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
  65. return PeerType::Bot;
  66. }, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
  67. return PeerType();
  68. });
  69. }
  70. return result;
  71. }
  72. HistoryMessageMarkupButton::HistoryMessageMarkupButton(
  73. Type type,
  74. const QString &text,
  75. const QByteArray &data,
  76. const QString &forwardText,
  77. int64 buttonId)
  78. : type(type)
  79. , text(text)
  80. , forwardText(forwardText)
  81. , data(data)
  82. , buttonId(buttonId) {
  83. }
  84. HistoryMessageMarkupButton *HistoryMessageMarkupButton::Get(
  85. not_null<Data::Session*> owner,
  86. FullMsgId itemId,
  87. int row,
  88. int column) {
  89. if (const auto item = owner->message(itemId)) {
  90. if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
  91. if (row < markup->data.rows.size()) {
  92. auto &buttons = markup->data.rows[row];
  93. if (column < buttons.size()) {
  94. return &buttons[column];
  95. }
  96. }
  97. }
  98. }
  99. return nullptr;
  100. }
  101. void HistoryMessageMarkupData::fillRows(
  102. const QVector<MTPKeyboardButtonRow> &list) {
  103. rows.clear();
  104. if (list.isEmpty()) {
  105. return;
  106. }
  107. using Type = Button::Type;
  108. rows.reserve(list.size());
  109. for (const auto &row : list) {
  110. row.match([&](const MTPDkeyboardButtonRow &data) {
  111. auto row = std::vector<Button>();
  112. row.reserve(data.vbuttons().v.size());
  113. for (const auto &button : data.vbuttons().v) {
  114. button.match([&](const MTPDkeyboardButton &data) {
  115. row.emplace_back(Type::Default, qs(data.vtext()));
  116. }, [&](const MTPDkeyboardButtonCallback &data) {
  117. row.emplace_back(
  118. (data.is_requires_password()
  119. ? Type::CallbackWithPassword
  120. : Type::Callback),
  121. qs(data.vtext()),
  122. qba(data.vdata()));
  123. }, [&](const MTPDkeyboardButtonRequestGeoLocation &data) {
  124. row.emplace_back(Type::RequestLocation, qs(data.vtext()));
  125. }, [&](const MTPDkeyboardButtonRequestPhone &data) {
  126. row.emplace_back(Type::RequestPhone, qs(data.vtext()));
  127. }, [&](const MTPDkeyboardButtonRequestPeer &data) {
  128. const auto query = RequestPeerQueryFromTL(data);
  129. row.emplace_back(
  130. Type::RequestPeer,
  131. qs(data.vtext()),
  132. QByteArray(
  133. reinterpret_cast<const char*>(&query),
  134. sizeof(query)),
  135. QString(),
  136. int64(data.vbutton_id().v));
  137. }, [&](const MTPDkeyboardButtonUrl &data) {
  138. row.emplace_back(
  139. Type::Url,
  140. qs(data.vtext()),
  141. qba(data.vurl()));
  142. }, [&](const MTPDkeyboardButtonSwitchInline &data) {
  143. const auto type = data.is_same_peer()
  144. ? Type::SwitchInlineSame
  145. : Type::SwitchInline;
  146. row.emplace_back(type, qs(data.vtext()), qba(data.vquery()));
  147. if (type == Type::SwitchInline) {
  148. // Optimization flag.
  149. // Fast check on all new messages if there is a switch button to auto-click it.
  150. flags |= ReplyMarkupFlag::HasSwitchInlineButton;
  151. if (const auto types = data.vpeer_types()) {
  152. row.back().peerTypes = PeerTypesFromMTP(*types);
  153. }
  154. }
  155. }, [&](const MTPDkeyboardButtonGame &data) {
  156. row.emplace_back(Type::Game, qs(data.vtext()));
  157. }, [&](const MTPDkeyboardButtonBuy &data) {
  158. row.emplace_back(Type::Buy, qs(data.vtext()));
  159. }, [&](const MTPDkeyboardButtonUrlAuth &data) {
  160. row.emplace_back(
  161. Type::Auth,
  162. qs(data.vtext()),
  163. qba(data.vurl()),
  164. qs(data.vfwd_text().value_or_empty()),
  165. data.vbutton_id().v);
  166. }, [&](const MTPDkeyboardButtonRequestPoll &data) {
  167. const auto quiz = [&] {
  168. if (!data.vquiz()) {
  169. return QByteArray();
  170. }
  171. return data.vquiz()->match([&](const MTPDboolTrue&) {
  172. return QByteArray(1, 1);
  173. }, [&](const MTPDboolFalse&) {
  174. return QByteArray(1, 0);
  175. });
  176. }();
  177. row.emplace_back(
  178. Type::RequestPoll,
  179. qs(data.vtext()),
  180. quiz);
  181. }, [&](const MTPDkeyboardButtonUserProfile &data) {
  182. row.emplace_back(
  183. Type::UserProfile,
  184. qs(data.vtext()),
  185. QByteArray::number(data.vuser_id().v));
  186. }, [&](const MTPDinputKeyboardButtonUrlAuth &data) {
  187. LOG(("API Error: inputKeyboardButtonUrlAuth."));
  188. // Should not get those for the users.
  189. }, [&](const MTPDinputKeyboardButtonUserProfile &data) {
  190. LOG(("API Error: inputKeyboardButtonUserProfile."));
  191. // Should not get those for the users.
  192. }, [&](const MTPDkeyboardButtonWebView &data) {
  193. row.emplace_back(
  194. Type::WebView,
  195. qs(data.vtext()),
  196. data.vurl().v);
  197. }, [&](const MTPDkeyboardButtonSimpleWebView &data) {
  198. row.emplace_back(
  199. Type::SimpleWebView,
  200. qs(data.vtext()),
  201. data.vurl().v);
  202. }, [&](const MTPDkeyboardButtonCopy &data) {
  203. row.emplace_back(
  204. Type::CopyText,
  205. qs(data.vtext()),
  206. data.vcopy_text().v);
  207. }, [&](const MTPDinputKeyboardButtonRequestPeer &data) {
  208. LOG(("API Error: inputKeyboardButtonRequestPeer."));
  209. // Should not get those for the users.
  210. });
  211. }
  212. if (!row.empty()) {
  213. rows.push_back(std::move(row));
  214. }
  215. });
  216. }
  217. if (rows.size() == 1
  218. && rows.front().size() == 1
  219. && rows.front().front().type == Type::Buy) {
  220. flags |= ReplyMarkupFlag::OnlyBuyButton;
  221. }
  222. }
  223. HistoryMessageMarkupData::HistoryMessageMarkupData(
  224. const MTPReplyMarkup *data) {
  225. if (!data) {
  226. return;
  227. }
  228. using Flag = ReplyMarkupFlag;
  229. data->match([&](const MTPDreplyKeyboardMarkup &data) {
  230. flags = (data.is_resize() ? Flag::Resize : Flag())
  231. | (data.is_selective() ? Flag::Selective : Flag())
  232. | (data.is_single_use() ? Flag::SingleUse : Flag())
  233. | (data.is_persistent() ? Flag::Persistent : Flag());
  234. placeholder = qs(data.vplaceholder().value_or_empty());
  235. fillRows(data.vrows().v);
  236. }, [&](const MTPDreplyInlineMarkup &data) {
  237. flags = Flag::Inline;
  238. placeholder = QString();
  239. fillRows(data.vrows().v);
  240. }, [&](const MTPDreplyKeyboardHide &data) {
  241. flags = Flag::None
  242. | (data.is_selective() ? Flag::Selective : Flag());
  243. placeholder = QString();
  244. }, [&](const MTPDreplyKeyboardForceReply &data) {
  245. flags = Flag::ForceReply
  246. | (data.is_selective() ? Flag::Selective : Flag())
  247. | (data.is_single_use() ? Flag::SingleUse : Flag());
  248. placeholder = qs(data.vplaceholder().value_or_empty());
  249. });
  250. }
  251. void HistoryMessageMarkupData::fillForwardedData(
  252. const HistoryMessageMarkupData &original) {
  253. Expects(isNull());
  254. Expects(!original.isNull());
  255. flags = original.flags;
  256. placeholder = original.placeholder;
  257. rows.reserve(original.rows.size());
  258. using Type = HistoryMessageMarkupButton::Type;
  259. for (const auto &existing : original.rows) {
  260. auto row = std::vector<Button>();
  261. row.reserve(existing.size());
  262. for (const auto &button : existing) {
  263. const auto newType = (button.type != Type::SwitchInlineSame)
  264. ? button.type
  265. : Type::SwitchInline;
  266. const auto text = button.forwardText.isEmpty()
  267. ? button.text
  268. : button.forwardText;
  269. row.emplace_back(
  270. newType,
  271. text,
  272. button.data,
  273. QString(),
  274. button.buttonId);
  275. }
  276. if (!row.empty()) {
  277. rows.push_back(std::move(row));
  278. }
  279. }
  280. }
  281. bool HistoryMessageMarkupData::isNull() const {
  282. if (flags & ReplyMarkupFlag::IsNull) {
  283. Assert(isTrivial());
  284. return true;
  285. }
  286. return false;
  287. }
  288. bool HistoryMessageMarkupData::isTrivial() const {
  289. return rows.empty()
  290. && placeholder.isEmpty()
  291. && !(flags & ~ReplyMarkupFlag::IsNull);
  292. }
  293. HistoryMessageRepliesData::HistoryMessageRepliesData(
  294. const MTPMessageReplies *data) {
  295. if (!data) {
  296. return;
  297. }
  298. const auto &fields = data->c_messageReplies();
  299. if (const auto list = fields.vrecent_repliers()) {
  300. recentRepliers.reserve(list->v.size());
  301. for (const auto &id : list->v) {
  302. recentRepliers.push_back(peerFromMTP(id));
  303. }
  304. }
  305. repliesCount = fields.vreplies().v;
  306. channelId = ChannelId(fields.vchannel_id().value_or_empty());
  307. readMaxId = fields.vread_max_id().value_or_empty();
  308. maxId = fields.vmax_id().value_or_empty();
  309. isNull = false;
  310. pts = fields.vreplies_pts().v;
  311. }