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> &current_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