1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/DialogAction.h"
8
9 #include "td/telegram/misc.h"
10 #include "td/telegram/ServerMessageId.h"
11
12 #include "td/utils/emoji.h"
13 #include "td/utils/misc.h"
14 #include "td/utils/Slice.h"
15 #include "td/utils/SliceBuilder.h"
16 #include "td/utils/utf8.h"
17
18 namespace td {
19
is_valid_emoji(string & emoji)20 bool DialogAction::is_valid_emoji(string &emoji) {
21 if (!clean_input_string(emoji)) {
22 return false;
23 }
24 return is_emoji(emoji);
25 }
26
init(Type type)27 void DialogAction::init(Type type) {
28 type_ = type;
29 progress_ = 0;
30 emoji_.clear();
31 }
32
init(Type type,int32 progress)33 void DialogAction::init(Type type, int32 progress) {
34 type_ = type;
35 progress_ = clamp(progress, 0, 100);
36 emoji_.clear();
37 }
38
init(Type type,string emoji)39 void DialogAction::init(Type type, string emoji) {
40 if (is_valid_emoji(emoji)) {
41 type_ = type;
42 progress_ = 0;
43 emoji_ = std::move(emoji);
44 } else {
45 init(Type::Cancel);
46 }
47 }
48
init(Type type,int32 message_id,string emoji,const string & data)49 void DialogAction::init(Type type, int32 message_id, string emoji, const string &data) {
50 if (ServerMessageId(message_id).is_valid() && is_valid_emoji(emoji) && check_utf8(data)) {
51 type_ = type;
52 progress_ = message_id;
53 emoji_ = PSTRING() << emoji << '\xFF' << data;
54 } else {
55 init(Type::Cancel);
56 }
57 }
58
DialogAction(Type type,int32 progress)59 DialogAction::DialogAction(Type type, int32 progress) {
60 init(type, progress);
61 }
62
DialogAction(td_api::object_ptr<td_api::ChatAction> && action)63 DialogAction::DialogAction(td_api::object_ptr<td_api::ChatAction> &&action) {
64 if (action == nullptr) {
65 return;
66 }
67
68 switch (action->get_id()) {
69 case td_api::chatActionCancel::ID:
70 init(Type::Cancel);
71 break;
72 case td_api::chatActionTyping::ID:
73 init(Type::Typing);
74 break;
75 case td_api::chatActionRecordingVideo::ID:
76 init(Type::RecordingVideo);
77 break;
78 case td_api::chatActionUploadingVideo::ID: {
79 auto uploading_action = move_tl_object_as<td_api::chatActionUploadingVideo>(action);
80 init(Type::UploadingVideo, uploading_action->progress_);
81 break;
82 }
83 case td_api::chatActionRecordingVoiceNote::ID:
84 init(Type::RecordingVoiceNote);
85 break;
86 case td_api::chatActionUploadingVoiceNote::ID: {
87 auto uploading_action = move_tl_object_as<td_api::chatActionUploadingVoiceNote>(action);
88 init(Type::UploadingVoiceNote, uploading_action->progress_);
89 break;
90 }
91 case td_api::chatActionUploadingPhoto::ID: {
92 auto uploading_action = move_tl_object_as<td_api::chatActionUploadingPhoto>(action);
93 init(Type::UploadingPhoto, uploading_action->progress_);
94 break;
95 }
96 case td_api::chatActionUploadingDocument::ID: {
97 auto uploading_action = move_tl_object_as<td_api::chatActionUploadingDocument>(action);
98 init(Type::UploadingDocument, uploading_action->progress_);
99 break;
100 }
101 case td_api::chatActionChoosingLocation::ID:
102 init(Type::ChoosingLocation);
103 break;
104 case td_api::chatActionChoosingContact::ID:
105 init(Type::ChoosingContact);
106 break;
107 case td_api::chatActionStartPlayingGame::ID:
108 init(Type::StartPlayingGame);
109 break;
110 case td_api::chatActionRecordingVideoNote::ID:
111 init(Type::RecordingVideoNote);
112 break;
113 case td_api::chatActionUploadingVideoNote::ID: {
114 auto uploading_action = move_tl_object_as<td_api::chatActionUploadingVideoNote>(action);
115 init(Type::UploadingVideoNote, uploading_action->progress_);
116 break;
117 }
118 case td_api::chatActionChoosingSticker::ID:
119 init(Type::ChoosingSticker);
120 break;
121 case td_api::chatActionWatchingAnimations::ID: {
122 auto watching_animations_action = move_tl_object_as<td_api::chatActionWatchingAnimations>(action);
123 init(Type::WatchingAnimations, std::move(watching_animations_action->emoji_));
124 break;
125 }
126 default:
127 UNREACHABLE();
128 break;
129 }
130 }
131
DialogAction(telegram_api::object_ptr<telegram_api::SendMessageAction> && action)132 DialogAction::DialogAction(telegram_api::object_ptr<telegram_api::SendMessageAction> &&action) {
133 switch (action->get_id()) {
134 case telegram_api::sendMessageCancelAction::ID:
135 init(Type::Cancel);
136 break;
137 case telegram_api::sendMessageTypingAction::ID:
138 init(Type::Typing);
139 break;
140 case telegram_api::sendMessageRecordVideoAction::ID:
141 init(Type::RecordingVideo);
142 break;
143 case telegram_api::sendMessageUploadVideoAction::ID: {
144 auto upload_video_action = move_tl_object_as<telegram_api::sendMessageUploadVideoAction>(action);
145 init(Type::UploadingVideo, upload_video_action->progress_);
146 break;
147 }
148 case telegram_api::sendMessageRecordAudioAction::ID:
149 init(Type::RecordingVoiceNote);
150 break;
151 case telegram_api::sendMessageUploadAudioAction::ID: {
152 auto upload_audio_action = move_tl_object_as<telegram_api::sendMessageUploadAudioAction>(action);
153 init(Type::UploadingVoiceNote, upload_audio_action->progress_);
154 break;
155 }
156 case telegram_api::sendMessageUploadPhotoAction::ID: {
157 auto upload_photo_action = move_tl_object_as<telegram_api::sendMessageUploadPhotoAction>(action);
158 init(Type::UploadingPhoto, upload_photo_action->progress_);
159 break;
160 }
161 case telegram_api::sendMessageUploadDocumentAction::ID: {
162 auto upload_document_action = move_tl_object_as<telegram_api::sendMessageUploadDocumentAction>(action);
163 init(Type::UploadingDocument, upload_document_action->progress_);
164 break;
165 }
166 case telegram_api::sendMessageGeoLocationAction::ID:
167 init(Type::ChoosingLocation);
168 break;
169 case telegram_api::sendMessageChooseContactAction::ID:
170 init(Type::ChoosingContact);
171 break;
172 case telegram_api::sendMessageGamePlayAction::ID:
173 init(Type::StartPlayingGame);
174 break;
175 case telegram_api::sendMessageRecordRoundAction::ID:
176 init(Type::RecordingVideoNote);
177 break;
178 case telegram_api::sendMessageUploadRoundAction::ID: {
179 auto upload_round_action = move_tl_object_as<telegram_api::sendMessageUploadRoundAction>(action);
180 init(Type::UploadingVideoNote, upload_round_action->progress_);
181 break;
182 }
183 case telegram_api::speakingInGroupCallAction::ID:
184 init(Type::SpeakingInVoiceChat);
185 break;
186 case telegram_api::sendMessageHistoryImportAction::ID: {
187 auto history_import_action = move_tl_object_as<telegram_api::sendMessageHistoryImportAction>(action);
188 init(Type::ImportingMessages, history_import_action->progress_);
189 break;
190 }
191 case telegram_api::sendMessageChooseStickerAction::ID:
192 init(Type::ChoosingSticker);
193 break;
194 case telegram_api::sendMessageEmojiInteractionSeen::ID: {
195 auto emoji_interaction_seen_action = move_tl_object_as<telegram_api::sendMessageEmojiInteractionSeen>(action);
196 init(Type::WatchingAnimations, std::move(emoji_interaction_seen_action->emoticon_));
197 break;
198 }
199 case telegram_api::sendMessageEmojiInteraction::ID: {
200 auto emoji_interaction_action = move_tl_object_as<telegram_api::sendMessageEmojiInteraction>(action);
201 init(Type::ClickingAnimatedEmoji, emoji_interaction_action->msg_id_,
202 std::move(emoji_interaction_action->emoticon_), emoji_interaction_action->interaction_->data_);
203 break;
204 }
205 default:
206 UNREACHABLE();
207 break;
208 }
209 }
210
get_input_send_message_action() const211 tl_object_ptr<telegram_api::SendMessageAction> DialogAction::get_input_send_message_action() const {
212 switch (type_) {
213 case Type::Cancel:
214 return make_tl_object<telegram_api::sendMessageCancelAction>();
215 case Type::Typing:
216 return make_tl_object<telegram_api::sendMessageTypingAction>();
217 case Type::RecordingVideo:
218 return make_tl_object<telegram_api::sendMessageRecordVideoAction>();
219 case Type::UploadingVideo:
220 return make_tl_object<telegram_api::sendMessageUploadVideoAction>(progress_);
221 case Type::RecordingVoiceNote:
222 return make_tl_object<telegram_api::sendMessageRecordAudioAction>();
223 case Type::UploadingVoiceNote:
224 return make_tl_object<telegram_api::sendMessageUploadAudioAction>(progress_);
225 case Type::UploadingPhoto:
226 return make_tl_object<telegram_api::sendMessageUploadPhotoAction>(progress_);
227 case Type::UploadingDocument:
228 return make_tl_object<telegram_api::sendMessageUploadDocumentAction>(progress_);
229 case Type::ChoosingLocation:
230 return make_tl_object<telegram_api::sendMessageGeoLocationAction>();
231 case Type::ChoosingContact:
232 return make_tl_object<telegram_api::sendMessageChooseContactAction>();
233 case Type::StartPlayingGame:
234 return make_tl_object<telegram_api::sendMessageGamePlayAction>();
235 case Type::RecordingVideoNote:
236 return make_tl_object<telegram_api::sendMessageRecordRoundAction>();
237 case Type::UploadingVideoNote:
238 return make_tl_object<telegram_api::sendMessageUploadRoundAction>(progress_);
239 case Type::SpeakingInVoiceChat:
240 return make_tl_object<telegram_api::speakingInGroupCallAction>();
241 case Type::ImportingMessages:
242 return make_tl_object<telegram_api::sendMessageHistoryImportAction>(progress_);
243 case Type::ChoosingSticker:
244 return make_tl_object<telegram_api::sendMessageChooseStickerAction>();
245 case Type::WatchingAnimations:
246 return make_tl_object<telegram_api::sendMessageEmojiInteractionSeen>(emoji_);
247 case Type::ClickingAnimatedEmoji:
248 default:
249 UNREACHABLE();
250 return nullptr;
251 }
252 }
253
get_secret_input_send_message_action() const254 tl_object_ptr<secret_api::SendMessageAction> DialogAction::get_secret_input_send_message_action() const {
255 switch (type_) {
256 case Type::Cancel:
257 return make_tl_object<secret_api::sendMessageCancelAction>();
258 case Type::Typing:
259 return make_tl_object<secret_api::sendMessageTypingAction>();
260 case Type::RecordingVideo:
261 return make_tl_object<secret_api::sendMessageRecordVideoAction>();
262 case Type::UploadingVideo:
263 return make_tl_object<secret_api::sendMessageUploadVideoAction>();
264 case Type::RecordingVoiceNote:
265 return make_tl_object<secret_api::sendMessageRecordAudioAction>();
266 case Type::UploadingVoiceNote:
267 return make_tl_object<secret_api::sendMessageUploadAudioAction>();
268 case Type::UploadingPhoto:
269 return make_tl_object<secret_api::sendMessageUploadPhotoAction>();
270 case Type::UploadingDocument:
271 return make_tl_object<secret_api::sendMessageUploadDocumentAction>();
272 case Type::ChoosingLocation:
273 return make_tl_object<secret_api::sendMessageGeoLocationAction>();
274 case Type::ChoosingContact:
275 return make_tl_object<secret_api::sendMessageChooseContactAction>();
276 case Type::StartPlayingGame:
277 return make_tl_object<secret_api::sendMessageTypingAction>();
278 case Type::RecordingVideoNote:
279 return make_tl_object<secret_api::sendMessageRecordRoundAction>();
280 case Type::UploadingVideoNote:
281 return make_tl_object<secret_api::sendMessageUploadRoundAction>();
282 case Type::SpeakingInVoiceChat:
283 return make_tl_object<secret_api::sendMessageTypingAction>();
284 case Type::ImportingMessages:
285 return make_tl_object<secret_api::sendMessageTypingAction>();
286 case Type::ChoosingSticker:
287 return make_tl_object<secret_api::sendMessageTypingAction>();
288 case Type::WatchingAnimations:
289 return make_tl_object<secret_api::sendMessageTypingAction>();
290 case Type::ClickingAnimatedEmoji:
291 default:
292 UNREACHABLE();
293 return nullptr;
294 }
295 }
296
get_chat_action_object() const297 tl_object_ptr<td_api::ChatAction> DialogAction::get_chat_action_object() const {
298 switch (type_) {
299 case Type::Cancel:
300 return td_api::make_object<td_api::chatActionCancel>();
301 case Type::Typing:
302 return td_api::make_object<td_api::chatActionTyping>();
303 case Type::RecordingVideo:
304 return td_api::make_object<td_api::chatActionRecordingVideo>();
305 case Type::UploadingVideo:
306 return td_api::make_object<td_api::chatActionUploadingVideo>(progress_);
307 case Type::RecordingVoiceNote:
308 return td_api::make_object<td_api::chatActionRecordingVoiceNote>();
309 case Type::UploadingVoiceNote:
310 return td_api::make_object<td_api::chatActionUploadingVoiceNote>(progress_);
311 case Type::UploadingPhoto:
312 return td_api::make_object<td_api::chatActionUploadingPhoto>(progress_);
313 case Type::UploadingDocument:
314 return td_api::make_object<td_api::chatActionUploadingDocument>(progress_);
315 case Type::ChoosingLocation:
316 return td_api::make_object<td_api::chatActionChoosingLocation>();
317 case Type::ChoosingContact:
318 return td_api::make_object<td_api::chatActionChoosingContact>();
319 case Type::StartPlayingGame:
320 return td_api::make_object<td_api::chatActionStartPlayingGame>();
321 case Type::RecordingVideoNote:
322 return td_api::make_object<td_api::chatActionRecordingVideoNote>();
323 case Type::UploadingVideoNote:
324 return td_api::make_object<td_api::chatActionUploadingVideoNote>(progress_);
325 case Type::ChoosingSticker:
326 return td_api::make_object<td_api::chatActionChoosingSticker>();
327 case Type::WatchingAnimations:
328 return td_api::make_object<td_api::chatActionWatchingAnimations>(emoji_);
329 case Type::ImportingMessages:
330 case Type::SpeakingInVoiceChat:
331 case Type::ClickingAnimatedEmoji:
332 default:
333 UNREACHABLE();
334 return td_api::make_object<td_api::chatActionCancel>();
335 }
336 }
337
is_canceled_by_message_of_type(MessageContentType message_content_type) const338 bool DialogAction::is_canceled_by_message_of_type(MessageContentType message_content_type) const {
339 if (message_content_type == MessageContentType::None) {
340 return true;
341 }
342
343 if (type_ == Type::Typing) {
344 return message_content_type == MessageContentType::Text || message_content_type == MessageContentType::Game ||
345 can_have_message_content_caption(message_content_type);
346 }
347
348 switch (message_content_type) {
349 case MessageContentType::Animation:
350 case MessageContentType::Audio:
351 case MessageContentType::Document:
352 return type_ == Type::UploadingDocument;
353 case MessageContentType::ExpiredPhoto:
354 case MessageContentType::Photo:
355 return type_ == Type::UploadingPhoto;
356 case MessageContentType::ExpiredVideo:
357 case MessageContentType::Video:
358 return type_ == Type::RecordingVideo || type_ == Type::UploadingVideo;
359 case MessageContentType::VideoNote:
360 return type_ == Type::RecordingVideoNote || type_ == Type::UploadingVideoNote;
361 case MessageContentType::VoiceNote:
362 return type_ == Type::RecordingVoiceNote || type_ == Type::UploadingVoiceNote;
363 case MessageContentType::Contact:
364 return type_ == Type::ChoosingContact;
365 case MessageContentType::LiveLocation:
366 case MessageContentType::Location:
367 case MessageContentType::Venue:
368 return type_ == Type::ChoosingLocation;
369 case MessageContentType::Sticker:
370 return type_ == Type::ChoosingSticker;
371 case MessageContentType::Game:
372 case MessageContentType::Invoice:
373 case MessageContentType::Text:
374 case MessageContentType::Unsupported:
375 case MessageContentType::ChatCreate:
376 case MessageContentType::ChatChangeTitle:
377 case MessageContentType::ChatChangePhoto:
378 case MessageContentType::ChatDeletePhoto:
379 case MessageContentType::ChatDeleteHistory:
380 case MessageContentType::ChatAddUsers:
381 case MessageContentType::ChatJoinedByLink:
382 case MessageContentType::ChatDeleteUser:
383 case MessageContentType::ChatMigrateTo:
384 case MessageContentType::ChannelCreate:
385 case MessageContentType::ChannelMigrateFrom:
386 case MessageContentType::PinMessage:
387 case MessageContentType::GameScore:
388 case MessageContentType::ScreenshotTaken:
389 case MessageContentType::ChatSetTtl:
390 case MessageContentType::Call:
391 case MessageContentType::PaymentSuccessful:
392 case MessageContentType::ContactRegistered:
393 case MessageContentType::CustomServiceAction:
394 case MessageContentType::WebsiteConnected:
395 case MessageContentType::PassportDataSent:
396 case MessageContentType::PassportDataReceived:
397 case MessageContentType::Poll:
398 case MessageContentType::Dice:
399 case MessageContentType::ProximityAlertTriggered:
400 case MessageContentType::GroupCall:
401 case MessageContentType::InviteToGroupCall:
402 case MessageContentType::ChatSetTheme:
403 return false;
404 default:
405 UNREACHABLE();
406 return false;
407 }
408 }
409
get_uploading_action(MessageContentType message_content_type,int32 progress)410 DialogAction DialogAction::get_uploading_action(MessageContentType message_content_type, int32 progress) {
411 switch (message_content_type) {
412 case MessageContentType::Animation:
413 case MessageContentType::Audio:
414 case MessageContentType::Document:
415 return DialogAction(Type::UploadingDocument, progress);
416 case MessageContentType::Photo:
417 return DialogAction(Type::UploadingPhoto, progress);
418 case MessageContentType::Video:
419 return DialogAction(Type::UploadingVideo, progress);
420 case MessageContentType::VideoNote:
421 return DialogAction(Type::UploadingVideoNote, progress);
422 case MessageContentType::VoiceNote:
423 return DialogAction(Type::UploadingVoiceNote, progress);
424 default:
425 return DialogAction();
426 }
427 }
428
get_typing_action()429 DialogAction DialogAction::get_typing_action() {
430 return DialogAction(Type::Typing, 0);
431 }
432
get_speaking_action()433 DialogAction DialogAction::get_speaking_action() {
434 return DialogAction(Type::SpeakingInVoiceChat, 0);
435 }
436
get_importing_messages_action_progress() const437 int32 DialogAction::get_importing_messages_action_progress() const {
438 if (type_ != Type::ImportingMessages) {
439 return -1;
440 }
441 return progress_;
442 }
443
get_watching_animations_emoji() const444 string DialogAction::get_watching_animations_emoji() const {
445 if (type_ == Type::WatchingAnimations) {
446 return emoji_;
447 }
448 return string();
449 }
450
get_clicking_animated_emoji_action_info() const451 DialogAction::ClickingAnimateEmojiInfo DialogAction::get_clicking_animated_emoji_action_info() const {
452 ClickingAnimateEmojiInfo result;
453 if (type_ == Type::ClickingAnimatedEmoji) {
454 auto pos = emoji_.find('\xFF');
455 CHECK(pos < emoji_.size());
456 result.message_id = progress_;
457 result.emoji = emoji_.substr(0, pos);
458 result.data = emoji_.substr(pos + 1);
459 }
460 return result;
461 }
462
operator <<(StringBuilder & string_builder,const DialogAction & action)463 StringBuilder &operator<<(StringBuilder &string_builder, const DialogAction &action) {
464 string_builder << "ChatAction";
465 const char *type = [action_type = action.type_] {
466 switch (action_type) {
467 case DialogAction::Type::Cancel:
468 return "Cancel";
469 case DialogAction::Type::Typing:
470 return "Typing";
471 case DialogAction::Type::RecordingVideo:
472 return "RecordingVideo";
473 case DialogAction::Type::UploadingVideo:
474 return "UploadingVideo";
475 case DialogAction::Type::RecordingVoiceNote:
476 return "RecordingVoiceNote";
477 case DialogAction::Type::UploadingVoiceNote:
478 return "UploadingVoiceNote";
479 case DialogAction::Type::UploadingPhoto:
480 return "UploadingPhoto";
481 case DialogAction::Type::UploadingDocument:
482 return "UploadingDocument";
483 case DialogAction::Type::ChoosingLocation:
484 return "ChoosingLocation";
485 case DialogAction::Type::ChoosingContact:
486 return "ChoosingContact";
487 case DialogAction::Type::StartPlayingGame:
488 return "StartPlayingGame";
489 case DialogAction::Type::RecordingVideoNote:
490 return "RecordingVideoNote";
491 case DialogAction::Type::UploadingVideoNote:
492 return "UploadingVideoNote";
493 case DialogAction::Type::SpeakingInVoiceChat:
494 return "SpeakingInVoiceChat";
495 case DialogAction::Type::ImportingMessages:
496 return "ImportingMessages";
497 case DialogAction::Type::ChoosingSticker:
498 return "ChoosingSticker";
499 case DialogAction::Type::WatchingAnimations:
500 return "WatchingAnimations";
501 case DialogAction::Type::ClickingAnimatedEmoji:
502 return "ClickingAnimatedEmoji";
503 default:
504 UNREACHABLE();
505 return "Cancel";
506 }
507 }();
508 string_builder << type << "Action";
509 if (action.type_ == DialogAction::Type::ClickingAnimatedEmoji) {
510 auto pos = action.emoji_.find('\xFF');
511 CHECK(pos < action.emoji_.size());
512 string_builder << '(' << action.progress_ << ")(" << Slice(action.emoji_).substr(0, pos) << ")("
513 << Slice(action.emoji_).substr(pos + 1) << ')';
514 } else {
515 if (action.progress_ != 0) {
516 string_builder << '(' << action.progress_ << "%)";
517 }
518 if (!action.emoji_.empty()) {
519 string_builder << '(' << action.emoji_ << ')';
520 }
521 }
522 return string_builder;
523 }
524
525 } // namespace td
526