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/StickersManager.h"
8
9 #include "td/telegram/AccessRights.h"
10 #include "td/telegram/AuthManager.h"
11 #include "td/telegram/ConfigManager.h"
12 #include "td/telegram/ConfigShared.h"
13 #include "td/telegram/ContactsManager.h"
14 #include "td/telegram/DialogId.h"
15 #include "td/telegram/Document.h"
16 #include "td/telegram/DocumentsManager.h"
17 #include "td/telegram/FileReferenceManager.h"
18 #include "td/telegram/files/FileLocation.h"
19 #include "td/telegram/files/FileManager.h"
20 #include "td/telegram/files/FileType.h"
21 #include "td/telegram/Global.h"
22 #include "td/telegram/LanguagePackManager.h"
23 #include "td/telegram/logevent/LogEvent.h"
24 #include "td/telegram/MessagesManager.h"
25 #include "td/telegram/misc.h"
26 #include "td/telegram/net/DcId.h"
27 #include "td/telegram/net/MtprotoHeader.h"
28 #include "td/telegram/net/NetQueryDispatcher.h"
29 #include "td/telegram/PhotoSizeSource.h"
30 #include "td/telegram/secret_api.h"
31 #include "td/telegram/StickerSetId.hpp"
32 #include "td/telegram/StickersManager.hpp"
33 #include "td/telegram/Td.h"
34 #include "td/telegram/td_api.h"
35 #include "td/telegram/TdDb.h"
36 #include "td/telegram/TdParameters.h"
37 #include "td/telegram/telegram_api.h"
38
39 #include "td/db/SqliteKeyValue.h"
40 #include "td/db/SqliteKeyValueAsync.h"
41
42 #include "td/actor/MultiPromise.h"
43 #include "td/actor/PromiseFuture.h"
44 #include "td/actor/SleepActor.h"
45
46 #include "td/utils/algorithm.h"
47 #include "td/utils/base64.h"
48 #include "td/utils/emoji.h"
49 #include "td/utils/format.h"
50 #include "td/utils/JsonBuilder.h"
51 #include "td/utils/logging.h"
52 #include "td/utils/MimeType.h"
53 #include "td/utils/misc.h"
54 #include "td/utils/PathView.h"
55 #include "td/utils/Random.h"
56 #include "td/utils/Slice.h"
57 #include "td/utils/SliceBuilder.h"
58 #include "td/utils/StackAllocator.h"
59 #include "td/utils/StringBuilder.h"
60 #include "td/utils/Time.h"
61 #include "td/utils/tl_helpers.h"
62 #include "td/utils/utf8.h"
63
64 #include <algorithm>
65 #include <cmath>
66 #include <limits>
67 #include <type_traits>
68 #include <unordered_set>
69
70 namespace td {
71
72 class GetAllStickersQuery final : public Td::ResultHandler {
73 bool is_masks_;
74
75 public:
send(bool is_masks,int64 hash)76 void send(bool is_masks, int64 hash) {
77 is_masks_ = is_masks;
78 if (is_masks) {
79 send_query(G()->net_query_creator().create(telegram_api::messages_getMaskStickers(hash)));
80 } else {
81 send_query(G()->net_query_creator().create(telegram_api::messages_getAllStickers(hash)));
82 }
83 }
84
on_result(BufferSlice packet)85 void on_result(BufferSlice packet) final {
86 static_assert(std::is_same<telegram_api::messages_getMaskStickers::ReturnType,
87 telegram_api::messages_getAllStickers::ReturnType>::value,
88 "");
89 auto result_ptr = fetch_result<telegram_api::messages_getAllStickers>(packet);
90 if (result_ptr.is_error()) {
91 return on_error(result_ptr.move_as_error());
92 }
93
94 auto ptr = result_ptr.move_as_ok();
95 LOG(DEBUG) << "Receive result for get all " << (is_masks_ ? "masks" : "stickers") << ": " << to_string(ptr);
96 td_->stickers_manager_->on_get_installed_sticker_sets(is_masks_, std::move(ptr));
97 }
98
on_error(Status status)99 void on_error(Status status) final {
100 if (!G()->is_expected_error(status)) {
101 LOG(ERROR) << "Receive error for get all stickers: " << status;
102 }
103 td_->stickers_manager_->on_get_installed_sticker_sets_failed(is_masks_, std::move(status));
104 }
105 };
106
107 class SearchStickersQuery final : public Td::ResultHandler {
108 string emoji_;
109
110 public:
send(string emoji,int64 hash)111 void send(string emoji, int64 hash) {
112 emoji_ = std::move(emoji);
113 send_query(G()->net_query_creator().create(telegram_api::messages_getStickers(emoji_, hash)));
114 }
115
on_result(BufferSlice packet)116 void on_result(BufferSlice packet) final {
117 auto result_ptr = fetch_result<telegram_api::messages_getStickers>(packet);
118 if (result_ptr.is_error()) {
119 return on_error(result_ptr.move_as_error());
120 }
121
122 auto ptr = result_ptr.move_as_ok();
123 LOG(INFO) << "Receive result for search stickers: " << to_string(ptr);
124 td_->stickers_manager_->on_find_stickers_success(emoji_, std::move(ptr));
125 }
126
on_error(Status status)127 void on_error(Status status) final {
128 if (!G()->is_expected_error(status)) {
129 LOG(ERROR) << "Receive error for search stickers: " << status;
130 }
131 td_->stickers_manager_->on_find_stickers_fail(emoji_, std::move(status));
132 }
133 };
134
135 class GetEmojiKeywordsLanguageQuery final : public Td::ResultHandler {
136 Promise<vector<string>> promise_;
137
138 public:
GetEmojiKeywordsLanguageQuery(Promise<vector<string>> && promise)139 explicit GetEmojiKeywordsLanguageQuery(Promise<vector<string>> &&promise) : promise_(std::move(promise)) {
140 }
141
send(vector<string> && language_codes)142 void send(vector<string> &&language_codes) {
143 send_query(
144 G()->net_query_creator().create(telegram_api::messages_getEmojiKeywordsLanguages(std::move(language_codes))));
145 }
on_result(BufferSlice packet)146 void on_result(BufferSlice packet) final {
147 auto result_ptr = fetch_result<telegram_api::messages_getEmojiKeywordsLanguages>(packet);
148 if (result_ptr.is_error()) {
149 return on_error(result_ptr.move_as_error());
150 }
151
152 auto result =
153 transform(result_ptr.move_as_ok(), [](auto &&emoji_language) { return std::move(emoji_language->lang_code_); });
154 promise_.set_value(std::move(result));
155 }
156
on_error(Status status)157 void on_error(Status status) final {
158 promise_.set_error(std::move(status));
159 }
160 };
161
162 class GetEmojiKeywordsQuery final : public Td::ResultHandler {
163 Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> promise_;
164
165 public:
GetEmojiKeywordsQuery(Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> && promise)166 explicit GetEmojiKeywordsQuery(Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&promise)
167 : promise_(std::move(promise)) {
168 }
169
send(const string & language_code)170 void send(const string &language_code) {
171 send_query(G()->net_query_creator().create(telegram_api::messages_getEmojiKeywords(language_code)));
172 }
173
on_result(BufferSlice packet)174 void on_result(BufferSlice packet) final {
175 auto result_ptr = fetch_result<telegram_api::messages_getEmojiKeywords>(packet);
176 if (result_ptr.is_error()) {
177 return on_error(result_ptr.move_as_error());
178 }
179
180 promise_.set_value(result_ptr.move_as_ok());
181 }
182
on_error(Status status)183 void on_error(Status status) final {
184 promise_.set_error(std::move(status));
185 }
186 };
187
188 class GetEmojiKeywordsDifferenceQuery final : public Td::ResultHandler {
189 Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> promise_;
190
191 public:
GetEmojiKeywordsDifferenceQuery(Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> && promise)192 explicit GetEmojiKeywordsDifferenceQuery(
193 Promise<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&promise)
194 : promise_(std::move(promise)) {
195 }
196
send(const string & language_code,int32 version)197 void send(const string &language_code, int32 version) {
198 send_query(
199 G()->net_query_creator().create(telegram_api::messages_getEmojiKeywordsDifference(language_code, version)));
200 }
201
on_result(BufferSlice packet)202 void on_result(BufferSlice packet) final {
203 auto result_ptr = fetch_result<telegram_api::messages_getEmojiKeywordsDifference>(packet);
204 if (result_ptr.is_error()) {
205 return on_error(result_ptr.move_as_error());
206 }
207
208 promise_.set_value(result_ptr.move_as_ok());
209 }
210
on_error(Status status)211 void on_error(Status status) final {
212 promise_.set_error(std::move(status));
213 }
214 };
215
216 class GetEmojiUrlQuery final : public Td::ResultHandler {
217 Promise<telegram_api::object_ptr<telegram_api::emojiURL>> promise_;
218
219 public:
GetEmojiUrlQuery(Promise<telegram_api::object_ptr<telegram_api::emojiURL>> && promise)220 explicit GetEmojiUrlQuery(Promise<telegram_api::object_ptr<telegram_api::emojiURL>> &&promise)
221 : promise_(std::move(promise)) {
222 }
223
send(const string & language_code)224 void send(const string &language_code) {
225 send_query(G()->net_query_creator().create(telegram_api::messages_getEmojiURL(language_code)));
226 }
227
on_result(BufferSlice packet)228 void on_result(BufferSlice packet) final {
229 auto result_ptr = fetch_result<telegram_api::messages_getEmojiURL>(packet);
230 if (result_ptr.is_error()) {
231 return on_error(result_ptr.move_as_error());
232 }
233
234 promise_.set_value(result_ptr.move_as_ok());
235 }
236
on_error(Status status)237 void on_error(Status status) final {
238 promise_.set_error(std::move(status));
239 }
240 };
241
242 class GetArchivedStickerSetsQuery final : public Td::ResultHandler {
243 Promise<Unit> promise_;
244 StickerSetId offset_sticker_set_id_;
245 bool is_masks_;
246
247 public:
GetArchivedStickerSetsQuery(Promise<Unit> && promise)248 explicit GetArchivedStickerSetsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
249 }
250
send(bool is_masks,StickerSetId offset_sticker_set_id,int32 limit)251 void send(bool is_masks, StickerSetId offset_sticker_set_id, int32 limit) {
252 offset_sticker_set_id_ = offset_sticker_set_id;
253 is_masks_ = is_masks;
254
255 int32 flags = 0;
256 if (is_masks_) {
257 flags |= telegram_api::messages_getArchivedStickers::MASKS_MASK;
258 }
259 send_query(G()->net_query_creator().create(
260 telegram_api::messages_getArchivedStickers(flags, is_masks /*ignored*/, offset_sticker_set_id.get(), limit)));
261 }
262
on_result(BufferSlice packet)263 void on_result(BufferSlice packet) final {
264 auto result_ptr = fetch_result<telegram_api::messages_getArchivedStickers>(packet);
265 if (result_ptr.is_error()) {
266 return on_error(result_ptr.move_as_error());
267 }
268
269 auto ptr = result_ptr.move_as_ok();
270 LOG(INFO) << "Receive result for GetArchivedStickerSetsQuery: " << to_string(ptr);
271 td_->stickers_manager_->on_get_archived_sticker_sets(is_masks_, offset_sticker_set_id_, std::move(ptr->sets_),
272 ptr->count_);
273
274 promise_.set_value(Unit());
275 }
276
on_error(Status status)277 void on_error(Status status) final {
278 promise_.set_error(std::move(status));
279 }
280 };
281
282 class GetFeaturedStickerSetsQuery final : public Td::ResultHandler {
283 public:
send(int64 hash)284 void send(int64 hash) {
285 send_query(G()->net_query_creator().create(telegram_api::messages_getFeaturedStickers(hash)));
286 }
287
on_result(BufferSlice packet)288 void on_result(BufferSlice packet) final {
289 auto result_ptr = fetch_result<telegram_api::messages_getFeaturedStickers>(packet);
290 if (result_ptr.is_error()) {
291 return on_error(result_ptr.move_as_error());
292 }
293
294 auto ptr = result_ptr.move_as_ok();
295 LOG(DEBUG) << "Receive result for GetFeaturedStickerSetsQuery: " << to_string(ptr);
296 td_->stickers_manager_->on_get_featured_sticker_sets(-1, -1, 0, std::move(ptr));
297 }
298
on_error(Status status)299 void on_error(Status status) final {
300 td_->stickers_manager_->on_get_featured_sticker_sets_failed(-1, -1, 0, std::move(status));
301 }
302 };
303
304 class GetOldFeaturedStickerSetsQuery final : public Td::ResultHandler {
305 int32 offset_;
306 int32 limit_;
307 uint32 generation_;
308
309 public:
send(int32 offset,int32 limit,uint32 generation)310 void send(int32 offset, int32 limit, uint32 generation) {
311 offset_ = offset;
312 limit_ = limit;
313 generation_ = generation;
314 send_query(G()->net_query_creator().create(telegram_api::messages_getOldFeaturedStickers(offset, limit, 0)));
315 }
316
on_result(BufferSlice packet)317 void on_result(BufferSlice packet) final {
318 auto result_ptr = fetch_result<telegram_api::messages_getOldFeaturedStickers>(packet);
319 if (result_ptr.is_error()) {
320 return on_error(result_ptr.move_as_error());
321 }
322
323 auto ptr = result_ptr.move_as_ok();
324 LOG(DEBUG) << "Receive result for GetOldFeaturedStickerSetsQuery: " << to_string(ptr);
325 td_->stickers_manager_->on_get_featured_sticker_sets(offset_, limit_, generation_, std::move(ptr));
326 }
327
on_error(Status status)328 void on_error(Status status) final {
329 td_->stickers_manager_->on_get_featured_sticker_sets_failed(offset_, limit_, generation_, std::move(status));
330 }
331 };
332
333 class GetAttachedStickerSetsQuery final : public Td::ResultHandler {
334 Promise<Unit> promise_;
335 FileId file_id_;
336 string file_reference_;
337
338 public:
GetAttachedStickerSetsQuery(Promise<Unit> && promise)339 explicit GetAttachedStickerSetsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
340 }
341
send(FileId file_id,string && file_reference,tl_object_ptr<telegram_api::InputStickeredMedia> && input_stickered_media)342 void send(FileId file_id, string &&file_reference,
343 tl_object_ptr<telegram_api::InputStickeredMedia> &&input_stickered_media) {
344 file_id_ = file_id;
345 file_reference_ = std::move(file_reference);
346 send_query(
347 G()->net_query_creator().create(telegram_api::messages_getAttachedStickers(std::move(input_stickered_media))));
348 }
349
on_result(BufferSlice packet)350 void on_result(BufferSlice packet) final {
351 auto result_ptr = fetch_result<telegram_api::messages_getAttachedStickers>(packet);
352 if (result_ptr.is_error()) {
353 return on_error(result_ptr.move_as_error());
354 }
355
356 td_->stickers_manager_->on_get_attached_sticker_sets(file_id_, result_ptr.move_as_ok());
357
358 promise_.set_value(Unit());
359 }
360
on_error(Status status)361 void on_error(Status status) final {
362 if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) {
363 VLOG(file_references) << "Receive " << status << " for " << file_id_;
364 td_->file_manager_->delete_file_reference(file_id_, file_reference_);
365 td_->file_reference_manager_->repair_file_reference(
366 file_id_,
367 PromiseCreator::lambda([file_id = file_id_, promise = std::move(promise_)](Result<Unit> result) mutable {
368 if (result.is_error()) {
369 return promise.set_error(Status::Error(400, "Failed to find the file"));
370 }
371
372 send_closure(G()->stickers_manager(), &StickersManager::send_get_attached_stickers_query, file_id,
373 std::move(promise));
374 }));
375 return;
376 }
377
378 promise_.set_error(std::move(status));
379 }
380 };
381
382 class GetRecentStickersQuery final : public Td::ResultHandler {
383 bool is_repair_ = false;
384 bool is_attached_ = false;
385
386 public:
send(bool is_repair,bool is_attached,int64 hash)387 void send(bool is_repair, bool is_attached, int64 hash) {
388 is_repair_ = is_repair;
389 is_attached_ = is_attached;
390 int32 flags = 0;
391 if (is_attached) {
392 flags |= telegram_api::messages_getRecentStickers::ATTACHED_MASK;
393 }
394
395 send_query(G()->net_query_creator().create(
396 telegram_api::messages_getRecentStickers(flags, is_attached /*ignored*/, hash)));
397 }
398
on_result(BufferSlice packet)399 void on_result(BufferSlice packet) final {
400 auto result_ptr = fetch_result<telegram_api::messages_getRecentStickers>(packet);
401 if (result_ptr.is_error()) {
402 return on_error(result_ptr.move_as_error());
403 }
404
405 auto ptr = result_ptr.move_as_ok();
406 LOG(DEBUG) << "Receive result for get recent " << (is_attached_ ? "attached " : "")
407 << "stickers: " << to_string(ptr);
408 td_->stickers_manager_->on_get_recent_stickers(is_repair_, is_attached_, std::move(ptr));
409 }
410
on_error(Status status)411 void on_error(Status status) final {
412 if (!G()->is_expected_error(status)) {
413 LOG(ERROR) << "Receive error for get recent " << (is_attached_ ? "attached " : "") << "stickers: " << status;
414 }
415 td_->stickers_manager_->on_get_recent_stickers_failed(is_repair_, is_attached_, std::move(status));
416 }
417 };
418
419 class SaveRecentStickerQuery final : public Td::ResultHandler {
420 Promise<Unit> promise_;
421 FileId file_id_;
422 string file_reference_;
423 bool unsave_ = false;
424 bool is_attached_;
425
426 public:
SaveRecentStickerQuery(Promise<Unit> && promise)427 explicit SaveRecentStickerQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
428 }
429
send(bool is_attached,FileId file_id,tl_object_ptr<telegram_api::inputDocument> && input_document,bool unsave)430 void send(bool is_attached, FileId file_id, tl_object_ptr<telegram_api::inputDocument> &&input_document,
431 bool unsave) {
432 CHECK(input_document != nullptr);
433 CHECK(file_id.is_valid());
434 file_id_ = file_id;
435 file_reference_ = input_document->file_reference_.as_slice().str();
436 unsave_ = unsave;
437 is_attached_ = is_attached;
438
439 int32 flags = 0;
440 if (is_attached) {
441 flags |= telegram_api::messages_saveRecentSticker::ATTACHED_MASK;
442 }
443
444 send_query(G()->net_query_creator().create(
445 telegram_api::messages_saveRecentSticker(flags, is_attached /*ignored*/, std::move(input_document), unsave)));
446 }
447
on_result(BufferSlice packet)448 void on_result(BufferSlice packet) final {
449 auto result_ptr = fetch_result<telegram_api::messages_saveRecentSticker>(packet);
450 if (result_ptr.is_error()) {
451 return on_error(result_ptr.move_as_error());
452 }
453
454 bool result = result_ptr.move_as_ok();
455 LOG(INFO) << "Receive result for save recent " << (is_attached_ ? "attached " : "") << "sticker: " << result;
456 if (!result) {
457 td_->stickers_manager_->reload_recent_stickers(is_attached_, true);
458 }
459
460 promise_.set_value(Unit());
461 }
462
on_error(Status status)463 void on_error(Status status) final {
464 if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) {
465 VLOG(file_references) << "Receive " << status << " for " << file_id_;
466 td_->file_manager_->delete_file_reference(file_id_, file_reference_);
467 td_->file_reference_manager_->repair_file_reference(
468 file_id_, PromiseCreator::lambda([sticker_id = file_id_, is_attached = is_attached_, unsave = unsave_,
469 promise = std::move(promise_)](Result<Unit> result) mutable {
470 if (result.is_error()) {
471 return promise.set_error(Status::Error(400, "Failed to find the sticker"));
472 }
473
474 send_closure(G()->stickers_manager(), &StickersManager::send_save_recent_sticker_query, is_attached,
475 sticker_id, unsave, std::move(promise));
476 }));
477 return;
478 }
479
480 if (!G()->is_expected_error(status)) {
481 LOG(ERROR) << "Receive error for save recent " << (is_attached_ ? "attached " : "") << "sticker: " << status;
482 }
483 td_->stickers_manager_->reload_recent_stickers(is_attached_, true);
484 promise_.set_error(std::move(status));
485 }
486 };
487
488 class ClearRecentStickersQuery final : public Td::ResultHandler {
489 Promise<Unit> promise_;
490 bool is_attached_;
491
492 public:
ClearRecentStickersQuery(Promise<Unit> && promise)493 explicit ClearRecentStickersQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
494 }
495
send(bool is_attached)496 void send(bool is_attached) {
497 is_attached_ = is_attached;
498
499 int32 flags = 0;
500 if (is_attached) {
501 flags |= telegram_api::messages_clearRecentStickers::ATTACHED_MASK;
502 }
503
504 send_query(
505 G()->net_query_creator().create(telegram_api::messages_clearRecentStickers(flags, is_attached /*ignored*/)));
506 }
507
on_result(BufferSlice packet)508 void on_result(BufferSlice packet) final {
509 auto result_ptr = fetch_result<telegram_api::messages_clearRecentStickers>(packet);
510 if (result_ptr.is_error()) {
511 return on_error(result_ptr.move_as_error());
512 }
513
514 bool result = result_ptr.move_as_ok();
515 LOG(INFO) << "Receive result for clear recent " << (is_attached_ ? "attached " : "") << "stickers: " << result;
516 if (!result) {
517 td_->stickers_manager_->reload_recent_stickers(is_attached_, true);
518 }
519
520 promise_.set_value(Unit());
521 }
522
on_error(Status status)523 void on_error(Status status) final {
524 if (!G()->is_expected_error(status)) {
525 LOG(ERROR) << "Receive error for clear recent " << (is_attached_ ? "attached " : "") << "stickers: " << status;
526 }
527 td_->stickers_manager_->reload_recent_stickers(is_attached_, true);
528 promise_.set_error(std::move(status));
529 }
530 };
531
532 class GetFavedStickersQuery final : public Td::ResultHandler {
533 bool is_repair_ = false;
534
535 public:
send(bool is_repair,int64 hash)536 void send(bool is_repair, int64 hash) {
537 is_repair_ = is_repair;
538 send_query(G()->net_query_creator().create(telegram_api::messages_getFavedStickers(hash)));
539 }
540
on_result(BufferSlice packet)541 void on_result(BufferSlice packet) final {
542 auto result_ptr = fetch_result<telegram_api::messages_getFavedStickers>(packet);
543 if (result_ptr.is_error()) {
544 return on_error(result_ptr.move_as_error());
545 }
546
547 auto ptr = result_ptr.move_as_ok();
548 td_->stickers_manager_->on_get_favorite_stickers(is_repair_, std::move(ptr));
549 }
550
on_error(Status status)551 void on_error(Status status) final {
552 if (!G()->is_expected_error(status)) {
553 LOG(ERROR) << "Receive error for get favorite stickers: " << status;
554 }
555 td_->stickers_manager_->on_get_favorite_stickers_failed(is_repair_, std::move(status));
556 }
557 };
558
559 class FaveStickerQuery final : public Td::ResultHandler {
560 FileId file_id_;
561 string file_reference_;
562 bool unsave_ = false;
563
564 Promise<Unit> promise_;
565
566 public:
FaveStickerQuery(Promise<Unit> && promise)567 explicit FaveStickerQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
568 }
569
send(FileId file_id,tl_object_ptr<telegram_api::inputDocument> && input_document,bool unsave)570 void send(FileId file_id, tl_object_ptr<telegram_api::inputDocument> &&input_document, bool unsave) {
571 CHECK(input_document != nullptr);
572 CHECK(file_id.is_valid());
573 file_id_ = file_id;
574 file_reference_ = input_document->file_reference_.as_slice().str();
575 unsave_ = unsave;
576
577 send_query(G()->net_query_creator().create(telegram_api::messages_faveSticker(std::move(input_document), unsave)));
578 }
579
on_result(BufferSlice packet)580 void on_result(BufferSlice packet) final {
581 auto result_ptr = fetch_result<telegram_api::messages_faveSticker>(packet);
582 if (result_ptr.is_error()) {
583 return on_error(result_ptr.move_as_error());
584 }
585
586 bool result = result_ptr.move_as_ok();
587 LOG(INFO) << "Receive result for fave sticker: " << result;
588 if (!result) {
589 td_->stickers_manager_->reload_favorite_stickers(true);
590 }
591
592 promise_.set_value(Unit());
593 }
594
on_error(Status status)595 void on_error(Status status) final {
596 if (!td_->auth_manager_->is_bot() && FileReferenceManager::is_file_reference_error(status)) {
597 VLOG(file_references) << "Receive " << status << " for " << file_id_;
598 td_->file_manager_->delete_file_reference(file_id_, file_reference_);
599 td_->file_reference_manager_->repair_file_reference(
600 file_id_, PromiseCreator::lambda([sticker_id = file_id_, unsave = unsave_,
601 promise = std::move(promise_)](Result<Unit> result) mutable {
602 if (result.is_error()) {
603 return promise.set_error(Status::Error(400, "Failed to find the sticker"));
604 }
605
606 send_closure(G()->stickers_manager(), &StickersManager::send_fave_sticker_query, sticker_id, unsave,
607 std::move(promise));
608 }));
609 return;
610 }
611
612 if (!G()->is_expected_error(status)) {
613 LOG(ERROR) << "Receive error for fave sticker: " << status;
614 }
615 td_->stickers_manager_->reload_favorite_stickers(true);
616 promise_.set_error(std::move(status));
617 }
618 };
619
620 class ReorderStickerSetsQuery final : public Td::ResultHandler {
621 bool is_masks_;
622
623 public:
send(bool is_masks,const vector<StickerSetId> & sticker_set_ids)624 void send(bool is_masks, const vector<StickerSetId> &sticker_set_ids) {
625 is_masks_ = is_masks;
626 int32 flags = 0;
627 if (is_masks) {
628 flags |= telegram_api::messages_reorderStickerSets::MASKS_MASK;
629 }
630 send_query(G()->net_query_creator().create(telegram_api::messages_reorderStickerSets(
631 flags, is_masks /*ignored*/, StickersManager::convert_sticker_set_ids(sticker_set_ids))));
632 }
633
on_result(BufferSlice packet)634 void on_result(BufferSlice packet) final {
635 auto result_ptr = fetch_result<telegram_api::messages_reorderStickerSets>(packet);
636 if (result_ptr.is_error()) {
637 return on_error(result_ptr.move_as_error());
638 }
639
640 bool result = result_ptr.move_as_ok();
641 if (!result) {
642 return on_error(Status::Error(400, "Result is false"));
643 }
644 }
645
on_error(Status status)646 void on_error(Status status) final {
647 if (!G()->is_expected_error(status)) {
648 LOG(ERROR) << "Receive error for ReorderStickerSetsQuery: " << status;
649 }
650 td_->stickers_manager_->reload_installed_sticker_sets(is_masks_, true);
651 }
652 };
653
654 class GetStickerSetQuery final : public Td::ResultHandler {
655 Promise<Unit> promise_;
656 StickerSetId sticker_set_id_;
657 string sticker_set_name_;
658
659 public:
GetStickerSetQuery(Promise<Unit> && promise)660 explicit GetStickerSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
661 }
662
send(StickerSetId sticker_set_id,tl_object_ptr<telegram_api::InputStickerSet> && input_sticker_set,int32 hash)663 void send(StickerSetId sticker_set_id, tl_object_ptr<telegram_api::InputStickerSet> &&input_sticker_set, int32 hash) {
664 sticker_set_id_ = sticker_set_id;
665 if (input_sticker_set->get_id() == telegram_api::inputStickerSetShortName::ID) {
666 sticker_set_name_ =
667 static_cast<const telegram_api::inputStickerSetShortName *>(input_sticker_set.get())->short_name_;
668 }
669 send_query(
670 G()->net_query_creator().create(telegram_api::messages_getStickerSet(std::move(input_sticker_set), hash)));
671 }
672
on_result(BufferSlice packet)673 void on_result(BufferSlice packet) final {
674 auto result_ptr = fetch_result<telegram_api::messages_getStickerSet>(packet);
675 if (result_ptr.is_error()) {
676 return on_error(result_ptr.move_as_error());
677 }
678
679 auto set_ptr = result_ptr.move_as_ok();
680 if (set_ptr->get_id() == telegram_api::messages_stickerSet::ID) {
681 auto set = static_cast<telegram_api::messages_stickerSet *>(set_ptr.get());
682 constexpr int64 GREAT_MINDS_COLOR_SET_ID = 151353307481243663;
683 if (set->set_->id_ == GREAT_MINDS_COLOR_SET_ID) {
684 string great_minds_name = "TelegramGreatMinds";
685 if (sticker_set_id_.get() == StickersManager::GREAT_MINDS_SET_ID ||
686 trim(to_lower(sticker_set_name_)) == to_lower(great_minds_name)) {
687 set->set_->id_ = StickersManager::GREAT_MINDS_SET_ID;
688 set->set_->short_name_ = std::move(great_minds_name);
689 }
690 }
691 }
692
693 td_->stickers_manager_->on_get_messages_sticker_set(sticker_set_id_, std::move(set_ptr), true,
694 "GetStickerSetQuery");
695
696 promise_.set_value(Unit());
697 }
698
on_error(Status status)699 void on_error(Status status) final {
700 LOG(INFO) << "Receive error for GetStickerSetQuery: " << status;
701 td_->stickers_manager_->on_load_sticker_set_fail(sticker_set_id_, status);
702 promise_.set_error(std::move(status));
703 }
704 };
705
706 class ReloadSpecialStickerSetQuery final : public Td::ResultHandler {
707 StickerSetId sticker_set_id_;
708 SpecialStickerSetType type_;
709
710 public:
send(StickerSetId sticker_set_id,SpecialStickerSetType type,int32 hash)711 void send(StickerSetId sticker_set_id, SpecialStickerSetType type, int32 hash) {
712 sticker_set_id_ = sticker_set_id;
713 type_ = std::move(type);
714 send_query(
715 G()->net_query_creator().create(telegram_api::messages_getStickerSet(type_.get_input_sticker_set(), hash)));
716 }
717
on_result(BufferSlice packet)718 void on_result(BufferSlice packet) final {
719 auto result_ptr = fetch_result<telegram_api::messages_getStickerSet>(packet);
720 if (result_ptr.is_error()) {
721 return on_error(result_ptr.move_as_error());
722 }
723
724 auto set_ptr = result_ptr.move_as_ok();
725 if (set_ptr->get_id() == telegram_api::messages_stickerSet::ID) {
726 // sticker_set_id_ needs to be replaced always
727 sticker_set_id_ = td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), std::move(set_ptr), true,
728 "ReloadSpecialStickerSetQuery");
729 } else if (sticker_set_id_.is_valid()) {
730 td_->stickers_manager_->on_get_messages_sticker_set(sticker_set_id_, std::move(set_ptr), false,
731 "ReloadSpecialStickerSetQuery");
732 }
733 if (sticker_set_id_.is_valid()) {
734 td_->stickers_manager_->on_get_special_sticker_set(type_, sticker_set_id_);
735 } else {
736 on_error(Status::Error(500, "Failed to add special sticker set"));
737 }
738 }
739
on_error(Status status)740 void on_error(Status status) final {
741 LOG(WARNING) << "Receive error for ReloadSpecialStickerSetQuery: " << status;
742 td_->stickers_manager_->on_load_special_sticker_set(type_, std::move(status));
743 }
744 };
745
746 class SearchStickerSetsQuery final : public Td::ResultHandler {
747 string query_;
748
749 public:
send(string query)750 void send(string query) {
751 query_ = std::move(query);
752 send_query(
753 G()->net_query_creator().create(telegram_api::messages_searchStickerSets(0, false /*ignored*/, query_, 0)));
754 }
755
on_result(BufferSlice packet)756 void on_result(BufferSlice packet) final {
757 auto result_ptr = fetch_result<telegram_api::messages_searchStickerSets>(packet);
758 if (result_ptr.is_error()) {
759 return on_error(result_ptr.move_as_error());
760 }
761
762 auto ptr = result_ptr.move_as_ok();
763 LOG(INFO) << "Receive result for search sticker sets: " << to_string(ptr);
764 td_->stickers_manager_->on_find_sticker_sets_success(query_, std::move(ptr));
765 }
766
on_error(Status status)767 void on_error(Status status) final {
768 if (!G()->is_expected_error(status)) {
769 LOG(ERROR) << "Receive error for search sticker sets: " << status;
770 }
771 td_->stickers_manager_->on_find_sticker_sets_fail(query_, std::move(status));
772 }
773 };
774
775 class InstallStickerSetQuery final : public Td::ResultHandler {
776 Promise<Unit> promise_;
777 StickerSetId set_id_;
778 bool is_archived_;
779
780 public:
InstallStickerSetQuery(Promise<Unit> && promise)781 explicit InstallStickerSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
782 }
783
send(StickerSetId set_id,tl_object_ptr<telegram_api::InputStickerSet> && input_set,bool is_archived)784 void send(StickerSetId set_id, tl_object_ptr<telegram_api::InputStickerSet> &&input_set, bool is_archived) {
785 set_id_ = set_id;
786 is_archived_ = is_archived;
787 send_query(
788 G()->net_query_creator().create(telegram_api::messages_installStickerSet(std::move(input_set), is_archived)));
789 }
790
on_result(BufferSlice packet)791 void on_result(BufferSlice packet) final {
792 auto result_ptr = fetch_result<telegram_api::messages_installStickerSet>(packet);
793 if (result_ptr.is_error()) {
794 return on_error(result_ptr.move_as_error());
795 }
796
797 td_->stickers_manager_->on_install_sticker_set(set_id_, is_archived_, result_ptr.move_as_ok());
798
799 promise_.set_value(Unit());
800 }
801
on_error(Status status)802 void on_error(Status status) final {
803 CHECK(status.is_error());
804 promise_.set_error(std::move(status));
805 }
806 };
807
808 class UninstallStickerSetQuery final : public Td::ResultHandler {
809 Promise<Unit> promise_;
810 StickerSetId set_id_;
811
812 public:
UninstallStickerSetQuery(Promise<Unit> && promise)813 explicit UninstallStickerSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
814 }
815
send(StickerSetId set_id,tl_object_ptr<telegram_api::InputStickerSet> && input_set)816 void send(StickerSetId set_id, tl_object_ptr<telegram_api::InputStickerSet> &&input_set) {
817 set_id_ = set_id;
818 send_query(G()->net_query_creator().create(telegram_api::messages_uninstallStickerSet(std::move(input_set))));
819 }
820
on_result(BufferSlice packet)821 void on_result(BufferSlice packet) final {
822 auto result_ptr = fetch_result<telegram_api::messages_uninstallStickerSet>(packet);
823 if (result_ptr.is_error()) {
824 return on_error(result_ptr.move_as_error());
825 }
826
827 bool result = result_ptr.move_as_ok();
828 if (!result) {
829 LOG(WARNING) << "Receive false in result to uninstallStickerSet";
830 } else {
831 td_->stickers_manager_->on_uninstall_sticker_set(set_id_);
832 }
833
834 promise_.set_value(Unit());
835 }
836
on_error(Status status)837 void on_error(Status status) final {
838 CHECK(status.is_error());
839 promise_.set_error(std::move(status));
840 }
841 };
842
843 class ReadFeaturedStickerSetsQuery final : public Td::ResultHandler {
844 public:
send(const vector<StickerSetId> & sticker_set_ids)845 void send(const vector<StickerSetId> &sticker_set_ids) {
846 send_query(G()->net_query_creator().create(
847 telegram_api::messages_readFeaturedStickers(StickersManager::convert_sticker_set_ids(sticker_set_ids))));
848 }
849
on_result(BufferSlice packet)850 void on_result(BufferSlice packet) final {
851 auto result_ptr = fetch_result<telegram_api::messages_readFeaturedStickers>(packet);
852 if (result_ptr.is_error()) {
853 return on_error(result_ptr.move_as_error());
854 }
855
856 bool result = result_ptr.move_as_ok();
857 (void)result;
858 }
859
on_error(Status status)860 void on_error(Status status) final {
861 if (!G()->is_expected_error(status)) {
862 LOG(ERROR) << "Receive error for ReadFeaturedStickerSetsQuery: " << status;
863 }
864 td_->stickers_manager_->reload_featured_sticker_sets(true);
865 }
866 };
867
868 class UploadStickerFileQuery final : public Td::ResultHandler {
869 Promise<Unit> promise_;
870 FileId file_id_;
871 bool was_uploaded_ = false;
872
873 public:
UploadStickerFileQuery(Promise<Unit> && promise)874 explicit UploadStickerFileQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
875 }
876
send(tl_object_ptr<telegram_api::InputPeer> && input_peer,FileId file_id,tl_object_ptr<telegram_api::InputMedia> && input_media)877 void send(tl_object_ptr<telegram_api::InputPeer> &&input_peer, FileId file_id,
878 tl_object_ptr<telegram_api::InputMedia> &&input_media) {
879 CHECK(input_peer != nullptr);
880 CHECK(input_media != nullptr);
881 file_id_ = file_id;
882 was_uploaded_ = FileManager::extract_was_uploaded(input_media);
883 send_query(G()->net_query_creator().create(
884 telegram_api::messages_uploadMedia(std::move(input_peer), std::move(input_media))));
885 }
886
on_result(BufferSlice packet)887 void on_result(BufferSlice packet) final {
888 auto result_ptr = fetch_result<telegram_api::messages_uploadMedia>(packet);
889 if (result_ptr.is_error()) {
890 return on_error(result_ptr.move_as_error());
891 }
892
893 td_->stickers_manager_->on_uploaded_sticker_file(file_id_, result_ptr.move_as_ok(), std::move(promise_));
894 }
895
on_error(Status status)896 void on_error(Status status) final {
897 CHECK(status.is_error());
898 if (was_uploaded_) {
899 CHECK(file_id_.is_valid());
900 if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) {
901 // TODO td_->stickers_manager_->on_upload_sticker_file_part_missing(file_id_, to_integer<int32>(status.message().substr(10)));
902 // return;
903 } else {
904 if (status.code() != 429 && status.code() < 500 && !G()->close_flag()) {
905 td_->file_manager_->delete_partial_remote_location(file_id_);
906 }
907 }
908 } else if (FileReferenceManager::is_file_reference_error(status)) {
909 LOG(ERROR) << "Receive file reference error for UploadStickerFileQuery";
910 }
911 td_->file_manager_->cancel_upload(file_id_);
912 promise_.set_error(std::move(status));
913 }
914 };
915
916 class SuggestStickerSetShortNameQuery final : public Td::ResultHandler {
917 Promise<string> promise_;
918
919 public:
SuggestStickerSetShortNameQuery(Promise<string> && promise)920 explicit SuggestStickerSetShortNameQuery(Promise<string> &&promise) : promise_(std::move(promise)) {
921 }
922
send(const string & title)923 void send(const string &title) {
924 send_query(G()->net_query_creator().create(telegram_api::stickers_suggestShortName(title)));
925 }
on_result(BufferSlice packet)926 void on_result(BufferSlice packet) final {
927 auto result_ptr = fetch_result<telegram_api::stickers_suggestShortName>(packet);
928 if (result_ptr.is_error()) {
929 return on_error(result_ptr.move_as_error());
930 }
931
932 auto ptr = result_ptr.move_as_ok();
933 promise_.set_value(std::move(ptr->short_name_));
934 }
935
on_error(Status status)936 void on_error(Status status) final {
937 if (status.message() == "TITLE_INVALID") {
938 return promise_.set_value(string());
939 }
940 promise_.set_error(std::move(status));
941 }
942 };
943
944 class CheckStickerSetShortNameQuery final : public Td::ResultHandler {
945 Promise<bool> promise_;
946
947 public:
CheckStickerSetShortNameQuery(Promise<bool> && promise)948 explicit CheckStickerSetShortNameQuery(Promise<bool> &&promise) : promise_(std::move(promise)) {
949 }
950
send(const string & short_name)951 void send(const string &short_name) {
952 send_query(G()->net_query_creator().create(telegram_api::stickers_checkShortName(short_name)));
953 }
954
on_result(BufferSlice packet)955 void on_result(BufferSlice packet) final {
956 auto result_ptr = fetch_result<telegram_api::stickers_checkShortName>(packet);
957 if (result_ptr.is_error()) {
958 return on_error(result_ptr.move_as_error());
959 }
960
961 promise_.set_value(result_ptr.move_as_ok());
962 }
963
on_error(Status status)964 void on_error(Status status) final {
965 promise_.set_error(std::move(status));
966 }
967 };
968
969 class CreateNewStickerSetQuery final : public Td::ResultHandler {
970 Promise<Unit> promise_;
971
972 public:
CreateNewStickerSetQuery(Promise<Unit> && promise)973 explicit CreateNewStickerSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
974 }
975
send(tl_object_ptr<telegram_api::InputUser> && input_user,const string & title,const string & short_name,bool is_masks,bool is_animated,vector<tl_object_ptr<telegram_api::inputStickerSetItem>> && input_stickers,const string & software)976 void send(tl_object_ptr<telegram_api::InputUser> &&input_user, const string &title, const string &short_name,
977 bool is_masks, bool is_animated, vector<tl_object_ptr<telegram_api::inputStickerSetItem>> &&input_stickers,
978 const string &software) {
979 CHECK(input_user != nullptr);
980
981 int32 flags = 0;
982 if (is_masks) {
983 flags |= telegram_api::stickers_createStickerSet::MASKS_MASK;
984 }
985 if (is_animated) {
986 flags |= telegram_api::stickers_createStickerSet::ANIMATED_MASK;
987 }
988 if (!software.empty()) {
989 flags |= telegram_api::stickers_createStickerSet::SOFTWARE_MASK;
990 }
991
992 send_query(G()->net_query_creator().create(
993 telegram_api::stickers_createStickerSet(flags, false /*ignored*/, false /*ignored*/, std::move(input_user),
994 title, short_name, nullptr, std::move(input_stickers), software)));
995 }
996
on_result(BufferSlice packet)997 void on_result(BufferSlice packet) final {
998 auto result_ptr = fetch_result<telegram_api::stickers_createStickerSet>(packet);
999 if (result_ptr.is_error()) {
1000 return on_error(result_ptr.move_as_error());
1001 }
1002
1003 td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true,
1004 "CreateNewStickerSetQuery");
1005
1006 promise_.set_value(Unit());
1007 }
1008
on_error(Status status)1009 void on_error(Status status) final {
1010 CHECK(status.is_error());
1011 promise_.set_error(std::move(status));
1012 }
1013 };
1014
1015 class AddStickerToSetQuery final : public Td::ResultHandler {
1016 Promise<Unit> promise_;
1017
1018 public:
AddStickerToSetQuery(Promise<Unit> && promise)1019 explicit AddStickerToSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
1020 }
1021
send(const string & short_name,tl_object_ptr<telegram_api::inputStickerSetItem> && input_sticker)1022 void send(const string &short_name, tl_object_ptr<telegram_api::inputStickerSetItem> &&input_sticker) {
1023 send_query(G()->net_query_creator().create(telegram_api::stickers_addStickerToSet(
1024 make_tl_object<telegram_api::inputStickerSetShortName>(short_name), std::move(input_sticker))));
1025 }
1026
on_result(BufferSlice packet)1027 void on_result(BufferSlice packet) final {
1028 auto result_ptr = fetch_result<telegram_api::stickers_addStickerToSet>(packet);
1029 if (result_ptr.is_error()) {
1030 return on_error(result_ptr.move_as_error());
1031 }
1032
1033 td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true,
1034 "AddStickerToSetQuery");
1035
1036 promise_.set_value(Unit());
1037 }
1038
on_error(Status status)1039 void on_error(Status status) final {
1040 CHECK(status.is_error());
1041 promise_.set_error(std::move(status));
1042 }
1043 };
1044
1045 class SetStickerSetThumbnailQuery final : public Td::ResultHandler {
1046 Promise<Unit> promise_;
1047
1048 public:
SetStickerSetThumbnailQuery(Promise<Unit> && promise)1049 explicit SetStickerSetThumbnailQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
1050 }
1051
send(const string & short_name,tl_object_ptr<telegram_api::InputDocument> && input_document)1052 void send(const string &short_name, tl_object_ptr<telegram_api::InputDocument> &&input_document) {
1053 send_query(G()->net_query_creator().create(telegram_api::stickers_setStickerSetThumb(
1054 make_tl_object<telegram_api::inputStickerSetShortName>(short_name), std::move(input_document))));
1055 }
1056
on_result(BufferSlice packet)1057 void on_result(BufferSlice packet) final {
1058 auto result_ptr = fetch_result<telegram_api::stickers_setStickerSetThumb>(packet);
1059 if (result_ptr.is_error()) {
1060 return on_error(result_ptr.move_as_error());
1061 }
1062
1063 td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true,
1064 "SetStickerSetThumbnailQuery");
1065
1066 promise_.set_value(Unit());
1067 }
1068
on_error(Status status)1069 void on_error(Status status) final {
1070 CHECK(status.is_error());
1071 promise_.set_error(std::move(status));
1072 }
1073 };
1074
1075 class SetStickerPositionQuery final : public Td::ResultHandler {
1076 Promise<Unit> promise_;
1077
1078 public:
SetStickerPositionQuery(Promise<Unit> && promise)1079 explicit SetStickerPositionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
1080 }
1081
send(tl_object_ptr<telegram_api::inputDocument> && input_document,int32 position)1082 void send(tl_object_ptr<telegram_api::inputDocument> &&input_document, int32 position) {
1083 send_query(G()->net_query_creator().create(
1084 telegram_api::stickers_changeStickerPosition(std::move(input_document), position)));
1085 }
1086
on_result(BufferSlice packet)1087 void on_result(BufferSlice packet) final {
1088 auto result_ptr = fetch_result<telegram_api::stickers_changeStickerPosition>(packet);
1089 if (result_ptr.is_error()) {
1090 return on_error(result_ptr.move_as_error());
1091 }
1092
1093 td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true,
1094 "SetStickerPositionQuery");
1095
1096 promise_.set_value(Unit());
1097 }
1098
on_error(Status status)1099 void on_error(Status status) final {
1100 CHECK(status.is_error());
1101 promise_.set_error(std::move(status));
1102 }
1103 };
1104
1105 class DeleteStickerFromSetQuery final : public Td::ResultHandler {
1106 Promise<Unit> promise_;
1107
1108 public:
DeleteStickerFromSetQuery(Promise<Unit> && promise)1109 explicit DeleteStickerFromSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
1110 }
1111
send(tl_object_ptr<telegram_api::inputDocument> && input_document)1112 void send(tl_object_ptr<telegram_api::inputDocument> &&input_document) {
1113 send_query(G()->net_query_creator().create(telegram_api::stickers_removeStickerFromSet(std::move(input_document))));
1114 }
1115
on_result(BufferSlice packet)1116 void on_result(BufferSlice packet) final {
1117 auto result_ptr = fetch_result<telegram_api::stickers_removeStickerFromSet>(packet);
1118 if (result_ptr.is_error()) {
1119 return on_error(result_ptr.move_as_error());
1120 }
1121
1122 td_->stickers_manager_->on_get_messages_sticker_set(StickerSetId(), result_ptr.move_as_ok(), true,
1123 "DeleteStickerFromSetQuery");
1124
1125 promise_.set_value(Unit());
1126 }
1127
on_error(Status status)1128 void on_error(Status status) final {
1129 CHECK(status.is_error());
1130 promise_.set_error(std::move(status));
1131 }
1132 };
1133
1134 class SendAnimatedEmojiClicksQuery final : public Td::ResultHandler {
1135 DialogId dialog_id_;
1136 string emoji_;
1137
1138 public:
send(DialogId dialog_id,tl_object_ptr<telegram_api::InputPeer> && input_peer,tl_object_ptr<telegram_api::sendMessageEmojiInteraction> && action)1139 void send(DialogId dialog_id, tl_object_ptr<telegram_api::InputPeer> &&input_peer,
1140 tl_object_ptr<telegram_api::sendMessageEmojiInteraction> &&action) {
1141 dialog_id_ = dialog_id;
1142 CHECK(input_peer != nullptr);
1143 CHECK(action != nullptr);
1144 emoji_ = action->emoticon_;
1145
1146 int32 flags = 0;
1147 send_query(G()->net_query_creator().create(
1148 telegram_api::messages_setTyping(flags, std::move(input_peer), 0, std::move(action))));
1149 }
1150
on_result(BufferSlice packet)1151 void on_result(BufferSlice packet) final {
1152 auto result_ptr = fetch_result<telegram_api::messages_setTyping>(packet);
1153 if (result_ptr.is_error()) {
1154 return on_error(result_ptr.move_as_error());
1155 }
1156
1157 // ignore result
1158 }
1159
on_error(Status status)1160 void on_error(Status status) final {
1161 if (!td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendAnimatedEmojiClicksQuery")) {
1162 LOG(INFO) << "Receive error for send animated emoji clicks: " << status;
1163 }
1164
1165 td_->stickers_manager_->on_send_animated_emoji_clicks(dialog_id_, emoji_);
1166 }
1167 };
1168
1169 class StickersManager::StickerListLogEvent {
1170 public:
1171 vector<FileId> sticker_ids;
1172
1173 StickerListLogEvent() = default;
1174
StickerListLogEvent(vector<FileId> sticker_ids)1175 explicit StickerListLogEvent(vector<FileId> sticker_ids) : sticker_ids(std::move(sticker_ids)) {
1176 }
1177
1178 template <class StorerT>
store(StorerT & storer) const1179 void store(StorerT &storer) const {
1180 StickersManager *stickers_manager = storer.context()->td().get_actor_unsafe()->stickers_manager_.get();
1181 td::store(narrow_cast<int32>(sticker_ids.size()), storer);
1182 for (auto sticker_id : sticker_ids) {
1183 stickers_manager->store_sticker(sticker_id, false, storer, "StickerListLogEvent");
1184 }
1185 }
1186
1187 template <class ParserT>
parse(ParserT & parser)1188 void parse(ParserT &parser) {
1189 StickersManager *stickers_manager = parser.context()->td().get_actor_unsafe()->stickers_manager_.get();
1190 int32 size = parser.fetch_int();
1191 sticker_ids.resize(size);
1192 for (auto &sticker_id : sticker_ids) {
1193 sticker_id = stickers_manager->parse_sticker(false, parser);
1194 }
1195 }
1196 };
1197
1198 class StickersManager::StickerSetListLogEvent {
1199 public:
1200 vector<StickerSetId> sticker_set_ids;
1201
1202 StickerSetListLogEvent() = default;
1203
StickerSetListLogEvent(vector<StickerSetId> sticker_set_ids)1204 explicit StickerSetListLogEvent(vector<StickerSetId> sticker_set_ids) : sticker_set_ids(std::move(sticker_set_ids)) {
1205 }
1206
1207 template <class StorerT>
store(StorerT & storer) const1208 void store(StorerT &storer) const {
1209 td::store(sticker_set_ids, storer);
1210 }
1211
1212 template <class ParserT>
parse(ParserT & parser)1213 void parse(ParserT &parser) {
1214 td::parse(sticker_set_ids, parser);
1215 }
1216 };
1217
1218 class StickersManager::UploadStickerFileCallback final : public FileManager::UploadCallback {
1219 public:
on_upload_ok(FileId file_id,tl_object_ptr<telegram_api::InputFile> input_file)1220 void on_upload_ok(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file) final {
1221 send_closure_later(G()->stickers_manager(), &StickersManager::on_upload_sticker_file, file_id,
1222 std::move(input_file));
1223 }
1224
on_upload_encrypted_ok(FileId file_id,tl_object_ptr<telegram_api::InputEncryptedFile> input_file)1225 void on_upload_encrypted_ok(FileId file_id, tl_object_ptr<telegram_api::InputEncryptedFile> input_file) final {
1226 UNREACHABLE();
1227 }
1228
on_upload_secure_ok(FileId file_id,tl_object_ptr<telegram_api::InputSecureFile> input_file)1229 void on_upload_secure_ok(FileId file_id, tl_object_ptr<telegram_api::InputSecureFile> input_file) final {
1230 UNREACHABLE();
1231 }
1232
on_upload_error(FileId file_id,Status error)1233 void on_upload_error(FileId file_id, Status error) final {
1234 send_closure_later(G()->stickers_manager(), &StickersManager::on_upload_sticker_file_error, file_id,
1235 std::move(error));
1236 }
1237 };
1238
StickersManager(Td * td,ActorShared<> parent)1239 StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
1240 upload_sticker_file_callback_ = std::make_shared<UploadStickerFileCallback>();
1241
1242 on_update_animated_emoji_zoom();
1243
1244 on_update_recent_stickers_limit(
1245 narrow_cast<int32>(G()->shared_config().get_option_integer("recent_stickers_limit", 200)));
1246 on_update_favorite_stickers_limit(
1247 narrow_cast<int32>(G()->shared_config().get_option_integer("favorite_stickers_limit", 5)));
1248
1249 next_click_animated_emoji_message_time_ = Time::now();
1250 next_update_animated_emoji_clicked_time_ = Time::now();
1251 }
1252
start_up()1253 void StickersManager::start_up() {
1254 init();
1255 }
1256
init()1257 void StickersManager::init() {
1258 if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || G()->close_flag()) {
1259 return;
1260 }
1261 LOG(INFO) << "Init StickersManager";
1262 is_inited_ = true;
1263
1264 {
1265 // add animated emoji sticker set
1266 auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji());
1267 if (G()->is_test_dc()) {
1268 init_special_sticker_set(sticker_set, 1258816259751954, 4879754868529595811, "emojies");
1269 } else {
1270 init_special_sticker_set(sticker_set, 1258816259751983, 5100237018658464041, "AnimatedEmojies");
1271 }
1272 load_special_sticker_set_info_from_binlog(sticker_set);
1273 }
1274 if (!G()->is_test_dc()) {
1275 // add animated emoji click sticker set
1276 auto &sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click());
1277 load_special_sticker_set_info_from_binlog(sticker_set);
1278 }
1279
1280 dice_emojis_str_ =
1281 G()->shared_config().get_option_string("dice_emojis", "\x01\x01\x01⚽\x01⚽️\x01\x01");
1282 dice_emojis_ = full_split(dice_emojis_str_, '\x01');
1283 for (auto &dice_emoji : dice_emojis_) {
1284 auto &animated_dice_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(dice_emoji));
1285 load_special_sticker_set_info_from_binlog(animated_dice_sticker_set);
1286 }
1287 send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
1288
1289 on_update_dice_success_values();
1290
1291 on_update_emoji_sounds();
1292
1293 on_update_disable_animated_emojis();
1294 if (!disable_animated_emojis_) {
1295 load_special_sticker_set(add_special_sticker_set(SpecialStickerSetType::animated_emoji()));
1296 }
1297
1298 if (G()->parameters().use_file_db) {
1299 auto old_featured_sticker_set_count_str = G()->td_db()->get_binlog_pmc()->get("old_featured_sticker_set_count");
1300 if (!old_featured_sticker_set_count_str.empty()) {
1301 old_featured_sticker_set_count_ = to_integer<int32>(old_featured_sticker_set_count_str);
1302 }
1303 if (!G()->td_db()->get_binlog_pmc()->get("invalidate_old_featured_sticker_sets").empty()) {
1304 invalidate_old_featured_sticker_sets();
1305 }
1306 } else {
1307 G()->td_db()->get_binlog_pmc()->erase("old_featured_sticker_set_count");
1308 G()->td_db()->get_binlog_pmc()->erase("invalidate_old_featured_sticker_sets");
1309 }
1310
1311 G()->td_db()->get_binlog_pmc()->erase("animated_dice_sticker_set"); // legacy
1312 G()->shared_config().set_option_empty("animated_dice_sticker_set_name"); // legacy
1313 G()->shared_config().set_option_empty("animated_emoji_sticker_set_name"); // legacy
1314 }
1315
add_special_sticker_set(const SpecialStickerSetType & type)1316 StickersManager::SpecialStickerSet &StickersManager::add_special_sticker_set(const SpecialStickerSetType &type) {
1317 auto &result = special_sticker_sets_[type];
1318 if (result.type_.is_empty()) {
1319 result.type_ = type;
1320 } else {
1321 CHECK(result.type_ == type);
1322 }
1323 return result;
1324 }
1325
init_special_sticker_set(SpecialStickerSet & sticker_set,int64 sticker_set_id,int64 access_hash,string name)1326 void StickersManager::init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash,
1327 string name) {
1328 sticker_set.id_ = StickerSetId(sticker_set_id);
1329 sticker_set.access_hash_ = access_hash;
1330 sticker_set.short_name_ = std::move(name);
1331 }
1332
load_special_sticker_set_info_from_binlog(SpecialStickerSet & sticker_set)1333 void StickersManager::load_special_sticker_set_info_from_binlog(SpecialStickerSet &sticker_set) {
1334 if (G()->parameters().use_file_db) {
1335 string sticker_set_string = G()->td_db()->get_binlog_pmc()->get(sticker_set.type_.type_);
1336 if (!sticker_set_string.empty()) {
1337 auto parts = full_split(sticker_set_string);
1338 if (parts.size() != 3) {
1339 LOG(ERROR) << "Can't parse " << sticker_set_string;
1340 } else {
1341 auto r_sticker_set_id = to_integer_safe<int64>(parts[0]);
1342 auto r_sticker_set_access_hash = to_integer_safe<int64>(parts[1]);
1343 auto sticker_set_name = parts[2];
1344 if (r_sticker_set_id.is_error() || r_sticker_set_access_hash.is_error() ||
1345 clean_username(sticker_set_name) != sticker_set_name || sticker_set_name.empty()) {
1346 LOG(ERROR) << "Can't parse " << sticker_set_string;
1347 } else {
1348 init_special_sticker_set(sticker_set, r_sticker_set_id.ok(), r_sticker_set_access_hash.ok(),
1349 std::move(sticker_set_name));
1350 }
1351 }
1352 }
1353 } else {
1354 G()->td_db()->get_binlog_pmc()->erase(sticker_set.type_.type_);
1355 }
1356
1357 if (!sticker_set.id_.is_valid()) {
1358 return;
1359 }
1360
1361 add_sticker_set(sticker_set.id_, sticker_set.access_hash_);
1362 short_name_to_sticker_set_id_.emplace(sticker_set.short_name_, sticker_set.id_);
1363 }
1364
load_special_sticker_set_by_type(SpecialStickerSetType type)1365 void StickersManager::load_special_sticker_set_by_type(SpecialStickerSetType type) {
1366 if (G()->close_flag()) {
1367 return;
1368 }
1369
1370 auto &sticker_set = add_special_sticker_set(type);
1371 CHECK(sticker_set.is_being_loaded_);
1372 sticker_set.is_being_loaded_ = false;
1373 load_special_sticker_set(sticker_set);
1374 }
1375
load_special_sticker_set(SpecialStickerSet & sticker_set)1376 void StickersManager::load_special_sticker_set(SpecialStickerSet &sticker_set) {
1377 CHECK(!td_->auth_manager_->is_bot());
1378 if (sticker_set.is_being_loaded_) {
1379 return;
1380 }
1381 sticker_set.is_being_loaded_ = true;
1382 LOG(INFO) << "Load " << sticker_set.type_.type_ << " " << sticker_set.id_;
1383 if (sticker_set.id_.is_valid()) {
1384 auto promise = PromiseCreator::lambda([actor_id = actor_id(this), type = sticker_set.type_](Result<Unit> &&result) {
1385 send_closure(actor_id, &StickersManager::on_load_special_sticker_set, type,
1386 result.is_ok() ? Status::OK() : result.move_as_error());
1387 });
1388 load_sticker_sets({sticker_set.id_}, std::move(promise));
1389 } else {
1390 reload_special_sticker_set(sticker_set, 0);
1391 }
1392 }
1393
reload_special_sticker_set_by_type(SpecialStickerSetType type,bool is_recursive)1394 void StickersManager::reload_special_sticker_set_by_type(SpecialStickerSetType type, bool is_recursive) {
1395 if (G()->close_flag()) {
1396 return;
1397 }
1398
1399 auto &sticker_set = add_special_sticker_set(type);
1400 if (sticker_set.is_being_reloaded_) {
1401 return;
1402 }
1403
1404 if (!sticker_set.id_.is_valid()) {
1405 return reload_special_sticker_set(sticker_set, 0);
1406 }
1407
1408 const auto *s = get_sticker_set(sticker_set.id_);
1409 if (s != nullptr && s->is_inited && s->was_loaded) {
1410 return reload_special_sticker_set(sticker_set, s->is_loaded ? s->hash : 0);
1411 }
1412 if (!is_recursive) {
1413 auto promise = PromiseCreator::lambda([actor_id = actor_id(this), type = std::move(type)](Unit result) mutable {
1414 send_closure(actor_id, &StickersManager::reload_special_sticker_set_by_type, std::move(type), true);
1415 });
1416 return load_sticker_sets({sticker_set.id_}, std::move(promise));
1417 }
1418
1419 reload_special_sticker_set(sticker_set, 0);
1420 }
1421
reload_special_sticker_set(SpecialStickerSet & sticker_set,int32 hash)1422 void StickersManager::reload_special_sticker_set(SpecialStickerSet &sticker_set, int32 hash) {
1423 if (sticker_set.is_being_reloaded_) {
1424 return;
1425 }
1426 sticker_set.is_being_reloaded_ = true;
1427 td_->create_handler<ReloadSpecialStickerSetQuery>()->send(sticker_set.id_, sticker_set.type_, hash);
1428 }
1429
on_load_special_sticker_set(const SpecialStickerSetType & type,Status result)1430 void StickersManager::on_load_special_sticker_set(const SpecialStickerSetType &type, Status result) {
1431 if (G()->close_flag()) {
1432 return;
1433 }
1434
1435 auto &special_sticker_set = add_special_sticker_set(type);
1436 special_sticker_set.is_being_reloaded_ = false;
1437 if (!special_sticker_set.is_being_loaded_) {
1438 return;
1439 }
1440
1441 if (result.is_error()) {
1442 // failed to load the special sticker set; repeat after some time
1443 create_actor<SleepActor>("RetryLoadSpecialStickerSetActor", Random::fast(300, 600),
1444 PromiseCreator::lambda([actor_id = actor_id(this), type](Result<Unit> result) mutable {
1445 send_closure(actor_id, &StickersManager::load_special_sticker_set_by_type,
1446 std::move(type));
1447 }))
1448 .release();
1449 return;
1450 }
1451
1452 special_sticker_set.is_being_loaded_ = false;
1453
1454 if (type == SpecialStickerSetType::animated_emoji()) {
1455 auto promises = std::move(pending_get_animated_emoji_queries_);
1456 reset_to_empty(pending_get_animated_emoji_queries_);
1457 for (auto &promise : promises) {
1458 promise.set_value(Unit());
1459 }
1460 return;
1461 }
1462
1463 CHECK(special_sticker_set.id_.is_valid());
1464 auto sticker_set = get_sticker_set(special_sticker_set.id_);
1465 CHECK(sticker_set != nullptr);
1466 CHECK(sticker_set->was_loaded);
1467
1468 if (type == SpecialStickerSetType::animated_emoji_click()) {
1469 auto pending_get_requests = std::move(pending_get_animated_emoji_click_stickers_);
1470 reset_to_empty(pending_get_animated_emoji_click_stickers_);
1471 for (auto &pending_request : pending_get_requests) {
1472 choose_animated_emoji_click_sticker(sticker_set, pending_request.message_text_, pending_request.full_message_id_,
1473 pending_request.start_time_, std::move(pending_request.promise_));
1474 }
1475 auto pending_click_requests = std::move(pending_on_animated_emoji_message_clicked_);
1476 reset_to_empty(pending_on_animated_emoji_message_clicked_);
1477 for (auto &pending_request : pending_click_requests) {
1478 schedule_update_animated_emoji_clicked(sticker_set, pending_request.emoji_, pending_request.full_message_id_,
1479 std::move(pending_request.clicks_));
1480 }
1481 return;
1482 }
1483
1484 auto emoji = type.get_dice_emoji();
1485 CHECK(!emoji.empty());
1486
1487 auto it = dice_messages_.find(emoji);
1488 if (it == dice_messages_.end()) {
1489 return;
1490 }
1491
1492 vector<FullMessageId> full_message_ids;
1493 for (const auto &full_message_id : it->second) {
1494 full_message_ids.push_back(full_message_id);
1495 }
1496 CHECK(!full_message_ids.empty());
1497 for (const auto &full_message_id : full_message_ids) {
1498 td_->messages_manager_->on_external_update_message_content(full_message_id);
1499 }
1500 }
1501
tear_down()1502 void StickersManager::tear_down() {
1503 parent_.reset();
1504 }
1505
get_mask_point_object(int32 point)1506 tl_object_ptr<td_api::MaskPoint> StickersManager::get_mask_point_object(int32 point) {
1507 switch (point) {
1508 case 0:
1509 return td_api::make_object<td_api::maskPointForehead>();
1510 case 1:
1511 return td_api::make_object<td_api::maskPointEyes>();
1512 case 2:
1513 return td_api::make_object<td_api::maskPointMouth>();
1514 case 3:
1515 return td_api::make_object<td_api::maskPointChin>();
1516 default:
1517 UNREACHABLE();
1518 return nullptr;
1519 }
1520 }
1521
get_sticker_minithumbnail(CSlice path,StickerSetId sticker_set_id,int64 document_id,double zoom)1522 vector<td_api::object_ptr<td_api::closedVectorPath>> StickersManager::get_sticker_minithumbnail(
1523 CSlice path, StickerSetId sticker_set_id, int64 document_id, double zoom) {
1524 if (path.empty()) {
1525 return {};
1526 }
1527
1528 auto buf = StackAllocator::alloc(1 << 9);
1529 StringBuilder sb(buf.as_slice(), true);
1530
1531 sb << 'M';
1532 for (unsigned char c : path) {
1533 if (c >= 128 + 64) {
1534 sb << "AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,"[c - 128 - 64];
1535 } else {
1536 if (c >= 128) {
1537 sb << ',';
1538 } else if (c >= 64) {
1539 sb << '-';
1540 }
1541 sb << (c & 63);
1542 }
1543 }
1544 sb << 'z';
1545
1546 CHECK(!sb.is_error());
1547 path = sb.as_cslice();
1548 LOG(DEBUG) << "Transform SVG path " << path;
1549
1550 size_t pos = 0;
1551 auto skip_commas = [&path, &pos] {
1552 while (path[pos] == ',') {
1553 pos++;
1554 }
1555 };
1556 auto get_number = [&] {
1557 skip_commas();
1558 int sign = 1;
1559 if (path[pos] == '-') {
1560 sign = -1;
1561 pos++;
1562 }
1563 double res = 0;
1564 while (is_digit(path[pos])) {
1565 res = res * 10 + path[pos++] - '0';
1566 }
1567 if (path[pos] == '.') {
1568 pos++;
1569 double mul = 0.1;
1570 while (is_digit(path[pos])) {
1571 res += (path[pos] - '0') * mul;
1572 mul *= 0.1;
1573 pos++;
1574 }
1575 }
1576 return sign * res;
1577 };
1578 auto make_point = [zoom](double x, double y) {
1579 return td_api::make_object<td_api::point>(x * zoom, y * zoom);
1580 };
1581
1582 vector<td_api::object_ptr<td_api::closedVectorPath>> result;
1583 double x = 0;
1584 double y = 0;
1585 while (path[pos] != '\0') {
1586 skip_commas();
1587 if (path[pos] == '\0') {
1588 break;
1589 }
1590
1591 while (path[pos] == 'm' || path[pos] == 'M') {
1592 auto command = path[pos++];
1593 do {
1594 if (command == 'm') {
1595 x += get_number();
1596 y += get_number();
1597 } else {
1598 x = get_number();
1599 y = get_number();
1600 }
1601 skip_commas();
1602 } while (path[pos] != '\0' && !is_alpha(path[pos]));
1603 }
1604
1605 double start_x = x;
1606 double start_y = y;
1607
1608 vector<td_api::object_ptr<td_api::VectorPathCommand>> commands;
1609 bool have_last_end_control_point = false;
1610 double last_end_control_point_x = 0;
1611 double last_end_control_point_y = 0;
1612 bool is_closed = false;
1613 char command = '-';
1614 while (!is_closed) {
1615 skip_commas();
1616 if (path[pos] == '\0') {
1617 LOG(ERROR) << "Receive unclosed path " << path << " in a sticker " << document_id << " from " << sticker_set_id;
1618 return {};
1619 }
1620 if (is_alpha(path[pos])) {
1621 command = path[pos++];
1622 }
1623 switch (command) {
1624 case 'l':
1625 case 'L':
1626 case 'h':
1627 case 'H':
1628 case 'v':
1629 case 'V':
1630 if (command == 'l' || command == 'h') {
1631 x += get_number();
1632 } else if (command == 'L' || command == 'H') {
1633 x = get_number();
1634 }
1635 if (command == 'l' || command == 'v') {
1636 y += get_number();
1637 } else if (command == 'L' || command == 'V') {
1638 y = get_number();
1639 }
1640 commands.push_back(td_api::make_object<td_api::vectorPathCommandLine>(make_point(x, y)));
1641 have_last_end_control_point = false;
1642 break;
1643 case 'C':
1644 case 'c':
1645 case 'S':
1646 case 's': {
1647 double start_control_point_x;
1648 double start_control_point_y;
1649 if (command == 'S' || command == 's') {
1650 if (have_last_end_control_point) {
1651 start_control_point_x = 2 * x - last_end_control_point_x;
1652 start_control_point_y = 2 * y - last_end_control_point_y;
1653 } else {
1654 start_control_point_x = x;
1655 start_control_point_y = y;
1656 }
1657 } else {
1658 start_control_point_x = get_number();
1659 start_control_point_y = get_number();
1660 if (command == 'c') {
1661 start_control_point_x += x;
1662 start_control_point_y += y;
1663 }
1664 }
1665
1666 last_end_control_point_x = get_number();
1667 last_end_control_point_y = get_number();
1668 if (command == 'c' || command == 's') {
1669 last_end_control_point_x += x;
1670 last_end_control_point_y += y;
1671 }
1672 have_last_end_control_point = true;
1673
1674 if (command == 'c' || command == 's') {
1675 x += get_number();
1676 y += get_number();
1677 } else {
1678 x = get_number();
1679 y = get_number();
1680 }
1681
1682 commands.push_back(td_api::make_object<td_api::vectorPathCommandCubicBezierCurve>(
1683 make_point(start_control_point_x, start_control_point_y),
1684 make_point(last_end_control_point_x, last_end_control_point_y), make_point(x, y)));
1685 break;
1686 }
1687 case 'm':
1688 case 'M':
1689 pos--;
1690 // fallthrough
1691 case 'z':
1692 case 'Z':
1693 if (x != start_x || y != start_y) {
1694 x = start_x;
1695 y = start_y;
1696 commands.push_back(td_api::make_object<td_api::vectorPathCommandLine>(make_point(x, y)));
1697 }
1698 if (!commands.empty()) {
1699 result.push_back(td_api::make_object<td_api::closedVectorPath>(std::move(commands)));
1700 commands.clear();
1701 }
1702 is_closed = true;
1703 break;
1704 default:
1705 LOG(ERROR) << "Receive invalid command " << command << " at pos " << pos << " in a sticker " << document_id
1706 << " from " << sticker_set_id << ": " << path;
1707 return {};
1708 }
1709 }
1710 }
1711 /*
1712 string svg;
1713 for (const auto &vector_path : result) {
1714 CHECK(!vector_path->commands_.empty());
1715 svg += 'M';
1716 auto add_point = [&](const td_api::object_ptr<td_api::point> &p) {
1717 svg += to_string(static_cast<int>(p->x_));
1718 svg += ',';
1719 svg += to_string(static_cast<int>(p->y_));
1720 svg += ',';
1721 };
1722 auto last_command = vector_path->commands_.back().get();
1723 switch (last_command->get_id()) {
1724 case td_api::vectorPathCommandLine::ID:
1725 add_point(static_cast<const td_api::vectorPathCommandLine *>(last_command)->end_point_);
1726 break;
1727 case td_api::vectorPathCommandCubicBezierCurve::ID:
1728 add_point(static_cast<const td_api::vectorPathCommandCubicBezierCurve *>(last_command)->end_point_);
1729 break;
1730 default:
1731 UNREACHABLE();
1732 }
1733 for (auto &command : vector_path->commands_) {
1734 switch (command->get_id()) {
1735 case td_api::vectorPathCommandLine::ID: {
1736 auto line = static_cast<const td_api::vectorPathCommandLine *>(command.get());
1737 svg += 'L';
1738 add_point(line->end_point_);
1739 break;
1740 }
1741 case td_api::vectorPathCommandCubicBezierCurve::ID: {
1742 auto curve = static_cast<const td_api::vectorPathCommandCubicBezierCurve *>(command.get());
1743 svg += 'C';
1744 add_point(curve->start_control_point_);
1745 add_point(curve->end_control_point_);
1746 add_point(curve->end_point_);
1747 break;
1748 }
1749 default:
1750 UNREACHABLE();
1751 }
1752 }
1753 svg += 'z';
1754 }
1755 */
1756
1757 return result;
1758 }
1759
get_sticker_object(FileId file_id,bool for_animated_emoji,bool for_clicked_animated_emoji) const1760 tl_object_ptr<td_api::sticker> StickersManager::get_sticker_object(FileId file_id, bool for_animated_emoji,
1761 bool for_clicked_animated_emoji) const {
1762 if (!file_id.is_valid()) {
1763 return nullptr;
1764 }
1765
1766 auto it = stickers_.find(file_id);
1767 CHECK(it != stickers_.end());
1768 auto sticker = it->second.get();
1769 CHECK(sticker != nullptr);
1770 auto mask_position = sticker->point >= 0
1771 ? make_tl_object<td_api::maskPosition>(get_mask_point_object(sticker->point),
1772 sticker->x_shift, sticker->y_shift, sticker->scale)
1773 : nullptr;
1774
1775 const PhotoSize &thumbnail = sticker->m_thumbnail.file_id.is_valid() ? sticker->m_thumbnail : sticker->s_thumbnail;
1776 auto thumbnail_format = PhotoFormat::Webp;
1777 int64 document_id = -1;
1778 if (!sticker->set_id.is_valid()) {
1779 auto sticker_file_view = td_->file_manager_->get_file_view(sticker->file_id);
1780 if (sticker_file_view.is_encrypted()) {
1781 // uploaded to secret chats stickers have JPEG thumbnail instead of server-generated WEBP
1782 thumbnail_format = PhotoFormat::Jpeg;
1783 } else {
1784 if (sticker_file_view.has_remote_location() && sticker_file_view.remote_location().is_document()) {
1785 document_id = sticker_file_view.remote_location().get_id();
1786 }
1787
1788 if (thumbnail.file_id.is_valid()) {
1789 auto thumbnail_file_view = td_->file_manager_->get_file_view(thumbnail.file_id);
1790 if (ends_with(thumbnail_file_view.suggested_path(), ".jpg")) {
1791 thumbnail_format = PhotoFormat::Jpeg;
1792 }
1793 }
1794 }
1795 }
1796 auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format);
1797 int32 width = sticker->dimensions.width;
1798 int32 height = sticker->dimensions.height;
1799 double zoom = 1.0;
1800 if (sticker->is_animated && (for_animated_emoji || for_clicked_animated_emoji)) {
1801 zoom = for_clicked_animated_emoji ? 3 * animated_emoji_zoom_ : animated_emoji_zoom_;
1802 width = static_cast<int32>(width * zoom + 0.5);
1803 height = static_cast<int32>(height * zoom + 0.5);
1804 }
1805 return make_tl_object<td_api::sticker>(
1806 sticker->set_id.get(), width, height, sticker->alt, sticker->is_animated, sticker->is_mask,
1807 std::move(mask_position), get_sticker_minithumbnail(sticker->minithumbnail, sticker->set_id, document_id, zoom),
1808 std::move(thumbnail_object), td_->file_manager_->get_file_object(file_id));
1809 }
1810
get_stickers_object(const vector<FileId> & sticker_ids) const1811 tl_object_ptr<td_api::stickers> StickersManager::get_stickers_object(const vector<FileId> &sticker_ids) const {
1812 auto result = make_tl_object<td_api::stickers>();
1813 result->stickers_.reserve(sticker_ids.size());
1814 for (auto sticker_id : sticker_ids) {
1815 result->stickers_.push_back(get_sticker_object(sticker_id));
1816 }
1817 return result;
1818 }
1819
get_dice_stickers_object(const string & emoji,int32 value) const1820 tl_object_ptr<td_api::DiceStickers> StickersManager::get_dice_stickers_object(const string &emoji, int32 value) const {
1821 if (td_->auth_manager_->is_bot()) {
1822 return nullptr;
1823 }
1824 if (!td::contains(dice_emojis_, emoji)) {
1825 return nullptr;
1826 }
1827
1828 auto it = special_sticker_sets_.find(SpecialStickerSetType::animated_dice(emoji));
1829 if (it == special_sticker_sets_.end()) {
1830 return nullptr;
1831 }
1832
1833 auto sticker_set_id = it->second.id_;
1834 if (!sticker_set_id.is_valid()) {
1835 return nullptr;
1836 }
1837
1838 auto sticker_set = get_sticker_set(sticker_set_id);
1839 CHECK(sticker_set != nullptr);
1840 if (!sticker_set->was_loaded) {
1841 return nullptr;
1842 }
1843
1844 auto get_sticker = [&](int32 value) {
1845 return get_sticker_object(sticker_set->sticker_ids[value], true);
1846 };
1847
1848 if (emoji == "") {
1849 if (sticker_set->sticker_ids.size() < 21 || value < 0 || value > 64) {
1850 return nullptr;
1851 }
1852
1853 int32 background_id = value == 1 || value == 22 || value == 43 || value == 64 ? 1 : 0;
1854 int32 lever_id = 2;
1855 int32 left_reel_id = value == 64 ? 3 : 8;
1856 int32 center_reel_id = value == 64 ? 9 : 14;
1857 int32 right_reel_id = value == 64 ? 15 : 20;
1858 if (value != 0 && value != 64) {
1859 left_reel_id = 4 + (value % 4);
1860 center_reel_id = 10 + ((value + 3) / 4 % 4);
1861 right_reel_id = 16 + ((value + 15) / 16 % 4);
1862 }
1863 return td_api::make_object<td_api::diceStickersSlotMachine>(get_sticker(background_id), get_sticker(lever_id),
1864 get_sticker(left_reel_id), get_sticker(center_reel_id),
1865 get_sticker(right_reel_id));
1866 }
1867
1868 if (value >= 0 && value < static_cast<int32>(sticker_set->sticker_ids.size())) {
1869 return td_api::make_object<td_api::diceStickersRegular>(get_sticker(value));
1870 }
1871 return nullptr;
1872 }
1873
get_dice_success_animation_frame_number(const string & emoji,int32 value) const1874 int32 StickersManager::get_dice_success_animation_frame_number(const string &emoji, int32 value) const {
1875 if (td_->auth_manager_->is_bot()) {
1876 return std::numeric_limits<int32>::max();
1877 }
1878 if (value == 0 || !td::contains(dice_emojis_, emoji)) {
1879 return std::numeric_limits<int32>::max();
1880 }
1881 auto pos = static_cast<size_t>(std::find(dice_emojis_.begin(), dice_emojis_.end(), emoji) - dice_emojis_.begin());
1882 if (pos >= dice_success_values_.size()) {
1883 return std::numeric_limits<int32>::max();
1884 }
1885
1886 auto &result = dice_success_values_[pos];
1887 return result.first == value ? result.second : std::numeric_limits<int32>::max();
1888 }
1889
get_sticker_set_object(StickerSetId sticker_set_id) const1890 tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const {
1891 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
1892 CHECK(sticker_set != nullptr);
1893 CHECK(sticker_set->was_loaded);
1894 sticker_set->was_update_sent = true;
1895
1896 std::vector<tl_object_ptr<td_api::sticker>> stickers;
1897 std::vector<tl_object_ptr<td_api::emojis>> emojis;
1898 for (auto sticker_id : sticker_set->sticker_ids) {
1899 stickers.push_back(get_sticker_object(sticker_id));
1900
1901 vector<string> sticker_emojis;
1902 auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
1903 if (it != sticker_set->sticker_emojis_map_.end()) {
1904 sticker_emojis = it->second;
1905 }
1906 emojis.push_back(make_tl_object<td_api::emojis>(std::move(sticker_emojis)));
1907 }
1908 auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail,
1909 sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
1910 return make_tl_object<td_api::stickerSet>(
1911 sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
1912 get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2, 1.0),
1913 sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
1914 sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis));
1915 }
1916
get_sticker_sets_object(int32 total_count,const vector<StickerSetId> & sticker_set_ids,size_t covers_limit) const1917 tl_object_ptr<td_api::stickerSets> StickersManager::get_sticker_sets_object(int32 total_count,
1918 const vector<StickerSetId> &sticker_set_ids,
1919 size_t covers_limit) const {
1920 vector<tl_object_ptr<td_api::stickerSetInfo>> result;
1921 result.reserve(sticker_set_ids.size());
1922 for (auto sticker_set_id : sticker_set_ids) {
1923 auto sticker_set_info = get_sticker_set_info_object(sticker_set_id, covers_limit);
1924 if (sticker_set_info->size_ != 0) {
1925 result.push_back(std::move(sticker_set_info));
1926 }
1927 }
1928
1929 auto result_size = narrow_cast<int32>(result.size());
1930 if (total_count < result_size) {
1931 if (total_count != -1) {
1932 LOG(ERROR) << "Have total_count = " << total_count << ", but there are " << result_size << " results";
1933 }
1934 total_count = result_size;
1935 }
1936 return make_tl_object<td_api::stickerSets>(total_count, std::move(result));
1937 }
1938
get_sticker_set_info_object(StickerSetId sticker_set_id,size_t covers_limit) const1939 tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_object(StickerSetId sticker_set_id,
1940 size_t covers_limit) const {
1941 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
1942 CHECK(sticker_set != nullptr);
1943 CHECK(sticker_set->is_inited);
1944 sticker_set->was_update_sent = true;
1945
1946 std::vector<tl_object_ptr<td_api::sticker>> stickers;
1947 for (auto sticker_id : sticker_set->sticker_ids) {
1948 stickers.push_back(get_sticker_object(sticker_id));
1949 if (stickers.size() >= covers_limit) {
1950 break;
1951 }
1952 }
1953
1954 auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail,
1955 sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
1956 return make_tl_object<td_api::stickerSetInfo>(
1957 sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
1958 get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3, 1.0),
1959 sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
1960 sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed,
1961 sticker_set->was_loaded ? narrow_cast<int32>(sticker_set->sticker_ids.size()) : sticker_set->sticker_count,
1962 std::move(stickers));
1963 }
1964
get_animated_emoji_sticker_set()1965 const StickersManager::StickerSet *StickersManager::get_animated_emoji_sticker_set() {
1966 if (td_->auth_manager_->is_bot() || disable_animated_emojis_) {
1967 return nullptr;
1968 }
1969 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji());
1970 if (!special_sticker_set.id_.is_valid()) {
1971 load_special_sticker_set(special_sticker_set);
1972 return nullptr;
1973 }
1974
1975 auto sticker_set = get_sticker_set(special_sticker_set.id_);
1976 CHECK(sticker_set != nullptr);
1977 if (!sticker_set->was_loaded) {
1978 load_special_sticker_set(special_sticker_set);
1979 return nullptr;
1980 }
1981
1982 return sticker_set;
1983 }
1984
get_animated_emoji_sticker(const StickerSet * sticker_set,const string & emoji)1985 std::pair<FileId, int> StickersManager::get_animated_emoji_sticker(const StickerSet *sticker_set, const string &emoji) {
1986 if (sticker_set == nullptr) {
1987 return {};
1988 }
1989
1990 auto emoji_without_modifiers = remove_emoji_modifiers(emoji).str();
1991 auto it = sticker_set->emoji_stickers_map_.find(emoji_without_modifiers);
1992 if (it == sticker_set->emoji_stickers_map_.end()) {
1993 return {};
1994 }
1995
1996 // trying to find full emoji match
1997 for (const auto &sticker_id : it->second) {
1998 auto emoji_it = sticker_set->sticker_emojis_map_.find(sticker_id);
1999 CHECK(emoji_it != sticker_set->sticker_emojis_map_.end());
2000 if (td::contains(emoji_it->second, emoji)) {
2001 return {sticker_id, 0};
2002 }
2003 }
2004
2005 // trying to find match without Fitzpatrick modifiers
2006 int modifier_id = get_fitzpatrick_modifier(emoji);
2007 if (modifier_id > 0) {
2008 for (const auto &sticker_id : it->second) {
2009 auto emoji_it = sticker_set->sticker_emojis_map_.find(sticker_id);
2010 CHECK(emoji_it != sticker_set->sticker_emojis_map_.end());
2011 if (td::contains(emoji_it->second, Slice(emoji).remove_suffix(4))) {
2012 return {sticker_id, modifier_id};
2013 }
2014 }
2015 }
2016
2017 // there is no match
2018 return {};
2019 }
2020
get_animated_emoji_sticker(const string & emoji)2021 std::pair<FileId, int> StickersManager::get_animated_emoji_sticker(const string &emoji) {
2022 return get_animated_emoji_sticker(get_animated_emoji_sticker_set(), emoji);
2023 }
2024
get_animated_emoji_sound_file_id(const string & emoji) const2025 FileId StickersManager::get_animated_emoji_sound_file_id(const string &emoji) const {
2026 auto it = emoji_sounds_.find(remove_fitzpatrick_modifier(emoji).str());
2027 if (it == emoji_sounds_.end()) {
2028 return {};
2029 }
2030 return it->second;
2031 }
2032
get_animated_emoji_object(const string & emoji)2033 td_api::object_ptr<td_api::animatedEmoji> StickersManager::get_animated_emoji_object(const string &emoji) {
2034 auto it = emoji_messages_.find(emoji);
2035 if (it == emoji_messages_.end()) {
2036 return get_animated_emoji_object(get_animated_emoji_sticker(emoji), get_animated_emoji_sound_file_id(emoji));
2037 } else {
2038 return get_animated_emoji_object(it->second.animated_emoji_sticker, it->second.sound_file_id);
2039 }
2040 }
2041
get_animated_emoji_object(std::pair<FileId,int> animated_sticker,FileId sound_file_id) const2042 td_api::object_ptr<td_api::animatedEmoji> StickersManager::get_animated_emoji_object(
2043 std::pair<FileId, int> animated_sticker, FileId sound_file_id) const {
2044 if (!animated_sticker.first.is_valid()) {
2045 return nullptr;
2046 }
2047 return td_api::make_object<td_api::animatedEmoji>(
2048 get_sticker_object(animated_sticker.first, true), animated_sticker.second,
2049 sound_file_id.is_valid() ? td_->file_manager_->get_file_object(sound_file_id) : nullptr);
2050 }
2051
get_input_sticker_set(StickerSetId sticker_set_id) const2052 tl_object_ptr<telegram_api::InputStickerSet> StickersManager::get_input_sticker_set(StickerSetId sticker_set_id) const {
2053 auto sticker_set = get_sticker_set(sticker_set_id);
2054 if (sticker_set == nullptr) {
2055 return nullptr;
2056 }
2057
2058 return get_input_sticker_set(sticker_set);
2059 }
2060
on_get_sticker(unique_ptr<Sticker> new_sticker,bool replace)2061 FileId StickersManager::on_get_sticker(unique_ptr<Sticker> new_sticker, bool replace) {
2062 auto file_id = new_sticker->file_id;
2063 CHECK(file_id.is_valid());
2064 LOG(INFO) << "Receive sticker " << file_id;
2065 auto &s = stickers_[file_id];
2066 if (s == nullptr) {
2067 s = std::move(new_sticker);
2068 } else if (replace) {
2069 CHECK(s->file_id == file_id);
2070 if (s->dimensions != new_sticker->dimensions && new_sticker->dimensions.width != 0) {
2071 LOG(DEBUG) << "Sticker " << file_id << " dimensions have changed";
2072 s->dimensions = new_sticker->dimensions;
2073 }
2074 if (s->set_id != new_sticker->set_id && new_sticker->set_id.is_valid()) {
2075 LOG_IF(ERROR, s->set_id.is_valid()) << "Sticker " << file_id << " set_id has changed";
2076 s->set_id = new_sticker->set_id;
2077 }
2078 if (s->alt != new_sticker->alt && !new_sticker->alt.empty()) {
2079 LOG(DEBUG) << "Sticker " << file_id << " emoji has changed";
2080 s->alt = std::move(new_sticker->alt);
2081 }
2082 if (s->minithumbnail != new_sticker->minithumbnail) {
2083 LOG(DEBUG) << "Sticker " << file_id << " minithumbnail has changed";
2084 s->minithumbnail = std::move(new_sticker->minithumbnail);
2085 }
2086 if (s->s_thumbnail != new_sticker->s_thumbnail && new_sticker->s_thumbnail.file_id.is_valid()) {
2087 LOG_IF(INFO, s->s_thumbnail.file_id.is_valid()) << "Sticker " << file_id << " s thumbnail has changed from "
2088 << s->s_thumbnail << " to " << new_sticker->s_thumbnail;
2089 s->s_thumbnail = std::move(new_sticker->s_thumbnail);
2090 }
2091 if (s->m_thumbnail != new_sticker->m_thumbnail && new_sticker->m_thumbnail.file_id.is_valid()) {
2092 LOG_IF(INFO, s->m_thumbnail.file_id.is_valid()) << "Sticker " << file_id << " m thumbnail has changed from "
2093 << s->m_thumbnail << " to " << new_sticker->m_thumbnail;
2094 s->m_thumbnail = std::move(new_sticker->m_thumbnail);
2095 }
2096 if (s->is_animated != new_sticker->is_animated && new_sticker->is_animated) {
2097 s->is_animated = new_sticker->is_animated;
2098 }
2099 if (s->is_mask != new_sticker->is_mask && new_sticker->is_mask) {
2100 s->is_mask = new_sticker->is_mask;
2101 }
2102 if (s->point != new_sticker->point && new_sticker->point != -1) {
2103 s->point = new_sticker->point;
2104 s->x_shift = new_sticker->x_shift;
2105 s->y_shift = new_sticker->y_shift;
2106 s->scale = new_sticker->scale;
2107 }
2108 }
2109
2110 return file_id;
2111 }
2112
has_webp_thumbnail(const vector<tl_object_ptr<telegram_api::PhotoSize>> & thumbnails)2113 bool StickersManager::has_webp_thumbnail(const vector<tl_object_ptr<telegram_api::PhotoSize>> &thumbnails) {
2114 // server tries to always replace user-provided thumbnail with server-side WEBP thumbnail
2115 // but there can be some old sticker documents or some big stickers
2116 for (auto &size : thumbnails) {
2117 switch (size->get_id()) {
2118 case telegram_api::photoStrippedSize::ID:
2119 case telegram_api::photoSizeProgressive::ID:
2120 // WEBP thumbnail can't have stripped size or be progressive
2121 return false;
2122 default:
2123 break;
2124 }
2125 }
2126 return true;
2127 }
2128
on_get_sticker_document(tl_object_ptr<telegram_api::Document> && document_ptr)2129 std::pair<int64, FileId> StickersManager::on_get_sticker_document(
2130 tl_object_ptr<telegram_api::Document> &&document_ptr) {
2131 int32 document_constructor_id = document_ptr->get_id();
2132 if (document_constructor_id == telegram_api::documentEmpty::ID) {
2133 LOG(ERROR) << "Empty sticker document received";
2134 return {};
2135 }
2136 CHECK(document_constructor_id == telegram_api::document::ID);
2137 auto document = move_tl_object_as<telegram_api::document>(document_ptr);
2138
2139 if (!DcId::is_valid(document->dc_id_)) {
2140 LOG(ERROR) << "Wrong dc_id = " << document->dc_id_ << " in document " << to_string(document);
2141 return {};
2142 }
2143 auto dc_id = DcId::internal(document->dc_id_);
2144
2145 Dimensions dimensions;
2146 tl_object_ptr<telegram_api::documentAttributeSticker> sticker;
2147 for (auto &attribute : document->attributes_) {
2148 switch (attribute->get_id()) {
2149 case telegram_api::documentAttributeImageSize::ID: {
2150 auto image_size = move_tl_object_as<telegram_api::documentAttributeImageSize>(attribute);
2151 dimensions = get_dimensions(image_size->w_, image_size->h_, "sticker documentAttributeImageSize");
2152 break;
2153 }
2154 case telegram_api::documentAttributeSticker::ID:
2155 sticker = move_tl_object_as<telegram_api::documentAttributeSticker>(attribute);
2156 break;
2157 default:
2158 continue;
2159 }
2160 }
2161 if (sticker == nullptr) {
2162 if (document->mime_type_ != "application/x-bad-tgsticker") {
2163 LOG(ERROR) << "Have no attributeSticker in sticker " << to_string(document);
2164 }
2165 return {};
2166 }
2167
2168 bool is_animated = document->mime_type_ == "application/x-tgsticker";
2169 int64 document_id = document->id_;
2170 FileId sticker_id =
2171 td_->file_manager_->register_remote(FullRemoteFileLocation(FileType::Sticker, document_id, document->access_hash_,
2172 dc_id, document->file_reference_.as_slice().str()),
2173 FileLocationSource::FromServer, DialogId(), document->size_, 0,
2174 PSTRING() << document_id << (is_animated ? ".tgs" : ".webp"));
2175
2176 PhotoSize thumbnail;
2177 string minithumbnail;
2178 auto thumbnail_format = has_webp_thumbnail(document->thumbs_) ? PhotoFormat::Webp : PhotoFormat::Jpeg;
2179 for (auto &thumb : document->thumbs_) {
2180 auto photo_size = get_photo_size(td_->file_manager_.get(), PhotoSizeSource::thumbnail(FileType::Thumbnail, 0),
2181 document_id, document->access_hash_, document->file_reference_.as_slice().str(),
2182 dc_id, DialogId(), std::move(thumb), thumbnail_format);
2183 if (photo_size.get_offset() == 0) {
2184 if (!thumbnail.file_id.is_valid()) {
2185 thumbnail = std::move(photo_size.get<0>());
2186 }
2187 break;
2188 } else {
2189 if (thumbnail_format == PhotoFormat::Webp) {
2190 minithumbnail = std::move(photo_size.get<1>());
2191 }
2192 }
2193 }
2194
2195 create_sticker(sticker_id, std::move(minithumbnail), std::move(thumbnail), dimensions, std::move(sticker),
2196 is_animated, nullptr);
2197 return {document_id, sticker_id};
2198 }
2199
get_sticker(FileId file_id)2200 StickersManager::Sticker *StickersManager::get_sticker(FileId file_id) {
2201 auto sticker = stickers_.find(file_id);
2202 if (sticker == stickers_.end()) {
2203 return nullptr;
2204 }
2205
2206 CHECK(sticker->second->file_id == file_id);
2207 return sticker->second.get();
2208 }
2209
get_sticker(FileId file_id) const2210 const StickersManager::Sticker *StickersManager::get_sticker(FileId file_id) const {
2211 auto sticker = stickers_.find(file_id);
2212 if (sticker == stickers_.end()) {
2213 return nullptr;
2214 }
2215
2216 CHECK(sticker->second->file_id == file_id);
2217 return sticker->second.get();
2218 }
2219
get_sticker_set(StickerSetId sticker_set_id)2220 StickersManager::StickerSet *StickersManager::get_sticker_set(StickerSetId sticker_set_id) {
2221 auto sticker_set = sticker_sets_.find(sticker_set_id);
2222 if (sticker_set == sticker_sets_.end()) {
2223 return nullptr;
2224 }
2225
2226 return sticker_set->second.get();
2227 }
2228
get_sticker_set(StickerSetId sticker_set_id) const2229 const StickersManager::StickerSet *StickersManager::get_sticker_set(StickerSetId sticker_set_id) const {
2230 auto sticker_set = sticker_sets_.find(sticker_set_id);
2231 if (sticker_set == sticker_sets_.end()) {
2232 return nullptr;
2233 }
2234
2235 return sticker_set->second.get();
2236 }
2237
get_sticker_set_id(const tl_object_ptr<telegram_api::InputStickerSet> & set_ptr)2238 StickerSetId StickersManager::get_sticker_set_id(const tl_object_ptr<telegram_api::InputStickerSet> &set_ptr) {
2239 CHECK(set_ptr != nullptr);
2240 switch (set_ptr->get_id()) {
2241 case telegram_api::inputStickerSetEmpty::ID:
2242 return StickerSetId();
2243 case telegram_api::inputStickerSetID::ID:
2244 return StickerSetId(static_cast<const telegram_api::inputStickerSetID *>(set_ptr.get())->id_);
2245 case telegram_api::inputStickerSetShortName::ID:
2246 LOG(ERROR) << "Receive sticker set by its short name";
2247 return search_sticker_set(static_cast<const telegram_api::inputStickerSetShortName *>(set_ptr.get())->short_name_,
2248 Auto());
2249 case telegram_api::inputStickerSetAnimatedEmoji::ID:
2250 case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
2251 LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
2252 return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
2253 case telegram_api::inputStickerSetDice::ID:
2254 LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
2255 return StickerSetId();
2256 default:
2257 UNREACHABLE();
2258 return StickerSetId();
2259 }
2260 }
2261
add_sticker_set(tl_object_ptr<telegram_api::InputStickerSet> && set_ptr)2262 StickerSetId StickersManager::add_sticker_set(tl_object_ptr<telegram_api::InputStickerSet> &&set_ptr) {
2263 CHECK(set_ptr != nullptr);
2264 switch (set_ptr->get_id()) {
2265 case telegram_api::inputStickerSetEmpty::ID:
2266 return StickerSetId();
2267 case telegram_api::inputStickerSetID::ID: {
2268 auto set = move_tl_object_as<telegram_api::inputStickerSetID>(set_ptr);
2269 StickerSetId set_id{set->id_};
2270 add_sticker_set(set_id, set->access_hash_);
2271 return set_id;
2272 }
2273 case telegram_api::inputStickerSetShortName::ID: {
2274 auto set = move_tl_object_as<telegram_api::inputStickerSetShortName>(set_ptr);
2275 LOG(ERROR) << "Receive sticker set by its short name";
2276 return search_sticker_set(set->short_name_, Auto());
2277 }
2278 case telegram_api::inputStickerSetAnimatedEmoji::ID:
2279 case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
2280 LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
2281 return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
2282 case telegram_api::inputStickerSetDice::ID:
2283 LOG(ERROR) << "Receive special sticker set " << to_string(set_ptr);
2284 return StickerSetId();
2285 default:
2286 UNREACHABLE();
2287 return StickerSetId();
2288 }
2289 }
2290
add_sticker_set(StickerSetId sticker_set_id,int64 access_hash)2291 StickersManager::StickerSet *StickersManager::add_sticker_set(StickerSetId sticker_set_id, int64 access_hash) {
2292 auto &s = sticker_sets_[sticker_set_id];
2293 if (s == nullptr) {
2294 s = make_unique<StickerSet>();
2295
2296 s->id = sticker_set_id;
2297 s->access_hash = access_hash;
2298 s->is_changed = false;
2299 s->need_save_to_database = false;
2300 } else {
2301 CHECK(s->id == sticker_set_id);
2302 if (s->access_hash != access_hash) {
2303 LOG(INFO) << "Access hash of " << sticker_set_id << " changed";
2304 s->access_hash = access_hash;
2305 s->need_save_to_database = true;
2306 }
2307 }
2308 return s.get();
2309 }
2310
get_sticker_thumbnail_file_id(FileId file_id) const2311 FileId StickersManager::get_sticker_thumbnail_file_id(FileId file_id) const {
2312 auto sticker = get_sticker(file_id);
2313 CHECK(sticker != nullptr);
2314 return sticker->s_thumbnail.file_id;
2315 }
2316
delete_sticker_thumbnail(FileId file_id)2317 void StickersManager::delete_sticker_thumbnail(FileId file_id) {
2318 auto &sticker = stickers_[file_id];
2319 CHECK(sticker != nullptr);
2320 sticker->s_thumbnail = PhotoSize();
2321 }
2322
get_sticker_file_ids(FileId file_id) const2323 vector<FileId> StickersManager::get_sticker_file_ids(FileId file_id) const {
2324 vector<FileId> result;
2325 auto sticker = get_sticker(file_id);
2326 CHECK(sticker != nullptr);
2327 result.push_back(file_id);
2328 if (sticker->s_thumbnail.file_id.is_valid()) {
2329 result.push_back(sticker->s_thumbnail.file_id);
2330 }
2331 if (sticker->m_thumbnail.file_id.is_valid()) {
2332 result.push_back(sticker->m_thumbnail.file_id);
2333 }
2334 return result;
2335 }
2336
dup_sticker(FileId new_id,FileId old_id)2337 FileId StickersManager::dup_sticker(FileId new_id, FileId old_id) {
2338 const Sticker *old_sticker = get_sticker(old_id);
2339 CHECK(old_sticker != nullptr);
2340 auto &new_sticker = stickers_[new_id];
2341 CHECK(!new_sticker);
2342 new_sticker = make_unique<Sticker>(*old_sticker);
2343 new_sticker->file_id = new_id;
2344 // there is no reason to dup m_thumbnail
2345 new_sticker->s_thumbnail.file_id = td_->file_manager_->dup_file_id(new_sticker->s_thumbnail.file_id);
2346 return new_id;
2347 }
2348
merge_stickers(FileId new_id,FileId old_id,bool can_delete_old)2349 void StickersManager::merge_stickers(FileId new_id, FileId old_id, bool can_delete_old) {
2350 CHECK(old_id.is_valid() && new_id.is_valid());
2351 CHECK(new_id != old_id);
2352
2353 LOG(INFO) << "Merge stickers " << new_id << " and " << old_id;
2354 const Sticker *old_ = get_sticker(old_id);
2355 CHECK(old_ != nullptr);
2356
2357 auto new_it = stickers_.find(new_id);
2358 if (new_it == stickers_.end()) {
2359 auto &old = stickers_[old_id];
2360 if (!can_delete_old) {
2361 dup_sticker(new_id, old_id);
2362 } else {
2363 old->file_id = new_id;
2364 stickers_.emplace(new_id, std::move(old));
2365 }
2366 } else {
2367 Sticker *new_ = new_it->second.get();
2368 CHECK(new_ != nullptr);
2369
2370 if (old_->set_id == new_->set_id && (old_->alt != new_->alt || old_->set_id != new_->set_id ||
2371 (!old_->is_animated && !new_->is_animated && old_->dimensions.width != 0 &&
2372 old_->dimensions.height != 0 && old_->dimensions != new_->dimensions))) {
2373 LOG(ERROR) << "Sticker has changed: alt = (" << old_->alt << ", " << new_->alt << "), set_id = (" << old_->set_id
2374 << ", " << new_->set_id << "), dimensions = (" << old_->dimensions << ", " << new_->dimensions << ")";
2375 }
2376
2377 if (old_->s_thumbnail != new_->s_thumbnail) {
2378 // LOG_STATUS(td_->file_manager_->merge(new_->s_thumbnail.file_id, old_->s_thumbnail.file_id));
2379 }
2380 if (old_->m_thumbnail != new_->m_thumbnail) {
2381 // LOG_STATUS(td_->file_manager_->merge(new_->m_thumbnail.file_id, old_->m_thumbnail.file_id));
2382 }
2383 }
2384 LOG_STATUS(td_->file_manager_->merge(new_id, old_id));
2385 if (can_delete_old) {
2386 stickers_.erase(old_id);
2387 }
2388 }
2389
get_input_sticker_set(const StickerSet * set)2390 tl_object_ptr<telegram_api::InputStickerSet> StickersManager::get_input_sticker_set(const StickerSet *set) {
2391 CHECK(set != nullptr);
2392 return make_tl_object<telegram_api::inputStickerSetID>(set->id.get(), set->access_hash);
2393 }
2394
reload_installed_sticker_sets(bool is_masks,bool force)2395 void StickersManager::reload_installed_sticker_sets(bool is_masks, bool force) {
2396 if (G()->close_flag()) {
2397 return;
2398 }
2399
2400 auto &next_load_time = next_installed_sticker_sets_load_time_[is_masks];
2401 if (!td_->auth_manager_->is_bot() && next_load_time >= 0 && (next_load_time < Time::now() || force)) {
2402 LOG_IF(INFO, force) << "Reload sticker sets";
2403 next_load_time = -1;
2404 td_->create_handler<GetAllStickersQuery>()->send(is_masks, installed_sticker_sets_hash_[is_masks]);
2405 }
2406 }
2407
reload_featured_sticker_sets(bool force)2408 void StickersManager::reload_featured_sticker_sets(bool force) {
2409 if (G()->close_flag()) {
2410 return;
2411 }
2412
2413 auto &next_load_time = next_featured_sticker_sets_load_time_;
2414 if (!td_->auth_manager_->is_bot() && next_load_time >= 0 && (next_load_time < Time::now() || force)) {
2415 LOG_IF(INFO, force) << "Reload trending sticker sets";
2416 next_load_time = -1;
2417 td_->create_handler<GetFeaturedStickerSetsQuery>()->send(featured_sticker_sets_hash_);
2418 }
2419 }
2420
reload_old_featured_sticker_sets(uint32 generation)2421 void StickersManager::reload_old_featured_sticker_sets(uint32 generation) {
2422 if (generation != 0 && generation != old_featured_sticker_set_generation_) {
2423 return;
2424 }
2425 td_->create_handler<GetOldFeaturedStickerSetsQuery>()->send(static_cast<int32>(old_featured_sticker_set_ids_.size()),
2426 OLD_FEATURED_STICKER_SET_SLICE_SIZE,
2427 old_featured_sticker_set_generation_);
2428 }
2429
on_get_input_sticker_set(FileId sticker_file_id,tl_object_ptr<telegram_api::InputStickerSet> && set_ptr,MultiPromiseActor * load_data_multipromise_ptr)2430 StickerSetId StickersManager::on_get_input_sticker_set(FileId sticker_file_id,
2431 tl_object_ptr<telegram_api::InputStickerSet> &&set_ptr,
2432 MultiPromiseActor *load_data_multipromise_ptr) {
2433 if (set_ptr == nullptr) {
2434 return StickerSetId();
2435 }
2436 switch (set_ptr->get_id()) {
2437 case telegram_api::inputStickerSetEmpty::ID:
2438 return StickerSetId();
2439 case telegram_api::inputStickerSetID::ID: {
2440 auto set = move_tl_object_as<telegram_api::inputStickerSetID>(set_ptr);
2441 StickerSetId set_id{set->id_};
2442 add_sticker_set(set_id, set->access_hash_);
2443 return set_id;
2444 }
2445 case telegram_api::inputStickerSetShortName::ID: {
2446 auto set = move_tl_object_as<telegram_api::inputStickerSetShortName>(set_ptr);
2447 if (load_data_multipromise_ptr == nullptr) {
2448 LOG(ERROR) << "Receive sticker set " << set->short_name_ << " by its short name";
2449 return search_sticker_set(set->short_name_, Auto());
2450 }
2451 auto set_id = search_sticker_set(set->short_name_, load_data_multipromise_ptr->get_promise());
2452 if (!set_id.is_valid()) {
2453 load_data_multipromise_ptr->add_promise(PromiseCreator::lambda(
2454 [actor_id = actor_id(this), sticker_file_id, short_name = set->short_name_](Result<Unit> result) {
2455 if (result.is_ok()) {
2456 // just in case
2457 send_closure(actor_id, &StickersManager::on_resolve_sticker_set_short_name, sticker_file_id,
2458 short_name);
2459 }
2460 }));
2461 }
2462 // always return empty StickerSetId, because we can't trust the set_id provided by the peer in the secret chat
2463 // the real sticker set id will be set in on_get_sticker if and only if the sticker is really from the set
2464 return StickerSetId();
2465 }
2466 case telegram_api::inputStickerSetAnimatedEmoji::ID:
2467 case telegram_api::inputStickerSetAnimatedEmojiAnimations::ID:
2468 return add_special_sticker_set(SpecialStickerSetType(set_ptr)).id_;
2469 case telegram_api::inputStickerSetDice::ID:
2470 return StickerSetId();
2471 default:
2472 UNREACHABLE();
2473 return StickerSetId();
2474 }
2475 }
2476
on_resolve_sticker_set_short_name(FileId sticker_file_id,const string & short_name)2477 void StickersManager::on_resolve_sticker_set_short_name(FileId sticker_file_id, const string &short_name) {
2478 if (G()->close_flag()) {
2479 return;
2480 }
2481
2482 LOG(INFO) << "Resolve sticker " << sticker_file_id << " set to " << short_name;
2483 StickerSetId set_id = search_sticker_set(short_name, Auto());
2484 if (set_id.is_valid()) {
2485 auto &s = stickers_[sticker_file_id];
2486 CHECK(s != nullptr);
2487 CHECK(s->file_id == sticker_file_id);
2488 if (s->set_id != set_id) {
2489 s->set_id = set_id;
2490 }
2491 }
2492 }
2493
add_sticker_thumbnail(Sticker * s,PhotoSize thumbnail)2494 void StickersManager::add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail) {
2495 if (!thumbnail.file_id.is_valid()) {
2496 return;
2497 }
2498 if (thumbnail.type == 'm') {
2499 s->m_thumbnail = std::move(thumbnail);
2500 return;
2501 }
2502 if (thumbnail.type == 's' || thumbnail.type == 't') {
2503 s->s_thumbnail = std::move(thumbnail);
2504 return;
2505 }
2506 LOG(ERROR) << "Receive sticker thumbnail of unsupported type " << thumbnail.type;
2507 }
2508
create_sticker(FileId file_id,string minithumbnail,PhotoSize thumbnail,Dimensions dimensions,tl_object_ptr<telegram_api::documentAttributeSticker> sticker,bool is_animated,MultiPromiseActor * load_data_multipromise_ptr)2509 void StickersManager::create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions,
2510 tl_object_ptr<telegram_api::documentAttributeSticker> sticker, bool is_animated,
2511 MultiPromiseActor *load_data_multipromise_ptr) {
2512 if (is_animated && dimensions.width == 0) {
2513 dimensions.width = 512;
2514 dimensions.height = 512;
2515 }
2516
2517 auto s = make_unique<Sticker>();
2518 s->file_id = file_id;
2519 s->dimensions = dimensions;
2520 if (!td_->auth_manager_->is_bot()) {
2521 s->minithumbnail = std::move(minithumbnail);
2522 }
2523 add_sticker_thumbnail(s.get(), std::move(thumbnail));
2524 if (sticker != nullptr) {
2525 s->set_id = on_get_input_sticker_set(file_id, std::move(sticker->stickerset_), load_data_multipromise_ptr);
2526 s->alt = std::move(sticker->alt_);
2527
2528 s->is_mask = (sticker->flags_ & telegram_api::documentAttributeSticker::MASK_MASK) != 0;
2529 if ((sticker->flags_ & telegram_api::documentAttributeSticker::MASK_COORDS_MASK) != 0) {
2530 CHECK(sticker->mask_coords_ != nullptr);
2531 int32 point = sticker->mask_coords_->n_;
2532 if (0 <= point && point <= 3) {
2533 s->point = sticker->mask_coords_->n_;
2534 s->x_shift = sticker->mask_coords_->x_;
2535 s->y_shift = sticker->mask_coords_->y_;
2536 s->scale = sticker->mask_coords_->zoom_;
2537 }
2538 }
2539 }
2540 s->is_animated = is_animated;
2541 on_get_sticker(std::move(s), sticker != nullptr);
2542 }
2543
has_input_media(FileId sticker_file_id,bool is_secret) const2544 bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) const {
2545 auto file_view = td_->file_manager_->get_file_view(sticker_file_id);
2546 if (is_secret) {
2547 const Sticker *sticker = get_sticker(sticker_file_id);
2548 CHECK(sticker != nullptr);
2549 if (file_view.is_encrypted_secret()) {
2550 if (!file_view.encryption_key().empty() && file_view.has_remote_location() &&
2551 !sticker->s_thumbnail.file_id.is_valid()) {
2552 return true;
2553 }
2554 } else if (!file_view.is_encrypted()) {
2555 if (sticker->set_id.is_valid()) {
2556 // stickers within a set can be sent by id and access_hash
2557 return true;
2558 }
2559 }
2560 } else {
2561 if (file_view.is_encrypted()) {
2562 return false;
2563 }
2564 if (td_->auth_manager_->is_bot() && file_view.has_remote_location()) {
2565 return true;
2566 }
2567 // having remote location is not enough to have InputMedia, because the file may not have valid file_reference
2568 // also file_id needs to be duped, because upload can be called to repair the file_reference and every upload
2569 // request must have unique file_id
2570 if (/* file_view.has_remote_location() || */ file_view.has_url()) {
2571 return true;
2572 }
2573 }
2574
2575 return false;
2576 }
2577
get_secret_input_media(FileId sticker_file_id,tl_object_ptr<telegram_api::InputEncryptedFile> input_file,BufferSlice thumbnail) const2578 SecretInputMedia StickersManager::get_secret_input_media(FileId sticker_file_id,
2579 tl_object_ptr<telegram_api::InputEncryptedFile> input_file,
2580 BufferSlice thumbnail) const {
2581 const Sticker *sticker = get_sticker(sticker_file_id);
2582 CHECK(sticker != nullptr);
2583 auto file_view = td_->file_manager_->get_file_view(sticker_file_id);
2584 if (file_view.is_encrypted_secret()) {
2585 if (file_view.has_remote_location()) {
2586 input_file = file_view.main_remote_location().as_input_encrypted_file();
2587 }
2588 if (!input_file) {
2589 return {};
2590 }
2591 if (sticker->s_thumbnail.file_id.is_valid() && thumbnail.empty()) {
2592 return {};
2593 }
2594 } else if (!file_view.is_encrypted()) {
2595 if (!sticker->set_id.is_valid()) {
2596 // stickers without set can't be sent by id and access_hash
2597 return {};
2598 }
2599 } else {
2600 return {};
2601 }
2602
2603 tl_object_ptr<secret_api::InputStickerSet> input_sticker_set = make_tl_object<secret_api::inputStickerSetEmpty>();
2604 if (sticker->set_id.is_valid()) {
2605 const StickerSet *sticker_set = get_sticker_set(sticker->set_id);
2606 CHECK(sticker_set != nullptr);
2607 if (sticker_set->is_inited) {
2608 input_sticker_set = make_tl_object<secret_api::inputStickerSetShortName>(sticker_set->short_name);
2609 } else {
2610 // TODO load sticker set
2611 }
2612 }
2613
2614 vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
2615 attributes.push_back(
2616 secret_api::make_object<secret_api::documentAttributeSticker>(sticker->alt, std::move(input_sticker_set)));
2617 if (sticker->dimensions.width != 0 && sticker->dimensions.height != 0) {
2618 attributes.push_back(secret_api::make_object<secret_api::documentAttributeImageSize>(sticker->dimensions.width,
2619 sticker->dimensions.height));
2620 }
2621
2622 if (file_view.is_encrypted_secret()) {
2623 auto &encryption_key = file_view.encryption_key();
2624 return SecretInputMedia{std::move(input_file),
2625 make_tl_object<secret_api::decryptedMessageMediaDocument>(
2626 std::move(thumbnail), sticker->s_thumbnail.dimensions.width,
2627 sticker->s_thumbnail.dimensions.height, get_sticker_mime_type(sticker),
2628 narrow_cast<int32>(file_view.size()), BufferSlice(encryption_key.key_slice()),
2629 BufferSlice(encryption_key.iv_slice()), std::move(attributes), "")};
2630 } else {
2631 CHECK(!file_view.is_encrypted());
2632 auto &remote_location = file_view.remote_location();
2633 if (remote_location.is_web()) {
2634 // web stickers shouldn't have set_id
2635 LOG(ERROR) << "Have a web sticker in " << sticker->set_id;
2636 return {};
2637 }
2638 return SecretInputMedia{nullptr, make_tl_object<secret_api::decryptedMessageMediaExternalDocument>(
2639 remote_location.get_id(), remote_location.get_access_hash(), 0 /*date*/,
2640 get_sticker_mime_type(sticker), narrow_cast<int32>(file_view.size()),
2641 make_tl_object<secret_api::photoSizeEmpty>("t"),
2642 remote_location.get_dc_id().get_raw_id(), std::move(attributes))};
2643 }
2644 }
2645
get_input_media(FileId file_id,tl_object_ptr<telegram_api::InputFile> input_file,tl_object_ptr<telegram_api::InputFile> input_thumbnail,const string & emoji) const2646 tl_object_ptr<telegram_api::InputMedia> StickersManager::get_input_media(
2647 FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file,
2648 tl_object_ptr<telegram_api::InputFile> input_thumbnail, const string &emoji) const {
2649 auto file_view = td_->file_manager_->get_file_view(file_id);
2650 if (file_view.is_encrypted()) {
2651 return nullptr;
2652 }
2653 if (file_view.has_remote_location() && !file_view.main_remote_location().is_web() && input_file == nullptr) {
2654 int32 flags = 0;
2655 if (!emoji.empty()) {
2656 flags |= telegram_api::inputMediaDocument::QUERY_MASK;
2657 }
2658 return make_tl_object<telegram_api::inputMediaDocument>(flags, file_view.main_remote_location().as_input_document(),
2659 0, emoji);
2660 }
2661 if (file_view.has_url()) {
2662 return make_tl_object<telegram_api::inputMediaDocumentExternal>(0, file_view.url(), 0);
2663 }
2664
2665 if (input_file != nullptr) {
2666 const Sticker *s = get_sticker(file_id);
2667 CHECK(s != nullptr);
2668
2669 vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
2670 if (s->dimensions.width != 0 && s->dimensions.height != 0) {
2671 attributes.push_back(
2672 make_tl_object<telegram_api::documentAttributeImageSize>(s->dimensions.width, s->dimensions.height));
2673 }
2674 attributes.push_back(make_tl_object<telegram_api::documentAttributeSticker>(
2675 0, false /*ignored*/, s->alt, make_tl_object<telegram_api::inputStickerSetEmpty>(), nullptr));
2676
2677 int32 flags = 0;
2678 if (input_thumbnail != nullptr) {
2679 flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
2680 }
2681 auto mime_type = get_sticker_mime_type(s);
2682 if (!s->is_animated && !s->set_id.is_valid()) {
2683 auto suggested_path = file_view.suggested_path();
2684 const PathView path_view(suggested_path);
2685 if (path_view.extension() == "tgs") {
2686 mime_type = "application/x-tgsticker";
2687 }
2688 }
2689 return make_tl_object<telegram_api::inputMediaUploadedDocument>(
2690 flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
2691 std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
2692 } else {
2693 CHECK(!file_view.has_remote_location());
2694 }
2695
2696 return nullptr;
2697 }
2698
on_get_sticker_set(tl_object_ptr<telegram_api::stickerSet> && set,bool is_changed,const char * source)2699 StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::stickerSet> &&set, bool is_changed,
2700 const char *source) {
2701 CHECK(set != nullptr);
2702 StickerSetId set_id{set->id_};
2703 StickerSet *s = add_sticker_set(set_id, set->access_hash_);
2704
2705 bool is_installed = (set->flags_ & telegram_api::stickerSet::INSTALLED_DATE_MASK) != 0;
2706 bool is_archived = set->archived_;
2707 bool is_official = set->official_;
2708 bool is_animated = set->animated_;
2709 bool is_masks = set->masks_;
2710
2711 PhotoSize thumbnail;
2712 string minithumbnail;
2713 for (auto &thumb : set->thumbs_) {
2714 auto photo_size =
2715 get_photo_size(td_->file_manager_.get(),
2716 PhotoSizeSource::sticker_set_thumbnail(set_id.get(), s->access_hash, set->thumb_version_), 0, 0,
2717 "", DcId::create(set->thumb_dc_id_), DialogId(), std::move(thumb),
2718 is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
2719 if (photo_size.get_offset() == 0) {
2720 if (!thumbnail.file_id.is_valid()) {
2721 thumbnail = std::move(photo_size.get<0>());
2722 }
2723 } else {
2724 minithumbnail = std::move(photo_size.get<1>());
2725 }
2726 }
2727 if (!s->is_inited) {
2728 LOG(INFO) << "Init " << set_id;
2729 s->is_inited = true;
2730 s->title = std::move(set->title_);
2731 s->short_name = std::move(set->short_name_);
2732 if (!td_->auth_manager_->is_bot()) {
2733 s->minithumbnail = std::move(minithumbnail);
2734 }
2735 s->thumbnail = std::move(thumbnail);
2736 s->is_thumbnail_reloaded = true;
2737 s->are_legacy_sticker_thumbnails_reloaded = true;
2738 s->sticker_count = set->count_;
2739 s->hash = set->hash_;
2740 s->is_official = is_official;
2741 s->is_animated = is_animated;
2742 s->is_masks = is_masks;
2743 s->is_changed = true;
2744 } else {
2745 CHECK(s->id == set_id);
2746 if (s->access_hash != set->access_hash_) {
2747 LOG(INFO) << "Access hash of " << set_id << " has changed";
2748 s->access_hash = set->access_hash_;
2749 s->need_save_to_database = true;
2750 }
2751 if (s->title != set->title_) {
2752 LOG(INFO) << "Title of " << set_id << " has changed";
2753 s->title = std::move(set->title_);
2754 s->is_changed = true;
2755
2756 if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id.get())) {
2757 installed_sticker_sets_hints_[s->is_masks].add(set_id.get(), PSLICE() << s->title << ' ' << s->short_name);
2758 }
2759 }
2760 if (s->short_name != set->short_name_) {
2761 LOG(ERROR) << "Short name of " << set_id << " has changed from \"" << s->short_name << "\" to \""
2762 << set->short_name_ << "\" from " << source;
2763 short_name_to_sticker_set_id_.erase(clean_username(s->short_name));
2764 s->short_name = std::move(set->short_name_);
2765 s->is_changed = true;
2766
2767 if (installed_sticker_sets_hints_[s->is_masks].has_key(set_id.get())) {
2768 installed_sticker_sets_hints_[s->is_masks].add(set_id.get(), PSLICE() << s->title << ' ' << s->short_name);
2769 }
2770 }
2771 if (s->minithumbnail != minithumbnail) {
2772 LOG(INFO) << "Minithumbnail of " << set_id << " has changed";
2773 s->minithumbnail = std::move(minithumbnail);
2774 s->is_changed = true;
2775 }
2776 if (s->thumbnail != thumbnail) {
2777 LOG(INFO) << "Thumbnail of " << set_id << " has changed from " << s->thumbnail << " to " << thumbnail;
2778 s->thumbnail = std::move(thumbnail);
2779 s->is_changed = true;
2780 }
2781 if (!s->is_thumbnail_reloaded || !s->are_legacy_sticker_thumbnails_reloaded) {
2782 LOG(INFO) << "Sticker thumbnails and thumbnail of " << set_id << " was reloaded";
2783 s->is_thumbnail_reloaded = true;
2784 s->are_legacy_sticker_thumbnails_reloaded = true;
2785 s->need_save_to_database = true;
2786 }
2787
2788 if (s->sticker_count != set->count_ || s->hash != set->hash_) {
2789 LOG(INFO) << "Number of stickers in " << set_id << " changed from " << s->sticker_count << " to " << set->count_;
2790 s->is_loaded = false;
2791
2792 s->sticker_count = set->count_;
2793 s->hash = set->hash_;
2794 if (s->was_loaded) {
2795 s->need_save_to_database = true;
2796 } else {
2797 s->is_changed = true;
2798 }
2799 }
2800
2801 if (s->is_official != is_official) {
2802 LOG(INFO) << "Official flag of " << set_id << " changed to " << is_official;
2803 s->is_official = is_official;
2804 s->is_changed = true;
2805 }
2806 if (s->is_animated != is_animated) {
2807 LOG(ERROR) << "Animated type of " << set_id << "/" << s->short_name << " has changed from " << s->is_animated
2808 << " to " << is_animated << " from " << source;
2809 s->is_animated = is_animated;
2810 s->is_changed = true;
2811 }
2812 LOG_IF(ERROR, s->is_masks != is_masks) << "Masks type of " << set_id << "/" << s->short_name << " has changed from "
2813 << s->is_masks << " to " << is_masks << " from " << source;
2814 }
2815 short_name_to_sticker_set_id_.emplace(clean_username(s->short_name), set_id);
2816
2817 on_update_sticker_set(s, is_installed, is_archived, is_changed);
2818
2819 return set_id;
2820 }
2821
on_get_sticker_set_covered(tl_object_ptr<telegram_api::StickerSetCovered> && set_ptr,bool is_changed,const char * source)2822 StickerSetId StickersManager::on_get_sticker_set_covered(tl_object_ptr<telegram_api::StickerSetCovered> &&set_ptr,
2823 bool is_changed, const char *source) {
2824 StickerSetId set_id;
2825 switch (set_ptr->get_id()) {
2826 case telegram_api::stickerSetCovered::ID: {
2827 auto covered_set = move_tl_object_as<telegram_api::stickerSetCovered>(set_ptr);
2828 set_id = on_get_sticker_set(std::move(covered_set->set_), is_changed, source);
2829 if (!set_id.is_valid()) {
2830 break;
2831 }
2832
2833 auto sticker_set = get_sticker_set(set_id);
2834 CHECK(sticker_set != nullptr);
2835 CHECK(sticker_set->is_inited);
2836 if (sticker_set->was_loaded) {
2837 break;
2838 }
2839 if (sticker_set->sticker_count == 0) {
2840 break;
2841 }
2842
2843 auto &sticker_ids = sticker_set->sticker_ids;
2844
2845 auto sticker_id = on_get_sticker_document(std::move(covered_set->cover_)).second;
2846 if (sticker_id.is_valid() && !td::contains(sticker_ids, sticker_id)) {
2847 sticker_ids.push_back(sticker_id);
2848 sticker_set->is_changed = true;
2849 }
2850
2851 break;
2852 }
2853 case telegram_api::stickerSetMultiCovered::ID: {
2854 auto multicovered_set = move_tl_object_as<telegram_api::stickerSetMultiCovered>(set_ptr);
2855 set_id = on_get_sticker_set(std::move(multicovered_set->set_), is_changed, source);
2856 if (!set_id.is_valid()) {
2857 break;
2858 }
2859
2860 auto sticker_set = get_sticker_set(set_id);
2861 CHECK(sticker_set != nullptr);
2862 CHECK(sticker_set->is_inited);
2863 if (sticker_set->was_loaded) {
2864 break;
2865 }
2866 auto &sticker_ids = sticker_set->sticker_ids;
2867
2868 for (auto &cover : multicovered_set->covers_) {
2869 auto sticker_id = on_get_sticker_document(std::move(cover)).second;
2870 if (sticker_id.is_valid() && !td::contains(sticker_ids, sticker_id)) {
2871 sticker_ids.push_back(sticker_id);
2872 sticker_set->is_changed = true;
2873 }
2874 }
2875
2876 break;
2877 }
2878 default:
2879 UNREACHABLE();
2880 }
2881 return set_id;
2882 }
2883
on_get_messages_sticker_set(StickerSetId sticker_set_id,tl_object_ptr<telegram_api::messages_StickerSet> && set_ptr,bool is_changed,const char * source)2884 StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_set_id,
2885 tl_object_ptr<telegram_api::messages_StickerSet> &&set_ptr,
2886 bool is_changed, const char *source) {
2887 LOG(INFO) << "Receive sticker set " << to_string(set_ptr);
2888 if (set_ptr->get_id() == telegram_api::messages_stickerSetNotModified::ID) {
2889 if (!sticker_set_id.is_valid()) {
2890 LOG(ERROR) << "Receive unexpected stickerSetNotModified from " << source;
2891 } else {
2892 auto s = get_sticker_set(sticker_set_id);
2893 CHECK(s != nullptr);
2894 CHECK(s->is_inited);
2895 CHECK(s->was_loaded);
2896
2897 s->expires_at = G()->unix_time() +
2898 (td_->auth_manager_->is_bot() ? Random::fast(10 * 60, 15 * 60) : Random::fast(30 * 60, 50 * 60));
2899 }
2900 return sticker_set_id;
2901 }
2902 auto set = move_tl_object_as<telegram_api::messages_stickerSet>(set_ptr);
2903
2904 auto set_id = on_get_sticker_set(std::move(set->set_), is_changed, source);
2905 if (!set_id.is_valid()) {
2906 return set_id;
2907 }
2908 if (sticker_set_id.is_valid() && sticker_set_id != set_id) {
2909 LOG(ERROR) << "Expected " << sticker_set_id << ", but receive " << set_id << " from " << source;
2910 on_load_sticker_set_fail(sticker_set_id, Status::Error(500, "Internal Server Error: wrong sticker set received"));
2911 return StickerSetId();
2912 }
2913
2914 auto s = get_sticker_set(set_id);
2915 CHECK(s != nullptr);
2916 CHECK(s->is_inited);
2917
2918 s->expires_at = G()->unix_time() +
2919 (td_->auth_manager_->is_bot() ? Random::fast(10 * 60, 15 * 60) : Random::fast(30 * 60, 50 * 60));
2920
2921 if (s->is_loaded) {
2922 update_sticker_set(s, "on_get_messages_sticker_set");
2923 send_update_installed_sticker_sets();
2924 return set_id;
2925 }
2926 s->was_loaded = true;
2927 s->is_loaded = true;
2928 s->is_changed = true;
2929
2930 vector<tl_object_ptr<telegram_api::stickerPack>> packs = std::move(set->packs_);
2931 vector<tl_object_ptr<telegram_api::Document>> documents = std::move(set->documents_);
2932
2933 std::unordered_map<int64, FileId> document_id_to_sticker_id;
2934
2935 s->sticker_ids.clear();
2936 bool is_bot = td_->auth_manager_->is_bot();
2937 for (auto &document_ptr : documents) {
2938 auto sticker_id = on_get_sticker_document(std::move(document_ptr));
2939 if (!sticker_id.second.is_valid()) {
2940 continue;
2941 }
2942
2943 s->sticker_ids.push_back(sticker_id.second);
2944 if (!is_bot) {
2945 document_id_to_sticker_id.insert(sticker_id);
2946 }
2947 }
2948 if (static_cast<int32>(s->sticker_ids.size()) != s->sticker_count) {
2949 LOG(ERROR) << "Wrong sticker set size " << s->sticker_count << " instead of " << s->sticker_ids.size()
2950 << " specified in " << set_id << "/" << s->short_name << " from " << source;
2951 s->sticker_count = static_cast<int32>(s->sticker_ids.size());
2952 }
2953
2954 if (!is_bot) {
2955 s->emoji_stickers_map_.clear();
2956 s->sticker_emojis_map_.clear();
2957 for (auto &pack : packs) {
2958 vector<FileId> stickers;
2959 stickers.reserve(pack->documents_.size());
2960 for (int64 document_id : pack->documents_) {
2961 auto it = document_id_to_sticker_id.find(document_id);
2962 if (it == document_id_to_sticker_id.end()) {
2963 LOG(ERROR) << "Can't find document with ID " << document_id << " in " << set_id << "/" << s->short_name
2964 << " from " << source;
2965 continue;
2966 }
2967
2968 stickers.push_back(it->second);
2969 s->sticker_emojis_map_[it->second].push_back(pack->emoticon_);
2970 }
2971 auto &sticker_ids = s->emoji_stickers_map_[remove_emoji_modifiers(pack->emoticon_).str()];
2972 for (auto sticker_id : stickers) {
2973 if (!td::contains(sticker_ids, sticker_id)) {
2974 sticker_ids.push_back(sticker_id);
2975 }
2976 }
2977 }
2978 }
2979
2980 update_sticker_set(s, "on_get_messages_sticker_set 2");
2981 update_load_requests(s, true, Status::OK());
2982 send_update_installed_sticker_sets();
2983
2984 if (set_id == add_special_sticker_set(SpecialStickerSetType::animated_emoji()).id_) {
2985 try_update_animated_emoji_messages();
2986 }
2987
2988 return set_id;
2989 }
2990
on_load_sticker_set_fail(StickerSetId sticker_set_id,const Status & error)2991 void StickersManager::on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error) {
2992 if (!sticker_set_id.is_valid()) {
2993 return;
2994 }
2995 update_load_requests(get_sticker_set(sticker_set_id), true, error);
2996 }
2997
update_load_requests(StickerSet * sticker_set,bool with_stickers,const Status & status)2998 void StickersManager::update_load_requests(StickerSet *sticker_set, bool with_stickers, const Status &status) {
2999 if (sticker_set == nullptr) {
3000 return;
3001 }
3002 if (with_stickers) {
3003 for (auto load_request_id : sticker_set->load_requests) {
3004 update_load_request(load_request_id, status);
3005 }
3006
3007 sticker_set->load_requests.clear();
3008 }
3009 for (auto load_request_id : sticker_set->load_without_stickers_requests) {
3010 update_load_request(load_request_id, status);
3011 }
3012
3013 sticker_set->load_without_stickers_requests.clear();
3014
3015 if (status.message() == "STICKERSET_INVALID") {
3016 // the sticker set is likely to be deleted
3017 // clear short_name_to_sticker_set_id_ to allow next searchStickerSet request to succeed
3018 short_name_to_sticker_set_id_.erase(clean_username(sticker_set->short_name));
3019 }
3020 }
3021
update_load_request(uint32 load_request_id,const Status & status)3022 void StickersManager::update_load_request(uint32 load_request_id, const Status &status) {
3023 auto it = sticker_set_load_requests_.find(load_request_id);
3024 CHECK(it != sticker_set_load_requests_.end());
3025 CHECK(it->second.left_queries > 0);
3026 if (status.is_error() && it->second.error.is_ok()) {
3027 it->second.error = status.clone();
3028 }
3029 if (--it->second.left_queries == 0) {
3030 if (it->second.error.is_ok()) {
3031 it->second.promise.set_value(Unit());
3032 } else {
3033 it->second.promise.set_error(std::move(it->second.error));
3034 }
3035 sticker_set_load_requests_.erase(it);
3036 }
3037 }
3038
on_get_special_sticker_set(const SpecialStickerSetType & type,StickerSetId sticker_set_id)3039 void StickersManager::on_get_special_sticker_set(const SpecialStickerSetType &type, StickerSetId sticker_set_id) {
3040 auto s = get_sticker_set(sticker_set_id);
3041 CHECK(s != nullptr);
3042 CHECK(s->is_inited);
3043 CHECK(s->is_loaded);
3044
3045 LOG(INFO) << "Receive special sticker set " << type.type_ << ": " << sticker_set_id << ' ' << s->access_hash << ' '
3046 << s->short_name;
3047 auto &sticker_set = add_special_sticker_set(type);
3048 if (sticker_set_id == sticker_set.id_ && s->access_hash == sticker_set.access_hash_ &&
3049 s->short_name == sticker_set.short_name_ && !s->short_name.empty()) {
3050 on_load_special_sticker_set(type, Status::OK());
3051 return;
3052 }
3053
3054 sticker_set.id_ = sticker_set_id;
3055 sticker_set.access_hash_ = s->access_hash;
3056 sticker_set.short_name_ = clean_username(s->short_name);
3057 sticker_set.type_ = type;
3058
3059 G()->td_db()->get_binlog_pmc()->set(type.type_, PSTRING() << sticker_set.id_.get() << ' ' << sticker_set.access_hash_
3060 << ' ' << sticker_set.short_name_);
3061 if (type == SpecialStickerSetType::animated_emoji()) {
3062 try_update_animated_emoji_messages();
3063 } else if (!type.get_dice_emoji().empty()) {
3064 sticker_set.is_being_loaded_ = true;
3065 }
3066 on_load_special_sticker_set(type, Status::OK());
3067 }
3068
on_get_installed_sticker_sets(bool is_masks,tl_object_ptr<telegram_api::messages_AllStickers> && stickers_ptr)3069 void StickersManager::on_get_installed_sticker_sets(bool is_masks,
3070 tl_object_ptr<telegram_api::messages_AllStickers> &&stickers_ptr) {
3071 next_installed_sticker_sets_load_time_[is_masks] = Time::now_cached() + Random::fast(30 * 60, 50 * 60);
3072
3073 CHECK(stickers_ptr != nullptr);
3074 int32 constructor_id = stickers_ptr->get_id();
3075 if (constructor_id == telegram_api::messages_allStickersNotModified::ID) {
3076 LOG(INFO) << (is_masks ? "Masks" : "Stickers") << " are not modified";
3077 return;
3078 }
3079 CHECK(constructor_id == telegram_api::messages_allStickers::ID);
3080 auto stickers = move_tl_object_as<telegram_api::messages_allStickers>(stickers_ptr);
3081
3082 std::unordered_set<StickerSetId, StickerSetIdHash> uninstalled_sticker_sets(
3083 installed_sticker_set_ids_[is_masks].begin(), installed_sticker_set_ids_[is_masks].end());
3084
3085 vector<StickerSetId> sets_to_load;
3086 vector<StickerSetId> installed_sticker_set_ids;
3087 vector<int32> debug_hashes;
3088 vector<int64> debug_sticker_set_ids;
3089 std::reverse(stickers->sets_.begin(), stickers->sets_.end()); // apply installed sticker sets in reverse order
3090 for (auto &set : stickers->sets_) {
3091 debug_hashes.push_back(set->hash_);
3092 debug_sticker_set_ids.push_back(set->id_);
3093 StickerSetId set_id = on_get_sticker_set(std::move(set), false, "on_get_installed_sticker_sets");
3094 if (!set_id.is_valid()) {
3095 continue;
3096 }
3097
3098 auto sticker_set = get_sticker_set(set_id);
3099 CHECK(sticker_set != nullptr);
3100 LOG_IF(ERROR, !sticker_set->is_installed) << "Receive non-installed sticker set in getAllStickers";
3101 LOG_IF(ERROR, sticker_set->is_archived) << "Receive archived sticker set in getAllStickers";
3102 LOG_IF(ERROR, sticker_set->is_masks != is_masks) << "Receive sticker set of a wrong type in getAllStickers";
3103 CHECK(sticker_set->is_inited);
3104
3105 if (sticker_set->is_installed && !sticker_set->is_archived && sticker_set->is_masks == is_masks) {
3106 installed_sticker_set_ids.push_back(set_id);
3107 uninstalled_sticker_sets.erase(set_id);
3108 }
3109 update_sticker_set(sticker_set, "on_get_installed_sticker_sets");
3110
3111 if (!sticker_set->is_archived && !sticker_set->is_loaded) {
3112 sets_to_load.push_back(set_id);
3113 }
3114 }
3115 std::reverse(debug_hashes.begin(), debug_hashes.end());
3116 std::reverse(installed_sticker_set_ids.begin(), installed_sticker_set_ids.end());
3117 std::reverse(debug_sticker_set_ids.begin(), debug_sticker_set_ids.end());
3118
3119 if (!sets_to_load.empty()) {
3120 load_sticker_sets(std::move(sets_to_load), Auto());
3121 }
3122
3123 for (auto set_id : uninstalled_sticker_sets) {
3124 auto sticker_set = get_sticker_set(set_id);
3125 CHECK(sticker_set != nullptr);
3126 CHECK(sticker_set->is_installed && !sticker_set->is_archived);
3127 on_update_sticker_set(sticker_set, false, false, true);
3128 update_sticker_set(sticker_set, "on_get_installed_sticker_sets 2");
3129 }
3130
3131 on_load_installed_sticker_sets_finished(is_masks, std::move(installed_sticker_set_ids));
3132
3133 if (installed_sticker_sets_hash_[is_masks] != stickers->hash_) {
3134 LOG(ERROR) << "Sticker sets hash mismatch: server hash list = " << format::as_array(debug_hashes)
3135 << ", client hash list = "
3136 << format::as_array(
3137 transform(installed_sticker_set_ids_[is_masks],
3138 [this](StickerSetId sticker_set_id) { return get_sticker_set(sticker_set_id)->hash; }))
3139 << ", server sticker set list = " << format::as_array(debug_sticker_set_ids)
3140 << ", client sticker set list = " << format::as_array(installed_sticker_set_ids_[is_masks])
3141 << ", server hash = " << stickers->hash_ << ", client hash = " << installed_sticker_sets_hash_[is_masks];
3142 }
3143 }
3144
on_get_installed_sticker_sets_failed(bool is_masks,Status error)3145 void StickersManager::on_get_installed_sticker_sets_failed(bool is_masks, Status error) {
3146 CHECK(error.is_error());
3147 next_installed_sticker_sets_load_time_[is_masks] = Time::now_cached() + Random::fast(5, 10);
3148 auto promises = std::move(load_installed_sticker_sets_queries_[is_masks]);
3149 load_installed_sticker_sets_queries_[is_masks].clear();
3150 for (auto &promise : promises) {
3151 promise.set_error(error.clone());
3152 }
3153 }
3154
get_stickers(string emoji,int32 limit,bool force,Promise<Unit> && promise)3155 vector<FileId> StickersManager::get_stickers(string emoji, int32 limit, bool force, Promise<Unit> &&promise) {
3156 if (limit <= 0) {
3157 promise.set_error(Status::Error(400, "Parameter limit must be positive"));
3158 return {};
3159 }
3160 if (!are_installed_sticker_sets_loaded_[0]) {
3161 load_installed_sticker_sets(false, std::move(promise));
3162 return {};
3163 }
3164
3165 remove_emoji_modifiers_in_place(emoji);
3166 if (!emoji.empty()) {
3167 if (!are_recent_stickers_loaded_[0]) {
3168 load_recent_stickers(false, std::move(promise));
3169 return {};
3170 }
3171 if (!are_favorite_stickers_loaded_) {
3172 load_favorite_stickers(std::move(promise));
3173 return {};
3174 }
3175 /*
3176 if (!are_featured_sticker_sets_loaded_) {
3177 load_featured_sticker_sets(std::move(promise));
3178 return {};
3179 }
3180 */
3181 }
3182
3183 vector<StickerSetId> sets_to_load;
3184 bool need_load = false;
3185 for (const auto &sticker_set_id : installed_sticker_set_ids_[0]) {
3186 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3187 CHECK(sticker_set != nullptr);
3188 CHECK(sticker_set->is_inited);
3189 CHECK(!sticker_set->is_archived);
3190 if (!sticker_set->is_loaded) {
3191 sets_to_load.push_back(sticker_set_id);
3192 if (!sticker_set->was_loaded) {
3193 need_load = true;
3194 }
3195 }
3196 }
3197
3198 vector<FileId> prepend_sticker_ids;
3199 if (!emoji.empty()) {
3200 prepend_sticker_ids.reserve(favorite_sticker_ids_.size() + recent_sticker_ids_[0].size());
3201 append(prepend_sticker_ids, recent_sticker_ids_[0]);
3202 for (auto sticker_id : favorite_sticker_ids_) {
3203 if (!td::contains(prepend_sticker_ids, sticker_id)) {
3204 prepend_sticker_ids.push_back(sticker_id);
3205 }
3206 }
3207
3208 auto prefer_animated = [this](FileId lhs, FileId rhs) {
3209 const Sticker *lhs_s = get_sticker(lhs);
3210 const Sticker *rhs_s = get_sticker(rhs);
3211 CHECK(lhs_s != nullptr && rhs_s != nullptr);
3212 return lhs_s->is_animated && !rhs_s->is_animated;
3213 };
3214 // std::stable_sort(prepend_sticker_ids.begin(), prepend_sticker_ids.begin() + recent_sticker_ids_[0].size(),
3215 // prefer_animated);
3216 std::stable_sort(prepend_sticker_ids.begin() + recent_sticker_ids_[0].size(), prepend_sticker_ids.end(),
3217 prefer_animated);
3218
3219 LOG(INFO) << "Have " << recent_sticker_ids_[0] << " recent and " << favorite_sticker_ids_ << " favorite stickers";
3220 for (const auto &sticker_id : prepend_sticker_ids) {
3221 const Sticker *s = get_sticker(sticker_id);
3222 CHECK(s != nullptr);
3223 LOG(INFO) << "Have prepend sticker " << sticker_id << " from " << s->set_id;
3224 if (s->set_id.is_valid() && !td::contains(sets_to_load, s->set_id)) {
3225 const StickerSet *sticker_set = get_sticker_set(s->set_id);
3226 if (sticker_set == nullptr || !sticker_set->is_loaded) {
3227 sets_to_load.push_back(s->set_id);
3228 if (sticker_set == nullptr || !sticker_set->was_loaded) {
3229 need_load = true;
3230 }
3231 }
3232 }
3233 }
3234 }
3235
3236 if (!sets_to_load.empty()) {
3237 if (need_load && !force) {
3238 load_sticker_sets(std::move(sets_to_load),
3239 PromiseCreator::lambda([promise = std::move(promise)](Result<Unit> result) mutable {
3240 if (result.is_error() && result.error().message() != "STICKERSET_INVALID") {
3241 LOG(ERROR) << "Failed to load sticker sets: " << result.error();
3242 }
3243 promise.set_value(Unit());
3244 }));
3245 return {};
3246 } else {
3247 load_sticker_sets(std::move(sets_to_load), Auto());
3248 }
3249 }
3250
3251 vector<FileId> result;
3252 auto limit_size_t = static_cast<size_t>(limit);
3253 if (emoji.empty()) {
3254 for (const auto &sticker_set_id : installed_sticker_set_ids_[0]) {
3255 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3256 if (sticker_set == nullptr || !sticker_set->was_loaded) {
3257 continue;
3258 }
3259
3260 append(result, sticker_set->sticker_ids);
3261 if (result.size() > limit_size_t) {
3262 result.resize(limit_size_t);
3263 break;
3264 }
3265 }
3266 } else {
3267 vector<const StickerSet *> examined_sticker_sets;
3268 for (const auto &sticker_set_id : installed_sticker_set_ids_[0]) {
3269 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3270 if (sticker_set == nullptr || !sticker_set->was_loaded) {
3271 continue;
3272 }
3273
3274 if (!td::contains(examined_sticker_sets, sticker_set)) {
3275 examined_sticker_sets.push_back(sticker_set);
3276 }
3277 }
3278 std::stable_sort(
3279 examined_sticker_sets.begin(), examined_sticker_sets.end(),
3280 [](const StickerSet *lhs, const StickerSet *rhs) { return lhs->is_animated && !rhs->is_animated; });
3281 for (auto sticker_set : examined_sticker_sets) {
3282 auto it = sticker_set->emoji_stickers_map_.find(emoji);
3283 if (it != sticker_set->emoji_stickers_map_.end()) {
3284 LOG(INFO) << "Add " << it->second << " stickers from " << sticker_set->id;
3285 append(result, it->second);
3286 }
3287 }
3288
3289 vector<FileId> sorted;
3290 sorted.reserve(min(limit_size_t, result.size()));
3291 auto recent_stickers_size = recent_sticker_ids_[0].size();
3292 const size_t MAX_RECENT_STICKERS = 5;
3293 for (size_t i = 0; i < prepend_sticker_ids.size(); i++) {
3294 if (sorted.size() == MAX_RECENT_STICKERS && i < recent_stickers_size) {
3295 LOG(INFO) << "Skip recent sticker " << prepend_sticker_ids[i];
3296 continue;
3297 }
3298
3299 auto sticker_id = prepend_sticker_ids[i];
3300 bool is_good = false;
3301 auto it = std::find(result.begin(), result.end(), sticker_id);
3302 if (it != result.end()) {
3303 LOG(INFO) << "Found prepend sticker " << sticker_id << " in installed packs at position "
3304 << (it - result.begin());
3305 *it = FileId();
3306 is_good = true;
3307 } else {
3308 const Sticker *s = get_sticker(sticker_id);
3309 CHECK(s != nullptr);
3310 if (remove_emoji_modifiers(s->alt) == emoji) {
3311 LOG(INFO) << "Found prepend sticker " << sticker_id << " main emoji matches";
3312 is_good = true;
3313 } else if (s->set_id.is_valid()) {
3314 const StickerSet *sticker_set = get_sticker_set(s->set_id);
3315 if (sticker_set != nullptr && sticker_set->was_loaded) {
3316 auto map_it = sticker_set->emoji_stickers_map_.find(emoji);
3317 if (map_it != sticker_set->emoji_stickers_map_.end()) {
3318 if (td::contains(map_it->second, sticker_id)) {
3319 LOG(INFO) << "Found prepend sticker " << sticker_id << " has matching emoji";
3320 is_good = true;
3321 }
3322 }
3323 }
3324 }
3325 }
3326
3327 if (is_good) {
3328 sorted.push_back(sticker_id);
3329 if (sorted.size() == limit_size_t) {
3330 break;
3331 }
3332 }
3333 }
3334 if (sorted.size() != limit_size_t) {
3335 for (const auto &sticker_id : result) {
3336 if (sticker_id.is_valid()) {
3337 LOG(INFO) << "Add sticker " << sticker_id << " from installed sticker set";
3338 sorted.push_back(sticker_id);
3339 if (sorted.size() == limit_size_t) {
3340 break;
3341 }
3342 } else {
3343 LOG(INFO) << "Skip already added sticker";
3344 }
3345 }
3346 }
3347
3348 result = std::move(sorted);
3349 }
3350
3351 promise.set_value(Unit());
3352 return result;
3353 }
3354
search_stickers(string emoji,int32 limit,Promise<Unit> && promise)3355 vector<FileId> StickersManager::search_stickers(string emoji, int32 limit, Promise<Unit> &&promise) {
3356 if (limit <= 0) {
3357 promise.set_error(Status::Error(400, "Parameter limit must be positive"));
3358 return {};
3359 }
3360 if (limit > MAX_FOUND_STICKERS) {
3361 limit = MAX_FOUND_STICKERS;
3362 }
3363 if (emoji.empty()) {
3364 promise.set_error(Status::Error(400, "Emoji must be non-empty"));
3365 return {};
3366 }
3367
3368 remove_emoji_modifiers_in_place(emoji);
3369 if (emoji.empty()) {
3370 promise.set_value(Unit());
3371 return {};
3372 }
3373
3374 auto it = found_stickers_.find(emoji);
3375 if (it != found_stickers_.end() && Time::now() < it->second.next_reload_time_) {
3376 promise.set_value(Unit());
3377 const auto &sticker_ids = it->second.sticker_ids_;
3378 auto result_size = min(static_cast<size_t>(limit), sticker_ids.size());
3379 return vector<FileId>(sticker_ids.begin(), sticker_ids.begin() + result_size);
3380 }
3381
3382 auto &promises = search_stickers_queries_[emoji];
3383 promises.push_back(std::move(promise));
3384 if (promises.size() == 1u) {
3385 int64 hash = 0;
3386 if (it != found_stickers_.end()) {
3387 hash = get_recent_stickers_hash(it->second.sticker_ids_);
3388 }
3389 td_->create_handler<SearchStickersQuery>()->send(std::move(emoji), hash);
3390 }
3391
3392 return {};
3393 }
3394
on_find_stickers_success(const string & emoji,tl_object_ptr<telegram_api::messages_Stickers> && stickers)3395 void StickersManager::on_find_stickers_success(const string &emoji,
3396 tl_object_ptr<telegram_api::messages_Stickers> &&stickers) {
3397 CHECK(stickers != nullptr);
3398 switch (stickers->get_id()) {
3399 case telegram_api::messages_stickersNotModified::ID: {
3400 auto it = found_stickers_.find(emoji);
3401 if (it == found_stickers_.end()) {
3402 return on_find_stickers_fail(emoji, Status::Error(500, "Receive messages.stickerNotModified"));
3403 }
3404 auto &found_stickers = it->second;
3405 found_stickers.next_reload_time_ = Time::now() + found_stickers.cache_time_;
3406 break;
3407 }
3408 case telegram_api::messages_stickers::ID: {
3409 auto received_stickers = move_tl_object_as<telegram_api::messages_stickers>(stickers);
3410
3411 auto &found_stickers = found_stickers_[emoji];
3412 found_stickers.cache_time_ = 300;
3413 found_stickers.next_reload_time_ = Time::now() + found_stickers.cache_time_;
3414 found_stickers.sticker_ids_.clear();
3415
3416 for (auto &sticker : received_stickers->stickers_) {
3417 FileId sticker_id = on_get_sticker_document(std::move(sticker)).second;
3418 if (sticker_id.is_valid()) {
3419 found_stickers.sticker_ids_.push_back(sticker_id);
3420 }
3421 }
3422 break;
3423 }
3424 default:
3425 UNREACHABLE();
3426 }
3427
3428 auto it = search_stickers_queries_.find(emoji);
3429 CHECK(it != search_stickers_queries_.end());
3430 CHECK(!it->second.empty());
3431 auto promises = std::move(it->second);
3432 search_stickers_queries_.erase(it);
3433
3434 for (auto &promise : promises) {
3435 promise.set_value(Unit());
3436 }
3437 }
3438
on_find_stickers_fail(const string & emoji,Status && error)3439 void StickersManager::on_find_stickers_fail(const string &emoji, Status &&error) {
3440 if (found_stickers_.count(emoji) != 0) {
3441 found_stickers_[emoji].cache_time_ = Random::fast(40, 80);
3442 return on_find_stickers_success(emoji, make_tl_object<telegram_api::messages_stickersNotModified>());
3443 }
3444
3445 auto it = search_stickers_queries_.find(emoji);
3446 CHECK(it != search_stickers_queries_.end());
3447 CHECK(!it->second.empty());
3448 auto promises = std::move(it->second);
3449 search_stickers_queries_.erase(it);
3450
3451 for (auto &promise : promises) {
3452 promise.set_error(error.clone());
3453 }
3454 }
3455
get_installed_sticker_sets(bool is_masks,Promise<Unit> && promise)3456 vector<StickerSetId> StickersManager::get_installed_sticker_sets(bool is_masks, Promise<Unit> &&promise) {
3457 if (!are_installed_sticker_sets_loaded_[is_masks]) {
3458 load_installed_sticker_sets(is_masks, std::move(promise));
3459 return {};
3460 }
3461 reload_installed_sticker_sets(is_masks, false);
3462
3463 promise.set_value(Unit());
3464 return installed_sticker_set_ids_[is_masks];
3465 }
3466
update_sticker_set_cache(const StickerSet * sticker_set,Promise<Unit> & promise)3467 bool StickersManager::update_sticker_set_cache(const StickerSet *sticker_set, Promise<Unit> &promise) {
3468 CHECK(sticker_set != nullptr);
3469 auto set_id = sticker_set->id;
3470 if (!sticker_set->is_loaded) {
3471 if (!sticker_set->was_loaded || td_->auth_manager_->is_bot()) {
3472 load_sticker_sets({set_id}, std::move(promise));
3473 return true;
3474 } else {
3475 load_sticker_sets({set_id}, Auto());
3476 }
3477 } else if (sticker_set->is_installed) {
3478 reload_installed_sticker_sets(sticker_set->is_masks, false);
3479 } else {
3480 if (G()->unix_time() >= sticker_set->expires_at) {
3481 if (td_->auth_manager_->is_bot()) {
3482 do_reload_sticker_set(set_id, get_input_sticker_set(sticker_set), sticker_set->hash, std::move(promise));
3483 return true;
3484 } else {
3485 do_reload_sticker_set(set_id, get_input_sticker_set(sticker_set), sticker_set->hash, Auto());
3486 }
3487 }
3488 }
3489
3490 return false;
3491 }
3492
get_sticker_set(StickerSetId set_id,Promise<Unit> && promise)3493 StickerSetId StickersManager::get_sticker_set(StickerSetId set_id, Promise<Unit> &&promise) {
3494 const StickerSet *sticker_set = get_sticker_set(set_id);
3495 if (sticker_set == nullptr) {
3496 if (set_id.get() == GREAT_MINDS_SET_ID) {
3497 do_reload_sticker_set(set_id, make_tl_object<telegram_api::inputStickerSetID>(set_id.get(), 0), 0,
3498 std::move(promise));
3499 return StickerSetId();
3500 }
3501
3502 promise.set_error(Status::Error(400, "Sticker set not found"));
3503 return StickerSetId();
3504 }
3505
3506 if (update_sticker_set_cache(sticker_set, promise)) {
3507 return StickerSetId();
3508 }
3509
3510 promise.set_value(Unit());
3511 return set_id;
3512 }
3513
search_sticker_set(const string & short_name_to_search,Promise<Unit> && promise)3514 StickerSetId StickersManager::search_sticker_set(const string &short_name_to_search, Promise<Unit> &&promise) {
3515 string short_name = clean_username(short_name_to_search);
3516 auto it = short_name_to_sticker_set_id_.find(short_name);
3517 const StickerSet *sticker_set = it == short_name_to_sticker_set_id_.end() ? nullptr : get_sticker_set(it->second);
3518
3519 if (sticker_set == nullptr) {
3520 auto set_to_load = make_tl_object<telegram_api::inputStickerSetShortName>(short_name);
3521 do_reload_sticker_set(StickerSetId(), std::move(set_to_load), 0, std::move(promise));
3522 return StickerSetId();
3523 }
3524
3525 if (update_sticker_set_cache(sticker_set, promise)) {
3526 return StickerSetId();
3527 }
3528
3529 promise.set_value(Unit());
3530 return sticker_set->id;
3531 }
3532
search_installed_sticker_sets(bool is_masks,const string & query,int32 limit,Promise<Unit> && promise)3533 std::pair<int32, vector<StickerSetId>> StickersManager::search_installed_sticker_sets(bool is_masks,
3534 const string &query, int32 limit,
3535 Promise<Unit> &&promise) {
3536 LOG(INFO) << "Search installed " << (is_masks ? "mask " : "") << "sticker sets with query = \"" << query
3537 << "\" and limit = " << limit;
3538
3539 if (limit < 0) {
3540 promise.set_error(Status::Error(400, "Limit must be non-negative"));
3541 return {};
3542 }
3543
3544 if (!are_installed_sticker_sets_loaded_[is_masks]) {
3545 load_installed_sticker_sets(is_masks, std::move(promise));
3546 return {};
3547 }
3548 reload_installed_sticker_sets(is_masks, false);
3549
3550 std::pair<size_t, vector<int64>> result = installed_sticker_sets_hints_[is_masks].search(query, limit);
3551 promise.set_value(Unit());
3552 return {narrow_cast<int32>(result.first), convert_sticker_set_ids(result.second)};
3553 }
3554
search_sticker_sets(const string & query,Promise<Unit> && promise)3555 vector<StickerSetId> StickersManager::search_sticker_sets(const string &query, Promise<Unit> &&promise) {
3556 auto q = clean_name(query, 1000);
3557 auto it = found_sticker_sets_.find(q);
3558 if (it != found_sticker_sets_.end()) {
3559 promise.set_value(Unit());
3560 return it->second;
3561 }
3562
3563 auto &promises = search_sticker_sets_queries_[q];
3564 promises.push_back(std::move(promise));
3565 if (promises.size() == 1u) {
3566 td_->create_handler<SearchStickerSetsQuery>()->send(std::move(q));
3567 }
3568
3569 return {};
3570 }
3571
on_find_sticker_sets_success(const string & query,tl_object_ptr<telegram_api::messages_FoundStickerSets> && sticker_sets)3572 void StickersManager::on_find_sticker_sets_success(
3573 const string &query, tl_object_ptr<telegram_api::messages_FoundStickerSets> &&sticker_sets) {
3574 CHECK(sticker_sets != nullptr);
3575 switch (sticker_sets->get_id()) {
3576 case telegram_api::messages_foundStickerSetsNotModified::ID:
3577 return on_find_sticker_sets_fail(query, Status::Error(500, "Receive messages.foundStickerSetsNotModified"));
3578 case telegram_api::messages_foundStickerSets::ID: {
3579 auto found_stickers_sets = move_tl_object_as<telegram_api::messages_foundStickerSets>(sticker_sets);
3580 vector<StickerSetId> &sticker_set_ids = found_sticker_sets_[query];
3581 CHECK(sticker_set_ids.empty());
3582
3583 for (auto &sticker_set : found_stickers_sets->sets_) {
3584 StickerSetId set_id = on_get_sticker_set_covered(std::move(sticker_set), true, "on_find_sticker_sets_success");
3585 if (!set_id.is_valid()) {
3586 continue;
3587 }
3588
3589 update_sticker_set(get_sticker_set(set_id), "on_find_sticker_sets_success");
3590 sticker_set_ids.push_back(set_id);
3591 }
3592
3593 send_update_installed_sticker_sets();
3594 break;
3595 }
3596 default:
3597 UNREACHABLE();
3598 }
3599
3600 auto it = search_sticker_sets_queries_.find(query);
3601 CHECK(it != search_sticker_sets_queries_.end());
3602 CHECK(!it->second.empty());
3603 auto promises = std::move(it->second);
3604 search_sticker_sets_queries_.erase(it);
3605
3606 for (auto &promise : promises) {
3607 promise.set_value(Unit());
3608 }
3609 }
3610
on_find_sticker_sets_fail(const string & query,Status && error)3611 void StickersManager::on_find_sticker_sets_fail(const string &query, Status &&error) {
3612 CHECK(found_sticker_sets_.count(query) == 0);
3613
3614 auto it = search_sticker_sets_queries_.find(query);
3615 CHECK(it != search_sticker_sets_queries_.end());
3616 CHECK(!it->second.empty());
3617 auto promises = std::move(it->second);
3618 search_sticker_sets_queries_.erase(it);
3619
3620 for (auto &promise : promises) {
3621 promise.set_error(error.clone());
3622 }
3623 }
3624
change_sticker_set(StickerSetId set_id,bool is_installed,bool is_archived,Promise<Unit> && promise)3625 void StickersManager::change_sticker_set(StickerSetId set_id, bool is_installed, bool is_archived,
3626 Promise<Unit> &&promise) {
3627 if (is_installed && is_archived) {
3628 return promise.set_error(Status::Error(400, "Sticker set can't be installed and archived simultaneously"));
3629 }
3630 const StickerSet *sticker_set = get_sticker_set(set_id);
3631 if (sticker_set == nullptr) {
3632 return promise.set_error(Status::Error(400, "Sticker set not found"));
3633 }
3634 if (!sticker_set->is_inited) {
3635 load_sticker_sets({set_id}, std::move(promise));
3636 return;
3637 }
3638 if (!are_installed_sticker_sets_loaded_[sticker_set->is_masks]) {
3639 load_installed_sticker_sets(sticker_set->is_masks, std::move(promise));
3640 return;
3641 }
3642
3643 if (is_archived) {
3644 is_installed = true;
3645 }
3646 if (is_installed) {
3647 if (sticker_set->is_installed && is_archived == sticker_set->is_archived) {
3648 return promise.set_value(Unit());
3649 }
3650
3651 td_->create_handler<InstallStickerSetQuery>(std::move(promise))
3652 ->send(set_id, get_input_sticker_set(sticker_set), is_archived);
3653 return;
3654 }
3655
3656 if (!sticker_set->is_installed) {
3657 return promise.set_value(Unit());
3658 }
3659
3660 td_->create_handler<UninstallStickerSetQuery>(std::move(promise))->send(set_id, get_input_sticker_set(sticker_set));
3661 }
3662
on_update_sticker_set(StickerSet * sticker_set,bool is_installed,bool is_archived,bool is_changed,bool from_database)3663 void StickersManager::on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived,
3664 bool is_changed, bool from_database) {
3665 LOG(INFO) << "Update " << sticker_set->id << ": installed = " << is_installed << ", archived = " << is_archived
3666 << ", changed = " << is_changed << ", from_database = " << from_database;
3667 CHECK(sticker_set->is_inited);
3668 if (is_archived) {
3669 is_installed = true;
3670 }
3671 if (sticker_set->is_installed == is_installed && sticker_set->is_archived == is_archived) {
3672 return;
3673 }
3674
3675 bool was_added = sticker_set->is_installed && !sticker_set->is_archived;
3676 bool was_archived = sticker_set->is_archived;
3677 sticker_set->is_installed = is_installed;
3678 sticker_set->is_archived = is_archived;
3679 if (!from_database) {
3680 sticker_set->is_changed = true;
3681 }
3682
3683 bool is_added = sticker_set->is_installed && !sticker_set->is_archived;
3684 if (was_added != is_added) {
3685 vector<StickerSetId> &sticker_set_ids = installed_sticker_set_ids_[sticker_set->is_masks];
3686 need_update_installed_sticker_sets_[sticker_set->is_masks] = true;
3687
3688 if (is_added) {
3689 installed_sticker_sets_hints_[sticker_set->is_masks].add(
3690 sticker_set->id.get(), PSLICE() << sticker_set->title << ' ' << sticker_set->short_name);
3691 sticker_set_ids.insert(sticker_set_ids.begin(), sticker_set->id);
3692 } else {
3693 installed_sticker_sets_hints_[sticker_set->is_masks].remove(sticker_set->id.get());
3694 td::remove(sticker_set_ids, sticker_set->id);
3695 }
3696 }
3697 if (was_archived != is_archived && is_changed) {
3698 int32 &total_count = total_archived_sticker_set_count_[sticker_set->is_masks];
3699 vector<StickerSetId> &sticker_set_ids = archived_sticker_set_ids_[sticker_set->is_masks];
3700 if (total_count < 0) {
3701 return;
3702 }
3703
3704 if (is_archived) {
3705 if (!td::contains(sticker_set_ids, sticker_set->id)) {
3706 total_count++;
3707 sticker_set_ids.insert(sticker_set_ids.begin(), sticker_set->id);
3708 }
3709 } else {
3710 total_count--;
3711 if (total_count < 0) {
3712 LOG(ERROR) << "Total count of archived sticker sets became negative";
3713 total_count = 0;
3714 }
3715 td::remove(sticker_set_ids, sticker_set->id);
3716 }
3717 }
3718 }
3719
load_installed_sticker_sets(bool is_masks,Promise<Unit> && promise)3720 void StickersManager::load_installed_sticker_sets(bool is_masks, Promise<Unit> &&promise) {
3721 if (td_->auth_manager_->is_bot()) {
3722 are_installed_sticker_sets_loaded_[is_masks] = true;
3723 }
3724 if (are_installed_sticker_sets_loaded_[is_masks]) {
3725 promise.set_value(Unit());
3726 return;
3727 }
3728 load_installed_sticker_sets_queries_[is_masks].push_back(std::move(promise));
3729 if (load_installed_sticker_sets_queries_[is_masks].size() == 1u) {
3730 if (G()->parameters().use_file_db) {
3731 LOG(INFO) << "Trying to load installed " << (is_masks ? "mask " : "") << "sticker sets from database";
3732 G()->td_db()->get_sqlite_pmc()->get(is_masks ? "sss1" : "sss0", PromiseCreator::lambda([is_masks](string value) {
3733 send_closure(G()->stickers_manager(),
3734 &StickersManager::on_load_installed_sticker_sets_from_database,
3735 is_masks, std::move(value));
3736 }));
3737 } else {
3738 LOG(INFO) << "Trying to load installed " << (is_masks ? "mask " : "") << "sticker sets from server";
3739 reload_installed_sticker_sets(is_masks, true);
3740 }
3741 }
3742 }
3743
on_load_installed_sticker_sets_from_database(bool is_masks,string value)3744 void StickersManager::on_load_installed_sticker_sets_from_database(bool is_masks, string value) {
3745 if (G()->close_flag()) {
3746 return;
3747 }
3748 if (value.empty()) {
3749 LOG(INFO) << "Installed " << (is_masks ? "mask " : "") << "sticker sets aren't found in database";
3750 reload_installed_sticker_sets(is_masks, true);
3751 return;
3752 }
3753
3754 LOG(INFO) << "Successfully loaded installed " << (is_masks ? "mask " : "") << "sticker set list of size "
3755 << value.size() << " from database";
3756
3757 StickerSetListLogEvent log_event;
3758 auto status = log_event_parse(log_event, value);
3759 if (status.is_error()) {
3760 // can't happen unless database is broken
3761 LOG(ERROR) << "Can't load installed sticker set list: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
3762 return reload_installed_sticker_sets(is_masks, true);
3763 }
3764
3765 vector<StickerSetId> sets_to_load;
3766 for (auto sticker_set_id : log_event.sticker_set_ids) {
3767 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3768 CHECK(sticker_set != nullptr);
3769 if (!sticker_set->is_inited) {
3770 sets_to_load.push_back(sticker_set_id);
3771 }
3772 }
3773 std::reverse(sets_to_load.begin(), sets_to_load.end()); // load installed sticker sets in reverse order
3774
3775 load_sticker_sets_without_stickers(
3776 std::move(sets_to_load),
3777 PromiseCreator::lambda(
3778 [is_masks, sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
3779 if (result.is_ok()) {
3780 send_closure(G()->stickers_manager(), &StickersManager::on_load_installed_sticker_sets_finished, is_masks,
3781 std::move(sticker_set_ids), true);
3782 } else {
3783 send_closure(G()->stickers_manager(), &StickersManager::reload_installed_sticker_sets, is_masks, true);
3784 }
3785 }));
3786 }
3787
on_load_installed_sticker_sets_finished(bool is_masks,vector<StickerSetId> && installed_sticker_set_ids,bool from_database)3788 void StickersManager::on_load_installed_sticker_sets_finished(bool is_masks,
3789 vector<StickerSetId> &&installed_sticker_set_ids,
3790 bool from_database) {
3791 bool need_reload = false;
3792 vector<StickerSetId> old_installed_sticker_set_ids;
3793 if (!are_installed_sticker_sets_loaded_[is_masks] && !installed_sticker_set_ids_[is_masks].empty()) {
3794 old_installed_sticker_set_ids = std::move(installed_sticker_set_ids_[is_masks]);
3795 }
3796 installed_sticker_set_ids_[is_masks].clear();
3797 for (auto set_id : installed_sticker_set_ids) {
3798 CHECK(set_id.is_valid());
3799
3800 auto sticker_set = get_sticker_set(set_id);
3801 CHECK(sticker_set != nullptr);
3802 CHECK(sticker_set->is_inited);
3803 CHECK(sticker_set->is_masks == is_masks);
3804 if (sticker_set->is_installed && !sticker_set->is_archived) {
3805 installed_sticker_set_ids_[is_masks].push_back(set_id);
3806 } else {
3807 need_reload = true;
3808 }
3809 }
3810 if (need_reload) {
3811 LOG(ERROR) << "Reload installed " << (is_masks ? "mask " : "") << "sticker sets, because only "
3812 << installed_sticker_set_ids_[is_masks].size() << " of " << installed_sticker_set_ids.size()
3813 << " are really installed after loading from " << (from_database ? "database" : "server");
3814 reload_installed_sticker_sets(is_masks, true);
3815 } else if (!old_installed_sticker_set_ids.empty() &&
3816 old_installed_sticker_set_ids != installed_sticker_set_ids_[is_masks]) {
3817 LOG(ERROR) << "Reload installed " << (is_masks ? "mask " : "") << "sticker sets, because they has changed from "
3818 << old_installed_sticker_set_ids << " to " << installed_sticker_set_ids_[is_masks]
3819 << " after loading from " << (from_database ? "database" : "server");
3820 reload_installed_sticker_sets(is_masks, true);
3821 }
3822
3823 are_installed_sticker_sets_loaded_[is_masks] = true;
3824 need_update_installed_sticker_sets_[is_masks] = true;
3825 send_update_installed_sticker_sets(from_database);
3826 auto promises = std::move(load_installed_sticker_sets_queries_[is_masks]);
3827 load_installed_sticker_sets_queries_[is_masks].clear();
3828 for (auto &promise : promises) {
3829 promise.set_value(Unit());
3830 }
3831 }
3832
get_sticker_set_database_key(StickerSetId set_id)3833 string StickersManager::get_sticker_set_database_key(StickerSetId set_id) {
3834 return PSTRING() << "ss" << set_id.get();
3835 }
3836
get_full_sticker_set_database_key(StickerSetId set_id)3837 string StickersManager::get_full_sticker_set_database_key(StickerSetId set_id) {
3838 return PSTRING() << "ssf" << set_id.get();
3839 }
3840
get_sticker_set_database_value(const StickerSet * s,bool with_stickers,const char * source)3841 string StickersManager::get_sticker_set_database_value(const StickerSet *s, bool with_stickers, const char *source) {
3842 LogEventStorerCalcLength storer_calc_length;
3843 store_sticker_set(s, with_stickers, storer_calc_length, source);
3844
3845 BufferSlice value_buffer{storer_calc_length.get_length()};
3846 auto value = value_buffer.as_slice();
3847
3848 LOG(DEBUG) << "Serialized size of " << s->id << " is " << value.size();
3849
3850 LogEventStorerUnsafe storer_unsafe(value.ubegin());
3851 store_sticker_set(s, with_stickers, storer_unsafe, source);
3852
3853 return value.str();
3854 }
3855
update_sticker_set(StickerSet * sticker_set,const char * source)3856 void StickersManager::update_sticker_set(StickerSet *sticker_set, const char *source) {
3857 CHECK(sticker_set != nullptr);
3858 if (sticker_set->is_changed || sticker_set->need_save_to_database) {
3859 if (G()->parameters().use_file_db && !G()->close_flag()) {
3860 LOG(INFO) << "Save " << sticker_set->id << " to database from " << source;
3861 if (sticker_set->is_inited) {
3862 G()->td_db()->get_sqlite_pmc()->set(get_sticker_set_database_key(sticker_set->id),
3863 get_sticker_set_database_value(sticker_set, false, source), Auto());
3864 }
3865 if (sticker_set->was_loaded) {
3866 G()->td_db()->get_sqlite_pmc()->set(get_full_sticker_set_database_key(sticker_set->id),
3867 get_sticker_set_database_value(sticker_set, true, source), Auto());
3868 }
3869 }
3870 if (sticker_set->is_changed && sticker_set->was_loaded && sticker_set->was_update_sent) {
3871 send_closure(G()->td(), &Td::send_update,
3872 td_api::make_object<td_api::updateStickerSet>(get_sticker_set_object(sticker_set->id)));
3873 }
3874 sticker_set->is_changed = false;
3875 sticker_set->need_save_to_database = false;
3876 if (sticker_set->is_inited) {
3877 update_load_requests(sticker_set, false, Status::OK());
3878 }
3879 }
3880 }
3881
load_sticker_sets(vector<StickerSetId> && sticker_set_ids,Promise<Unit> && promise)3882 void StickersManager::load_sticker_sets(vector<StickerSetId> &&sticker_set_ids, Promise<Unit> &&promise) {
3883 if (sticker_set_ids.empty()) {
3884 promise.set_value(Unit());
3885 return;
3886 }
3887
3888 auto load_request_id = current_sticker_set_load_request_++;
3889 StickerSetLoadRequest &load_request = sticker_set_load_requests_[load_request_id];
3890 load_request.promise = std::move(promise);
3891 load_request.left_queries = sticker_set_ids.size();
3892
3893 for (auto sticker_set_id : sticker_set_ids) {
3894 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3895 CHECK(sticker_set != nullptr);
3896 CHECK(!sticker_set->is_loaded);
3897
3898 sticker_set->load_requests.push_back(load_request_id);
3899 if (sticker_set->load_requests.size() == 1u) {
3900 if (G()->parameters().use_file_db && !sticker_set->was_loaded) {
3901 LOG(INFO) << "Trying to load " << sticker_set_id << " with stickers from database";
3902 G()->td_db()->get_sqlite_pmc()->get(
3903 get_full_sticker_set_database_key(sticker_set_id), PromiseCreator::lambda([sticker_set_id](string value) {
3904 send_closure(G()->stickers_manager(), &StickersManager::on_load_sticker_set_from_database, sticker_set_id,
3905 true, std::move(value));
3906 }));
3907 } else {
3908 LOG(INFO) << "Trying to load " << sticker_set_id << " with stickers from server";
3909 do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), 0, Auto());
3910 }
3911 }
3912 }
3913 }
3914
load_sticker_sets_without_stickers(vector<StickerSetId> && sticker_set_ids,Promise<Unit> && promise)3915 void StickersManager::load_sticker_sets_without_stickers(vector<StickerSetId> &&sticker_set_ids,
3916 Promise<Unit> &&promise) {
3917 if (sticker_set_ids.empty()) {
3918 promise.set_value(Unit());
3919 return;
3920 }
3921
3922 auto load_request_id = current_sticker_set_load_request_++;
3923 StickerSetLoadRequest &load_request = sticker_set_load_requests_[load_request_id];
3924 load_request.promise = std::move(promise);
3925 load_request.left_queries = sticker_set_ids.size();
3926
3927 for (auto sticker_set_id : sticker_set_ids) {
3928 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3929 CHECK(sticker_set != nullptr);
3930 CHECK(!sticker_set->is_inited);
3931
3932 if (!sticker_set->load_requests.empty()) {
3933 sticker_set->load_requests.push_back(load_request_id);
3934 } else {
3935 sticker_set->load_without_stickers_requests.push_back(load_request_id);
3936 if (sticker_set->load_without_stickers_requests.size() == 1u) {
3937 if (G()->parameters().use_file_db) {
3938 LOG(INFO) << "Trying to load " << sticker_set_id << " from database";
3939 G()->td_db()->get_sqlite_pmc()->get(
3940 get_sticker_set_database_key(sticker_set_id), PromiseCreator::lambda([sticker_set_id](string value) {
3941 send_closure(G()->stickers_manager(), &StickersManager::on_load_sticker_set_from_database,
3942 sticker_set_id, false, std::move(value));
3943 }));
3944 } else {
3945 LOG(INFO) << "Trying to load " << sticker_set_id << " from server";
3946 do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), 0, Auto());
3947 }
3948 }
3949 }
3950 }
3951 }
3952
on_load_sticker_set_from_database(StickerSetId sticker_set_id,bool with_stickers,string value)3953 void StickersManager::on_load_sticker_set_from_database(StickerSetId sticker_set_id, bool with_stickers, string value) {
3954 if (G()->close_flag()) {
3955 return;
3956 }
3957 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
3958 CHECK(sticker_set != nullptr);
3959 if (sticker_set->was_loaded) {
3960 LOG(INFO) << "Receive from database previously loaded " << sticker_set_id;
3961 return;
3962 }
3963 if (!with_stickers && sticker_set->is_inited) {
3964 LOG(INFO) << "Receive from database previously inited " << sticker_set_id;
3965 return;
3966 }
3967
3968 // it is possible that a server reload_sticker_set request has failed and cleared requests list with an error
3969 if (with_stickers) {
3970 // CHECK(!sticker_set->load_requests.empty());
3971 } else {
3972 // CHECK(!sticker_set->load_without_stickers_requests.empty());
3973 }
3974
3975 if (value.empty()) {
3976 LOG(INFO) << "Failed to find in the database " << sticker_set_id;
3977 return do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), 0, Auto());
3978 }
3979
3980 LOG(INFO) << "Successfully loaded " << sticker_set_id << " with" << (with_stickers ? "" : "out")
3981 << " stickers of size " << value.size() << " from database";
3982
3983 auto old_sticker_count = sticker_set->sticker_ids.size();
3984
3985 {
3986 LOG_IF(ERROR, sticker_set->is_changed) << sticker_set_id << " with" << (with_stickers ? "" : "out")
3987 << " stickers was changed before it is loaded from database";
3988 LogEventParser parser(value);
3989 parse_sticker_set(sticker_set, parser);
3990 LOG_IF(ERROR, sticker_set->is_changed)
3991 << sticker_set_id << " with" << (with_stickers ? "" : "out") << " stickers is changed";
3992 parser.fetch_end();
3993 auto status = parser.get_status();
3994 if (status.is_error()) {
3995 G()->td_db()->get_sqlite_sync_pmc()->erase(with_stickers ? get_full_sticker_set_database_key(sticker_set_id)
3996 : get_sticker_set_database_key(sticker_set_id));
3997 // need to crash, because the current StickerSet state is spoiled by parse_sticker_set
3998 LOG(FATAL) << "Failed to parse " << sticker_set_id << ": " << status << ' '
3999 << format::as_hex_dump<4>(Slice(value));
4000 }
4001 }
4002 if (!sticker_set->is_thumbnail_reloaded || !sticker_set->are_legacy_sticker_thumbnails_reloaded) {
4003 do_reload_sticker_set(sticker_set_id, get_input_sticker_set(sticker_set), 0, Auto());
4004 }
4005
4006 if (with_stickers && old_sticker_count < 5 && old_sticker_count < sticker_set->sticker_ids.size()) {
4007 sticker_set->need_save_to_database = true;
4008 update_sticker_set(sticker_set, "on_load_sticker_set_from_database");
4009 }
4010
4011 update_load_requests(sticker_set, with_stickers, Status::OK());
4012 }
4013
reload_sticker_set(StickerSetId sticker_set_id,int64 access_hash,Promise<Unit> && promise)4014 void StickersManager::reload_sticker_set(StickerSetId sticker_set_id, int64 access_hash, Promise<Unit> &&promise) {
4015 do_reload_sticker_set(sticker_set_id,
4016 make_tl_object<telegram_api::inputStickerSetID>(sticker_set_id.get(), access_hash), 0,
4017 std::move(promise));
4018 }
4019
do_reload_sticker_set(StickerSetId sticker_set_id,tl_object_ptr<telegram_api::InputStickerSet> && input_sticker_set,int32 hash,Promise<Unit> && promise) const4020 void StickersManager::do_reload_sticker_set(StickerSetId sticker_set_id,
4021 tl_object_ptr<telegram_api::InputStickerSet> &&input_sticker_set,
4022 int32 hash, Promise<Unit> &&promise) const {
4023 TRY_STATUS_PROMISE(promise, G()->close_status());
4024 td_->create_handler<GetStickerSetQuery>(std::move(promise))->send(sticker_set_id, std::move(input_sticker_set), hash);
4025 }
4026
on_install_sticker_set(StickerSetId set_id,bool is_archived,tl_object_ptr<telegram_api::messages_StickerSetInstallResult> && result)4027 void StickersManager::on_install_sticker_set(StickerSetId set_id, bool is_archived,
4028 tl_object_ptr<telegram_api::messages_StickerSetInstallResult> &&result) {
4029 StickerSet *sticker_set = get_sticker_set(set_id);
4030 CHECK(sticker_set != nullptr);
4031 on_update_sticker_set(sticker_set, true, is_archived, true);
4032 update_sticker_set(sticker_set, "on_install_sticker_set");
4033
4034 switch (result->get_id()) {
4035 case telegram_api::messages_stickerSetInstallResultSuccess::ID:
4036 break;
4037 case telegram_api::messages_stickerSetInstallResultArchive::ID: {
4038 auto archived_sets = move_tl_object_as<telegram_api::messages_stickerSetInstallResultArchive>(result);
4039 for (auto &archived_set_ptr : archived_sets->sets_) {
4040 StickerSetId archived_sticker_set_id =
4041 on_get_sticker_set_covered(std::move(archived_set_ptr), true, "on_install_sticker_set");
4042 if (archived_sticker_set_id.is_valid()) {
4043 auto archived_sticker_set = get_sticker_set(archived_sticker_set_id);
4044 CHECK(archived_sticker_set != nullptr);
4045 update_sticker_set(archived_sticker_set, "on_install_sticker_set 2");
4046 }
4047 }
4048 break;
4049 }
4050 default:
4051 UNREACHABLE();
4052 }
4053
4054 send_update_installed_sticker_sets();
4055 }
4056
on_uninstall_sticker_set(StickerSetId set_id)4057 void StickersManager::on_uninstall_sticker_set(StickerSetId set_id) {
4058 StickerSet *sticker_set = get_sticker_set(set_id);
4059 CHECK(sticker_set != nullptr);
4060 on_update_sticker_set(sticker_set, false, false, true);
4061 update_sticker_set(sticker_set, "on_uninstall_sticker_set");
4062 send_update_installed_sticker_sets();
4063 }
4064
get_update_dice_emojis_object() const4065 td_api::object_ptr<td_api::updateDiceEmojis> StickersManager::get_update_dice_emojis_object() const {
4066 return td_api::make_object<td_api::updateDiceEmojis>(vector<string>(dice_emojis_));
4067 }
4068
on_update_dice_emojis()4069 void StickersManager::on_update_dice_emojis() {
4070 if (G()->close_flag()) {
4071 return;
4072 }
4073 if (td_->auth_manager_->is_bot()) {
4074 G()->shared_config().set_option_empty("dice_emojis");
4075 return;
4076 }
4077 if (!is_inited_) {
4078 return;
4079 }
4080
4081 auto dice_emojis_str =
4082 G()->shared_config().get_option_string("dice_emojis", "\x01\x01\x01⚽\x01⚽️\x01\x01");
4083 if (dice_emojis_str == dice_emojis_str_) {
4084 return;
4085 }
4086 dice_emojis_str_ = std::move(dice_emojis_str);
4087 auto new_dice_emojis = full_split(dice_emojis_str_, '\x01');
4088 for (auto &emoji : new_dice_emojis) {
4089 if (!td::contains(dice_emojis_, emoji)) {
4090 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(emoji));
4091 if (special_sticker_set.id_.is_valid()) {
4092 // drop information about the sticker set to reload it
4093 special_sticker_set.id_ = StickerSetId();
4094 special_sticker_set.access_hash_ = 0;
4095 special_sticker_set.short_name_.clear();
4096 }
4097
4098 if (G()->parameters().use_file_db) {
4099 LOG(INFO) << "Load new dice sticker set for emoji " << emoji;
4100 load_special_sticker_set(special_sticker_set);
4101 }
4102 }
4103 }
4104 dice_emojis_ = std::move(new_dice_emojis);
4105
4106 send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
4107 }
4108
on_update_dice_success_values()4109 void StickersManager::on_update_dice_success_values() {
4110 if (G()->close_flag()) {
4111 return;
4112 }
4113 if (td_->auth_manager_->is_bot()) {
4114 G()->shared_config().set_option_empty("dice_success_values");
4115 return;
4116 }
4117 if (!is_inited_) {
4118 return;
4119 }
4120
4121 auto dice_success_values_str =
4122 G()->shared_config().get_option_string("dice_success_values", "0,6:62,5:110,5:110,5:110,64:110,6:110");
4123 if (dice_success_values_str == dice_success_values_str_) {
4124 return;
4125 }
4126
4127 LOG(INFO) << "Change dice success values to " << dice_success_values_str;
4128 dice_success_values_str_ = std::move(dice_success_values_str);
4129 dice_success_values_ = transform(full_split(dice_success_values_str_, ','), [](Slice value) {
4130 auto result = split(value, ':');
4131 return std::make_pair(to_integer<int32>(result.first), to_integer<int32>(result.second));
4132 });
4133 }
4134
on_update_emoji_sounds()4135 void StickersManager::on_update_emoji_sounds() {
4136 if (G()->close_flag() || !is_inited_ || td_->auth_manager_->is_bot()) {
4137 return;
4138 }
4139
4140 auto emoji_sounds_str = G()->shared_config().get_option_string("emoji_sounds");
4141 if (emoji_sounds_str == emoji_sounds_str_) {
4142 return;
4143 }
4144
4145 LOG(INFO) << "Change emoji sounds to " << emoji_sounds_str;
4146 emoji_sounds_str_ = std::move(emoji_sounds_str);
4147
4148 vector<FileId> old_file_ids;
4149 for (auto &emoji_sound : emoji_sounds_) {
4150 old_file_ids.push_back(emoji_sound.second);
4151 }
4152 emoji_sounds_.clear();
4153
4154 vector<FileId> new_file_ids;
4155 auto sounds = full_split(Slice(emoji_sounds_str_), ',');
4156 CHECK(sounds.size() % 2 == 0);
4157 for (size_t i = 0; i < sounds.size(); i += 2) {
4158 vector<Slice> parts = full_split(sounds[i + 1], ':');
4159 CHECK(parts.size() == 3);
4160 auto id = to_integer<int64>(parts[0]);
4161 auto access_hash = to_integer<int64>(parts[1]);
4162 auto dc_id = G()->net_query_dispatcher().get_main_dc_id();
4163 auto file_reference = base64url_decode(parts[2]).move_as_ok();
4164 int32 expected_size = 7000;
4165 auto suggested_file_name = PSTRING() << static_cast<uint64>(id) << '.'
4166 << MimeType::to_extension("audio/ogg", "oga");
4167 auto file_id = td_->file_manager_->register_remote(
4168 FullRemoteFileLocation(FileType::VoiceNote, id, access_hash, dc_id, std::move(file_reference)),
4169 FileLocationSource::FromServer, DialogId(), 0, expected_size, std::move(suggested_file_name));
4170 CHECK(file_id.is_valid());
4171 emoji_sounds_.emplace(remove_fitzpatrick_modifier(sounds[i]).str(), file_id);
4172 new_file_ids.push_back(file_id);
4173 }
4174 td_->file_manager_->change_files_source(get_app_config_file_source_id(), old_file_ids, new_file_ids);
4175
4176 try_update_animated_emoji_messages();
4177 }
4178
on_update_disable_animated_emojis()4179 void StickersManager::on_update_disable_animated_emojis() {
4180 if (G()->close_flag() || !is_inited_ || td_->auth_manager_->is_bot()) {
4181 return;
4182 }
4183
4184 auto disable_animated_emojis = G()->shared_config().get_option_boolean("disable_animated_emoji");
4185 if (disable_animated_emojis == disable_animated_emojis_) {
4186 return;
4187 }
4188 disable_animated_emojis_ = disable_animated_emojis;
4189 if (!disable_animated_emojis_) {
4190 reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji());
4191 reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji_click());
4192 }
4193 try_update_animated_emoji_messages();
4194 }
4195
on_update_sticker_sets()4196 void StickersManager::on_update_sticker_sets() {
4197 // TODO better support
4198 archived_sticker_set_ids_[0].clear();
4199 total_archived_sticker_set_count_[0] = -1;
4200 reload_installed_sticker_sets(false, true);
4201
4202 archived_sticker_set_ids_[1].clear();
4203 total_archived_sticker_set_count_[1] = -1;
4204 reload_installed_sticker_sets(true, true);
4205 }
4206
try_update_animated_emoji_messages()4207 void StickersManager::try_update_animated_emoji_messages() {
4208 auto sticker_set = get_animated_emoji_sticker_set();
4209 vector<FullMessageId> full_message_ids;
4210 for (auto &it : emoji_messages_) {
4211 auto new_animated_sticker = get_animated_emoji_sticker(sticker_set, it.first);
4212 auto new_sound_file_id = get_animated_emoji_sound_file_id(it.first);
4213 if (new_animated_sticker != it.second.animated_emoji_sticker ||
4214 (new_animated_sticker.first.is_valid() && new_sound_file_id != it.second.sound_file_id)) {
4215 it.second.animated_emoji_sticker = new_animated_sticker;
4216 it.second.sound_file_id = new_sound_file_id;
4217 for (const auto &full_message_id : it.second.full_message_ids) {
4218 full_message_ids.push_back(full_message_id);
4219 }
4220 }
4221 }
4222 for (const auto &full_message_id : full_message_ids) {
4223 td_->messages_manager_->on_external_update_message_content(full_message_id);
4224 }
4225 }
4226
register_dice(const string & emoji,int32 value,FullMessageId full_message_id,const char * source)4227 void StickersManager::register_dice(const string &emoji, int32 value, FullMessageId full_message_id,
4228 const char *source) {
4229 CHECK(!emoji.empty());
4230 if (td_->auth_manager_->is_bot()) {
4231 return;
4232 }
4233
4234 LOG(INFO) << "Register dice " << emoji << " with value " << value << " from " << full_message_id << " from "
4235 << source;
4236 bool is_inserted = dice_messages_[emoji].insert(full_message_id).second;
4237 LOG_CHECK(is_inserted) << source << " " << emoji << " " << value << " " << full_message_id;
4238
4239 if (!td::contains(dice_emojis_, emoji)) {
4240 if (full_message_id.get_message_id().is_any_server() &&
4241 full_message_id.get_dialog_id().get_type() != DialogType::SecretChat) {
4242 send_closure(G()->config_manager(), &ConfigManager::reget_app_config, Promise<Unit>());
4243 }
4244 return;
4245 }
4246
4247 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(emoji));
4248 bool need_load = false;
4249 StickerSet *sticker_set = nullptr;
4250 if (!special_sticker_set.id_.is_valid()) {
4251 need_load = true;
4252 } else {
4253 sticker_set = get_sticker_set(special_sticker_set.id_);
4254 CHECK(sticker_set != nullptr);
4255 need_load = !sticker_set->was_loaded;
4256 }
4257
4258 if (need_load) {
4259 LOG(INFO) << "Waiting for a dice sticker set needed in " << full_message_id;
4260 load_special_sticker_set(special_sticker_set);
4261 } else {
4262 // TODO reload once in a while
4263 // reload_special_sticker_set(special_sticker_set, sticker_set->is_loaded ? sticker_set->hash : 0);
4264 }
4265 }
4266
unregister_dice(const string & emoji,int32 value,FullMessageId full_message_id,const char * source)4267 void StickersManager::unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id,
4268 const char *source) {
4269 CHECK(!emoji.empty());
4270 if (td_->auth_manager_->is_bot()) {
4271 return;
4272 }
4273
4274 LOG(INFO) << "Unregister dice " << emoji << " with value " << value << " from " << full_message_id << " from "
4275 << source;
4276 auto &message_ids = dice_messages_[emoji];
4277 auto is_deleted = message_ids.erase(full_message_id) > 0;
4278 LOG_CHECK(is_deleted) << source << " " << emoji << " " << value << " " << full_message_id;
4279
4280 if (message_ids.empty()) {
4281 dice_messages_.erase(emoji);
4282 }
4283 }
4284
register_emoji(const string & emoji,FullMessageId full_message_id,const char * source)4285 void StickersManager::register_emoji(const string &emoji, FullMessageId full_message_id, const char *source) {
4286 CHECK(!emoji.empty());
4287 if (td_->auth_manager_->is_bot()) {
4288 return;
4289 }
4290
4291 LOG(INFO) << "Register emoji " << emoji << " from " << full_message_id << " from " << source;
4292 auto &emoji_messages = emoji_messages_[emoji];
4293 if (emoji_messages.full_message_ids.empty()) {
4294 emoji_messages.animated_emoji_sticker = get_animated_emoji_sticker(emoji);
4295 emoji_messages.sound_file_id = get_animated_emoji_sound_file_id(emoji);
4296 }
4297 bool is_inserted = emoji_messages.full_message_ids.insert(full_message_id).second;
4298 LOG_CHECK(is_inserted) << source << ' ' << emoji << ' ' << full_message_id;
4299 }
4300
unregister_emoji(const string & emoji,FullMessageId full_message_id,const char * source)4301 void StickersManager::unregister_emoji(const string &emoji, FullMessageId full_message_id, const char *source) {
4302 CHECK(!emoji.empty());
4303 if (td_->auth_manager_->is_bot()) {
4304 return;
4305 }
4306
4307 LOG(INFO) << "Unregister emoji " << emoji << " from " << full_message_id << " from " << source;
4308 auto it = emoji_messages_.find(emoji);
4309 CHECK(it != emoji_messages_.end());
4310 auto &full_message_ids = it->second.full_message_ids;
4311 auto is_deleted = full_message_ids.erase(full_message_id) > 0;
4312 LOG_CHECK(is_deleted) << source << ' ' << emoji << ' ' << full_message_id;
4313
4314 if (full_message_ids.empty()) {
4315 emoji_messages_.erase(it);
4316 }
4317 }
4318
get_animated_emoji(string emoji,bool is_recursive,Promise<td_api::object_ptr<td_api::animatedEmoji>> && promise)4319 void StickersManager::get_animated_emoji(string emoji, bool is_recursive,
4320 Promise<td_api::object_ptr<td_api::animatedEmoji>> &&promise) {
4321 TRY_STATUS_PROMISE(promise, G()->close_status());
4322
4323 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji());
4324 auto sticker_set = get_sticker_set(special_sticker_set.id_);
4325 if (sticker_set == nullptr || !sticker_set->was_loaded) {
4326 if (is_recursive) {
4327 return promise.set_value(nullptr);
4328 }
4329
4330 pending_get_animated_emoji_queries_.push_back(
4331 PromiseCreator::lambda([actor_id = actor_id(this), emoji = std::move(emoji),
4332 promise = std::move(promise)](Result<Unit> &&result) mutable {
4333 if (result.is_error()) {
4334 promise.set_error(result.move_as_error());
4335 } else {
4336 send_closure(actor_id, &StickersManager::get_animated_emoji, std::move(emoji), true, std::move(promise));
4337 }
4338 }));
4339 load_special_sticker_set(special_sticker_set);
4340 return;
4341 }
4342
4343 promise.set_value(get_animated_emoji_object(get_animated_emoji_sticker(sticker_set, emoji),
4344 get_animated_emoji_sound_file_id(emoji)));
4345 }
4346
get_animated_emoji_click_sticker(const string & message_text,FullMessageId full_message_id,Promise<td_api::object_ptr<td_api::sticker>> && promise)4347 void StickersManager::get_animated_emoji_click_sticker(const string &message_text, FullMessageId full_message_id,
4348 Promise<td_api::object_ptr<td_api::sticker>> &&promise) {
4349 if (disable_animated_emojis_ || td_->auth_manager_->is_bot()) {
4350 return promise.set_value(nullptr);
4351 }
4352
4353 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click());
4354 if (!special_sticker_set.id_.is_valid()) {
4355 // don't wait for the first load of the sticker set from the server
4356 load_special_sticker_set(special_sticker_set);
4357 return promise.set_value(nullptr);
4358 }
4359
4360 auto sticker_set = get_sticker_set(special_sticker_set.id_);
4361 CHECK(sticker_set != nullptr);
4362 if (sticker_set->was_loaded) {
4363 return choose_animated_emoji_click_sticker(sticker_set, message_text, full_message_id, Time::now(),
4364 std::move(promise));
4365 }
4366
4367 LOG(INFO) << "Waiting for an emoji click sticker set needed in " << full_message_id;
4368 load_special_sticker_set(special_sticker_set);
4369
4370 PendingGetAnimatedEmojiClickSticker pending_request;
4371 pending_request.message_text_ = message_text;
4372 pending_request.full_message_id_ = full_message_id;
4373 pending_request.start_time_ = Time::now();
4374 pending_request.promise_ = std::move(promise);
4375 pending_get_animated_emoji_click_stickers_.push_back(std::move(pending_request));
4376 }
4377
get_emoji_number(Slice emoji)4378 int StickersManager::get_emoji_number(Slice emoji) {
4379 // '0'-'9' + U+20E3
4380 auto data = emoji.ubegin();
4381 if (emoji.size() != 4 || emoji[0] < '0' || emoji[0] > '9' || data[1] != 0xE2 || data[2] != 0x83 || data[3] != 0xA3) {
4382 return -1;
4383 }
4384 return emoji[0] - '0';
4385 }
4386
get_animated_emoji_click_stickers(const StickerSet * sticker_set,Slice emoji) const4387 vector<FileId> StickersManager::get_animated_emoji_click_stickers(const StickerSet *sticker_set, Slice emoji) const {
4388 vector<FileId> result;
4389 for (auto sticker_id : sticker_set->sticker_ids) {
4390 auto s = get_sticker(sticker_id);
4391 CHECK(s != nullptr);
4392 if (remove_emoji_modifiers(s->alt) == emoji) {
4393 result.push_back(sticker_id);
4394 }
4395 }
4396 if (result.empty()) {
4397 const static vector<string> heart_emojis{"", "", "", "", "", "", "", ""};
4398 if (td::contains(heart_emojis, emoji)) {
4399 return get_animated_emoji_click_stickers(sticker_set, Slice("❤"));
4400 }
4401 }
4402 return result;
4403 }
4404
choose_animated_emoji_click_sticker(const StickerSet * sticker_set,Slice message_text,FullMessageId full_message_id,double start_time,Promise<td_api::object_ptr<td_api::sticker>> && promise)4405 void StickersManager::choose_animated_emoji_click_sticker(const StickerSet *sticker_set, Slice message_text,
4406 FullMessageId full_message_id, double start_time,
4407 Promise<td_api::object_ptr<td_api::sticker>> &&promise) {
4408 CHECK(sticker_set->was_loaded);
4409 message_text = remove_emoji_modifiers(message_text);
4410 if (message_text.empty()) {
4411 return promise.set_error(Status::Error(400, "Message is not an animated emoji message"));
4412 }
4413
4414 if (disable_animated_emojis_ || td_->auth_manager_->is_bot()) {
4415 return promise.set_value(nullptr);
4416 }
4417
4418 auto now = Time::now();
4419 if (last_clicked_animated_emoji_ == message_text && last_clicked_animated_emoji_full_message_id_ == full_message_id &&
4420 next_click_animated_emoji_message_time_ >= now + 2 * MIN_ANIMATED_EMOJI_CLICK_DELAY) {
4421 return promise.set_value(nullptr);
4422 }
4423
4424 auto all_sticker_ids = get_animated_emoji_click_stickers(sticker_set, message_text);
4425 vector<std::pair<int, FileId>> found_stickers;
4426 for (auto sticker_id : all_sticker_ids) {
4427 auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
4428 if (it != sticker_set->sticker_emojis_map_.end()) {
4429 for (auto &emoji : it->second) {
4430 auto number = get_emoji_number(emoji);
4431 if (number > 0) {
4432 found_stickers.emplace_back(number, sticker_id);
4433 }
4434 }
4435 }
4436 }
4437 if (found_stickers.empty()) {
4438 return promise.set_value(nullptr);
4439 }
4440
4441 if (last_clicked_animated_emoji_full_message_id_ != full_message_id) {
4442 flush_pending_animated_emoji_clicks();
4443 last_clicked_animated_emoji_full_message_id_ = full_message_id;
4444 }
4445 if (last_clicked_animated_emoji_ != message_text) {
4446 pending_animated_emoji_clicks_.clear();
4447 last_clicked_animated_emoji_ = message_text.str();
4448 }
4449
4450 if (!pending_animated_emoji_clicks_.empty() && found_stickers.size() >= 2) {
4451 for (auto it = found_stickers.begin(); it != found_stickers.end(); ++it) {
4452 if (it->first == pending_animated_emoji_clicks_.back().first) {
4453 found_stickers.erase(it);
4454 break;
4455 }
4456 }
4457 }
4458
4459 CHECK(!found_stickers.empty());
4460 auto result = found_stickers[Random::fast(0, narrow_cast<int>(found_stickers.size()) - 1)];
4461
4462 pending_animated_emoji_clicks_.emplace_back(result.first, start_time);
4463 if (pending_animated_emoji_clicks_.size() == 5) {
4464 flush_pending_animated_emoji_clicks();
4465 } else {
4466 set_timeout_in(0.5);
4467 }
4468 if (now >= next_click_animated_emoji_message_time_) {
4469 next_click_animated_emoji_message_time_ = now + MIN_ANIMATED_EMOJI_CLICK_DELAY;
4470 promise.set_value(get_sticker_object(result.second, false, true));
4471 } else {
4472 create_actor<SleepActor>("SendClickAnimatedEmojiMessageResponse", next_click_animated_emoji_message_time_ - now,
4473 PromiseCreator::lambda([actor_id = actor_id(this), sticker_id = result.second,
4474 promise = std::move(promise)](Result<Unit> result) mutable {
4475 send_closure(actor_id, &StickersManager::send_click_animated_emoji_message_response,
4476 sticker_id, std::move(promise));
4477 }))
4478 .release();
4479 next_click_animated_emoji_message_time_ += MIN_ANIMATED_EMOJI_CLICK_DELAY;
4480 }
4481 }
4482
send_click_animated_emoji_message_response(FileId sticker_id,Promise<td_api::object_ptr<td_api::sticker>> && promise)4483 void StickersManager::send_click_animated_emoji_message_response(
4484 FileId sticker_id, Promise<td_api::object_ptr<td_api::sticker>> &&promise) {
4485 TRY_STATUS_PROMISE(promise, G()->close_status());
4486 promise.set_value(get_sticker_object(sticker_id, false, true));
4487 }
4488
timeout_expired()4489 void StickersManager::timeout_expired() {
4490 flush_pending_animated_emoji_clicks();
4491 }
4492
flush_pending_animated_emoji_clicks()4493 void StickersManager::flush_pending_animated_emoji_clicks() {
4494 if (pending_animated_emoji_clicks_.empty()) {
4495 return;
4496 }
4497 auto clicks = std::move(pending_animated_emoji_clicks_);
4498 pending_animated_emoji_clicks_.clear();
4499 auto full_message_id = last_clicked_animated_emoji_full_message_id_;
4500 last_clicked_animated_emoji_full_message_id_ = FullMessageId();
4501 auto emoji = std::move(last_clicked_animated_emoji_);
4502 last_clicked_animated_emoji_.clear();
4503
4504 if (td_->messages_manager_->is_message_edited_recently(full_message_id, 1)) {
4505 // includes deleted full_message_id
4506 return;
4507 }
4508 auto dialog_id = full_message_id.get_dialog_id();
4509 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
4510 if (input_peer == nullptr) {
4511 return;
4512 }
4513
4514 double start_time = clicks[0].second;
4515 auto data = json_encode<string>(json_object([&clicks, start_time](auto &o) {
4516 o("v", 1);
4517 o("a", json_array(clicks, [start_time](auto &click) {
4518 return json_object([&click, start_time](auto &o) {
4519 o("i", click.first);
4520 auto t = static_cast<int32>((click.second - start_time) * 100);
4521 o("t", JsonRaw(PSLICE() << (t / 100) << '.' << (t < 10 ? "0" : "") << (t % 100)));
4522 });
4523 }));
4524 }));
4525
4526 td_->create_handler<SendAnimatedEmojiClicksQuery>()->send(
4527 dialog_id, std::move(input_peer),
4528 make_tl_object<telegram_api::sendMessageEmojiInteraction>(
4529 emoji, full_message_id.get_message_id().get_server_message_id().get(),
4530 make_tl_object<telegram_api::dataJSON>(data)));
4531
4532 on_send_animated_emoji_clicks(dialog_id, emoji);
4533 }
4534
on_send_animated_emoji_clicks(DialogId dialog_id,const string & emoji)4535 void StickersManager::on_send_animated_emoji_clicks(DialogId dialog_id, const string &emoji) {
4536 flush_sent_animated_emoji_clicks();
4537
4538 if (!sent_animated_emoji_clicks_.empty() && sent_animated_emoji_clicks_.back().dialog_id == dialog_id &&
4539 sent_animated_emoji_clicks_.back().emoji == emoji) {
4540 sent_animated_emoji_clicks_.back().send_time = Time::now();
4541 return;
4542 }
4543
4544 SentAnimatedEmojiClicks clicks;
4545 clicks.send_time = Time::now();
4546 clicks.dialog_id = dialog_id;
4547 clicks.emoji = emoji;
4548 sent_animated_emoji_clicks_.push_back(std::move(clicks));
4549 }
4550
flush_sent_animated_emoji_clicks()4551 void StickersManager::flush_sent_animated_emoji_clicks() {
4552 if (sent_animated_emoji_clicks_.empty()) {
4553 return;
4554 }
4555 auto min_send_time = Time::now() - 30.0;
4556 auto it = sent_animated_emoji_clicks_.begin();
4557 while (it != sent_animated_emoji_clicks_.end() && it->send_time <= min_send_time) {
4558 ++it;
4559 }
4560 sent_animated_emoji_clicks_.erase(sent_animated_emoji_clicks_.begin(), it);
4561 }
4562
is_sent_animated_emoji_click(DialogId dialog_id,Slice emoji)4563 bool StickersManager::is_sent_animated_emoji_click(DialogId dialog_id, Slice emoji) {
4564 flush_sent_animated_emoji_clicks();
4565 for (const auto &click : sent_animated_emoji_clicks_) {
4566 if (click.dialog_id == dialog_id && click.emoji == emoji) {
4567 return true;
4568 }
4569 }
4570 return false;
4571 }
4572
on_animated_emoji_message_clicked(Slice emoji,FullMessageId full_message_id,string data)4573 Status StickersManager::on_animated_emoji_message_clicked(Slice emoji, FullMessageId full_message_id, string data) {
4574 if (td_->auth_manager_->is_bot() || disable_animated_emojis_) {
4575 return Status::OK();
4576 }
4577
4578 TRY_RESULT(value, json_decode(data));
4579 if (value.type() != JsonValue::Type::Object) {
4580 return Status::Error("Expected an object");
4581 }
4582 auto &object = value.get_object();
4583 TRY_RESULT(version, get_json_object_int_field(object, "v", false));
4584 if (version != 1) {
4585 return Status::OK();
4586 }
4587 TRY_RESULT(array_value, get_json_object_field(object, "a", JsonValue::Type::Array, false));
4588 auto &array = array_value.get_array();
4589 if (array.size() > 20) {
4590 return Status::Error("Click array is too big");
4591 }
4592 vector<std::pair<int, double>> clicks;
4593 double previous_start_time = 0.0;
4594 double adjustment = 0.0;
4595 for (auto &click : array) {
4596 if (click.type() != JsonValue::Type::Object) {
4597 return Status::Error("Expected clicks as JSON objects");
4598 }
4599 auto &click_object = click.get_object();
4600 TRY_RESULT(index, get_json_object_int_field(click_object, "i", false));
4601 if (index <= 0 || index > 9) {
4602 return Status::Error("Wrong index");
4603 }
4604 TRY_RESULT(start_time, get_json_object_double_field(click_object, "t", false));
4605 if (!std::isfinite(start_time)) {
4606 return Status::Error("Receive invalid start time");
4607 }
4608 if (start_time < previous_start_time) {
4609 return Status::Error("Non-monotonic start time");
4610 }
4611 if (start_time > previous_start_time + 3) {
4612 return Status::Error("Too big delay between clicks");
4613 }
4614 previous_start_time = start_time;
4615
4616 auto adjusted_start_time =
4617 clicks.empty() ? 0.0 : max(start_time + adjustment, clicks.back().second + MIN_ANIMATED_EMOJI_CLICK_DELAY);
4618 adjustment = adjusted_start_time - start_time;
4619 clicks.emplace_back(static_cast<int>(index), adjusted_start_time);
4620 }
4621
4622 auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_emoji_click());
4623 if (special_sticker_set.id_.is_valid()) {
4624 auto sticker_set = get_sticker_set(special_sticker_set.id_);
4625 CHECK(sticker_set != nullptr);
4626 if (sticker_set->was_loaded) {
4627 schedule_update_animated_emoji_clicked(sticker_set, emoji, full_message_id, std::move(clicks));
4628 return Status::OK();
4629 }
4630 }
4631
4632 LOG(INFO) << "Waiting for an emoji click sticker set needed in " << full_message_id;
4633 load_special_sticker_set(special_sticker_set);
4634
4635 PendingOnAnimatedEmojiClicked pending_request;
4636 pending_request.emoji_ = emoji.str();
4637 pending_request.full_message_id_ = full_message_id;
4638 pending_request.clicks_ = std::move(clicks);
4639 pending_on_animated_emoji_message_clicked_.push_back(std::move(pending_request));
4640 return Status::OK();
4641 }
4642
schedule_update_animated_emoji_clicked(const StickerSet * sticker_set,Slice emoji,FullMessageId full_message_id,vector<std::pair<int,double>> clicks)4643 void StickersManager::schedule_update_animated_emoji_clicked(const StickerSet *sticker_set, Slice emoji,
4644 FullMessageId full_message_id,
4645 vector<std::pair<int, double>> clicks) {
4646 if (clicks.empty()) {
4647 return;
4648 }
4649 if (td_->messages_manager_->is_message_edited_recently(full_message_id, 2)) {
4650 // includes deleted full_message_id
4651 return;
4652 }
4653 auto dialog_id = full_message_id.get_dialog_id();
4654 if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Write)) {
4655 return;
4656 }
4657
4658 auto all_sticker_ids = get_animated_emoji_click_stickers(sticker_set, emoji);
4659 std::unordered_map<int, FileId> sticker_ids;
4660 for (auto sticker_id : all_sticker_ids) {
4661 auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
4662 if (it != sticker_set->sticker_emojis_map_.end()) {
4663 for (auto &sticker_emoji : it->second) {
4664 auto number = get_emoji_number(sticker_emoji);
4665 if (number > 0) {
4666 sticker_ids[number] = sticker_id;
4667 }
4668 }
4669 }
4670 }
4671
4672 auto now = Time::now();
4673 auto start_time = max(now, next_update_animated_emoji_clicked_time_);
4674 for (const auto &click : clicks) {
4675 auto index = click.first;
4676 auto sticker_id = sticker_ids[index];
4677 if (!sticker_id.is_valid()) {
4678 LOG(INFO) << "Failed to find sticker for " << emoji << " with index " << index;
4679 return;
4680 }
4681 create_actor<SleepActor>(
4682 "SendUpdateAnimatedEmojiClicked", start_time + click.second - now,
4683 PromiseCreator::lambda([actor_id = actor_id(this), full_message_id, sticker_id](Result<Unit> result) {
4684 send_closure(actor_id, &StickersManager::send_update_animated_emoji_clicked, full_message_id, sticker_id);
4685 }))
4686 .release();
4687 }
4688 next_update_animated_emoji_clicked_time_ = start_time + clicks.back().second + MIN_ANIMATED_EMOJI_CLICK_DELAY;
4689 }
4690
send_update_animated_emoji_clicked(FullMessageId full_message_id,FileId sticker_id)4691 void StickersManager::send_update_animated_emoji_clicked(FullMessageId full_message_id, FileId sticker_id) {
4692 if (G()->close_flag() || disable_animated_emojis_ || td_->auth_manager_->is_bot()) {
4693 return;
4694 }
4695 if (td_->messages_manager_->is_message_edited_recently(full_message_id, 2)) {
4696 // includes deleted full_message_id
4697 return;
4698 }
4699 auto dialog_id = full_message_id.get_dialog_id();
4700 if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Write)) {
4701 return;
4702 }
4703
4704 send_closure(
4705 G()->td(), &Td::send_update,
4706 td_api::make_object<td_api::updateAnimatedEmojiMessageClicked>(
4707 dialog_id.get(), full_message_id.get_message_id().get(), get_sticker_object(sticker_id, false, true)));
4708 }
4709
view_featured_sticker_sets(const vector<StickerSetId> & sticker_set_ids)4710 void StickersManager::view_featured_sticker_sets(const vector<StickerSetId> &sticker_set_ids) {
4711 for (auto sticker_set_id : sticker_set_ids) {
4712 auto set = get_sticker_set(sticker_set_id);
4713 if (set != nullptr && !set->is_viewed) {
4714 if (td::contains(featured_sticker_set_ids_, sticker_set_id)) {
4715 need_update_featured_sticker_sets_ = true;
4716 }
4717 set->is_viewed = true;
4718 pending_viewed_featured_sticker_set_ids_.insert(sticker_set_id);
4719 update_sticker_set(set, "view_featured_sticker_sets");
4720 }
4721 }
4722
4723 send_update_featured_sticker_sets();
4724
4725 if (!pending_viewed_featured_sticker_set_ids_.empty() && !pending_featured_sticker_set_views_timeout_.has_timeout()) {
4726 LOG(INFO) << "Have pending viewed trending sticker sets";
4727 pending_featured_sticker_set_views_timeout_.set_callback(read_featured_sticker_sets);
4728 pending_featured_sticker_set_views_timeout_.set_callback_data(static_cast<void *>(td_));
4729 pending_featured_sticker_set_views_timeout_.set_timeout_in(MAX_FEATURED_STICKER_SET_VIEW_DELAY);
4730 }
4731 }
4732
read_featured_sticker_sets(void * td_void)4733 void StickersManager::read_featured_sticker_sets(void *td_void) {
4734 if (G()->close_flag()) {
4735 return;
4736 }
4737
4738 CHECK(td_void != nullptr);
4739 auto td = static_cast<Td *>(td_void);
4740
4741 auto &set_ids = td->stickers_manager_->pending_viewed_featured_sticker_set_ids_;
4742 td->create_handler<ReadFeaturedStickerSetsQuery>()->send(vector<StickerSetId>(set_ids.begin(), set_ids.end()));
4743 set_ids.clear();
4744 }
4745
get_archived_sticker_sets(bool is_masks,StickerSetId offset_sticker_set_id,int32 limit,bool force,Promise<Unit> && promise)4746 std::pair<int32, vector<StickerSetId>> StickersManager::get_archived_sticker_sets(bool is_masks,
4747 StickerSetId offset_sticker_set_id,
4748 int32 limit, bool force,
4749 Promise<Unit> &&promise) {
4750 if (limit <= 0) {
4751 promise.set_error(Status::Error(400, "Parameter limit must be positive"));
4752 return {};
4753 }
4754
4755 vector<StickerSetId> &sticker_set_ids = archived_sticker_set_ids_[is_masks];
4756 int32 total_count = total_archived_sticker_set_count_[is_masks];
4757 if (total_count >= 0) {
4758 auto offset_it = sticker_set_ids.begin();
4759 if (offset_sticker_set_id.is_valid()) {
4760 offset_it = std::find(sticker_set_ids.begin(), sticker_set_ids.end(), offset_sticker_set_id);
4761 if (offset_it == sticker_set_ids.end()) {
4762 offset_it = sticker_set_ids.begin();
4763 } else {
4764 ++offset_it;
4765 }
4766 }
4767 vector<StickerSetId> result;
4768 while (result.size() < static_cast<size_t>(limit)) {
4769 if (offset_it == sticker_set_ids.end()) {
4770 break;
4771 }
4772 auto sticker_set_id = *offset_it++;
4773 if (!sticker_set_id.is_valid()) { // end of the list
4774 promise.set_value(Unit());
4775 return {total_count, std::move(result)};
4776 }
4777 result.push_back(sticker_set_id);
4778 }
4779 if (result.size() == static_cast<size_t>(limit) || force) {
4780 promise.set_value(Unit());
4781 return {total_count, std::move(result)};
4782 }
4783 }
4784
4785 td_->create_handler<GetArchivedStickerSetsQuery>(std::move(promise))->send(is_masks, offset_sticker_set_id, limit);
4786 return {};
4787 }
4788
on_get_archived_sticker_sets(bool is_masks,StickerSetId offset_sticker_set_id,vector<tl_object_ptr<telegram_api::StickerSetCovered>> && sticker_sets,int32 total_count)4789 void StickersManager::on_get_archived_sticker_sets(
4790 bool is_masks, StickerSetId offset_sticker_set_id,
4791 vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets, int32 total_count) {
4792 vector<StickerSetId> &sticker_set_ids = archived_sticker_set_ids_[is_masks];
4793 if (!sticker_set_ids.empty() && sticker_set_ids.back() == StickerSetId()) {
4794 return;
4795 }
4796 if (total_count < 0) {
4797 LOG(ERROR) << "Receive " << total_count << " as total count of archived sticker sets";
4798 }
4799
4800 // if 0 sticker sets are received, then set offset_sticker_set_id was found and there are no stickers after it
4801 // or it wasn't found and there are no archived sets at all
4802 bool is_last =
4803 sticker_sets.empty() && (!offset_sticker_set_id.is_valid() ||
4804 (!sticker_set_ids.empty() && offset_sticker_set_id == sticker_set_ids.back()));
4805
4806 total_archived_sticker_set_count_[is_masks] = total_count;
4807 for (auto &sticker_set_covered : sticker_sets) {
4808 auto sticker_set_id =
4809 on_get_sticker_set_covered(std::move(sticker_set_covered), false, "on_get_archived_sticker_sets");
4810 if (sticker_set_id.is_valid()) {
4811 auto sticker_set = get_sticker_set(sticker_set_id);
4812 CHECK(sticker_set != nullptr);
4813 update_sticker_set(sticker_set, "on_get_archived_sticker_sets");
4814
4815 if (!td::contains(sticker_set_ids, sticker_set_id)) {
4816 sticker_set_ids.push_back(sticker_set_id);
4817 }
4818 }
4819 }
4820 if (sticker_set_ids.size() >= static_cast<size_t>(total_count) || is_last) {
4821 if (sticker_set_ids.size() != static_cast<size_t>(total_count)) {
4822 LOG(ERROR) << "Expected total of " << total_count << " archived sticker sets, but " << sticker_set_ids.size()
4823 << " found";
4824 total_archived_sticker_set_count_[is_masks] = static_cast<int32>(sticker_set_ids.size());
4825 }
4826 sticker_set_ids.push_back(StickerSetId());
4827 }
4828 send_update_installed_sticker_sets();
4829 }
4830
get_featured_sticker_sets(int32 offset,int32 limit,Promise<Unit> && promise)4831 std::pair<int32, vector<StickerSetId>> StickersManager::get_featured_sticker_sets(int32 offset, int32 limit,
4832 Promise<Unit> &&promise) {
4833 if (offset < 0) {
4834 promise.set_error(Status::Error(400, "Parameter offset must be non-negative"));
4835 return {};
4836 }
4837
4838 if (limit < 0) {
4839 promise.set_error(Status::Error(400, "Parameter limit must be non-negative"));
4840 return {};
4841 }
4842 if (limit == 0) {
4843 offset = 0;
4844 }
4845
4846 if (!are_featured_sticker_sets_loaded_) {
4847 load_featured_sticker_sets(std::move(promise));
4848 return {};
4849 }
4850 reload_featured_sticker_sets(false);
4851
4852 auto set_count = static_cast<int32>(featured_sticker_set_ids_.size());
4853 auto total_count = set_count + (old_featured_sticker_set_count_ == -1 ? 1 : old_featured_sticker_set_count_);
4854 if (offset < set_count) {
4855 if (limit > set_count - offset) {
4856 limit = set_count - offset;
4857 }
4858 promise.set_value(Unit());
4859 auto begin = featured_sticker_set_ids_.begin() + offset;
4860 return {total_count, {begin, begin + limit}};
4861 }
4862
4863 if (offset == set_count && are_old_featured_sticker_sets_invalidated_) {
4864 invalidate_old_featured_sticker_sets();
4865 }
4866
4867 if (offset < total_count || old_featured_sticker_set_count_ == -1) {
4868 offset -= set_count;
4869 set_count = static_cast<int32>(old_featured_sticker_set_ids_.size());
4870 if (offset < set_count) {
4871 if (limit > set_count - offset) {
4872 limit = set_count - offset;
4873 }
4874 promise.set_value(Unit());
4875 auto begin = old_featured_sticker_set_ids_.begin() + offset;
4876 return {total_count, {begin, begin + limit}};
4877 }
4878 if (offset > set_count) {
4879 promise.set_error(
4880 Status::Error(400, "Too big offset specified; trending sticker sets can be received only consequently"));
4881 return {};
4882 }
4883
4884 load_old_featured_sticker_sets(std::move(promise));
4885 return {};
4886 }
4887
4888 promise.set_value(Unit());
4889 return {total_count, vector<StickerSetId>()};
4890 }
4891
on_old_featured_sticker_sets_invalidated()4892 void StickersManager::on_old_featured_sticker_sets_invalidated() {
4893 LOG(INFO) << "Invalidate old trending sticker sets";
4894 are_old_featured_sticker_sets_invalidated_ = true;
4895
4896 if (!G()->parameters().use_file_db) {
4897 return;
4898 }
4899
4900 G()->td_db()->get_binlog_pmc()->set("invalidate_old_featured_sticker_sets", "1");
4901 }
4902
invalidate_old_featured_sticker_sets()4903 void StickersManager::invalidate_old_featured_sticker_sets() {
4904 if (G()->close_flag()) {
4905 return;
4906 }
4907
4908 LOG(INFO) << "Invalidate old featured sticker sets";
4909 if (G()->parameters().use_file_db) {
4910 G()->td_db()->get_binlog_pmc()->erase("invalidate_old_featured_sticker_sets");
4911 G()->td_db()->get_sqlite_pmc()->erase_by_prefix("sssoldfeatured", Auto());
4912 }
4913 are_old_featured_sticker_sets_invalidated_ = false;
4914 old_featured_sticker_set_ids_.clear();
4915
4916 old_featured_sticker_set_generation_++;
4917 auto promises = std::move(load_old_featured_sticker_sets_queries_);
4918 load_old_featured_sticker_sets_queries_.clear();
4919 for (auto &promise : promises) {
4920 promise.set_error(Status::Error(400, "Trending sticker sets were updated"));
4921 }
4922 }
4923
set_old_featured_sticker_set_count(int32 count)4924 void StickersManager::set_old_featured_sticker_set_count(int32 count) {
4925 if (old_featured_sticker_set_count_ == count) {
4926 return;
4927 }
4928
4929 on_old_featured_sticker_sets_invalidated();
4930
4931 old_featured_sticker_set_count_ = count;
4932 need_update_featured_sticker_sets_ = true;
4933
4934 if (!G()->parameters().use_file_db) {
4935 return;
4936 }
4937
4938 LOG(INFO) << "Save old trending sticker set count " << count << " to binlog";
4939 G()->td_db()->get_binlog_pmc()->set("old_featured_sticker_set_count", to_string(count));
4940 }
4941
fix_old_featured_sticker_set_count()4942 void StickersManager::fix_old_featured_sticker_set_count() {
4943 auto known_count = static_cast<int32>(old_featured_sticker_set_ids_.size());
4944 if (old_featured_sticker_set_count_ < known_count) {
4945 if (old_featured_sticker_set_count_ >= 0) {
4946 LOG(ERROR) << "Have old trending sticker set count " << old_featured_sticker_set_count_ << ", but have "
4947 << known_count << " old trending sticker sets";
4948 }
4949 set_old_featured_sticker_set_count(known_count);
4950 }
4951 if (old_featured_sticker_set_count_ > known_count && known_count % OLD_FEATURED_STICKER_SET_SLICE_SIZE != 0) {
4952 LOG(ERROR) << "Have " << known_count << " old sticker sets out of " << old_featured_sticker_set_count_;
4953 set_old_featured_sticker_set_count(known_count);
4954 }
4955 }
4956
on_get_featured_sticker_sets(int32 offset,int32 limit,uint32 generation,tl_object_ptr<telegram_api::messages_FeaturedStickers> && sticker_sets_ptr)4957 void StickersManager::on_get_featured_sticker_sets(
4958 int32 offset, int32 limit, uint32 generation,
4959 tl_object_ptr<telegram_api::messages_FeaturedStickers> &&sticker_sets_ptr) {
4960 if (offset < 0) {
4961 next_featured_sticker_sets_load_time_ = Time::now_cached() + Random::fast(30 * 60, 50 * 60);
4962 }
4963
4964 int32 constructor_id = sticker_sets_ptr->get_id();
4965 if (constructor_id == telegram_api::messages_featuredStickersNotModified::ID) {
4966 LOG(INFO) << "Trending sticker sets are not modified";
4967 auto *stickers = static_cast<const telegram_api::messages_featuredStickersNotModified *>(sticker_sets_ptr.get());
4968 if (offset >= 0 && generation == old_featured_sticker_set_generation_) {
4969 set_old_featured_sticker_set_count(stickers->count_);
4970 fix_old_featured_sticker_set_count();
4971 }
4972 send_update_featured_sticker_sets();
4973 return;
4974 }
4975 CHECK(constructor_id == telegram_api::messages_featuredStickers::ID);
4976 auto featured_stickers = move_tl_object_as<telegram_api::messages_featuredStickers>(sticker_sets_ptr);
4977
4978 if (offset >= 0 && generation == old_featured_sticker_set_generation_) {
4979 set_old_featured_sticker_set_count(featured_stickers->count_);
4980 // the count will be fixed in on_load_old_featured_sticker_sets_finished
4981 }
4982
4983 std::unordered_set<StickerSetId, StickerSetIdHash> unread_sticker_set_ids;
4984 for (auto &unread_sticker_set_id : featured_stickers->unread_) {
4985 unread_sticker_set_ids.insert(StickerSetId(unread_sticker_set_id));
4986 }
4987
4988 vector<StickerSetId> featured_sticker_set_ids;
4989 for (auto &sticker_set : featured_stickers->sets_) {
4990 StickerSetId set_id = on_get_sticker_set_covered(std::move(sticker_set), true, "on_get_featured_sticker_sets");
4991 if (!set_id.is_valid()) {
4992 continue;
4993 }
4994
4995 auto set = get_sticker_set(set_id);
4996 CHECK(set != nullptr);
4997 bool is_viewed = unread_sticker_set_ids.count(set_id) == 0;
4998 if (is_viewed != set->is_viewed) {
4999 set->is_viewed = is_viewed;
5000 set->is_changed = true;
5001 }
5002
5003 update_sticker_set(set, "on_get_archived_sticker_sets 2");
5004
5005 featured_sticker_set_ids.push_back(set_id);
5006 }
5007
5008 send_update_installed_sticker_sets();
5009
5010 if (offset >= 0) {
5011 if (generation == old_featured_sticker_set_generation_) {
5012 if (G()->parameters().use_file_db && !G()->close_flag()) {
5013 LOG(INFO) << "Save old trending sticker sets to database with offset " << old_featured_sticker_set_ids_.size();
5014 CHECK(old_featured_sticker_set_ids_.size() % OLD_FEATURED_STICKER_SET_SLICE_SIZE == 0);
5015 StickerSetListLogEvent log_event(featured_sticker_set_ids);
5016 G()->td_db()->get_sqlite_pmc()->set(PSTRING() << "sssoldfeatured" << old_featured_sticker_set_ids_.size(),
5017 log_event_store(log_event).as_slice().str(), Auto());
5018 }
5019 on_load_old_featured_sticker_sets_finished(generation, std::move(featured_sticker_set_ids));
5020 }
5021
5022 send_update_featured_sticker_sets(); // because of changed count
5023 return;
5024 }
5025
5026 on_load_featured_sticker_sets_finished(std::move(featured_sticker_set_ids));
5027
5028 LOG_IF(ERROR, featured_sticker_sets_hash_ != featured_stickers->hash_) << "Trending sticker sets hash mismatch";
5029
5030 if (!G()->parameters().use_file_db || G()->close_flag()) {
5031 return;
5032 }
5033
5034 LOG(INFO) << "Save trending sticker sets to database";
5035 StickerSetListLogEvent log_event(featured_sticker_set_ids_);
5036 G()->td_db()->get_sqlite_pmc()->set("sssfeatured", log_event_store(log_event).as_slice().str(), Auto());
5037 }
5038
on_get_featured_sticker_sets_failed(int32 offset,int32 limit,uint32 generation,Status error)5039 void StickersManager::on_get_featured_sticker_sets_failed(int32 offset, int32 limit, uint32 generation, Status error) {
5040 CHECK(error.is_error());
5041 vector<Promise<Unit>> promises;
5042 if (offset >= 0) {
5043 if (generation != old_featured_sticker_set_generation_) {
5044 return;
5045 }
5046 promises = std::move(load_old_featured_sticker_sets_queries_);
5047 load_old_featured_sticker_sets_queries_.clear();
5048 } else {
5049 next_featured_sticker_sets_load_time_ = Time::now_cached() + Random::fast(5, 10);
5050 promises = std::move(load_featured_sticker_sets_queries_);
5051 load_featured_sticker_sets_queries_.clear();
5052 }
5053
5054 for (auto &promise : promises) {
5055 promise.set_error(error.clone());
5056 }
5057 }
5058
load_featured_sticker_sets(Promise<Unit> && promise)5059 void StickersManager::load_featured_sticker_sets(Promise<Unit> &&promise) {
5060 if (td_->auth_manager_->is_bot()) {
5061 are_featured_sticker_sets_loaded_ = true;
5062 old_featured_sticker_set_count_ = 0;
5063 }
5064 if (are_featured_sticker_sets_loaded_) {
5065 promise.set_value(Unit());
5066 return;
5067 }
5068 load_featured_sticker_sets_queries_.push_back(std::move(promise));
5069 if (load_featured_sticker_sets_queries_.size() == 1u) {
5070 if (G()->parameters().use_file_db) {
5071 LOG(INFO) << "Trying to load trending sticker sets from database";
5072 G()->td_db()->get_sqlite_pmc()->get("sssfeatured", PromiseCreator::lambda([](string value) {
5073 send_closure(G()->stickers_manager(),
5074 &StickersManager::on_load_featured_sticker_sets_from_database,
5075 std::move(value));
5076 }));
5077 } else {
5078 LOG(INFO) << "Trying to load trending sticker sets from server";
5079 reload_featured_sticker_sets(true);
5080 }
5081 }
5082 }
5083
on_load_featured_sticker_sets_from_database(string value)5084 void StickersManager::on_load_featured_sticker_sets_from_database(string value) {
5085 if (G()->close_flag()) {
5086 return;
5087 }
5088 if (value.empty()) {
5089 LOG(INFO) << "Trending sticker sets aren't found in database";
5090 reload_featured_sticker_sets(true);
5091 return;
5092 }
5093
5094 LOG(INFO) << "Successfully loaded trending sticker set list of size " << value.size() << " from database";
5095
5096 StickerSetListLogEvent log_event;
5097 auto status = log_event_parse(log_event, value);
5098 if (status.is_error()) {
5099 // can't happen unless database is broken
5100 LOG(ERROR) << "Can't load trending sticker set list: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
5101 return reload_featured_sticker_sets(true);
5102 }
5103
5104 vector<StickerSetId> sets_to_load;
5105 for (auto sticker_set_id : log_event.sticker_set_ids) {
5106 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
5107 CHECK(sticker_set != nullptr);
5108 if (!sticker_set->is_inited) {
5109 sets_to_load.push_back(sticker_set_id);
5110 }
5111 }
5112
5113 load_sticker_sets_without_stickers(
5114 std::move(sets_to_load),
5115 PromiseCreator::lambda([sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
5116 if (result.is_ok()) {
5117 send_closure(G()->stickers_manager(), &StickersManager::on_load_featured_sticker_sets_finished,
5118 std::move(sticker_set_ids));
5119 } else {
5120 send_closure(G()->stickers_manager(), &StickersManager::reload_featured_sticker_sets, true);
5121 }
5122 }));
5123 }
5124
on_load_featured_sticker_sets_finished(vector<StickerSetId> && featured_sticker_set_ids)5125 void StickersManager::on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids) {
5126 if (!featured_sticker_set_ids_.empty() && featured_sticker_set_ids != featured_sticker_set_ids_) {
5127 // always invalidate old featured sticker sets when current featured sticker sets change
5128 on_old_featured_sticker_sets_invalidated();
5129 }
5130 featured_sticker_set_ids_ = std::move(featured_sticker_set_ids);
5131 are_featured_sticker_sets_loaded_ = true;
5132 need_update_featured_sticker_sets_ = true;
5133 send_update_featured_sticker_sets();
5134 auto promises = std::move(load_featured_sticker_sets_queries_);
5135 load_featured_sticker_sets_queries_.clear();
5136 for (auto &promise : promises) {
5137 promise.set_value(Unit());
5138 }
5139 }
5140
load_old_featured_sticker_sets(Promise<Unit> && promise)5141 void StickersManager::load_old_featured_sticker_sets(Promise<Unit> &&promise) {
5142 CHECK(!td_->auth_manager_->is_bot());
5143 CHECK(old_featured_sticker_set_ids_.size() % OLD_FEATURED_STICKER_SET_SLICE_SIZE == 0);
5144 load_old_featured_sticker_sets_queries_.push_back(std::move(promise));
5145 if (load_old_featured_sticker_sets_queries_.size() == 1u) {
5146 if (G()->parameters().use_file_db) {
5147 LOG(INFO) << "Trying to load old trending sticker sets from database with offset "
5148 << old_featured_sticker_set_ids_.size();
5149 G()->td_db()->get_sqlite_pmc()->get(
5150 PSTRING() << "sssoldfeatured" << old_featured_sticker_set_ids_.size(),
5151 PromiseCreator::lambda([generation = old_featured_sticker_set_generation_](string value) {
5152 send_closure(G()->stickers_manager(), &StickersManager::on_load_old_featured_sticker_sets_from_database,
5153 generation, std::move(value));
5154 }));
5155 } else {
5156 LOG(INFO) << "Trying to load old trending sticker sets from server with offset "
5157 << old_featured_sticker_set_ids_.size();
5158 reload_old_featured_sticker_sets();
5159 }
5160 }
5161 }
5162
on_load_old_featured_sticker_sets_from_database(uint32 generation,string value)5163 void StickersManager::on_load_old_featured_sticker_sets_from_database(uint32 generation, string value) {
5164 if (G()->close_flag()) {
5165 return;
5166 }
5167 if (generation != old_featured_sticker_set_generation_) {
5168 return;
5169 }
5170 if (value.empty()) {
5171 LOG(INFO) << "Old trending sticker sets aren't found in database";
5172 return reload_old_featured_sticker_sets();
5173 }
5174
5175 LOG(INFO) << "Successfully loaded old trending sticker set list of size " << value.size()
5176 << " from database with offset " << old_featured_sticker_set_ids_.size();
5177
5178 StickerSetListLogEvent log_event;
5179 auto status = log_event_parse(log_event, value);
5180 if (status.is_error()) {
5181 // can't happen unless database is broken
5182 LOG(ERROR) << "Can't load old trending sticker set list: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
5183 return reload_old_featured_sticker_sets();
5184 }
5185
5186 vector<StickerSetId> sets_to_load;
5187 for (auto sticker_set_id : log_event.sticker_set_ids) {
5188 StickerSet *sticker_set = get_sticker_set(sticker_set_id);
5189 CHECK(sticker_set != nullptr);
5190 if (!sticker_set->is_inited) {
5191 sets_to_load.push_back(sticker_set_id);
5192 }
5193 }
5194
5195 load_sticker_sets_without_stickers(
5196 std::move(sets_to_load),
5197 PromiseCreator::lambda(
5198 [generation, sticker_set_ids = std::move(log_event.sticker_set_ids)](Result<> result) mutable {
5199 if (result.is_ok()) {
5200 send_closure(G()->stickers_manager(), &StickersManager::on_load_old_featured_sticker_sets_finished,
5201 generation, std::move(sticker_set_ids));
5202 } else {
5203 send_closure(G()->stickers_manager(), &StickersManager::reload_old_featured_sticker_sets, generation);
5204 }
5205 }));
5206 }
5207
on_load_old_featured_sticker_sets_finished(uint32 generation,vector<StickerSetId> && featured_sticker_set_ids)5208 void StickersManager::on_load_old_featured_sticker_sets_finished(uint32 generation,
5209 vector<StickerSetId> &&featured_sticker_set_ids) {
5210 if (generation != old_featured_sticker_set_generation_) {
5211 fix_old_featured_sticker_set_count(); // must never be needed
5212 return;
5213 }
5214 append(old_featured_sticker_set_ids_, std::move(featured_sticker_set_ids));
5215 fix_old_featured_sticker_set_count();
5216 auto promises = std::move(load_old_featured_sticker_sets_queries_);
5217 load_old_featured_sticker_sets_queries_.clear();
5218 for (auto &promise : promises) {
5219 promise.set_value(Unit());
5220 }
5221 }
5222
get_attached_sticker_sets(FileId file_id,Promise<Unit> && promise)5223 vector<StickerSetId> StickersManager::get_attached_sticker_sets(FileId file_id, Promise<Unit> &&promise) {
5224 if (!file_id.is_valid()) {
5225 promise.set_error(Status::Error(400, "Wrong file_id specified"));
5226 return {};
5227 }
5228
5229 auto it = attached_sticker_sets_.find(file_id);
5230 if (it != attached_sticker_sets_.end()) {
5231 promise.set_value(Unit());
5232 return it->second;
5233 }
5234
5235 send_get_attached_stickers_query(file_id, std::move(promise));
5236 return {};
5237 }
5238
send_get_attached_stickers_query(FileId file_id,Promise<Unit> && promise)5239 void StickersManager::send_get_attached_stickers_query(FileId file_id, Promise<Unit> &&promise) {
5240 auto file_view = td_->file_manager_->get_file_view(file_id);
5241 if (file_view.empty()) {
5242 return promise.set_error(Status::Error(400, "File not found"));
5243 }
5244 if (!file_view.has_remote_location() ||
5245 (!file_view.remote_location().is_document() && !file_view.remote_location().is_photo()) ||
5246 file_view.remote_location().is_web()) {
5247 return promise.set_value(Unit());
5248 }
5249
5250 tl_object_ptr<telegram_api::InputStickeredMedia> input_stickered_media;
5251 string file_reference;
5252 if (file_view.main_remote_location().is_photo()) {
5253 auto input_photo = file_view.main_remote_location().as_input_photo();
5254 file_reference = input_photo->file_reference_.as_slice().str();
5255 input_stickered_media = make_tl_object<telegram_api::inputStickeredMediaPhoto>(std::move(input_photo));
5256 } else {
5257 auto input_document = file_view.main_remote_location().as_input_document();
5258 file_reference = input_document->file_reference_.as_slice().str();
5259 input_stickered_media = make_tl_object<telegram_api::inputStickeredMediaDocument>(std::move(input_document));
5260 }
5261
5262 td_->create_handler<GetAttachedStickerSetsQuery>(std::move(promise))
5263 ->send(file_id, std::move(file_reference), std::move(input_stickered_media));
5264 }
5265
on_get_attached_sticker_sets(FileId file_id,vector<tl_object_ptr<telegram_api::StickerSetCovered>> && sticker_sets)5266 void StickersManager::on_get_attached_sticker_sets(
5267 FileId file_id, vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets) {
5268 vector<StickerSetId> &sticker_set_ids = attached_sticker_sets_[file_id];
5269 sticker_set_ids.clear();
5270 for (auto &sticker_set_covered : sticker_sets) {
5271 auto sticker_set_id =
5272 on_get_sticker_set_covered(std::move(sticker_set_covered), true, "on_get_attached_sticker_sets");
5273 if (sticker_set_id.is_valid()) {
5274 auto sticker_set = get_sticker_set(sticker_set_id);
5275 CHECK(sticker_set != nullptr);
5276 update_sticker_set(sticker_set, "on_get_attached_sticker_sets");
5277
5278 sticker_set_ids.push_back(sticker_set_id);
5279 }
5280 }
5281 send_update_installed_sticker_sets();
5282 }
5283
5284 // -1 - order can't be applied, because some sticker sets aren't loaded or aren't installed,
5285 // 0 - order wasn't changed, 1 - order was partly replaced by the new order, 2 - order was replaced by the new order
apply_installed_sticker_sets_order(bool is_masks,const vector<StickerSetId> & sticker_set_ids)5286 int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids) {
5287 if (!are_installed_sticker_sets_loaded_[is_masks]) {
5288 return -1;
5289 }
5290
5291 vector<StickerSetId> ¤t_sticker_set_ids = installed_sticker_set_ids_[is_masks];
5292 if (sticker_set_ids == current_sticker_set_ids) {
5293 return 0;
5294 }
5295
5296 std::unordered_set<StickerSetId, StickerSetIdHash> valid_set_ids(current_sticker_set_ids.begin(),
5297 current_sticker_set_ids.end());
5298 vector<StickerSetId> new_sticker_set_ids;
5299 for (auto sticker_set_id : sticker_set_ids) {
5300 auto it = valid_set_ids.find(sticker_set_id);
5301 if (it != valid_set_ids.end()) {
5302 new_sticker_set_ids.push_back(sticker_set_id);
5303 valid_set_ids.erase(it);
5304 } else {
5305 return -1;
5306 }
5307 }
5308 if (new_sticker_set_ids.empty()) {
5309 return 0;
5310 }
5311 if (!valid_set_ids.empty()) {
5312 vector<StickerSetId> missed_sticker_set_ids;
5313 for (auto sticker_set_id : current_sticker_set_ids) {
5314 auto it = valid_set_ids.find(sticker_set_id);
5315 if (it != valid_set_ids.end()) {
5316 missed_sticker_set_ids.push_back(sticker_set_id);
5317 valid_set_ids.erase(it);
5318 }
5319 }
5320 append(missed_sticker_set_ids, new_sticker_set_ids);
5321 new_sticker_set_ids = std::move(missed_sticker_set_ids);
5322 }
5323 CHECK(valid_set_ids.empty());
5324
5325 if (new_sticker_set_ids == current_sticker_set_ids) {
5326 return 0;
5327 }
5328 current_sticker_set_ids = std::move(new_sticker_set_ids);
5329
5330 need_update_installed_sticker_sets_[is_masks] = true;
5331 if (sticker_set_ids != current_sticker_set_ids) {
5332 return 1;
5333 }
5334 return 2;
5335 }
5336
on_update_sticker_sets_order(bool is_masks,const vector<StickerSetId> & sticker_set_ids)5337 void StickersManager::on_update_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids) {
5338 int result = apply_installed_sticker_sets_order(is_masks, sticker_set_ids);
5339 if (result < 0) {
5340 return reload_installed_sticker_sets(is_masks, true);
5341 }
5342 if (result > 0) {
5343 send_update_installed_sticker_sets();
5344 }
5345 }
5346
reorder_installed_sticker_sets(bool is_masks,const vector<StickerSetId> & sticker_set_ids,Promise<Unit> && promise)5347 void StickersManager::reorder_installed_sticker_sets(bool is_masks, const vector<StickerSetId> &sticker_set_ids,
5348 Promise<Unit> &&promise) {
5349 auto result = apply_installed_sticker_sets_order(is_masks, sticker_set_ids);
5350 if (result < 0) {
5351 return promise.set_error(Status::Error(400, "Wrong sticker set list"));
5352 }
5353 if (result > 0) {
5354 td_->create_handler<ReorderStickerSetsQuery>()->send(is_masks, installed_sticker_set_ids_[is_masks]);
5355 send_update_installed_sticker_sets();
5356 }
5357 promise.set_value(Unit());
5358 }
5359
get_input_sticker_emojis(td_api::InputSticker * sticker)5360 string &StickersManager::get_input_sticker_emojis(td_api::InputSticker *sticker) {
5361 CHECK(sticker != nullptr);
5362 auto constructor_id = sticker->get_id();
5363 if (constructor_id == td_api::inputStickerStatic::ID) {
5364 return static_cast<td_api::inputStickerStatic *>(sticker)->emojis_;
5365 }
5366 CHECK(constructor_id == td_api::inputStickerAnimated::ID);
5367 return static_cast<td_api::inputStickerAnimated *>(sticker)->emojis_;
5368 }
5369
prepare_input_sticker(td_api::InputSticker * sticker)5370 Result<std::tuple<FileId, bool, bool, bool>> StickersManager::prepare_input_sticker(td_api::InputSticker *sticker) {
5371 if (sticker == nullptr) {
5372 return Status::Error(400, "Input sticker must be non-empty");
5373 }
5374
5375 if (!clean_input_string(get_input_sticker_emojis(sticker))) {
5376 return Status::Error(400, "Emojis must be encoded in UTF-8");
5377 }
5378
5379 switch (sticker->get_id()) {
5380 case td_api::inputStickerStatic::ID:
5381 return prepare_input_file(static_cast<td_api::inputStickerStatic *>(sticker)->sticker_, false, false);
5382 case td_api::inputStickerAnimated::ID:
5383 return prepare_input_file(static_cast<td_api::inputStickerAnimated *>(sticker)->sticker_, true, false);
5384 default:
5385 UNREACHABLE();
5386 return {};
5387 }
5388 }
5389
prepare_input_file(const tl_object_ptr<td_api::InputFile> & input_file,bool is_animated,bool for_thumbnail)5390 Result<std::tuple<FileId, bool, bool, bool>> StickersManager::prepare_input_file(
5391 const tl_object_ptr<td_api::InputFile> &input_file, bool is_animated, bool for_thumbnail) {
5392 auto r_file_id = td_->file_manager_->get_input_file_id(is_animated ? FileType::Sticker : FileType::Document,
5393 input_file, DialogId(), for_thumbnail, false);
5394 if (r_file_id.is_error()) {
5395 return Status::Error(400, r_file_id.error().message());
5396 }
5397 auto file_id = r_file_id.move_as_ok();
5398 if (file_id.empty()) {
5399 return std::make_tuple(FileId(), false, false, false);
5400 }
5401
5402 if (is_animated) {
5403 int32 width = for_thumbnail ? 100 : 512;
5404 create_sticker(file_id, string(), PhotoSize(), get_dimensions(width, width, "prepare_input_file"), nullptr, true,
5405 nullptr);
5406 } else {
5407 td_->documents_manager_->create_document(file_id, string(), PhotoSize(), "sticker.png", "image/png", false);
5408 }
5409
5410 FileView file_view = td_->file_manager_->get_file_view(file_id);
5411 if (file_view.is_encrypted()) {
5412 return Status::Error(400, "Can't use encrypted file");
5413 }
5414
5415 if (file_view.has_remote_location() && file_view.main_remote_location().is_web()) {
5416 return Status::Error(400, "Can't use web file to create a sticker");
5417 }
5418 bool is_url = false;
5419 bool is_local = false;
5420 if (file_view.has_remote_location()) {
5421 CHECK(file_view.main_remote_location().is_document());
5422 } else {
5423 if (file_view.has_url()) {
5424 is_url = true;
5425 } else {
5426 auto max_file_size = [&] {
5427 if (for_thumbnail) {
5428 return is_animated ? MAX_ANIMATED_THUMBNAIL_FILE_SIZE : MAX_THUMBNAIL_FILE_SIZE;
5429 } else {
5430 return is_animated ? MAX_ANIMATED_STICKER_FILE_SIZE : MAX_STICKER_FILE_SIZE;
5431 }
5432 }();
5433 if (file_view.has_local_location() && file_view.expected_size() > max_file_size) {
5434 return Status::Error(400, "File is too big");
5435 }
5436 is_local = true;
5437 }
5438 }
5439 return std::make_tuple(file_id, is_url, is_local, is_animated);
5440 }
5441
upload_sticker_file(UserId user_id,tl_object_ptr<td_api::InputSticker> && sticker,Promise<Unit> && promise)5442 FileId StickersManager::upload_sticker_file(UserId user_id, tl_object_ptr<td_api::InputSticker> &&sticker,
5443 Promise<Unit> &&promise) {
5444 bool is_bot = td_->auth_manager_->is_bot();
5445 if (!is_bot) {
5446 user_id = td_->contacts_manager_->get_my_id();
5447 }
5448
5449 auto input_user = td_->contacts_manager_->get_input_user(user_id);
5450 if (input_user == nullptr) {
5451 promise.set_error(Status::Error(400, "User not found"));
5452 return FileId();
5453 }
5454 DialogId dialog_id(user_id);
5455 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
5456 if (input_peer == nullptr) {
5457 promise.set_error(Status::Error(400, "Have no access to the user"));
5458 return FileId();
5459 }
5460
5461 auto r_file_id = prepare_input_sticker(sticker.get());
5462 if (r_file_id.is_error()) {
5463 promise.set_error(r_file_id.move_as_error());
5464 return FileId();
5465 }
5466 auto file_id = std::get<0>(r_file_id.ok());
5467 auto is_url = std::get<1>(r_file_id.ok());
5468 auto is_local = std::get<2>(r_file_id.ok());
5469
5470 if (is_url) {
5471 do_upload_sticker_file(user_id, file_id, nullptr, std::move(promise));
5472 } else if (is_local) {
5473 upload_sticker_file(user_id, file_id, std::move(promise));
5474 } else {
5475 promise.set_value(Unit());
5476 }
5477
5478 return file_id;
5479 }
5480
get_input_sticker(td_api::InputSticker * sticker,FileId file_id) const5481 tl_object_ptr<telegram_api::inputStickerSetItem> StickersManager::get_input_sticker(td_api::InputSticker *sticker,
5482 FileId file_id) const {
5483 CHECK(sticker != nullptr);
5484 FileView file_view = td_->file_manager_->get_file_view(file_id);
5485 CHECK(file_view.has_remote_location());
5486 auto input_document = file_view.main_remote_location().as_input_document();
5487
5488 tl_object_ptr<telegram_api::maskCoords> mask_coords;
5489 if (sticker->get_id() == td_api::inputStickerStatic::ID) {
5490 auto mask_position = static_cast<td_api::inputStickerStatic *>(sticker)->mask_position_.get();
5491 if (mask_position != nullptr && mask_position->point_ != nullptr) {
5492 auto point = [mask_point = std::move(mask_position->point_)] {
5493 switch (mask_point->get_id()) {
5494 case td_api::maskPointForehead::ID:
5495 return 0;
5496 case td_api::maskPointEyes::ID:
5497 return 1;
5498 case td_api::maskPointMouth::ID:
5499 return 2;
5500 case td_api::maskPointChin::ID:
5501 return 3;
5502 default:
5503 UNREACHABLE();
5504 return -1;
5505 }
5506 }();
5507 mask_coords = make_tl_object<telegram_api::maskCoords>(point, mask_position->x_shift_, mask_position->y_shift_,
5508 mask_position->scale_);
5509 }
5510 }
5511
5512 int32 flags = 0;
5513 if (mask_coords != nullptr) {
5514 flags |= telegram_api::inputStickerSetItem::MASK_COORDS_MASK;
5515 }
5516
5517 return make_tl_object<telegram_api::inputStickerSetItem>(flags, std::move(input_document),
5518 get_input_sticker_emojis(sticker), std::move(mask_coords));
5519 }
5520
get_suggested_sticker_set_name(string title,Promise<string> && promise)5521 void StickersManager::get_suggested_sticker_set_name(string title, Promise<string> &&promise) {
5522 title = strip_empty_characters(title, MAX_STICKER_SET_TITLE_LENGTH);
5523 if (title.empty()) {
5524 return promise.set_error(Status::Error(400, "Sticker set title can't be empty"));
5525 }
5526
5527 td_->create_handler<SuggestStickerSetShortNameQuery>(std::move(promise))->send(title);
5528 }
5529
check_sticker_set_name(const string & name,Promise<CheckStickerSetNameResult> && promise)5530 void StickersManager::check_sticker_set_name(const string &name, Promise<CheckStickerSetNameResult> &&promise) {
5531 if (name.empty()) {
5532 return promise.set_value(CheckStickerSetNameResult::Invalid);
5533 }
5534
5535 auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<bool> result) mutable {
5536 if (result.is_error()) {
5537 auto error = result.move_as_error();
5538 if (error.message() == "SHORT_NAME_INVALID") {
5539 return promise.set_value(CheckStickerSetNameResult::Invalid);
5540 }
5541 if (error.message() == "SHORT_NAME_OCCUPIED") {
5542 return promise.set_value(CheckStickerSetNameResult::Occupied);
5543 }
5544 return promise.set_error(std::move(error));
5545 }
5546
5547 promise.set_value(CheckStickerSetNameResult::Ok);
5548 });
5549
5550 return td_->create_handler<CheckStickerSetShortNameQuery>(std::move(request_promise))->send(name);
5551 }
5552
get_check_sticker_set_name_result_object(CheckStickerSetNameResult result)5553 td_api::object_ptr<td_api::CheckStickerSetNameResult> StickersManager::get_check_sticker_set_name_result_object(
5554 CheckStickerSetNameResult result) {
5555 switch (result) {
5556 case CheckStickerSetNameResult::Ok:
5557 return td_api::make_object<td_api::checkStickerSetNameResultOk>();
5558 case CheckStickerSetNameResult::Invalid:
5559 return td_api::make_object<td_api::checkStickerSetNameResultNameInvalid>();
5560 case CheckStickerSetNameResult::Occupied:
5561 return td_api::make_object<td_api::checkStickerSetNameResultNameOccupied>();
5562 default:
5563 UNREACHABLE();
5564 return nullptr;
5565 }
5566 }
5567
create_new_sticker_set(UserId user_id,string & title,string & short_name,bool is_masks,vector<tl_object_ptr<td_api::InputSticker>> && stickers,string software,Promise<Unit> && promise)5568 void StickersManager::create_new_sticker_set(UserId user_id, string &title, string &short_name, bool is_masks,
5569 vector<tl_object_ptr<td_api::InputSticker>> &&stickers, string software,
5570 Promise<Unit> &&promise) {
5571 bool is_bot = td_->auth_manager_->is_bot();
5572 if (!is_bot) {
5573 user_id = td_->contacts_manager_->get_my_id();
5574 }
5575 auto input_user = td_->contacts_manager_->get_input_user(user_id);
5576 if (input_user == nullptr) {
5577 return promise.set_error(Status::Error(400, "User not found"));
5578 }
5579 DialogId dialog_id(user_id);
5580 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
5581 if (input_peer == nullptr) {
5582 return promise.set_error(Status::Error(400, "Have no access to the user"));
5583 }
5584
5585 title = strip_empty_characters(title, MAX_STICKER_SET_TITLE_LENGTH);
5586 if (title.empty()) {
5587 return promise.set_error(Status::Error(400, "Sticker set title can't be empty"));
5588 }
5589
5590 short_name = strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH);
5591 if (short_name.empty()) {
5592 return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
5593 }
5594
5595 if (stickers.empty()) {
5596 return promise.set_error(Status::Error(400, "At least 1 sticker must be specified"));
5597 }
5598
5599 vector<FileId> file_ids;
5600 file_ids.reserve(stickers.size());
5601 vector<FileId> local_file_ids;
5602 vector<FileId> url_file_ids;
5603 size_t animated_sticker_count = 0;
5604 for (auto &sticker : stickers) {
5605 auto r_file_id = prepare_input_sticker(sticker.get());
5606 if (r_file_id.is_error()) {
5607 return promise.set_error(r_file_id.move_as_error());
5608 }
5609 auto file_id = std::get<0>(r_file_id.ok());
5610 auto is_url = std::get<1>(r_file_id.ok());
5611 auto is_local = std::get<2>(r_file_id.ok());
5612 auto is_animated = std::get<3>(r_file_id.ok());
5613 if (is_animated) {
5614 animated_sticker_count++;
5615 if (is_url) {
5616 return promise.set_error(Status::Error(400, "Animated stickers can't be uploaded by URL"));
5617 }
5618 }
5619
5620 file_ids.push_back(file_id);
5621 if (is_url) {
5622 url_file_ids.push_back(file_id);
5623 } else if (is_local) {
5624 local_file_ids.push_back(file_id);
5625 }
5626 }
5627 if (animated_sticker_count != stickers.size() && animated_sticker_count != 0) {
5628 return promise.set_error(Status::Error(400, "All stickers must be either animated or static"));
5629 }
5630 bool is_animated = animated_sticker_count == stickers.size();
5631
5632 auto pending_new_sticker_set = make_unique<PendingNewStickerSet>();
5633 pending_new_sticker_set->user_id = user_id;
5634 pending_new_sticker_set->title = std::move(title);
5635 pending_new_sticker_set->short_name = short_name;
5636 pending_new_sticker_set->is_masks = is_masks;
5637 pending_new_sticker_set->is_animated = is_animated;
5638 pending_new_sticker_set->file_ids = std::move(file_ids);
5639 pending_new_sticker_set->stickers = std::move(stickers);
5640 pending_new_sticker_set->software = std::move(software);
5641 pending_new_sticker_set->promise = std::move(promise);
5642
5643 auto &multipromise = pending_new_sticker_set->upload_files_multipromise;
5644
5645 int64 random_id;
5646 do {
5647 random_id = Random::secure_int64();
5648 } while (random_id == 0 || pending_new_sticker_sets_.find(random_id) != pending_new_sticker_sets_.end());
5649 pending_new_sticker_sets_[random_id] = std::move(pending_new_sticker_set);
5650
5651 multipromise.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), random_id](Result<Unit> result) {
5652 send_closure_later(actor_id, &StickersManager::on_new_stickers_uploaded, random_id, std::move(result));
5653 }));
5654 auto lock_promise = multipromise.get_promise();
5655
5656 for (auto file_id : url_file_ids) {
5657 do_upload_sticker_file(user_id, file_id, nullptr, multipromise.get_promise());
5658 }
5659
5660 for (auto file_id : local_file_ids) {
5661 upload_sticker_file(user_id, file_id, multipromise.get_promise());
5662 }
5663
5664 lock_promise.set_value(Unit());
5665 }
5666
upload_sticker_file(UserId user_id,FileId file_id,Promise<Unit> && promise)5667 void StickersManager::upload_sticker_file(UserId user_id, FileId file_id, Promise<Unit> &&promise) {
5668 FileId upload_file_id;
5669 if (td_->file_manager_->get_file_view(file_id).get_type() == FileType::Sticker) {
5670 CHECK(get_input_media(file_id, nullptr, nullptr, string()) == nullptr);
5671 upload_file_id = dup_sticker(td_->file_manager_->dup_file_id(file_id), file_id);
5672 } else {
5673 CHECK(td_->documents_manager_->get_input_media(file_id, nullptr, nullptr) == nullptr);
5674 upload_file_id = td_->documents_manager_->dup_document(td_->file_manager_->dup_file_id(file_id), file_id);
5675 }
5676
5677 being_uploaded_files_[upload_file_id] = {user_id, std::move(promise)};
5678 LOG(INFO) << "Ask to upload sticker file " << upload_file_id;
5679 td_->file_manager_->upload(upload_file_id, upload_sticker_file_callback_, 2, 0);
5680 }
5681
on_upload_sticker_file(FileId file_id,tl_object_ptr<telegram_api::InputFile> input_file)5682 void StickersManager::on_upload_sticker_file(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file) {
5683 LOG(INFO) << "Sticker file " << file_id << " has been uploaded";
5684
5685 auto it = being_uploaded_files_.find(file_id);
5686 CHECK(it != being_uploaded_files_.end());
5687
5688 auto user_id = it->second.first;
5689 auto promise = std::move(it->second.second);
5690
5691 being_uploaded_files_.erase(it);
5692
5693 do_upload_sticker_file(user_id, file_id, std::move(input_file), std::move(promise));
5694 }
5695
on_upload_sticker_file_error(FileId file_id,Status status)5696 void StickersManager::on_upload_sticker_file_error(FileId file_id, Status status) {
5697 if (G()->close_flag()) {
5698 // do not fail upload if closing
5699 return;
5700 }
5701
5702 LOG(WARNING) << "Sticker file " << file_id << " has upload error " << status;
5703 CHECK(status.is_error());
5704
5705 auto it = being_uploaded_files_.find(file_id);
5706 CHECK(it != being_uploaded_files_.end());
5707
5708 auto promise = std::move(it->second.second);
5709
5710 being_uploaded_files_.erase(it);
5711
5712 // TODO FILE_PART_X_MISSING support
5713
5714 promise.set_error(Status::Error(status.code() > 0 ? status.code() : 500,
5715 status.message())); // TODO CHECK that status has always a code
5716 }
5717
do_upload_sticker_file(UserId user_id,FileId file_id,tl_object_ptr<telegram_api::InputFile> && input_file,Promise<Unit> && promise)5718 void StickersManager::do_upload_sticker_file(UserId user_id, FileId file_id,
5719 tl_object_ptr<telegram_api::InputFile> &&input_file,
5720 Promise<Unit> &&promise) {
5721 DialogId dialog_id(user_id);
5722 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
5723 if (input_peer == nullptr) {
5724 return promise.set_error(Status::Error(400, "Have no access to the user"));
5725 }
5726
5727 FileView file_view = td_->file_manager_->get_file_view(file_id);
5728 bool is_animated = file_view.get_type() == FileType::Sticker;
5729
5730 bool had_input_file = input_file != nullptr;
5731 auto input_media = is_animated ? get_input_media(file_id, std::move(input_file), nullptr, string())
5732 : td_->documents_manager_->get_input_media(file_id, std::move(input_file), nullptr);
5733 CHECK(input_media != nullptr);
5734 if (had_input_file && !FileManager::extract_was_uploaded(input_media)) {
5735 // if we had InputFile, but has failed to use it, then we need to immediately cancel file upload
5736 // so the next upload with the same file can succeed
5737 td_->file_manager_->cancel_upload(file_id);
5738 }
5739
5740 td_->create_handler<UploadStickerFileQuery>(std::move(promise))
5741 ->send(std::move(input_peer), file_id, std::move(input_media));
5742 }
5743
on_uploaded_sticker_file(FileId file_id,tl_object_ptr<telegram_api::MessageMedia> media,Promise<Unit> && promise)5744 void StickersManager::on_uploaded_sticker_file(FileId file_id, tl_object_ptr<telegram_api::MessageMedia> media,
5745 Promise<Unit> &&promise) {
5746 CHECK(media != nullptr);
5747 LOG(INFO) << "Receive uploaded sticker file " << to_string(media);
5748 if (media->get_id() != telegram_api::messageMediaDocument::ID) {
5749 return promise.set_error(Status::Error(400, "Can't upload sticker file: wrong file type"));
5750 }
5751
5752 auto message_document = move_tl_object_as<telegram_api::messageMediaDocument>(media);
5753 auto document_ptr = std::move(message_document->document_);
5754 int32 document_id = document_ptr->get_id();
5755 if (document_id == telegram_api::documentEmpty::ID) {
5756 return promise.set_error(Status::Error(400, "Can't upload sticker file: empty file"));
5757 }
5758 CHECK(document_id == telegram_api::document::ID);
5759
5760 FileView file_view = td_->file_manager_->get_file_view(file_id);
5761 bool is_animated = file_view.get_type() == FileType::Sticker;
5762 auto expected_document_type = is_animated ? Document::Type::Sticker : Document::Type::General;
5763
5764 auto parsed_document = td_->documents_manager_->on_get_document(
5765 move_tl_object_as<telegram_api::document>(document_ptr), DialogId(), nullptr);
5766 if (parsed_document.type != expected_document_type) {
5767 return promise.set_error(Status::Error(400, "Wrong file type"));
5768 }
5769
5770 if (parsed_document.file_id != file_id) {
5771 if (is_animated) {
5772 merge_stickers(parsed_document.file_id, file_id, false);
5773 } else {
5774 // must not delete the old document, because the file_id could be used for simultaneous URL uploads
5775 td_->documents_manager_->merge_documents(parsed_document.file_id, file_id, false);
5776 }
5777 }
5778 promise.set_value(Unit());
5779 }
5780
on_new_stickers_uploaded(int64 random_id,Result<Unit> result)5781 void StickersManager::on_new_stickers_uploaded(int64 random_id, Result<Unit> result) {
5782 auto it = pending_new_sticker_sets_.find(random_id);
5783 CHECK(it != pending_new_sticker_sets_.end());
5784
5785 auto pending_new_sticker_set = std::move(it->second);
5786 CHECK(pending_new_sticker_set != nullptr);
5787
5788 pending_new_sticker_sets_.erase(it);
5789
5790 if (G()->close_flag()) {
5791 result = Global::request_aborted_error();
5792 }
5793 if (result.is_error()) {
5794 pending_new_sticker_set->promise.set_error(result.move_as_error());
5795 return;
5796 }
5797
5798 CHECK(pending_new_sticker_set->upload_files_multipromise.promise_count() == 0);
5799
5800 auto input_user = td_->contacts_manager_->get_input_user(pending_new_sticker_set->user_id);
5801 if (input_user == nullptr) {
5802 return pending_new_sticker_set->promise.set_error(Status::Error(400, "User not found"));
5803 }
5804
5805 bool is_masks = pending_new_sticker_set->is_masks;
5806 bool is_animated = pending_new_sticker_set->is_animated;
5807
5808 auto sticker_count = pending_new_sticker_set->stickers.size();
5809 vector<tl_object_ptr<telegram_api::inputStickerSetItem>> input_stickers;
5810 input_stickers.reserve(sticker_count);
5811 for (size_t i = 0; i < sticker_count; i++) {
5812 input_stickers.push_back(
5813 get_input_sticker(pending_new_sticker_set->stickers[i].get(), pending_new_sticker_set->file_ids[i]));
5814 }
5815
5816 td_->create_handler<CreateNewStickerSetQuery>(std::move(pending_new_sticker_set->promise))
5817 ->send(std::move(input_user), pending_new_sticker_set->title, pending_new_sticker_set->short_name, is_masks,
5818 is_animated, std::move(input_stickers), pending_new_sticker_set->software);
5819 }
5820
add_sticker_to_set(UserId user_id,string & short_name,tl_object_ptr<td_api::InputSticker> && sticker,Promise<Unit> && promise)5821 void StickersManager::add_sticker_to_set(UserId user_id, string &short_name,
5822 tl_object_ptr<td_api::InputSticker> &&sticker, Promise<Unit> &&promise) {
5823 auto input_user = td_->contacts_manager_->get_input_user(user_id);
5824 if (input_user == nullptr) {
5825 return promise.set_error(Status::Error(400, "User not found"));
5826 }
5827 DialogId dialog_id(user_id);
5828 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
5829 if (input_peer == nullptr) {
5830 return promise.set_error(Status::Error(400, "Have no access to the user"));
5831 }
5832
5833 short_name = strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH);
5834 if (short_name.empty()) {
5835 return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
5836 }
5837
5838 auto r_file_id = prepare_input_sticker(sticker.get());
5839 if (r_file_id.is_error()) {
5840 return promise.set_error(r_file_id.move_as_error());
5841 }
5842 auto file_id = std::get<0>(r_file_id.ok());
5843 auto is_url = std::get<1>(r_file_id.ok());
5844 auto is_local = std::get<2>(r_file_id.ok());
5845
5846 auto pending_add_sticker_to_set = make_unique<PendingAddStickerToSet>();
5847 pending_add_sticker_to_set->short_name = short_name;
5848 pending_add_sticker_to_set->file_id = file_id;
5849 pending_add_sticker_to_set->sticker = std::move(sticker);
5850 pending_add_sticker_to_set->promise = std::move(promise);
5851
5852 int64 random_id;
5853 do {
5854 random_id = Random::secure_int64();
5855 } while (random_id == 0 || pending_add_sticker_to_sets_.find(random_id) != pending_add_sticker_to_sets_.end());
5856 pending_add_sticker_to_sets_[random_id] = std::move(pending_add_sticker_to_set);
5857
5858 auto on_upload_promise = PromiseCreator::lambda([random_id](Result<Unit> result) {
5859 send_closure(G()->stickers_manager(), &StickersManager::on_added_sticker_uploaded, random_id, std::move(result));
5860 });
5861
5862 if (is_url) {
5863 do_upload_sticker_file(user_id, file_id, nullptr, std::move(on_upload_promise));
5864 } else if (is_local) {
5865 upload_sticker_file(user_id, file_id, std::move(on_upload_promise));
5866 } else {
5867 on_upload_promise.set_value(Unit());
5868 }
5869 }
5870
on_added_sticker_uploaded(int64 random_id,Result<Unit> result)5871 void StickersManager::on_added_sticker_uploaded(int64 random_id, Result<Unit> result) {
5872 auto it = pending_add_sticker_to_sets_.find(random_id);
5873 CHECK(it != pending_add_sticker_to_sets_.end());
5874
5875 auto pending_add_sticker_to_set = std::move(it->second);
5876 CHECK(pending_add_sticker_to_set != nullptr);
5877
5878 pending_add_sticker_to_sets_.erase(it);
5879
5880 if (result.is_error()) {
5881 pending_add_sticker_to_set->promise.set_error(result.move_as_error());
5882 return;
5883 }
5884
5885 td_->create_handler<AddStickerToSetQuery>(std::move(pending_add_sticker_to_set->promise))
5886 ->send(pending_add_sticker_to_set->short_name,
5887 get_input_sticker(pending_add_sticker_to_set->sticker.get(), pending_add_sticker_to_set->file_id));
5888 }
5889
set_sticker_set_thumbnail(UserId user_id,string & short_name,tl_object_ptr<td_api::InputFile> && thumbnail,Promise<Unit> && promise)5890 void StickersManager::set_sticker_set_thumbnail(UserId user_id, string &short_name,
5891 tl_object_ptr<td_api::InputFile> &&thumbnail, Promise<Unit> &&promise) {
5892 auto input_user = td_->contacts_manager_->get_input_user(user_id);
5893 if (input_user == nullptr) {
5894 return promise.set_error(Status::Error(400, "User not found"));
5895 }
5896 DialogId dialog_id(user_id);
5897 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
5898 if (input_peer == nullptr) {
5899 return promise.set_error(Status::Error(400, "Have no access to the user"));
5900 }
5901
5902 short_name = clean_username(strip_empty_characters(short_name, MAX_STICKER_SET_SHORT_NAME_LENGTH));
5903 if (short_name.empty()) {
5904 return promise.set_error(Status::Error(400, "Sticker set name can't be empty"));
5905 }
5906
5907 auto it = short_name_to_sticker_set_id_.find(short_name);
5908 const StickerSet *sticker_set = it == short_name_to_sticker_set_id_.end() ? nullptr : get_sticker_set(it->second);
5909 if (sticker_set != nullptr && sticker_set->was_loaded) {
5910 return do_set_sticker_set_thumbnail(user_id, short_name, std::move(thumbnail), std::move(promise));
5911 }
5912
5913 do_reload_sticker_set(
5914 StickerSetId(), make_tl_object<telegram_api::inputStickerSetShortName>(short_name), 0,
5915 PromiseCreator::lambda([actor_id = actor_id(this), user_id, short_name, thumbnail = std::move(thumbnail),
5916 promise = std::move(promise)](Result<Unit> result) mutable {
5917 if (result.is_error()) {
5918 promise.set_error(result.move_as_error());
5919 } else {
5920 send_closure(actor_id, &StickersManager::do_set_sticker_set_thumbnail, user_id, std::move(short_name),
5921 std::move(thumbnail), std::move(promise));
5922 }
5923 }));
5924 }
5925
do_set_sticker_set_thumbnail(UserId user_id,string short_name,tl_object_ptr<td_api::InputFile> && thumbnail,Promise<Unit> && promise)5926 void StickersManager::do_set_sticker_set_thumbnail(UserId user_id, string short_name,
5927 tl_object_ptr<td_api::InputFile> &&thumbnail,
5928 Promise<Unit> &&promise) {
5929 TRY_STATUS_PROMISE(promise, G()->close_status());
5930
5931 auto it = short_name_to_sticker_set_id_.find(short_name);
5932 const StickerSet *sticker_set = it == short_name_to_sticker_set_id_.end() ? nullptr : get_sticker_set(it->second);
5933 if (sticker_set == nullptr || !sticker_set->was_loaded) {
5934 return promise.set_error(Status::Error(400, "Sticker set not found"));
5935 }
5936
5937 auto r_file_id = prepare_input_file(thumbnail, sticker_set->is_animated, true);
5938 if (r_file_id.is_error()) {
5939 return promise.set_error(r_file_id.move_as_error());
5940 }
5941 auto file_id = std::get<0>(r_file_id.ok());
5942 auto is_url = std::get<1>(r_file_id.ok());
5943 auto is_local = std::get<2>(r_file_id.ok());
5944
5945 if (!file_id.is_valid()) {
5946 td_->create_handler<SetStickerSetThumbnailQuery>(std::move(promise))
5947 ->send(short_name, telegram_api::make_object<telegram_api::inputDocumentEmpty>());
5948 return;
5949 }
5950
5951 auto pending_set_sticker_set_thumbnail = make_unique<PendingSetStickerSetThumbnail>();
5952 pending_set_sticker_set_thumbnail->short_name = short_name;
5953 pending_set_sticker_set_thumbnail->file_id = file_id;
5954 pending_set_sticker_set_thumbnail->promise = std::move(promise);
5955
5956 int64 random_id;
5957 do {
5958 random_id = Random::secure_int64();
5959 } while (random_id == 0 ||
5960 pending_set_sticker_set_thumbnails_.find(random_id) != pending_set_sticker_set_thumbnails_.end());
5961 pending_set_sticker_set_thumbnails_[random_id] = std::move(pending_set_sticker_set_thumbnail);
5962
5963 auto on_upload_promise = PromiseCreator::lambda([random_id](Result<Unit> result) {
5964 send_closure(G()->stickers_manager(), &StickersManager::on_sticker_set_thumbnail_uploaded, random_id,
5965 std::move(result));
5966 });
5967
5968 if (is_url) {
5969 do_upload_sticker_file(user_id, file_id, nullptr, std::move(on_upload_promise));
5970 } else if (is_local) {
5971 upload_sticker_file(user_id, file_id, std::move(on_upload_promise));
5972 } else {
5973 on_upload_promise.set_value(Unit());
5974 }
5975 }
5976
on_sticker_set_thumbnail_uploaded(int64 random_id,Result<Unit> result)5977 void StickersManager::on_sticker_set_thumbnail_uploaded(int64 random_id, Result<Unit> result) {
5978 auto it = pending_set_sticker_set_thumbnails_.find(random_id);
5979 CHECK(it != pending_set_sticker_set_thumbnails_.end());
5980
5981 auto pending_set_sticker_set_thumbnail = std::move(it->second);
5982 CHECK(pending_set_sticker_set_thumbnail != nullptr);
5983
5984 pending_set_sticker_set_thumbnails_.erase(it);
5985
5986 if (result.is_error()) {
5987 pending_set_sticker_set_thumbnail->promise.set_error(result.move_as_error());
5988 return;
5989 }
5990
5991 FileView file_view = td_->file_manager_->get_file_view(pending_set_sticker_set_thumbnail->file_id);
5992 CHECK(file_view.has_remote_location());
5993
5994 td_->create_handler<SetStickerSetThumbnailQuery>(std::move(pending_set_sticker_set_thumbnail->promise))
5995 ->send(pending_set_sticker_set_thumbnail->short_name, file_view.main_remote_location().as_input_document());
5996 }
5997
set_sticker_position_in_set(const tl_object_ptr<td_api::InputFile> & sticker,int32 position,Promise<Unit> && promise)5998 void StickersManager::set_sticker_position_in_set(const tl_object_ptr<td_api::InputFile> &sticker, int32 position,
5999 Promise<Unit> &&promise) {
6000 if (position < 0) {
6001 return promise.set_error(Status::Error(400, "Wrong sticker position specified"));
6002 }
6003
6004 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, sticker, DialogId(), false, false);
6005 if (r_file_id.is_error()) {
6006 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6007 }
6008
6009 auto file_id = r_file_id.move_as_ok();
6010 auto file_view = td_->file_manager_->get_file_view(file_id);
6011 if (!file_view.has_remote_location() || !file_view.main_remote_location().is_document() ||
6012 file_view.main_remote_location().is_web()) {
6013 return promise.set_error(Status::Error(400, "Wrong sticker file specified"));
6014 }
6015
6016 td_->create_handler<SetStickerPositionQuery>(std::move(promise))
6017 ->send(file_view.main_remote_location().as_input_document(), position);
6018 }
6019
remove_sticker_from_set(const tl_object_ptr<td_api::InputFile> & sticker,Promise<Unit> && promise)6020 void StickersManager::remove_sticker_from_set(const tl_object_ptr<td_api::InputFile> &sticker,
6021 Promise<Unit> &&promise) {
6022 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, sticker, DialogId(), false, false);
6023 if (r_file_id.is_error()) {
6024 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6025 }
6026
6027 auto file_id = r_file_id.move_as_ok();
6028 auto file_view = td_->file_manager_->get_file_view(file_id);
6029 if (!file_view.has_remote_location() || !file_view.main_remote_location().is_document() ||
6030 file_view.main_remote_location().is_web()) {
6031 return promise.set_error(Status::Error(400, "Wrong sticker file specified"));
6032 }
6033
6034 td_->create_handler<DeleteStickerFromSetQuery>(std::move(promise))
6035 ->send(file_view.main_remote_location().as_input_document());
6036 }
6037
get_attached_sticker_file_ids(const vector<int32> & int_file_ids)6038 vector<FileId> StickersManager::get_attached_sticker_file_ids(const vector<int32> &int_file_ids) {
6039 vector<FileId> result;
6040
6041 result.reserve(int_file_ids.size());
6042 for (auto int_file_id : int_file_ids) {
6043 FileId file_id(int_file_id, 0);
6044 const Sticker *s = get_sticker(file_id);
6045 if (s == nullptr) {
6046 LOG(WARNING) << "Can't find sticker " << file_id;
6047 continue;
6048 }
6049 if (!s->set_id.is_valid()) {
6050 // only stickers from sticker sets can be attached to files
6051 continue;
6052 }
6053
6054 auto file_view = td_->file_manager_->get_file_view(file_id);
6055 CHECK(!file_view.empty());
6056 if (!file_view.has_remote_location()) {
6057 LOG(ERROR) << "Sticker " << file_id << " has no remote location";
6058 continue;
6059 }
6060 if (file_view.remote_location().is_web()) {
6061 LOG(ERROR) << "Sticker " << file_id << " is web";
6062 continue;
6063 }
6064 if (!file_view.remote_location().is_document()) {
6065 LOG(ERROR) << "Sticker " << file_id << " is encrypted";
6066 continue;
6067 }
6068 result.push_back(file_id);
6069
6070 if (!td_->auth_manager_->is_bot()) {
6071 add_recent_sticker_by_id(true, file_id);
6072 }
6073 }
6074
6075 return result;
6076 }
6077
get_sticker_sets_hash(const vector<StickerSetId> & sticker_set_ids) const6078 int64 StickersManager::get_sticker_sets_hash(const vector<StickerSetId> &sticker_set_ids) const {
6079 vector<uint64> numbers;
6080 numbers.reserve(sticker_set_ids.size());
6081 for (auto sticker_set_id : sticker_set_ids) {
6082 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
6083 CHECK(sticker_set != nullptr);
6084 CHECK(sticker_set->is_inited);
6085 numbers.push_back(sticker_set->hash);
6086 }
6087 return get_vector_hash(numbers);
6088 }
6089
get_featured_sticker_sets_hash() const6090 int64 StickersManager::get_featured_sticker_sets_hash() const {
6091 vector<uint64> numbers;
6092 numbers.reserve(featured_sticker_set_ids_.size() * 2);
6093 for (auto sticker_set_id : featured_sticker_set_ids_) {
6094 const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
6095 CHECK(sticker_set != nullptr);
6096 CHECK(sticker_set->is_inited);
6097
6098 numbers.push_back(sticker_set_id.get());
6099
6100 if (!sticker_set->is_viewed) {
6101 numbers.push_back(1);
6102 }
6103 }
6104 return get_vector_hash(numbers);
6105 }
6106
convert_sticker_set_ids(const vector<StickerSetId> & sticker_set_ids)6107 vector<int64> StickersManager::convert_sticker_set_ids(const vector<StickerSetId> &sticker_set_ids) {
6108 return transform(sticker_set_ids, [](StickerSetId sticker_set_id) { return sticker_set_id.get(); });
6109 }
6110
convert_sticker_set_ids(const vector<int64> & sticker_set_ids)6111 vector<StickerSetId> StickersManager::convert_sticker_set_ids(const vector<int64> &sticker_set_ids) {
6112 return transform(sticker_set_ids, [](int64 sticker_set_id) { return StickerSetId(sticker_set_id); });
6113 }
6114
get_update_installed_sticker_sets_object(int is_masks) const6115 td_api::object_ptr<td_api::updateInstalledStickerSets> StickersManager::get_update_installed_sticker_sets_object(
6116 int is_masks) const {
6117 return td_api::make_object<td_api::updateInstalledStickerSets>(
6118 is_masks != 0, convert_sticker_set_ids(installed_sticker_set_ids_[is_masks]));
6119 }
6120
send_update_installed_sticker_sets(bool from_database)6121 void StickersManager::send_update_installed_sticker_sets(bool from_database) {
6122 for (int is_masks = 0; is_masks < 2; is_masks++) {
6123 if (need_update_installed_sticker_sets_[is_masks]) {
6124 need_update_installed_sticker_sets_[is_masks] = false;
6125 if (are_installed_sticker_sets_loaded_[is_masks]) {
6126 installed_sticker_sets_hash_[is_masks] = get_sticker_sets_hash(installed_sticker_set_ids_[is_masks]);
6127 send_closure(G()->td(), &Td::send_update, get_update_installed_sticker_sets_object(is_masks));
6128
6129 if (G()->parameters().use_file_db && !from_database && !G()->close_flag()) {
6130 LOG(INFO) << "Save installed " << (is_masks ? "mask " : "") << "sticker sets to database";
6131 StickerSetListLogEvent log_event(installed_sticker_set_ids_[is_masks]);
6132 G()->td_db()->get_sqlite_pmc()->set(is_masks ? "sss1" : "sss0", log_event_store(log_event).as_slice().str(),
6133 Auto());
6134 }
6135 }
6136 }
6137 }
6138 }
6139
get_update_trending_sticker_sets_object() const6140 td_api::object_ptr<td_api::updateTrendingStickerSets> StickersManager::get_update_trending_sticker_sets_object() const {
6141 auto total_count = static_cast<int32>(featured_sticker_set_ids_.size()) +
6142 (old_featured_sticker_set_count_ == -1 ? 1 : old_featured_sticker_set_count_);
6143 return td_api::make_object<td_api::updateTrendingStickerSets>(
6144 get_sticker_sets_object(total_count, featured_sticker_set_ids_, 5));
6145 }
6146
send_update_featured_sticker_sets()6147 void StickersManager::send_update_featured_sticker_sets() {
6148 if (need_update_featured_sticker_sets_) {
6149 need_update_featured_sticker_sets_ = false;
6150 featured_sticker_sets_hash_ = get_featured_sticker_sets_hash();
6151
6152 send_closure(G()->td(), &Td::send_update, get_update_trending_sticker_sets_object());
6153 }
6154 }
6155
reload_recent_stickers(bool is_attached,bool force)6156 void StickersManager::reload_recent_stickers(bool is_attached, bool force) {
6157 if (G()->close_flag()) {
6158 return;
6159 }
6160
6161 auto &next_load_time = next_recent_stickers_load_time_[is_attached];
6162 if (!td_->auth_manager_->is_bot() && next_load_time >= 0 && (next_load_time < Time::now() || force)) {
6163 LOG_IF(INFO, force) << "Reload recent " << (is_attached ? "attached " : "") << "stickers";
6164 next_load_time = -1;
6165 td_->create_handler<GetRecentStickersQuery>()->send(false, is_attached, recent_stickers_hash_[is_attached]);
6166 }
6167 }
6168
repair_recent_stickers(bool is_attached,Promise<Unit> && promise)6169 void StickersManager::repair_recent_stickers(bool is_attached, Promise<Unit> &&promise) {
6170 if (td_->auth_manager_->is_bot()) {
6171 return promise.set_error(Status::Error(400, "Bots have no recent stickers"));
6172 }
6173
6174 repair_recent_stickers_queries_[is_attached].push_back(std::move(promise));
6175 if (repair_recent_stickers_queries_[is_attached].size() == 1u) {
6176 td_->create_handler<GetRecentStickersQuery>()->send(true, is_attached, 0);
6177 }
6178 }
6179
get_recent_stickers(bool is_attached,Promise<Unit> && promise)6180 vector<FileId> StickersManager::get_recent_stickers(bool is_attached, Promise<Unit> &&promise) {
6181 if (!are_recent_stickers_loaded_[is_attached]) {
6182 load_recent_stickers(is_attached, std::move(promise));
6183 return {};
6184 }
6185 reload_recent_stickers(is_attached, false);
6186
6187 promise.set_value(Unit());
6188 return recent_sticker_ids_[is_attached];
6189 }
6190
load_recent_stickers(bool is_attached,Promise<Unit> && promise)6191 void StickersManager::load_recent_stickers(bool is_attached, Promise<Unit> &&promise) {
6192 if (td_->auth_manager_->is_bot()) {
6193 are_recent_stickers_loaded_[is_attached] = true;
6194 }
6195 if (are_recent_stickers_loaded_[is_attached]) {
6196 promise.set_value(Unit());
6197 return;
6198 }
6199 load_recent_stickers_queries_[is_attached].push_back(std::move(promise));
6200 if (load_recent_stickers_queries_[is_attached].size() == 1u) {
6201 if (G()->parameters().use_file_db) {
6202 LOG(INFO) << "Trying to load recent " << (is_attached ? "attached " : "") << "stickers from database";
6203 G()->td_db()->get_sqlite_pmc()->get(
6204 is_attached ? "ssr1" : "ssr0", PromiseCreator::lambda([is_attached](string value) {
6205 send_closure(G()->stickers_manager(), &StickersManager::on_load_recent_stickers_from_database, is_attached,
6206 std::move(value));
6207 }));
6208 } else {
6209 LOG(INFO) << "Trying to load recent " << (is_attached ? "attached " : "") << "stickers from server";
6210 reload_recent_stickers(is_attached, true);
6211 }
6212 }
6213 }
6214
on_load_recent_stickers_from_database(bool is_attached,string value)6215 void StickersManager::on_load_recent_stickers_from_database(bool is_attached, string value) {
6216 if (G()->close_flag()) {
6217 return;
6218 }
6219 if (value.empty()) {
6220 LOG(INFO) << "Recent " << (is_attached ? "attached " : "") << "stickers aren't found in database";
6221 reload_recent_stickers(is_attached, true);
6222 return;
6223 }
6224
6225 LOG(INFO) << "Successfully loaded recent " << (is_attached ? "attached " : "") << "stickers list of size "
6226 << value.size() << " from database";
6227
6228 StickerListLogEvent log_event;
6229 auto status = log_event_parse(log_event, value);
6230 if (status.is_error()) {
6231 // can't happen unless database is broken, but has been seen in the wild
6232 LOG(ERROR) << "Can't load recent stickers: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
6233 return reload_recent_stickers(is_attached, true);
6234 }
6235
6236 on_load_recent_stickers_finished(is_attached, std::move(log_event.sticker_ids), true);
6237 }
6238
on_load_recent_stickers_finished(bool is_attached,vector<FileId> && recent_sticker_ids,bool from_database)6239 void StickersManager::on_load_recent_stickers_finished(bool is_attached, vector<FileId> &&recent_sticker_ids,
6240 bool from_database) {
6241 if (static_cast<int32>(recent_sticker_ids.size()) > recent_stickers_limit_) {
6242 recent_sticker_ids.resize(recent_stickers_limit_);
6243 }
6244 recent_sticker_ids_[is_attached] = std::move(recent_sticker_ids);
6245 are_recent_stickers_loaded_[is_attached] = true;
6246 send_update_recent_stickers(is_attached, from_database);
6247 auto promises = std::move(load_recent_stickers_queries_[is_attached]);
6248 load_recent_stickers_queries_[is_attached].clear();
6249 for (auto &promise : promises) {
6250 promise.set_value(Unit());
6251 }
6252 }
6253
on_get_recent_stickers(bool is_repair,bool is_attached,tl_object_ptr<telegram_api::messages_RecentStickers> && stickers_ptr)6254 void StickersManager::on_get_recent_stickers(bool is_repair, bool is_attached,
6255 tl_object_ptr<telegram_api::messages_RecentStickers> &&stickers_ptr) {
6256 CHECK(!td_->auth_manager_->is_bot());
6257 if (!is_repair) {
6258 next_recent_stickers_load_time_[is_attached] = Time::now_cached() + Random::fast(30 * 60, 50 * 60);
6259 }
6260
6261 CHECK(stickers_ptr != nullptr);
6262 int32 constructor_id = stickers_ptr->get_id();
6263 if (constructor_id == telegram_api::messages_recentStickersNotModified::ID) {
6264 if (is_repair) {
6265 return on_get_recent_stickers_failed(true, is_attached, Status::Error(500, "Failed to reload recent stickers"));
6266 }
6267 LOG(INFO) << (is_attached ? "Attached r" : "R") << "ecent stickers are not modified";
6268 return;
6269 }
6270 CHECK(constructor_id == telegram_api::messages_recentStickers::ID);
6271 auto stickers = move_tl_object_as<telegram_api::messages_recentStickers>(stickers_ptr);
6272
6273 vector<FileId> recent_sticker_ids;
6274 recent_sticker_ids.reserve(stickers->stickers_.size());
6275 for (auto &document_ptr : stickers->stickers_) {
6276 auto sticker_id = on_get_sticker_document(std::move(document_ptr)).second;
6277 if (!sticker_id.is_valid()) {
6278 continue;
6279 }
6280 recent_sticker_ids.push_back(sticker_id);
6281 }
6282
6283 if (is_repair) {
6284 auto promises = std::move(repair_recent_stickers_queries_[is_attached]);
6285 repair_recent_stickers_queries_[is_attached].clear();
6286 for (auto &promise : promises) {
6287 promise.set_value(Unit());
6288 }
6289 } else {
6290 on_load_recent_stickers_finished(is_attached, std::move(recent_sticker_ids));
6291
6292 LOG_IF(ERROR, recent_stickers_hash_[is_attached] != stickers->hash_) << "Stickers hash mismatch";
6293 }
6294 }
6295
on_get_recent_stickers_failed(bool is_repair,bool is_attached,Status error)6296 void StickersManager::on_get_recent_stickers_failed(bool is_repair, bool is_attached, Status error) {
6297 CHECK(error.is_error());
6298 if (!is_repair) {
6299 next_recent_stickers_load_time_[is_attached] = Time::now_cached() + Random::fast(5, 10);
6300 }
6301 auto &queries = is_repair ? repair_recent_stickers_queries_[is_attached] : load_recent_stickers_queries_[is_attached];
6302 auto promises = std::move(queries);
6303 queries.clear();
6304 for (auto &promise : promises) {
6305 promise.set_error(error.clone());
6306 }
6307 }
6308
get_recent_stickers_hash(const vector<FileId> & sticker_ids) const6309 int64 StickersManager::get_recent_stickers_hash(const vector<FileId> &sticker_ids) const {
6310 vector<uint64> numbers;
6311 numbers.reserve(sticker_ids.size());
6312 for (auto sticker_id : sticker_ids) {
6313 auto sticker = get_sticker(sticker_id);
6314 CHECK(sticker != nullptr);
6315 auto file_view = td_->file_manager_->get_file_view(sticker_id);
6316 CHECK(file_view.has_remote_location());
6317 if (!file_view.remote_location().is_document()) {
6318 LOG(ERROR) << "Recent sticker remote location is not document: " << file_view.remote_location();
6319 continue;
6320 }
6321 numbers.push_back(file_view.remote_location().get_id());
6322 }
6323 return get_vector_hash(numbers);
6324 }
6325
get_recent_stickers_file_source_id(int is_attached)6326 FileSourceId StickersManager::get_recent_stickers_file_source_id(int is_attached) {
6327 if (!recent_stickers_file_source_id_[is_attached].is_valid()) {
6328 recent_stickers_file_source_id_[is_attached] =
6329 td_->file_reference_manager_->create_recent_stickers_file_source(is_attached != 0);
6330 }
6331 return recent_stickers_file_source_id_[is_attached];
6332 }
6333
add_recent_sticker(bool is_attached,const tl_object_ptr<td_api::InputFile> & input_file,Promise<Unit> && promise)6334 void StickersManager::add_recent_sticker(bool is_attached, const tl_object_ptr<td_api::InputFile> &input_file,
6335 Promise<Unit> &&promise) {
6336 if (!are_recent_stickers_loaded_[is_attached]) {
6337 load_recent_stickers(is_attached, std::move(promise));
6338 return;
6339 }
6340
6341 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, input_file, DialogId(), false, false);
6342 if (r_file_id.is_error()) {
6343 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6344 }
6345
6346 add_recent_sticker_impl(is_attached, r_file_id.ok(), true, std::move(promise));
6347 }
6348
send_save_recent_sticker_query(bool is_attached,FileId sticker_id,bool unsave,Promise<Unit> && promise)6349 void StickersManager::send_save_recent_sticker_query(bool is_attached, FileId sticker_id, bool unsave,
6350 Promise<Unit> &&promise) {
6351 TRY_STATUS_PROMISE(promise, G()->close_status());
6352
6353 // TODO invokeAfter and log event
6354 auto file_view = td_->file_manager_->get_file_view(sticker_id);
6355 CHECK(file_view.has_remote_location());
6356 CHECK(file_view.remote_location().is_document());
6357 CHECK(!file_view.remote_location().is_web());
6358 td_->create_handler<SaveRecentStickerQuery>(std::move(promise))
6359 ->send(is_attached, sticker_id, file_view.remote_location().as_input_document(), unsave);
6360 }
6361
add_recent_sticker_by_id(bool is_attached,FileId sticker_id)6362 void StickersManager::add_recent_sticker_by_id(bool is_attached, FileId sticker_id) {
6363 // TODO log event
6364 add_recent_sticker_impl(is_attached, sticker_id, false, Auto());
6365 }
6366
add_recent_sticker_impl(bool is_attached,FileId sticker_id,bool add_on_server,Promise<Unit> && promise)6367 void StickersManager::add_recent_sticker_impl(bool is_attached, FileId sticker_id, bool add_on_server,
6368 Promise<Unit> &&promise) {
6369 CHECK(!td_->auth_manager_->is_bot());
6370
6371 LOG(INFO) << "Add recent " << (is_attached ? "attached " : "") << "sticker " << sticker_id;
6372 if (!are_recent_stickers_loaded_[is_attached]) {
6373 load_recent_stickers(is_attached, PromiseCreator::lambda([is_attached, sticker_id, add_on_server,
6374 promise = std::move(promise)](Result<> result) mutable {
6375 if (result.is_ok()) {
6376 send_closure(G()->stickers_manager(), &StickersManager::add_recent_sticker_impl,
6377 is_attached, sticker_id, add_on_server, std::move(promise));
6378 } else {
6379 promise.set_error(result.move_as_error());
6380 }
6381 }));
6382 return;
6383 }
6384
6385 auto is_equal = [sticker_id](FileId file_id) {
6386 return file_id == sticker_id || (file_id.get_remote() == sticker_id.get_remote() && sticker_id.get_remote() != 0);
6387 };
6388
6389 vector<FileId> &sticker_ids = recent_sticker_ids_[is_attached];
6390 if (!sticker_ids.empty() && is_equal(sticker_ids[0])) {
6391 if (sticker_ids[0].get_remote() == 0 && sticker_id.get_remote() != 0) {
6392 sticker_ids[0] = sticker_id;
6393 save_recent_stickers_to_database(is_attached);
6394 }
6395
6396 return promise.set_value(Unit());
6397 }
6398
6399 auto sticker = get_sticker(sticker_id);
6400 if (sticker == nullptr) {
6401 return promise.set_error(Status::Error(400, "Sticker not found"));
6402 }
6403 if (!sticker->set_id.is_valid()) {
6404 return promise.set_error(Status::Error(400, "Stickers without sticker set can't be added to recent"));
6405 }
6406
6407 auto file_view = td_->file_manager_->get_file_view(sticker_id);
6408 if (!file_view.has_remote_location()) {
6409 return promise.set_error(Status::Error(400, "Can save only sent stickers"));
6410 }
6411 if (file_view.remote_location().is_web()) {
6412 return promise.set_error(Status::Error(400, "Can't save web stickers"));
6413 }
6414 if (!file_view.remote_location().is_document()) {
6415 return promise.set_error(Status::Error(400, "Can't save encrypted stickers"));
6416 }
6417
6418 auto it = std::find_if(sticker_ids.begin(), sticker_ids.end(), is_equal);
6419 if (it == sticker_ids.end()) {
6420 if (static_cast<int32>(sticker_ids.size()) == recent_stickers_limit_) {
6421 sticker_ids.back() = sticker_id;
6422 } else {
6423 sticker_ids.push_back(sticker_id);
6424 }
6425 it = sticker_ids.end() - 1;
6426 }
6427 std::rotate(sticker_ids.begin(), it, it + 1);
6428 if (sticker_ids[0].get_remote() == 0 && sticker_id.get_remote() != 0) {
6429 sticker_ids[0] = sticker_id;
6430 }
6431
6432 send_update_recent_stickers(is_attached);
6433 if (add_on_server) {
6434 send_save_recent_sticker_query(is_attached, sticker_id, false, std::move(promise));
6435 }
6436 }
6437
remove_recent_sticker(bool is_attached,const tl_object_ptr<td_api::InputFile> & input_file,Promise<Unit> && promise)6438 void StickersManager::remove_recent_sticker(bool is_attached, const tl_object_ptr<td_api::InputFile> &input_file,
6439 Promise<Unit> &&promise) {
6440 if (!are_recent_stickers_loaded_[is_attached]) {
6441 load_recent_stickers(is_attached, std::move(promise));
6442 return;
6443 }
6444
6445 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, input_file, DialogId(), false, false);
6446 if (r_file_id.is_error()) {
6447 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6448 }
6449
6450 vector<FileId> &sticker_ids = recent_sticker_ids_[is_attached];
6451 FileId file_id = r_file_id.ok();
6452 if (!td::remove(sticker_ids, file_id)) {
6453 return promise.set_value(Unit());
6454 }
6455
6456 auto sticker = get_sticker(file_id);
6457 if (sticker == nullptr) {
6458 return promise.set_error(Status::Error(400, "Sticker not found"));
6459 }
6460
6461 send_save_recent_sticker_query(is_attached, file_id, true, std::move(promise));
6462
6463 send_update_recent_stickers(is_attached);
6464 }
6465
clear_recent_stickers(bool is_attached,Promise<Unit> && promise)6466 void StickersManager::clear_recent_stickers(bool is_attached, Promise<Unit> &&promise) {
6467 if (!are_recent_stickers_loaded_[is_attached]) {
6468 load_recent_stickers(is_attached, std::move(promise));
6469 return;
6470 }
6471
6472 vector<FileId> &sticker_ids = recent_sticker_ids_[is_attached];
6473 if (sticker_ids.empty()) {
6474 return promise.set_value(Unit());
6475 }
6476
6477 // TODO invokeAfter
6478 td_->create_handler<ClearRecentStickersQuery>(std::move(promise))->send(is_attached);
6479
6480 sticker_ids.clear();
6481
6482 send_update_recent_stickers(is_attached);
6483 }
6484
get_update_recent_stickers_object(int is_attached) const6485 td_api::object_ptr<td_api::updateRecentStickers> StickersManager::get_update_recent_stickers_object(
6486 int is_attached) const {
6487 return td_api::make_object<td_api::updateRecentStickers>(
6488 is_attached != 0, td_->file_manager_->get_file_ids_object(recent_sticker_ids_[is_attached]));
6489 }
6490
send_update_recent_stickers(bool is_attached,bool from_database)6491 void StickersManager::send_update_recent_stickers(bool is_attached, bool from_database) {
6492 if (!are_recent_stickers_loaded_[is_attached]) {
6493 return;
6494 }
6495
6496 vector<FileId> new_recent_sticker_file_ids;
6497 for (auto &sticker_id : recent_sticker_ids_[is_attached]) {
6498 append(new_recent_sticker_file_ids, get_sticker_file_ids(sticker_id));
6499 }
6500 std::sort(new_recent_sticker_file_ids.begin(), new_recent_sticker_file_ids.end());
6501 if (new_recent_sticker_file_ids != recent_sticker_file_ids_[is_attached]) {
6502 td_->file_manager_->change_files_source(get_recent_stickers_file_source_id(is_attached),
6503 recent_sticker_file_ids_[is_attached], new_recent_sticker_file_ids);
6504 recent_sticker_file_ids_[is_attached] = std::move(new_recent_sticker_file_ids);
6505 }
6506
6507 recent_stickers_hash_[is_attached] = get_recent_stickers_hash(recent_sticker_ids_[is_attached]);
6508 send_closure(G()->td(), &Td::send_update, get_update_recent_stickers_object(is_attached));
6509
6510 if (!from_database) {
6511 save_recent_stickers_to_database(is_attached != 0);
6512 }
6513 }
6514
save_recent_stickers_to_database(bool is_attached)6515 void StickersManager::save_recent_stickers_to_database(bool is_attached) {
6516 if (G()->parameters().use_file_db && !G()->close_flag()) {
6517 LOG(INFO) << "Save recent " << (is_attached ? "attached " : "") << "stickers to database";
6518 StickerListLogEvent log_event(recent_sticker_ids_[is_attached]);
6519 G()->td_db()->get_sqlite_pmc()->set(is_attached ? "ssr1" : "ssr0", log_event_store(log_event).as_slice().str(),
6520 Auto());
6521 }
6522 }
6523
on_update_animated_emoji_zoom()6524 void StickersManager::on_update_animated_emoji_zoom() {
6525 animated_emoji_zoom_ =
6526 static_cast<double>(G()->shared_config().get_option_integer("animated_emoji_zoom", 625000000)) * 1e-9;
6527 }
6528
on_update_recent_stickers_limit(int32 recent_stickers_limit)6529 void StickersManager::on_update_recent_stickers_limit(int32 recent_stickers_limit) {
6530 if (recent_stickers_limit != recent_stickers_limit_) {
6531 if (recent_stickers_limit > 0) {
6532 LOG(INFO) << "Update recent stickers limit to " << recent_stickers_limit;
6533 recent_stickers_limit_ = recent_stickers_limit;
6534 for (int is_attached = 0; is_attached < 2; is_attached++) {
6535 if (static_cast<int32>(recent_sticker_ids_[is_attached].size()) > recent_stickers_limit) {
6536 recent_sticker_ids_[is_attached].resize(recent_stickers_limit);
6537 send_update_recent_stickers(is_attached);
6538 }
6539 }
6540 } else {
6541 LOG(ERROR) << "Receive wrong recent stickers limit = " << recent_stickers_limit;
6542 }
6543 }
6544 }
6545
on_update_favorite_stickers_limit(int32 favorite_stickers_limit)6546 void StickersManager::on_update_favorite_stickers_limit(int32 favorite_stickers_limit) {
6547 if (favorite_stickers_limit != favorite_stickers_limit_) {
6548 if (favorite_stickers_limit > 0) {
6549 LOG(INFO) << "Update favorite stickers limit to " << favorite_stickers_limit;
6550 favorite_stickers_limit_ = favorite_stickers_limit;
6551 if (static_cast<int32>(favorite_sticker_ids_.size()) > favorite_stickers_limit) {
6552 favorite_sticker_ids_.resize(favorite_stickers_limit);
6553 send_update_favorite_stickers();
6554 }
6555 } else {
6556 LOG(ERROR) << "Receive wrong favorite stickers limit = " << favorite_stickers_limit;
6557 }
6558 }
6559 }
6560
reload_favorite_stickers(bool force)6561 void StickersManager::reload_favorite_stickers(bool force) {
6562 if (G()->close_flag()) {
6563 return;
6564 }
6565
6566 auto &next_load_time = next_favorite_stickers_load_time_;
6567 if (!td_->auth_manager_->is_bot() && next_load_time >= 0 && (next_load_time < Time::now() || force)) {
6568 LOG_IF(INFO, force) << "Reload favorite stickers";
6569 next_load_time = -1;
6570 td_->create_handler<GetFavedStickersQuery>()->send(false, get_favorite_stickers_hash());
6571 }
6572 }
6573
repair_favorite_stickers(Promise<Unit> && promise)6574 void StickersManager::repair_favorite_stickers(Promise<Unit> &&promise) {
6575 if (td_->auth_manager_->is_bot()) {
6576 return promise.set_error(Status::Error(400, "Bots have no favorite stickers"));
6577 }
6578
6579 repair_favorite_stickers_queries_.push_back(std::move(promise));
6580 if (repair_favorite_stickers_queries_.size() == 1u) {
6581 td_->create_handler<GetFavedStickersQuery>()->send(true, 0);
6582 }
6583 }
6584
get_favorite_stickers(Promise<Unit> && promise)6585 vector<FileId> StickersManager::get_favorite_stickers(Promise<Unit> &&promise) {
6586 if (!are_favorite_stickers_loaded_) {
6587 load_favorite_stickers(std::move(promise));
6588 return {};
6589 }
6590 reload_favorite_stickers(false);
6591
6592 promise.set_value(Unit());
6593 return favorite_sticker_ids_;
6594 }
6595
load_favorite_stickers(Promise<Unit> && promise)6596 void StickersManager::load_favorite_stickers(Promise<Unit> &&promise) {
6597 if (td_->auth_manager_->is_bot()) {
6598 are_favorite_stickers_loaded_ = true;
6599 }
6600 if (are_favorite_stickers_loaded_) {
6601 promise.set_value(Unit());
6602 return;
6603 }
6604 load_favorite_stickers_queries_.push_back(std::move(promise));
6605 if (load_favorite_stickers_queries_.size() == 1u) {
6606 if (G()->parameters().use_file_db) {
6607 LOG(INFO) << "Trying to load favorite stickers from database";
6608 G()->td_db()->get_sqlite_pmc()->get("ssfav", PromiseCreator::lambda([](string value) {
6609 send_closure(G()->stickers_manager(),
6610 &StickersManager::on_load_favorite_stickers_from_database,
6611 std::move(value));
6612 }));
6613 } else {
6614 LOG(INFO) << "Trying to load favorite stickers from server";
6615 reload_favorite_stickers(true);
6616 }
6617 }
6618 }
6619
on_load_favorite_stickers_from_database(const string & value)6620 void StickersManager::on_load_favorite_stickers_from_database(const string &value) {
6621 if (G()->close_flag()) {
6622 return;
6623 }
6624 if (value.empty()) {
6625 LOG(INFO) << "Favorite stickers aren't found in database";
6626 reload_favorite_stickers(true);
6627 return;
6628 }
6629
6630 LOG(INFO) << "Successfully loaded favorite stickers list of size " << value.size() << " from database";
6631
6632 StickerListLogEvent log_event;
6633 auto status = log_event_parse(log_event, value);
6634 if (status.is_error()) {
6635 // can't happen unless database is broken, but has been seen in the wild
6636 LOG(ERROR) << "Can't load favorite stickers: " << status << ' ' << format::as_hex_dump<4>(Slice(value));
6637 return reload_favorite_stickers(true);
6638 }
6639
6640 on_load_favorite_stickers_finished(std::move(log_event.sticker_ids), true);
6641 }
6642
on_load_favorite_stickers_finished(vector<FileId> && favorite_sticker_ids,bool from_database)6643 void StickersManager::on_load_favorite_stickers_finished(vector<FileId> &&favorite_sticker_ids, bool from_database) {
6644 if (static_cast<int32>(favorite_sticker_ids.size()) > favorite_stickers_limit_) {
6645 favorite_sticker_ids.resize(favorite_stickers_limit_);
6646 }
6647 favorite_sticker_ids_ = std::move(favorite_sticker_ids);
6648 are_favorite_stickers_loaded_ = true;
6649 send_update_favorite_stickers(from_database);
6650 auto promises = std::move(load_favorite_stickers_queries_);
6651 load_favorite_stickers_queries_.clear();
6652 for (auto &promise : promises) {
6653 promise.set_value(Unit());
6654 }
6655 }
6656
on_get_favorite_stickers(bool is_repair,tl_object_ptr<telegram_api::messages_FavedStickers> && favorite_stickers_ptr)6657 void StickersManager::on_get_favorite_stickers(
6658 bool is_repair, tl_object_ptr<telegram_api::messages_FavedStickers> &&favorite_stickers_ptr) {
6659 CHECK(!td_->auth_manager_->is_bot());
6660 if (!is_repair) {
6661 next_favorite_stickers_load_time_ = Time::now_cached() + Random::fast(30 * 60, 50 * 60);
6662 }
6663
6664 CHECK(favorite_stickers_ptr != nullptr);
6665 int32 constructor_id = favorite_stickers_ptr->get_id();
6666 if (constructor_id == telegram_api::messages_favedStickersNotModified::ID) {
6667 if (is_repair) {
6668 return on_get_favorite_stickers_failed(true, Status::Error(500, "Failed to reload favorite stickers"));
6669 }
6670 LOG(INFO) << "Favorite stickers are not modified";
6671 return;
6672 }
6673 CHECK(constructor_id == telegram_api::messages_favedStickers::ID);
6674 auto favorite_stickers = move_tl_object_as<telegram_api::messages_favedStickers>(favorite_stickers_ptr);
6675
6676 // TODO use favorite_stickers->packs_
6677
6678 vector<FileId> favorite_sticker_ids;
6679 favorite_sticker_ids.reserve(favorite_stickers->stickers_.size());
6680 for (auto &document_ptr : favorite_stickers->stickers_) {
6681 auto sticker_id = on_get_sticker_document(std::move(document_ptr)).second;
6682 if (!sticker_id.is_valid()) {
6683 continue;
6684 }
6685
6686 favorite_sticker_ids.push_back(sticker_id);
6687 }
6688
6689 if (is_repair) {
6690 auto promises = std::move(repair_favorite_stickers_queries_);
6691 repair_favorite_stickers_queries_.clear();
6692 for (auto &promise : promises) {
6693 promise.set_value(Unit());
6694 }
6695 } else {
6696 on_load_favorite_stickers_finished(std::move(favorite_sticker_ids));
6697
6698 LOG_IF(ERROR, get_favorite_stickers_hash() != favorite_stickers->hash_) << "Favorite stickers hash mismatch";
6699 }
6700 }
6701
on_get_favorite_stickers_failed(bool is_repair,Status error)6702 void StickersManager::on_get_favorite_stickers_failed(bool is_repair, Status error) {
6703 CHECK(error.is_error());
6704 if (!is_repair) {
6705 next_favorite_stickers_load_time_ = Time::now_cached() + Random::fast(5, 10);
6706 }
6707 auto &queries = is_repair ? repair_favorite_stickers_queries_ : load_favorite_stickers_queries_;
6708 auto promises = std::move(queries);
6709 queries.clear();
6710 for (auto &promise : promises) {
6711 promise.set_error(error.clone());
6712 }
6713 }
6714
get_favorite_stickers_hash() const6715 int64 StickersManager::get_favorite_stickers_hash() const {
6716 return get_recent_stickers_hash(favorite_sticker_ids_);
6717 }
6718
get_app_config_file_source_id()6719 FileSourceId StickersManager::get_app_config_file_source_id() {
6720 if (!app_config_file_source_id_.is_valid()) {
6721 app_config_file_source_id_ = td_->file_reference_manager_->create_app_config_file_source();
6722 }
6723 return app_config_file_source_id_;
6724 }
6725
get_favorite_stickers_file_source_id()6726 FileSourceId StickersManager::get_favorite_stickers_file_source_id() {
6727 if (!favorite_stickers_file_source_id_.is_valid()) {
6728 favorite_stickers_file_source_id_ = td_->file_reference_manager_->create_favorite_stickers_file_source();
6729 }
6730 return favorite_stickers_file_source_id_;
6731 }
6732
add_favorite_sticker(const tl_object_ptr<td_api::InputFile> & input_file,Promise<Unit> && promise)6733 void StickersManager::add_favorite_sticker(const tl_object_ptr<td_api::InputFile> &input_file,
6734 Promise<Unit> &&promise) {
6735 if (!are_favorite_stickers_loaded_) {
6736 load_favorite_stickers(std::move(promise));
6737 return;
6738 }
6739
6740 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, input_file, DialogId(), false, false);
6741 if (r_file_id.is_error()) {
6742 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6743 }
6744
6745 add_favorite_sticker_impl(r_file_id.ok(), true, std::move(promise));
6746 }
6747
send_fave_sticker_query(FileId sticker_id,bool unsave,Promise<Unit> && promise)6748 void StickersManager::send_fave_sticker_query(FileId sticker_id, bool unsave, Promise<Unit> &&promise) {
6749 TRY_STATUS_PROMISE(promise, G()->close_status());
6750
6751 // TODO invokeAfter and log event
6752 auto file_view = td_->file_manager_->get_file_view(sticker_id);
6753 CHECK(file_view.has_remote_location());
6754 CHECK(file_view.remote_location().is_document());
6755 CHECK(!file_view.remote_location().is_web());
6756 td_->create_handler<FaveStickerQuery>(std::move(promise))
6757 ->send(sticker_id, file_view.remote_location().as_input_document(), unsave);
6758 }
6759
add_favorite_sticker_by_id(FileId sticker_id)6760 void StickersManager::add_favorite_sticker_by_id(FileId sticker_id) {
6761 // TODO log event
6762 add_favorite_sticker_impl(sticker_id, false, Auto());
6763 }
6764
add_favorite_sticker_impl(FileId sticker_id,bool add_on_server,Promise<Unit> && promise)6765 void StickersManager::add_favorite_sticker_impl(FileId sticker_id, bool add_on_server, Promise<Unit> &&promise) {
6766 CHECK(!td_->auth_manager_->is_bot());
6767
6768 if (!are_favorite_stickers_loaded_) {
6769 load_favorite_stickers(
6770 PromiseCreator::lambda([sticker_id, add_on_server, promise = std::move(promise)](Result<> result) mutable {
6771 if (result.is_ok()) {
6772 send_closure(G()->stickers_manager(), &StickersManager::add_favorite_sticker_impl, sticker_id,
6773 add_on_server, std::move(promise));
6774 } else {
6775 promise.set_error(result.move_as_error());
6776 }
6777 }));
6778 return;
6779 }
6780
6781 auto is_equal = [sticker_id](FileId file_id) {
6782 return file_id == sticker_id || (file_id.get_remote() == sticker_id.get_remote() && sticker_id.get_remote() != 0);
6783 };
6784
6785 if (!favorite_sticker_ids_.empty() && is_equal(favorite_sticker_ids_[0])) {
6786 if (favorite_sticker_ids_[0].get_remote() == 0 && sticker_id.get_remote() != 0) {
6787 favorite_sticker_ids_[0] = sticker_id;
6788 save_favorite_stickers_to_database();
6789 }
6790
6791 return promise.set_value(Unit());
6792 }
6793
6794 auto sticker = get_sticker(sticker_id);
6795 if (sticker == nullptr) {
6796 return promise.set_error(Status::Error(400, "Sticker not found"));
6797 }
6798 if (!sticker->set_id.is_valid()) {
6799 return promise.set_error(Status::Error(400, "Stickers without sticker set can't be favorite"));
6800 }
6801
6802 auto file_view = td_->file_manager_->get_file_view(sticker_id);
6803 if (!file_view.has_remote_location()) {
6804 return promise.set_error(Status::Error(400, "Can add to favorites only sent stickers"));
6805 }
6806 if (file_view.remote_location().is_web()) {
6807 return promise.set_error(Status::Error(400, "Can't add to favorites web stickers"));
6808 }
6809 if (!file_view.remote_location().is_document()) {
6810 return promise.set_error(Status::Error(400, "Can't add to favorites encrypted stickers"));
6811 }
6812
6813 auto it = std::find_if(favorite_sticker_ids_.begin(), favorite_sticker_ids_.end(), is_equal);
6814 if (it == favorite_sticker_ids_.end()) {
6815 if (static_cast<int32>(favorite_sticker_ids_.size()) == favorite_stickers_limit_) {
6816 favorite_sticker_ids_.back() = sticker_id;
6817 } else {
6818 favorite_sticker_ids_.push_back(sticker_id);
6819 }
6820 it = favorite_sticker_ids_.end() - 1;
6821 }
6822 std::rotate(favorite_sticker_ids_.begin(), it, it + 1);
6823 if (favorite_sticker_ids_[0].get_remote() == 0 && sticker_id.get_remote() != 0) {
6824 favorite_sticker_ids_[0] = sticker_id;
6825 }
6826
6827 send_update_favorite_stickers();
6828 if (add_on_server) {
6829 send_fave_sticker_query(sticker_id, false, std::move(promise));
6830 }
6831 }
6832
remove_favorite_sticker(const tl_object_ptr<td_api::InputFile> & input_file,Promise<Unit> && promise)6833 void StickersManager::remove_favorite_sticker(const tl_object_ptr<td_api::InputFile> &input_file,
6834 Promise<Unit> &&promise) {
6835 if (!are_favorite_stickers_loaded_) {
6836 load_favorite_stickers(std::move(promise));
6837 return;
6838 }
6839
6840 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, input_file, DialogId(), false, false);
6841 if (r_file_id.is_error()) {
6842 return promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6843 }
6844
6845 FileId file_id = r_file_id.ok();
6846 if (!td::remove(favorite_sticker_ids_, file_id)) {
6847 return promise.set_value(Unit());
6848 }
6849
6850 auto sticker = get_sticker(file_id);
6851 if (sticker == nullptr) {
6852 return promise.set_error(Status::Error(400, "Sticker not found"));
6853 }
6854
6855 send_fave_sticker_query(file_id, true, std::move(promise));
6856
6857 send_update_favorite_stickers();
6858 }
6859
get_update_favorite_stickers_object() const6860 td_api::object_ptr<td_api::updateFavoriteStickers> StickersManager::get_update_favorite_stickers_object() const {
6861 return td_api::make_object<td_api::updateFavoriteStickers>(
6862 td_->file_manager_->get_file_ids_object(favorite_sticker_ids_));
6863 }
6864
send_update_favorite_stickers(bool from_database)6865 void StickersManager::send_update_favorite_stickers(bool from_database) {
6866 if (are_favorite_stickers_loaded_) {
6867 vector<FileId> new_favorite_sticker_file_ids;
6868 for (auto &sticker_id : favorite_sticker_ids_) {
6869 append(new_favorite_sticker_file_ids, get_sticker_file_ids(sticker_id));
6870 }
6871 std::sort(new_favorite_sticker_file_ids.begin(), new_favorite_sticker_file_ids.end());
6872 if (new_favorite_sticker_file_ids != favorite_sticker_file_ids_) {
6873 td_->file_manager_->change_files_source(get_favorite_stickers_file_source_id(), favorite_sticker_file_ids_,
6874 new_favorite_sticker_file_ids);
6875 favorite_sticker_file_ids_ = std::move(new_favorite_sticker_file_ids);
6876 }
6877
6878 send_closure(G()->td(), &Td::send_update, get_update_favorite_stickers_object());
6879
6880 if (!from_database) {
6881 save_favorite_stickers_to_database();
6882 }
6883 }
6884 }
6885
save_favorite_stickers_to_database()6886 void StickersManager::save_favorite_stickers_to_database() {
6887 if (G()->parameters().use_file_db && !G()->close_flag()) {
6888 LOG(INFO) << "Save favorite stickers to database";
6889 StickerListLogEvent log_event(favorite_sticker_ids_);
6890 G()->td_db()->get_sqlite_pmc()->set("ssfav", log_event_store(log_event).as_slice().str(), Auto());
6891 }
6892 }
6893
get_sticker_emojis(const tl_object_ptr<td_api::InputFile> & input_file,Promise<Unit> && promise)6894 vector<string> StickersManager::get_sticker_emojis(const tl_object_ptr<td_api::InputFile> &input_file,
6895 Promise<Unit> &&promise) {
6896 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Sticker, input_file, DialogId(), false, false);
6897 if (r_file_id.is_error()) {
6898 promise.set_error(Status::Error(400, r_file_id.error().message())); // TODO do not drop error code
6899 return {};
6900 }
6901
6902 FileId file_id = r_file_id.ok();
6903
6904 auto sticker = get_sticker(file_id);
6905 if (sticker == nullptr) {
6906 promise.set_value(Unit());
6907 return {};
6908 }
6909 if (!sticker->set_id.is_valid()) {
6910 promise.set_value(Unit());
6911 return {};
6912 }
6913
6914 auto file_view = td_->file_manager_->get_file_view(file_id);
6915 if (!file_view.has_remote_location()) {
6916 promise.set_value(Unit());
6917 return {};
6918 }
6919 if (!file_view.remote_location().is_document()) {
6920 promise.set_value(Unit());
6921 return {};
6922 }
6923 if (file_view.remote_location().is_web()) {
6924 promise.set_value(Unit());
6925 return {};
6926 }
6927
6928 const StickerSet *sticker_set = get_sticker_set(sticker->set_id);
6929 if (update_sticker_set_cache(sticker_set, promise)) {
6930 return {};
6931 }
6932
6933 promise.set_value(Unit());
6934 auto it = sticker_set->sticker_emojis_map_.find(file_id);
6935 if (it == sticker_set->sticker_emojis_map_.end()) {
6936 return {};
6937 }
6938
6939 return it->second;
6940 }
6941
get_sticker_mime_type(const Sticker * s)6942 string StickersManager::get_sticker_mime_type(const Sticker *s) {
6943 return s->is_animated ? "application/x-tgsticker" : "image/webp";
6944 }
6945
get_emoji_language_code_version_database_key(const string & language_code)6946 string StickersManager::get_emoji_language_code_version_database_key(const string &language_code) {
6947 return PSTRING() << "emojiv$" << language_code;
6948 }
6949
get_emoji_language_code_version(const string & language_code)6950 int32 StickersManager::get_emoji_language_code_version(const string &language_code) {
6951 auto it = emoji_language_code_versions_.find(language_code);
6952 if (it != emoji_language_code_versions_.end()) {
6953 return it->second;
6954 }
6955 auto &result = emoji_language_code_versions_[language_code];
6956 result = to_integer<int32>(
6957 G()->td_db()->get_sqlite_sync_pmc()->get(get_emoji_language_code_version_database_key(language_code)));
6958 return result;
6959 }
6960
get_emoji_language_code_last_difference_time_database_key(const string & language_code)6961 string StickersManager::get_emoji_language_code_last_difference_time_database_key(const string &language_code) {
6962 return PSTRING() << "emojid$" << language_code;
6963 }
6964
get_emoji_language_code_last_difference_time(const string & language_code)6965 double StickersManager::get_emoji_language_code_last_difference_time(const string &language_code) {
6966 auto it = emoji_language_code_last_difference_times_.find(language_code);
6967 if (it != emoji_language_code_last_difference_times_.end()) {
6968 return it->second;
6969 }
6970 auto &result = emoji_language_code_last_difference_times_[language_code];
6971 auto old_unix_time = to_integer<int32>(G()->td_db()->get_sqlite_sync_pmc()->get(
6972 get_emoji_language_code_last_difference_time_database_key(language_code)));
6973 int32 passed_time = max(static_cast<int32>(0), G()->unix_time() - old_unix_time);
6974 result = Time::now_cached() - passed_time;
6975 return result;
6976 }
6977
get_language_emojis_database_key(const string & language_code,const string & text)6978 string StickersManager::get_language_emojis_database_key(const string &language_code, const string &text) {
6979 return PSTRING() << "emoji$" << language_code << '$' << text;
6980 }
6981
search_language_emojis(const string & language_code,const string & text,bool exact_match)6982 vector<string> StickersManager::search_language_emojis(const string &language_code, const string &text,
6983 bool exact_match) {
6984 LOG(INFO) << "Search for \"" << text << "\" in language " << language_code;
6985 auto key = get_language_emojis_database_key(language_code, text);
6986 if (exact_match) {
6987 string emojis = G()->td_db()->get_sqlite_sync_pmc()->get(key);
6988 return full_split(emojis, '$');
6989 } else {
6990 vector<string> result;
6991 G()->td_db()->get_sqlite_sync_pmc()->get_by_prefix(key, [&result](Slice key, Slice value) {
6992 for (auto &emoji : full_split(value, '$')) {
6993 result.push_back(emoji.str());
6994 }
6995 return true;
6996 });
6997 return result;
6998 }
6999 }
7000
get_emoji_language_codes_database_key(const vector<string> & language_codes)7001 string StickersManager::get_emoji_language_codes_database_key(const vector<string> &language_codes) {
7002 return PSTRING() << "emojilc$" << implode(language_codes, '$');
7003 }
7004
load_language_codes(vector<string> language_codes,string key,Promise<Unit> && promise)7005 void StickersManager::load_language_codes(vector<string> language_codes, string key, Promise<Unit> &&promise) {
7006 auto &promises = load_language_codes_queries_[key];
7007 promises.push_back(std::move(promise));
7008 if (promises.size() != 1) {
7009 // query has already been sent, just wait for the result
7010 return;
7011 }
7012
7013 auto query_promise =
7014 PromiseCreator::lambda([actor_id = actor_id(this), key = std::move(key)](Result<vector<string>> &&result) {
7015 send_closure(actor_id, &StickersManager::on_get_language_codes, key, std::move(result));
7016 });
7017 td_->create_handler<GetEmojiKeywordsLanguageQuery>(std::move(query_promise))->send(std::move(language_codes));
7018 }
7019
on_get_language_codes(const string & key,Result<vector<string>> && result)7020 void StickersManager::on_get_language_codes(const string &key, Result<vector<string>> &&result) {
7021 auto queries_it = load_language_codes_queries_.find(key);
7022 CHECK(queries_it != load_language_codes_queries_.end());
7023 CHECK(!queries_it->second.empty());
7024 auto promises = std::move(queries_it->second);
7025 load_language_codes_queries_.erase(queries_it);
7026
7027 if (result.is_error()) {
7028 if (!G()->is_expected_error(result.error())) {
7029 LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsLanguageQuery";
7030 }
7031 for (auto &promise : promises) {
7032 promise.set_error(result.error().clone());
7033 }
7034 return;
7035 }
7036
7037 auto language_codes = result.move_as_ok();
7038 LOG(INFO) << "Receive language codes " << language_codes << " for emojis search with key " << key;
7039 td::remove_if(language_codes, [](const string &language_code) {
7040 if (language_code.empty() || language_code.find('$') != string::npos) {
7041 LOG(ERROR) << "Receive language_code \"" << language_code << '"';
7042 return true;
7043 }
7044 return false;
7045 });
7046 if (language_codes.empty()) {
7047 LOG(ERROR) << "Language codes list is empty";
7048 language_codes.emplace_back("en");
7049 }
7050 td::unique(language_codes);
7051
7052 auto it = emoji_language_codes_.find(key);
7053 CHECK(it != emoji_language_codes_.end());
7054 if (it->second != language_codes) {
7055 LOG(INFO) << "Update emoji language codes for " << key << " to " << language_codes;
7056 if (!G()->close_flag()) {
7057 CHECK(G()->parameters().use_file_db);
7058 G()->td_db()->get_sqlite_pmc()->set(key, implode(language_codes, '$'), Auto());
7059 }
7060 it->second = std::move(language_codes);
7061 }
7062
7063 for (auto &promise : promises) {
7064 promise.set_value(Unit());
7065 }
7066 }
7067
get_emoji_language_codes(const vector<string> & input_language_codes,Slice text,Promise<Unit> & promise)7068 vector<string> StickersManager::get_emoji_language_codes(const vector<string> &input_language_codes, Slice text,
7069 Promise<Unit> &promise) {
7070 vector<string> language_codes = td_->language_pack_manager_->get_actor_unsafe()->get_used_language_codes();
7071 auto system_language_code = G()->mtproto_header().get_system_language_code();
7072 if (system_language_code.size() >= 2 && system_language_code.find('$') == string::npos &&
7073 (system_language_code.size() == 2 || system_language_code[2] == '-')) {
7074 language_codes.push_back(system_language_code.substr(0, 2));
7075 }
7076 for (auto &input_language_code : input_language_codes) {
7077 if (input_language_code.size() >= 2 && input_language_code.find('$') == string::npos &&
7078 (input_language_code.size() == 2 || input_language_code[2] == '-')) {
7079 language_codes.push_back(input_language_code.substr(0, 2));
7080 }
7081 }
7082 if (!text.empty()) {
7083 uint32 code = 0;
7084 next_utf8_unsafe(text.ubegin(), &code, "get_emoji_language_codes");
7085 if ((0x410 <= code && code <= 0x44F) || code == 0x401 || code == 0x451) {
7086 // the first letter is cyrillic
7087 if (!td::contains(language_codes, "ru") && !td::contains(language_codes, "uk") &&
7088 !td::contains(language_codes, "bg") && !td::contains(language_codes, "be") &&
7089 !td::contains(language_codes, "mk") && !td::contains(language_codes, "sr") &&
7090 !td::contains(language_codes, "mn") && !td::contains(language_codes, "ky") &&
7091 !td::contains(language_codes, "kk") && !td::contains(language_codes, "uz") &&
7092 !td::contains(language_codes, "tk")) {
7093 language_codes.push_back("ru");
7094 }
7095 }
7096 }
7097
7098 if (language_codes.empty()) {
7099 LOG(INFO) << "List of language codes is empty";
7100 language_codes.push_back("en");
7101 }
7102 td::unique(language_codes);
7103
7104 LOG(DEBUG) << "Have language codes " << language_codes;
7105 auto key = get_emoji_language_codes_database_key(language_codes);
7106 auto it = emoji_language_codes_.find(key);
7107 if (it == emoji_language_codes_.end()) {
7108 it = emoji_language_codes_.emplace(key, full_split(G()->td_db()->get_sqlite_sync_pmc()->get(key), '$')).first;
7109 }
7110 if (it->second.empty()) {
7111 load_language_codes(std::move(language_codes), std::move(key), std::move(promise));
7112 } else {
7113 LOG(DEBUG) << "Have emoji language codes " << it->second;
7114 double now = Time::now_cached();
7115 for (auto &language_code : it->second) {
7116 double last_difference_time = get_emoji_language_code_last_difference_time(language_code);
7117 if (last_difference_time < now - EMOJI_KEYWORDS_UPDATE_DELAY &&
7118 get_emoji_language_code_version(language_code) != 0) {
7119 load_emoji_keywords_difference(language_code);
7120 }
7121 }
7122 if (reloaded_emoji_keywords_.insert(key).second) {
7123 load_language_codes(std::move(language_codes), std::move(key), Auto());
7124 }
7125 }
7126 return it->second;
7127 }
7128
load_emoji_keywords(const string & language_code,Promise<Unit> && promise)7129 void StickersManager::load_emoji_keywords(const string &language_code, Promise<Unit> &&promise) {
7130 auto &promises = load_emoji_keywords_queries_[language_code];
7131 promises.push_back(std::move(promise));
7132 if (promises.size() != 1) {
7133 // query has already been sent, just wait for the result
7134 return;
7135 }
7136
7137 auto query_promise = PromiseCreator::lambda(
7138 [actor_id = actor_id(this),
7139 language_code](Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&result) mutable {
7140 send_closure(actor_id, &StickersManager::on_get_emoji_keywords, language_code, std::move(result));
7141 });
7142 td_->create_handler<GetEmojiKeywordsQuery>(std::move(query_promise))->send(language_code);
7143 }
7144
on_get_emoji_keywords(const string & language_code,Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> && result)7145 void StickersManager::on_get_emoji_keywords(
7146 const string &language_code, Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&result) {
7147 auto it = load_emoji_keywords_queries_.find(language_code);
7148 CHECK(it != load_emoji_keywords_queries_.end());
7149 auto promises = std::move(it->second);
7150 CHECK(!promises.empty());
7151 load_emoji_keywords_queries_.erase(it);
7152
7153 if (result.is_error()) {
7154 if (!G()->is_expected_error(result.error())) {
7155 LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsQuery";
7156 }
7157 for (auto &promise : promises) {
7158 promise.set_error(result.error().clone());
7159 }
7160 return;
7161 }
7162
7163 auto version = get_emoji_language_code_version(language_code);
7164 CHECK(version == 0);
7165
7166 MultiPromiseActorSafe mpas{"SaveEmojiKeywordsMultiPromiseActor"};
7167 for (auto &promise : promises) {
7168 mpas.add_promise(std::move(promise));
7169 }
7170
7171 auto lock = mpas.get_promise();
7172
7173 auto keywords = result.move_as_ok();
7174 LOG(INFO) << "Receive " << keywords->keywords_.size() << " emoji keywords for language " << language_code;
7175 LOG_IF(ERROR, language_code != keywords->lang_code_)
7176 << "Receive keywords for " << keywords->lang_code_ << " instead of " << language_code;
7177 LOG_IF(ERROR, keywords->from_version_ != 0) << "Receive keywords from version " << keywords->from_version_;
7178 version = keywords->version_;
7179 if (version <= 0) {
7180 LOG(ERROR) << "Receive keywords of version " << version;
7181 version = 1;
7182 }
7183 for (auto &keyword_ptr : keywords->keywords_) {
7184 switch (keyword_ptr->get_id()) {
7185 case telegram_api::emojiKeyword::ID: {
7186 auto keyword = telegram_api::move_object_as<telegram_api::emojiKeyword>(keyword_ptr);
7187 auto text = utf8_to_lower(keyword->keyword_);
7188 bool is_good = true;
7189 for (auto &emoji : keyword->emoticons_) {
7190 if (emoji.find('$') != string::npos) {
7191 LOG(ERROR) << "Receive emoji \"" << emoji << "\" from server for " << text;
7192 is_good = false;
7193 }
7194 }
7195 if (is_good && !G()->close_flag()) {
7196 CHECK(G()->parameters().use_file_db);
7197 G()->td_db()->get_sqlite_pmc()->set(get_language_emojis_database_key(language_code, text),
7198 implode(keyword->emoticons_, '$'), mpas.get_promise());
7199 }
7200 break;
7201 }
7202 case telegram_api::emojiKeywordDeleted::ID:
7203 LOG(ERROR) << "Receive emojiKeywordDeleted in keywords for " << language_code;
7204 break;
7205 default:
7206 UNREACHABLE();
7207 }
7208 }
7209 if (!G()->close_flag()) {
7210 CHECK(G()->parameters().use_file_db);
7211 G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_version_database_key(language_code), to_string(version),
7212 mpas.get_promise());
7213 G()->td_db()->get_sqlite_pmc()->set(get_emoji_language_code_last_difference_time_database_key(language_code),
7214 to_string(G()->unix_time()), mpas.get_promise());
7215 }
7216 emoji_language_code_versions_[language_code] = version;
7217 emoji_language_code_last_difference_times_[language_code] = static_cast<int32>(Time::now_cached());
7218
7219 lock.set_value(Unit());
7220 }
7221
load_emoji_keywords_difference(const string & language_code)7222 void StickersManager::load_emoji_keywords_difference(const string &language_code) {
7223 LOG(INFO) << "Load emoji keywords difference for language " << language_code;
7224 emoji_language_code_last_difference_times_[language_code] =
7225 Time::now_cached() + 1e9; // prevent simultaneous requests
7226 int32 from_version = get_emoji_language_code_version(language_code);
7227 auto query_promise = PromiseCreator::lambda(
7228 [actor_id = actor_id(this), language_code,
7229 from_version](Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&result) mutable {
7230 send_closure(actor_id, &StickersManager::on_get_emoji_keywords_difference, language_code, from_version,
7231 std::move(result));
7232 });
7233 td_->create_handler<GetEmojiKeywordsDifferenceQuery>(std::move(query_promise))->send(language_code, from_version);
7234 }
7235
on_get_emoji_keywords_difference(const string & language_code,int32 from_version,Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> && result)7236 void StickersManager::on_get_emoji_keywords_difference(
7237 const string &language_code, int32 from_version,
7238 Result<telegram_api::object_ptr<telegram_api::emojiKeywordsDifference>> &&result) {
7239 if (result.is_error()) {
7240 if (!G()->is_expected_error(result.error())) {
7241 LOG(ERROR) << "Receive " << result.error() << " from GetEmojiKeywordsDifferenceQuery";
7242 }
7243 emoji_language_code_last_difference_times_[language_code] = Time::now_cached() - EMOJI_KEYWORDS_UPDATE_DELAY - 2;
7244 return;
7245 }
7246
7247 auto version = get_emoji_language_code_version(language_code);
7248 CHECK(version == from_version);
7249
7250 auto keywords = result.move_as_ok();
7251 LOG(INFO) << "Receive " << keywords->keywords_.size() << " emoji keywords difference for language " << language_code;
7252 LOG_IF(ERROR, language_code != keywords->lang_code_)
7253 << "Receive keywords for " << keywords->lang_code_ << " instead of " << language_code;
7254 LOG_IF(ERROR, keywords->from_version_ != from_version)
7255 << "Receive keywords from version " << keywords->from_version_ << " instead of " << from_version;
7256 if (keywords->version_ < version) {
7257 LOG(ERROR) << "Receive keywords of version " << keywords->version_ << ", but have of version " << version;
7258 keywords->version_ = version;
7259 }
7260 version = keywords->version_;
7261 auto *pmc = G()->td_db()->get_sqlite_sync_pmc();
7262 pmc->begin_write_transaction().ensure();
7263 pmc->set(get_emoji_language_code_version_database_key(language_code), to_string(version));
7264 pmc->set(get_emoji_language_code_last_difference_time_database_key(language_code), to_string(G()->unix_time()));
7265 for (auto &keyword_ptr : keywords->keywords_) {
7266 switch (keyword_ptr->get_id()) {
7267 case telegram_api::emojiKeyword::ID: {
7268 auto keyword = telegram_api::move_object_as<telegram_api::emojiKeyword>(keyword_ptr);
7269 auto text = utf8_to_lower(keyword->keyword_);
7270 bool is_good = true;
7271 for (auto &emoji : keyword->emoticons_) {
7272 if (emoji.find('$') != string::npos) {
7273 LOG(ERROR) << "Receive emoji \"" << emoji << "\" from server for " << text;
7274 is_good = false;
7275 }
7276 }
7277 if (is_good) {
7278 vector<string> emojis = search_language_emojis(language_code, text, true);
7279 bool is_changed = false;
7280 for (auto &emoji : keyword->emoticons_) {
7281 if (!td::contains(emojis, emoji)) {
7282 emojis.push_back(emoji);
7283 is_changed = true;
7284 }
7285 }
7286 if (is_changed) {
7287 pmc->set(get_language_emojis_database_key(language_code, text), implode(emojis, '$'));
7288 } else {
7289 LOG(ERROR) << "Emoji keywords not changed for \"" << text << "\" from version " << from_version
7290 << " to version " << version;
7291 }
7292 }
7293 break;
7294 }
7295 case telegram_api::emojiKeywordDeleted::ID: {
7296 auto keyword = telegram_api::move_object_as<telegram_api::emojiKeywordDeleted>(keyword_ptr);
7297 auto text = utf8_to_lower(keyword->keyword_);
7298 vector<string> emojis = search_language_emojis(language_code, text, true);
7299 bool is_changed = false;
7300 for (auto &emoji : keyword->emoticons_) {
7301 if (td::remove(emojis, emoji)) {
7302 is_changed = true;
7303 }
7304 }
7305 if (is_changed) {
7306 pmc->set(get_language_emojis_database_key(language_code, text), implode(emojis, '$'));
7307 } else {
7308 LOG(ERROR) << "Emoji keywords not changed for \"" << text << "\" from version " << from_version
7309 << " to version " << version;
7310 }
7311 break;
7312 }
7313 default:
7314 UNREACHABLE();
7315 }
7316 }
7317 pmc->commit_transaction().ensure();
7318 emoji_language_code_versions_[language_code] = version;
7319 emoji_language_code_last_difference_times_[language_code] = static_cast<int32>(Time::now_cached());
7320 }
7321
search_emojis(const string & text,bool exact_match,const vector<string> & input_language_codes,bool force,Promise<Unit> && promise)7322 vector<string> StickersManager::search_emojis(const string &text, bool exact_match,
7323 const vector<string> &input_language_codes, bool force,
7324 Promise<Unit> &&promise) {
7325 if (text.empty() || !G()->parameters().use_file_db /* have SQLite PMC */) {
7326 promise.set_value(Unit());
7327 return {};
7328 }
7329
7330 auto language_codes = get_emoji_language_codes(input_language_codes, text, promise);
7331 if (language_codes.empty()) {
7332 // promise was consumed
7333 return {};
7334 }
7335
7336 vector<string> languages_to_load;
7337 for (auto &language_code : language_codes) {
7338 auto version = get_emoji_language_code_version(language_code);
7339 if (version == 0) {
7340 languages_to_load.push_back(language_code);
7341 } else {
7342 LOG(DEBUG) << "Found language " << language_code << " with version " << version;
7343 }
7344 }
7345
7346 if (!languages_to_load.empty()) {
7347 if (!force) {
7348 MultiPromiseActorSafe mpas{"LoadEmojiLanguagesMultiPromiseActor"};
7349 mpas.add_promise(std::move(promise));
7350
7351 auto lock = mpas.get_promise();
7352 for (auto &language_code : languages_to_load) {
7353 load_emoji_keywords(language_code, mpas.get_promise());
7354 }
7355 lock.set_value(Unit());
7356 return {};
7357 } else {
7358 LOG(ERROR) << "Have no " << languages_to_load << " emoji keywords";
7359 }
7360 }
7361
7362 auto text_lowered = utf8_to_lower(text);
7363 vector<string> result;
7364 for (auto &language_code : language_codes) {
7365 combine(result, search_language_emojis(language_code, text_lowered, exact_match));
7366 }
7367
7368 td::unique(result);
7369
7370 promise.set_value(Unit());
7371 return result;
7372 }
7373
get_emoji_suggestions_url(const string & language_code,Promise<Unit> && promise)7374 int64 StickersManager::get_emoji_suggestions_url(const string &language_code, Promise<Unit> &&promise) {
7375 int64 random_id = 0;
7376 do {
7377 random_id = Random::secure_int64();
7378 } while (random_id == 0 || emoji_suggestions_urls_.find(random_id) != emoji_suggestions_urls_.end());
7379 emoji_suggestions_urls_[random_id]; // reserve place for result
7380
7381 auto query_promise =
7382 PromiseCreator::lambda([actor_id = actor_id(this), random_id, promise = std::move(promise)](
7383 Result<telegram_api::object_ptr<telegram_api::emojiURL>> &&result) mutable {
7384 send_closure(actor_id, &StickersManager::on_get_emoji_suggestions_url, random_id, std::move(promise),
7385 std::move(result));
7386 });
7387 td_->create_handler<GetEmojiUrlQuery>(std::move(query_promise))->send(language_code);
7388 return random_id;
7389 }
7390
on_get_emoji_suggestions_url(int64 random_id,Promise<Unit> && promise,Result<telegram_api::object_ptr<telegram_api::emojiURL>> && r_emoji_url)7391 void StickersManager::on_get_emoji_suggestions_url(
7392 int64 random_id, Promise<Unit> &&promise, Result<telegram_api::object_ptr<telegram_api::emojiURL>> &&r_emoji_url) {
7393 auto it = emoji_suggestions_urls_.find(random_id);
7394 CHECK(it != emoji_suggestions_urls_.end());
7395 auto &result = it->second;
7396 CHECK(result.empty());
7397
7398 if (r_emoji_url.is_error()) {
7399 emoji_suggestions_urls_.erase(it);
7400 return promise.set_error(r_emoji_url.move_as_error());
7401 }
7402
7403 auto emoji_url = r_emoji_url.move_as_ok();
7404 result = std::move(emoji_url->url_);
7405 promise.set_value(Unit());
7406 }
7407
get_emoji_suggestions_url_result(int64 random_id)7408 td_api::object_ptr<td_api::httpUrl> StickersManager::get_emoji_suggestions_url_result(int64 random_id) {
7409 auto it = emoji_suggestions_urls_.find(random_id);
7410 CHECK(it != emoji_suggestions_urls_.end());
7411 auto result = td_api::make_object<td_api::httpUrl>(it->second);
7412 emoji_suggestions_urls_.erase(it);
7413 return result;
7414 }
7415
after_get_difference()7416 void StickersManager::after_get_difference() {
7417 if (td_->auth_manager_->is_bot()) {
7418 return;
7419 }
7420 if (td_->is_online()) {
7421 get_installed_sticker_sets(false, Auto());
7422 get_installed_sticker_sets(true, Auto());
7423 get_featured_sticker_sets(0, 1000, Auto());
7424 get_recent_stickers(false, Auto());
7425 get_recent_stickers(true, Auto());
7426 get_favorite_stickers(Auto());
7427
7428 if (!disable_animated_emojis_) {
7429 reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji());
7430 reload_special_sticker_set_by_type(SpecialStickerSetType::animated_emoji_click());
7431 }
7432 }
7433 }
7434
get_current_state(vector<td_api::object_ptr<td_api::Update>> & updates) const7435 void StickersManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
7436 if (td_->auth_manager_->is_bot()) {
7437 return;
7438 }
7439
7440 for (int is_masks = 0; is_masks < 2; is_masks++) {
7441 if (are_installed_sticker_sets_loaded_[is_masks]) {
7442 updates.push_back(get_update_installed_sticker_sets_object(is_masks));
7443 }
7444 }
7445 if (are_featured_sticker_sets_loaded_) {
7446 updates.push_back(get_update_trending_sticker_sets_object());
7447 }
7448 for (int is_attached = 0; is_attached < 2; is_attached++) {
7449 if (are_recent_stickers_loaded_[is_attached]) {
7450 updates.push_back(get_update_recent_stickers_object(is_attached));
7451 }
7452 }
7453 if (are_favorite_stickers_loaded_) {
7454 updates.push_back(get_update_favorite_stickers_object());
7455 }
7456 if (!dice_emojis_.empty()) {
7457 updates.push_back(get_update_dice_emojis_object());
7458 }
7459 }
7460
7461 } // namespace td
7462