1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "api/api_text_entities.h"
9 
10 #include "main/main_session.h"
11 #include "data/data_session.h"
12 #include "data/data_user.h"
13 
14 namespace Api {
15 namespace {
16 
17 using namespace TextUtilities;
18 
19 } // namespace
20 
EntitiesFromMTP(Main::Session * session,const QVector<MTPMessageEntity> & entities)21 EntitiesInText EntitiesFromMTP(
22 		Main::Session *session,
23 		const QVector<MTPMessageEntity> &entities) {
24 	auto result = EntitiesInText();
25 	if (!entities.isEmpty()) {
26 		result.reserve(entities.size());
27 		for (const auto &entity : entities) {
28 			switch (entity.type()) {
29 			case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
30 			case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break;
31 			case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
32 			case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
33 			case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
34 			case mtpc_messageEntityPhone: break; // Skipping phones.
35 			case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
36 			case mtpc_messageEntityMentionName: {
37 				const auto &d = entity.c_messageEntityMentionName();
38 				const auto userId = UserId(d.vuser_id());
39 				const auto data = [&] {
40 					if (session) {
41 						if (const auto user = session->data().userLoaded(userId)) {
42 							return MentionNameDataFromFields({
43 								userId.bare,
44 								user->accessHash()
45 							});
46 						}
47 					}
48 					return MentionNameDataFromFields(userId.bare);
49 				}();
50 				result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
51 			} break;
52 			case mtpc_inputMessageEntityMentionName: {
53 				const auto &d = entity.c_inputMessageEntityMentionName();
54 				const auto data = [&] {
55 					if (session && d.vuser_id().type() == mtpc_inputUserSelf) {
56 						return MentionNameDataFromFields(session->userId().bare);
57 					} else if (d.vuser_id().type() == mtpc_inputUser) {
58 						auto &user = d.vuser_id().c_inputUser();
59 						const auto userId = UserId(user.vuser_id());
60 						return MentionNameDataFromFields({ userId.bare, user.vaccess_hash().v });
61 					}
62 					return QString();
63 				}();
64 				if (!data.isEmpty()) {
65 					result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
66 				}
67 			} break;
68 			case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break;
69 			case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break;
70 			case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break;
71 			case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
72 			case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
73 			case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
74 			case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
75 			case mtpc_messageEntityBankCard: break; // Skipping cards.
76 				// #TODO entities
77 			}
78 		}
79 	}
80 	return result;
81 }
82 
EntitiesToMTP(not_null<Main::Session * > session,const EntitiesInText & entities,ConvertOption option)83 MTPVector<MTPMessageEntity> EntitiesToMTP(
84 		not_null<Main::Session*> session,
85 		const EntitiesInText &entities,
86 		ConvertOption option) {
87 	auto v = QVector<MTPMessageEntity>();
88 	v.reserve(entities.size());
89 	for (const auto &entity : entities) {
90 		if (entity.length() <= 0) continue;
91 		if (option == ConvertOption::SkipLocal
92 			&& entity.type() != EntityType::Bold
93 			//&& entity.type() != EntityType::Semibold // Not in API.
94 			&& entity.type() != EntityType::Italic
95 			&& entity.type() != EntityType::Underline
96 			&& entity.type() != EntityType::StrikeOut
97 			&& entity.type() != EntityType::Code // #TODO entities
98 			&& entity.type() != EntityType::Pre
99 			&& entity.type() != EntityType::MentionName
100 			&& entity.type() != EntityType::CustomUrl) {
101 			continue;
102 		}
103 
104 		auto offset = MTP_int(entity.offset());
105 		auto length = MTP_int(entity.length());
106 		switch (entity.type()) {
107 		case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break;
108 		case EntityType::CustomUrl: v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(entity.data()))); break;
109 		case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break;
110 		case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break;
111 		case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break;
112 		case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break;
113 		case EntityType::MentionName: {
114 			auto inputUser = [&](const QString &data) -> MTPInputUser {
115 				auto fields = MentionNameDataToFields(data);
116 				if (session && fields.userId == session->userId().bare) {
117 					return MTP_inputUserSelf();
118 				} else if (fields.userId) {
119 					return MTP_inputUser(MTP_long(fields.userId), MTP_long(fields.accessHash));
120 				}
121 				return MTP_inputUserEmpty();
122 			}(entity.data());
123 			if (inputUser.type() != mtpc_inputUserEmpty) {
124 				v.push_back(MTP_inputMessageEntityMentionName(offset, length, inputUser));
125 			}
126 		} break;
127 		case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break;
128 		case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break;
129 		case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break;
130 		case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break;
131 		case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break;
132 		case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities
133 		case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break;
134 		}
135 	}
136 	return MTP_vector<MTPMessageEntity>(std::move(v));
137 }
138 
139 } // namespace Api
140