api_text_entities.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 "api/api_text_entities.h"
  8. #include "data/data_document.h"
  9. #include "data/data_session.h"
  10. #include "data/data_user.h"
  11. #include "data/stickers/data_custom_emoji.h"
  12. #include "data/stickers/data_stickers_set.h"
  13. #include "main/main_session.h"
  14. namespace Api {
  15. namespace {
  16. using namespace TextUtilities;
  17. [[nodiscard]] QString CustomEmojiEntityData(
  18. const MTPDmessageEntityCustomEmoji &data) {
  19. return Data::SerializeCustomEmojiId(data.vdocument_id().v);
  20. }
  21. [[nodiscard]] std::optional<MTPMessageEntity> CustomEmojiEntity(
  22. MTPint offset,
  23. MTPint length,
  24. const QString &data) {
  25. const auto parsed = Data::ParseCustomEmojiData(data);
  26. if (!parsed) {
  27. return {};
  28. }
  29. return MTP_messageEntityCustomEmoji(
  30. offset,
  31. length,
  32. MTP_long(parsed));
  33. }
  34. [[nodiscard]] std::optional<MTPMessageEntity> MentionNameEntity(
  35. not_null<Main::Session*> session,
  36. MTPint offset,
  37. MTPint length,
  38. const QString &data) {
  39. const auto parsed = MentionNameDataToFields(data);
  40. if (!parsed.userId || parsed.selfId != session->userId().bare) {
  41. return {};
  42. }
  43. return MTP_inputMessageEntityMentionName(
  44. offset,
  45. length,
  46. (parsed.userId == parsed.selfId
  47. ? MTP_inputUserSelf()
  48. : MTP_inputUser(
  49. MTP_long(parsed.userId),
  50. MTP_long(parsed.accessHash))));
  51. }
  52. } // namespace
  53. EntitiesInText EntitiesFromMTP(
  54. Main::Session *session,
  55. const QVector<MTPMessageEntity> &entities) {
  56. if (entities.isEmpty()) {
  57. return {};
  58. }
  59. auto result = EntitiesInText();
  60. result.reserve(entities.size());
  61. for (const auto &entity : entities) {
  62. entity.match([&](const MTPDmessageEntityUnknown &d) {
  63. }, [&](const MTPDmessageEntityMention &d) {
  64. result.push_back({
  65. EntityType::Mention,
  66. d.voffset().v,
  67. d.vlength().v,
  68. });
  69. }, [&](const MTPDmessageEntityHashtag &d) {
  70. result.push_back({
  71. EntityType::Hashtag,
  72. d.voffset().v,
  73. d.vlength().v,
  74. });
  75. }, [&](const MTPDmessageEntityBotCommand &d) {
  76. result.push_back({
  77. EntityType::BotCommand,
  78. d.voffset().v,
  79. d.vlength().v,
  80. });
  81. }, [&](const MTPDmessageEntityUrl &d) {
  82. result.push_back({
  83. EntityType::Url,
  84. d.voffset().v,
  85. d.vlength().v,
  86. });
  87. }, [&](const MTPDmessageEntityEmail &d) {
  88. result.push_back({
  89. EntityType::Email,
  90. d.voffset().v,
  91. d.vlength().v,
  92. });
  93. }, [&](const MTPDmessageEntityBold &d) {
  94. result.push_back({
  95. EntityType::Bold,
  96. d.voffset().v,
  97. d.vlength().v,
  98. });
  99. }, [&](const MTPDmessageEntityItalic &d) {
  100. result.push_back({
  101. EntityType::Italic,
  102. d.voffset().v,
  103. d.vlength().v,
  104. });
  105. }, [&](const MTPDmessageEntityCode &d) {
  106. result.push_back({
  107. EntityType::Code,
  108. d.voffset().v,
  109. d.vlength().v,
  110. });
  111. }, [&](const MTPDmessageEntityPre &d) {
  112. result.push_back({
  113. EntityType::Pre,
  114. d.voffset().v,
  115. d.vlength().v,
  116. qs(d.vlanguage()),
  117. });
  118. }, [&](const MTPDmessageEntityTextUrl &d) {
  119. result.push_back({
  120. EntityType::CustomUrl,
  121. d.voffset().v,
  122. d.vlength().v,
  123. qs(d.vurl()),
  124. });
  125. }, [&](const MTPDmessageEntityMentionName &d) {
  126. if (!session) {
  127. return;
  128. }
  129. const auto userId = UserId(d.vuser_id());
  130. const auto user = session->data().userLoaded(userId);
  131. const auto data = MentionNameDataFromFields({
  132. .selfId = session->userId().bare,
  133. .userId = userId.bare,
  134. .accessHash = user ? user->accessHash() : 0,
  135. });
  136. result.push_back({
  137. EntityType::MentionName,
  138. d.voffset().v,
  139. d.vlength().v,
  140. data,
  141. });
  142. }, [&](const MTPDinputMessageEntityMentionName &d) {
  143. if (!session) {
  144. return;
  145. }
  146. const auto data = d.vuser_id().match([&](
  147. const MTPDinputUserSelf &) {
  148. return MentionNameDataFromFields({
  149. .selfId = session->userId().bare,
  150. .userId = session->userId().bare,
  151. .accessHash = session->user()->accessHash(),
  152. });
  153. }, [&](const MTPDinputUser &data) {
  154. return MentionNameDataFromFields({
  155. .selfId = session->userId().bare,
  156. .userId = UserId(data.vuser_id()).bare,
  157. .accessHash = data.vaccess_hash().v,
  158. });
  159. }, [](const auto &) {
  160. return QString();
  161. });
  162. if (!data.isEmpty()) {
  163. result.push_back({
  164. EntityType::MentionName,
  165. d.voffset().v,
  166. d.vlength().v,
  167. data,
  168. });
  169. }
  170. }, [&](const MTPDmessageEntityPhone &d) {
  171. result.push_back({
  172. EntityType::Phone,
  173. d.voffset().v,
  174. d.vlength().v,
  175. });
  176. }, [&](const MTPDmessageEntityCashtag &d) {
  177. result.push_back({
  178. EntityType::Cashtag,
  179. d.voffset().v,
  180. d.vlength().v,
  181. });
  182. }, [&](const MTPDmessageEntityUnderline &d) {
  183. result.push_back({
  184. EntityType::Underline,
  185. d.voffset().v,
  186. d.vlength().v,
  187. });
  188. }, [&](const MTPDmessageEntityStrike &d) {
  189. result.push_back({
  190. EntityType::StrikeOut,
  191. d.voffset().v,
  192. d.vlength().v,
  193. });
  194. }, [&](const MTPDmessageEntityBankCard &d) {
  195. // Skipping cards. // #TODO entities
  196. }, [&](const MTPDmessageEntitySpoiler &d) {
  197. result.push_back({
  198. EntityType::Spoiler,
  199. d.voffset().v,
  200. d.vlength().v,
  201. });
  202. }, [&](const MTPDmessageEntityCustomEmoji &d) {
  203. result.push_back({
  204. EntityType::CustomEmoji,
  205. d.voffset().v,
  206. d.vlength().v,
  207. CustomEmojiEntityData(d),
  208. });
  209. }, [&](const MTPDmessageEntityBlockquote &d) {
  210. result.push_back({
  211. EntityType::Blockquote,
  212. d.voffset().v,
  213. d.vlength().v,
  214. d.is_collapsed() ? u"1"_q : QString(),
  215. });
  216. });
  217. }
  218. return result;
  219. }
  220. MTPVector<MTPMessageEntity> EntitiesToMTP(
  221. Main::Session *session,
  222. const EntitiesInText &entities,
  223. ConvertOption option) {
  224. auto v = QVector<MTPMessageEntity>();
  225. v.reserve(entities.size());
  226. for (const auto &entity : entities) {
  227. if (entity.length() <= 0) {
  228. continue;
  229. }
  230. if (option == ConvertOption::SkipLocal
  231. && entity.type() != EntityType::Bold
  232. //&& entity.type() != EntityType::Semibold // Not in API.
  233. && entity.type() != EntityType::Italic
  234. && entity.type() != EntityType::Underline
  235. && entity.type() != EntityType::StrikeOut
  236. && entity.type() != EntityType::Code // #TODO entities
  237. && entity.type() != EntityType::Pre
  238. && entity.type() != EntityType::Blockquote
  239. && entity.type() != EntityType::Spoiler
  240. && entity.type() != EntityType::MentionName
  241. && entity.type() != EntityType::CustomUrl
  242. && entity.type() != EntityType::CustomEmoji) {
  243. continue;
  244. }
  245. auto offset = MTP_int(entity.offset());
  246. auto length = MTP_int(entity.length());
  247. switch (entity.type()) {
  248. case EntityType::Url: {
  249. v.push_back(MTP_messageEntityUrl(offset, length));
  250. } break;
  251. case EntityType::CustomUrl: {
  252. v.push_back(
  253. MTP_messageEntityTextUrl(
  254. offset,
  255. length,
  256. MTP_string(entity.data())));
  257. } break;
  258. case EntityType::Email: {
  259. v.push_back(MTP_messageEntityEmail(offset, length));
  260. } break;
  261. case EntityType::Phone: {
  262. v.push_back(MTP_messageEntityPhone(offset, length));
  263. } break;
  264. case EntityType::Hashtag: {
  265. v.push_back(MTP_messageEntityHashtag(offset, length));
  266. } break;
  267. case EntityType::Cashtag: {
  268. v.push_back(MTP_messageEntityCashtag(offset, length));
  269. } break;
  270. case EntityType::Mention: {
  271. v.push_back(MTP_messageEntityMention(offset, length));
  272. } break;
  273. case EntityType::MentionName: {
  274. Assert(session != nullptr);
  275. const auto valid = MentionNameEntity(
  276. session,
  277. offset,
  278. length,
  279. entity.data());
  280. if (valid) {
  281. v.push_back(*valid);
  282. }
  283. } break;
  284. case EntityType::BotCommand: {
  285. v.push_back(MTP_messageEntityBotCommand(offset, length));
  286. } break;
  287. case EntityType::Bold: {
  288. v.push_back(MTP_messageEntityBold(offset, length));
  289. } break;
  290. case EntityType::Italic: {
  291. v.push_back(MTP_messageEntityItalic(offset, length));
  292. } break;
  293. case EntityType::Underline: {
  294. v.push_back(MTP_messageEntityUnderline(offset, length));
  295. } break;
  296. case EntityType::StrikeOut: {
  297. v.push_back(MTP_messageEntityStrike(offset, length));
  298. } break;
  299. case EntityType::Code: {
  300. // #TODO entities.
  301. v.push_back(MTP_messageEntityCode(offset, length));
  302. } break;
  303. case EntityType::Pre: {
  304. v.push_back(
  305. MTP_messageEntityPre(
  306. offset,
  307. length,
  308. MTP_string(entity.data())));
  309. } break;
  310. case EntityType::Blockquote: {
  311. using Flag = MTPDmessageEntityBlockquote::Flag;
  312. const auto collapsed = !entity.data().isEmpty();
  313. v.push_back(
  314. MTP_messageEntityBlockquote(
  315. MTP_flags(collapsed ? Flag::f_collapsed : Flag()),
  316. offset,
  317. length));
  318. } break;
  319. case EntityType::Spoiler: {
  320. v.push_back(MTP_messageEntitySpoiler(offset, length));
  321. } break;
  322. case EntityType::CustomEmoji: {
  323. const auto valid = CustomEmojiEntity(
  324. offset,
  325. length,
  326. entity.data());
  327. if (valid) {
  328. v.push_back(*valid);
  329. }
  330. } break;
  331. }
  332. }
  333. return MTP_vector<MTPMessageEntity>(std::move(v));
  334. }
  335. TextWithEntities ParseTextWithEntities(
  336. Main::Session *session,
  337. const MTPTextWithEntities &text) {
  338. const auto &data = text.data();
  339. return {
  340. .text = qs(data.vtext()),
  341. .entities = EntitiesFromMTP(session, data.ventities().v),
  342. };
  343. }
  344. } // namespace Api