Merge commit 'd26cf8f8a3b38bbecf116a399fa7937302fdccf6'

This commit is contained in:
Andrea Cavalli 2020-04-25 12:33:10 +02:00
commit 3bc0461de9
49 changed files with 1748 additions and 577 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TDLib VERSION 1.6.2 LANGUAGES CXX C)
project(TDLib VERSION 1.6.3 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR "lib")
@ -475,6 +475,7 @@ set(TDLIB_SOURCE
td/telegram/SecureValue.cpp
td/telegram/SendCodeHelper.cpp
td/telegram/SequenceDispatcher.cpp
td/telegram/SpecialStickerSetType.cpp
td/telegram/StateManager.cpp
td/telegram/StickersManager.cpp
td/telegram/StorageManager.cpp
@ -656,6 +657,7 @@ set(TDLIB_SOURCE
td/telegram/SequenceDispatcher.h
td/telegram/ServerMessageId.h
td/telegram/SetWithPosition.h
td/telegram/SpecialStickerSetType.h
td/telegram/StateManager.h
td/telegram/StickerSetId.h
td/telegram/StickersManager.h

View File

@ -146,7 +146,7 @@ target_link_libraries(YourTarget PRIVATE Td::TdStatic)
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
```
find_package(Td 1.6.2 REQUIRED)
find_package(Td 1.6.3 REQUIRED)
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt).

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(TdExample VERSION 1.0 LANGUAGES CXX)
find_package(Td 1.6.2 REQUIRED)
find_package(Td 1.6.3 REQUIRED)
add_executable(tdjson_example tdjson_example.cpp)
target_link_libraries(tdjson_example PRIVATE Td::TdJson)

View File

@ -1,6 +1,6 @@
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
<Metadata>
<Identity Id="Telegram.Td.UWP" Version="1.6.2" Language="en-US" Publisher="Telegram LLC" />
<Identity Id="Telegram.Td.UWP" Version="1.6.3" Language="en-US" Publisher="Telegram LLC" />
<DisplayName>TDLib for Universal Windows Platform</DisplayName>
<Description>TDLib is a library for building Telegram clients</Description>
<MoreInfo>https://core.telegram.org/tdlib</MoreInfo>

View File

@ -216,8 +216,10 @@ pollOption text:string voter_count:int32 vote_percentage:int32 is_chosen:Bool is
//@description A regular poll @allow_multiple_answers True, if multiple answer options can be chosen simultaneously
pollTypeRegular allow_multiple_answers:Bool = PollType;
//@description A poll in quiz mode, which has exactly one correct answer option and can be answered only once @correct_option_id 0-based identifier of the correct answer option; -1 for a yet unanswered poll
pollTypeQuiz correct_option_id:int32 = PollType;
//@description A poll in quiz mode, which has exactly one correct answer option and can be answered only once
//@correct_option_id 0-based identifier of the correct answer option; -1 for a yet unanswered poll
//@explanation Text that is shown when the user chooses an incorrect answer or taps on the lamp icon, 0-200 characters with at most 2 line feeds; empty for a yet unanswered poll
pollTypeQuiz correct_option_id:int32 explanation:formattedText = PollType;
//@description Describes an animation file. The animation must be encoded in GIF or MPEG4 format @duration Duration of the animation, in seconds; as defined by the sender @width Width of the animation @height Height of the animation
@ -268,8 +270,9 @@ game id:int64 short_name:string title:string text:formattedText description:stri
//@description Describes a poll @id Unique poll identifier @question Poll question, 1-255 characters @options List of poll answer options
//@total_voter_count Total number of voters, participating in the poll @recent_voter_user_ids User identifiers of recent voters, if the poll is non-anonymous
//@is_anonymous True, if the poll is anonymous @type Type of the poll @is_closed True, if the poll is closed
poll id:int64 question:string options:vector<pollOption> total_voter_count:int32 recent_voter_user_ids:vector<int32> is_anonymous:Bool type:PollType is_closed:Bool = Poll;
//@is_anonymous True, if the poll is anonymous @type Type of the poll
//@open_period Amount of time the poll will be active after creation, in seconds @close_date Point in time (Unix timestamp) when the poll will be automatically closed @is_closed True, if the poll is closed
poll id:int64 question:string options:vector<pollOption> total_voter_count:int32 recent_voter_user_ids:vector<int32> is_anonymous:Bool type:PollType open_period:int32 close_date:int32 is_closed:Bool = Poll;
//@description Describes a user profile photo @id Photo identifier; 0 for an empty photo. Can be used to find a photo in a list of userProfilePhotos
@ -1377,8 +1380,13 @@ messageVenue venue:venue = MessageContent;
//@description A message with a user contact @contact The contact description
messageContact contact:contact = MessageContent;
//@description A dice message. The dice value is randomly generated by the server @value The dice value; 0-6. If the value is 0, the dice must roll infinitely
messageDice value:int32 = MessageContent;
//@description A dice message. The dice value is randomly generated by the server
//@initial_state_sticker The animated sticker with the initial dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known
//@final_state_sticker The animated sticker with the final dice animation; may be null if unknown. updateMessageContent will be sent when the sticker became known
//@emoji Emoji on which the dice throw animation is based
//@value The dice value. If the value is 0, the dice don't have final state yet
//@success_animation_frame_number Number of frame after which a success animation like a shower of confetti needs to be shown on updateMessageSendSucceeded
messageDice initial_state_sticker:sticker final_state_sticker:sticker emoji:string value:int32 success_animation_frame_number:int32 = MessageContent;
//@description A message with a game @game The game description
messageGame game:game = MessageContent;
@ -1581,8 +1589,8 @@ inputMessageVenue venue:venue = InputMessageContent;
//@description A message containing a user contact @contact Contact to send
inputMessageContact contact:contact = InputMessageContent;
//@description A dice message
inputMessageDice = InputMessageContent;
//@description A dice message @emoji Emoji on which the dice throw animation is based @clear_draft True, if a chat message draft should be deleted
inputMessageDice emoji:string clear_draft:Bool = InputMessageContent;
//@description A message with a game; not supported for channels or secret chats @bot_user_id User identifier of the bot that owns the game @game_short_name Short name of the game
inputMessageGame bot_user_id:int32 game_short_name:string = InputMessageContent;
@ -1592,8 +1600,11 @@ inputMessageGame bot_user_id:int32 game_short_name:string = InputMessageContent;
inputMessageInvoice invoice:invoice title:string description:string photo_url:string photo_size:int32 photo_width:int32 photo_height:int32 payload:bytes provider_token:string provider_data:string start_parameter:string = InputMessageContent;
//@description A message with a poll. Polls can't be sent to secret chats. Polls can be sent only to a private chat with a bot @question Poll question, 1-255 characters @options List of poll answer options, 2-10 strings 1-100 characters each
//@is_anonymous True, if the poll voters are anonymous. Non-anonymous polls can't be sent or forwarded to channels @type Type of the poll @is_closed True, if the poll needs to be sent already closed; for bots only
inputMessagePoll question:string options:vector<string> is_anonymous:Bool type:PollType is_closed:Bool = InputMessageContent;
//@is_anonymous True, if the poll voters are anonymous. Non-anonymous polls can't be sent or forwarded to channels @type Type of the poll
//@open_period Amount of time the poll will be active after creation, in seconds; for bots only
//@close_date Point in time (Unix timestamp) when the poll will be automatically closed; for bots only
//@is_closed True, if the poll needs to be sent already closed; for bots only
inputMessagePoll question:string options:vector<string> is_anonymous:Bool type:PollType open_period:int32 close_date:int32 is_closed:Bool = InputMessageContent;
//@description A forwarded message @from_chat_id Identifier for the chat this forwarded message came from @message_id Identifier of the message to forward
//@in_game_share True, if a game message should be shared within a launched game; applies only to game messages
@ -2770,7 +2781,7 @@ dateRange start_date:int32 end_date:int32 = DateRange;
statisticsValue value:double previous_value:double growth_rate_percentage:double = StatisticsValue;
//@class StatisticsGraph @description Descrbes a statistics graph
//@class StatisticsGraph @description Describes a statistics graph
//@description A graph data @json_data Graph data in JSON format @zoom_token If non-empty, a token which can be used to receive a zoomed in graph
statisticsGraphData json_data:string zoom_token:string = StatisticsGraph;
@ -3004,10 +3015,13 @@ updateUnreadChatCount chat_list:ChatList total_count:int32 unread_count:int32 un
//@description An option changed its value @name The option name @value The new option value
updateOption name:string value:OptionValue = Update;
//@description A sticker set has changed @sticker_set The sticker set
updateStickerSet sticker_set:stickerSet = Update;
//@description The list of installed sticker sets was updated @is_masks True, if the list of installed mask sticker sets was updated @sticker_set_ids The new list of installed ordinary sticker sets
updateInstalledStickerSets is_masks:Bool sticker_set_ids:vector<int64> = Update;
//@description The list of trending sticker sets was updated or some of them were viewed @sticker_sets The new list of trending sticker sets
//@description The list of trending sticker sets was updated or some of them were viewed @sticker_sets The prefix of the list of trending sticker sets with the newest trending sticker sets
updateTrendingStickerSets sticker_sets:stickerSets = Update;
//@description The list of recently used stickers was updated @is_attached True, if the list of stickers attached to photo or video files was updated, otherwise the list of sent stickers is updated @sticker_ids The new list of file identifiers of recently used stickers
@ -3031,9 +3045,12 @@ updateConnectionState state:ConnectionState = Update;
//@description New terms of service must be accepted by the user. If the terms of service are declined, then the deleteAccount method should be called with the reason "Decline ToS update" @terms_of_service_id Identifier of the terms of service @terms_of_service The new terms of service
updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService = Update;
//@description List of users nearby has changed. The update is sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
//@description The list of users nearby has changed. The update is sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description The list of supported dice emojis has changed @emojis The new list of supported dice emojis
updateDiceEmojis emojis:vector<string> = Update;
//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null
//@query Text of the query @offset Offset of the first entry to return
updateNewInlineQuery id:int64 sender_user_id:int32 user_location:location query:string offset:string = Update;
@ -3884,8 +3901,10 @@ getInstalledStickerSets is_masks:Bool = StickerSets;
//@description Returns a list of archived sticker sets @is_masks Pass true to return mask stickers sets; pass false to return ordinary sticker sets @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return
getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = StickerSets;
//@description Returns a list of trending sticker sets
getTrendingStickerSets = StickerSets;
//@description Returns a list of trending sticker sets. For the optimal performance the number of returned sticker sets is chosen by the library
//@offset The offset from which to return the sticker sets; must be non-negative
//@limit The maximum number of sticker sets to be returned; must be non-negative. Fewer sticker sets may be returned than specified by the limit, even if the end of the list has not been reached
getTrendingStickerSets offset:int32 limit:int32 = StickerSets;
//@description Returns a list of sticker sets attached to a file. Currently only photos and videos can have attached sticker sets @file_id File identifier
getAttachedStickerSets file_id:int32 = StickerSets;
@ -3937,8 +3956,8 @@ removeFavoriteSticker sticker:InputFile = Ok;
//@description Returns emoji corresponding to a sticker. The list is only for informational purposes, because a sticker is always sent with a fixed emoji from the corresponding Sticker object @sticker Sticker file identifier
getStickerEmojis sticker:InputFile = Emojis;
//@description Searches for emojis by keywords. Supported only if the file database is enabled @text Text to search for @exact_match True, if only emojis, which exactly match text needs to be returned @input_language_code IETF language tag of the user's input language; may be empty if unknown
searchEmojis text:string exact_match:Bool input_language_code:string = Emojis;
//@description Searches for emojis by keywords. Supported only if the file database is enabled @text Text to search for @exact_match True, if only emojis, which exactly match text needs to be returned @input_language_codes List of possible IETF language tags of the user's input language; may be empty if unknown
searchEmojis text:string exact_match:Bool input_language_codes:vector<string> = Emojis;
//@description Returns an HTTP URL which can be used to automatically log in to the translation platform and suggest new emoji replacements. The URL will be valid for 30 seconds after generation @language_code Language code for which the emoji replacements will be suggested
getEmojiSuggestionsUrl language_code:string = HttpUrl;
@ -4189,7 +4208,7 @@ getChatStatisticsUrl chat_id:int53 parameters:string is_dark:Bool = HttpUrl;
//@description Returns detailed statistics about a chat. Currently this method can be used only for channels. Requires administrator rights in the channel @chat_id Chat identifier @is_dark Pass true if a dark theme is used by the app
getChatStatistics chat_id:int53 is_dark:Bool = ChatStatistics;
//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifer @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise
//@description Loads asynchronous or zoomed in chat statistics graph @chat_id Chat identifier @token The token for graph loading @x X-value for zoomed in graph or 0 otherwise
getChatStatisticsGraph chat_id:int53 token:string x:int53 = StatisticsGraph;

Binary file not shown.

View File

@ -58,8 +58,8 @@ inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int =
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> = InputMedia;
inputMediaDice#aeffa807 = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
@ -144,7 +144,7 @@ messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#638fe46b value:int = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
@ -520,7 +520,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#79e21a53 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
@ -679,8 +679,8 @@ contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
messages.featuredStickers#f89d88e5 hash:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
messages.featuredStickers#b6abc341 hash:int count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
messages.recentStickers#22f3afb3 hash:int packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
@ -1011,11 +1011,11 @@ help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:strin
pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
poll#d5529d06 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> = Poll;
poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
pollResults#c87024a2 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> = PollResults;
pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
chatOnlines#f041e250 onlines:int = ChatOnlines;
@ -1131,7 +1131,7 @@ stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueA
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
@ -1369,6 +1369,7 @@ messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
messages.getOldFeaturedStickers#5fe7025b offset:int limit:int hash:int = messages.FeaturedStickers;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;

Binary file not shown.

View File

@ -20,6 +20,7 @@
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/NotificationManager.h"
#include "td/telegram/PasswordManager.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TopDialogManager.h"
@ -727,6 +728,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
G()->shared_config().set_option_integer("session_count", auth->tmp_sessions_);
}
td->notification_manager_->init();
td->stickers_manager_->init();
send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up);
td->updates_manager_->get_difference("on_get_authorization");
td->on_online_updated(false, true);

View File

@ -57,6 +57,7 @@
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
namespace td {
@ -1093,7 +1094,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
auto r_config = fetch_result<telegram_api::help_getConfig>(std::move(res));
if (r_config.is_error()) {
if (!G()->close_flag()) {
LOG(ERROR) << "getConfig failed: " << r_config.error();
LOG(WARNING) << "Failed to get config: " << r_config.error();
expire_time_ = Timestamp::in(60.0); // try again in a minute
set_timeout_in(expire_time_.in());
}
@ -1261,12 +1262,6 @@ void ConfigManager::process_config(tl_object_ptr<telegram_api::config> config) {
shared_config.set_option_empty("notify_default_delay_ms");
shared_config.set_option_empty("large_chat_size");
if (is_from_main_dc) {
for (auto &feature : shared_config.get_options("disabled_")) {
shared_config.set_option_empty(feature.first);
}
}
// TODO implement online status updates
// shared_config.set_option_integer("offline_blur_timeout_ms", config->offline_blur_timeout_ms_);
// shared_config.set_option_integer("offline_idle_timeout_ms", config->offline_idle_timeout_ms_);
@ -1291,6 +1286,9 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
string wallet_blockchain_name;
string wallet_config;
string ignored_restriction_reasons;
vector<string> dice_emojis;
std::unordered_map<string, size_t> dice_emoji_index;
std::unordered_map<string, string> dice_emoji_success_value;
if (config->get_id() == telegram_api::jsonObject::ID) {
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
Slice key = key_value->key_;
@ -1318,6 +1316,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (value->get_id() == telegram_api::jsonArray::ID) {
auto reasons = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &reason : reasons) {
CHECK(reason != nullptr);
if (reason->get_id() == telegram_api::jsonString::ID) {
Slice reason_name = static_cast<telegram_api::jsonString *>(reason.get())->value_;
if (!reason_name.empty() && reason_name.find(',') == Slice::npos) {
@ -1337,6 +1336,64 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
}
continue;
}
if (key == "emojies_send_dice") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto emojis = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &emoji : emojis) {
CHECK(emoji != nullptr);
if (emoji->get_id() == telegram_api::jsonString::ID) {
Slice emoji_text = static_cast<telegram_api::jsonString *>(emoji.get())->value_;
if (!emoji_text.empty()) {
dice_emoji_index[emoji_text.str()] = dice_emojis.size();
dice_emojis.push_back(emoji_text.str());
} else {
LOG(ERROR) << "Receive empty dice emoji";
}
} else {
LOG(ERROR) << "Receive unexpected dice emoji " << to_string(emoji);
}
}
} else {
LOG(ERROR) << "Receive unexpected emojies_send_dice " << to_string(*value);
}
continue;
}
if (key == "emojies_send_dice_success") {
if (value->get_id() == telegram_api::jsonObject::ID) {
auto success_values = std::move(static_cast<telegram_api::jsonObject *>(value)->value_);
for (auto &success_value : success_values) {
CHECK(success_value != nullptr);
if (success_value->value_->get_id() == telegram_api::jsonObject::ID) {
int32 dice_value = -1;
int32 frame_start = -1;
for (auto &dice_key_value :
static_cast<telegram_api::jsonObject *>(success_value->value_.get())->value_) {
if (dice_key_value->value_->get_id() != telegram_api::jsonNumber::ID) {
continue;
}
auto current_value =
static_cast<int32>(static_cast<telegram_api::jsonNumber *>(dice_key_value->value_.get())->value_);
if (dice_key_value->key_ == "value") {
dice_value = current_value;
}
if (dice_key_value->key_ == "frame_start") {
frame_start = current_value;
}
}
if (dice_value < 0 || frame_start < 0) {
LOG(ERROR) << "Receive unexpected dice success value " << to_string(success_value);
} else {
dice_emoji_success_value[success_value->key_] = PSTRING() << dice_value << ':' << frame_start;
}
} else {
LOG(ERROR) << "Receive unexpected dice success value " << to_string(success_value);
}
}
} else {
LOG(ERROR) << "Receive unexpected emojies_send_dice_success " << to_string(*value);
}
continue;
}
new_values.push_back(std::move(key_value));
}
@ -1367,6 +1424,19 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
get_content_settings(Auto());
}
}
if (!dice_emojis.empty()) {
shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01'));
vector<string> dice_success_values(dice_emojis.size());
for (auto &it : dice_emoji_success_value) {
if (dice_emoji_index.find(it.first) == dice_emoji_index.end()) {
LOG(ERROR) << "Can't find emoji " << it.first;
continue;
}
dice_success_values[dice_emoji_index[it.first]] = it.second;
}
shared_config.set_option_string("dice_success_values", implode(dice_success_values, ','));
}
}
} // namespace td

View File

@ -59,10 +59,6 @@ string ConfigShared::get_option(Slice name) const {
return config_pmc_->get(name.str());
}
std::unordered_map<string, string> ConfigShared::get_options(Slice prefix) const {
return config_pmc_->prefix_get(prefix);
}
std::unordered_map<string, string> ConfigShared::get_options() const {
return config_pmc_->get_all();
}

View File

@ -41,7 +41,6 @@ class ConfigShared {
void set_option_string(Slice name, Slice value);
bool have_option(Slice name) const;
std::unordered_map<string, string> get_options(Slice prefix) const;
std::unordered_map<string, string> get_options() const;
bool get_option_boolean(Slice name, bool default_value = false) const;

View File

@ -7,6 +7,8 @@
#pragma once
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Version.h"
@ -15,9 +17,6 @@
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_helpers.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include <functional>
#include <tuple>

View File

@ -9080,8 +9080,8 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
return promise.set_value(Unit());
}
auto participant_count =
(channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT) != 0 ? channel_full->participants_count_ : 0;
bool have_participant_count = (channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT) != 0;
auto participant_count = have_participant_count ? channel_full->participants_count_ : 0;
auto administrator_count =
(channel_full->flags_ & CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT) != 0 ? channel_full->admins_count_ : 0;
auto restricted_count =
@ -9127,7 +9127,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr<telegram_api::ChatFull> &&c
channel->is_changed = true;
if (participant_count != 0 && c->participant_count != participant_count) {
if (have_participant_count && c->participant_count != participant_count) {
c->participant_count = participant_count;
c->is_changed = true;
update_channel(c, channel_id);
@ -9315,8 +9315,13 @@ void ContactsManager::on_update_user_photo(UserId user_id, tl_object_ptr<telegra
void ContactsManager::on_update_user_photo(User *u, UserId user_id,
tl_object_ptr<telegram_api::UserProfilePhoto> &&photo, const char *source) {
if (td_->auth_manager_->is_bot() && !G()->parameters().use_file_db && !u->is_photo_inited) {
auto &old_photo = pending_user_photos_[user_id];
if (!LOG_IS_STRIPPED(ERROR) && to_string(old_photo) == to_string(photo)) {
return;
}
bool is_empty = photo == nullptr || photo->get_id() == telegram_api::userProfilePhotoEmpty::ID;
pending_user_photos_[user_id] = std::move(photo);
old_photo = std::move(photo);
drop_user_photos(user_id, is_empty, "on_update_user_photo");
return;
@ -11545,7 +11550,7 @@ UserId ContactsManager::get_me(Promise<Unit> &&promise) {
bool ContactsManager::get_user(UserId user_id, int left_tries, Promise<Unit> &&promise) {
if (!user_id.is_valid()) {
promise.set_error(Status::Error(6, "Invalid user ID"));
promise.set_error(Status::Error(6, "Invalid user identifier"));
return false;
}
@ -11616,7 +11621,7 @@ ContactsManager::UserFull *ContactsManager::add_user_full(UserId user_id) {
void ContactsManager::reload_user(UserId user_id, Promise<Unit> &&promise) {
if (!user_id.is_valid()) {
return promise.set_error(Status::Error(6, "Invalid user id"));
return promise.set_error(Status::Error(6, "Invalid user identifier"));
}
have_user_force(user_id);
@ -11850,7 +11855,7 @@ ContactsManager::Chat *ContactsManager::add_chat(ChatId chat_id) {
bool ContactsManager::get_chat(ChatId chat_id, int left_tries, Promise<Unit> &&promise) {
if (!chat_id.is_valid()) {
promise.set_error(Status::Error(6, "Invalid basic group id"));
promise.set_error(Status::Error(6, "Invalid basic group identifier"));
return false;
}
@ -11876,7 +11881,7 @@ bool ContactsManager::get_chat(ChatId chat_id, int left_tries, Promise<Unit> &&p
void ContactsManager::reload_chat(ChatId chat_id, Promise<Unit> &&promise) {
if (!chat_id.is_valid()) {
return promise.set_error(Status::Error(6, "Invalid basic group id"));
return promise.set_error(Status::Error(6, "Invalid basic group identifier"));
}
// there is no much reason to combine different requests into one request
@ -12169,7 +12174,7 @@ ContactsManager::Channel *ContactsManager::add_channel(ChannelId channel_id, con
bool ContactsManager::get_channel(ChannelId channel_id, int left_tries, Promise<Unit> &&promise) {
if (!channel_id.is_valid()) {
promise.set_error(Status::Error(6, "Invalid supergroup id"));
promise.set_error(Status::Error(6, "Invalid supergroup identifier"));
return false;
}
@ -12195,7 +12200,7 @@ bool ContactsManager::get_channel(ChannelId channel_id, int left_tries, Promise<
void ContactsManager::reload_channel(ChannelId channel_id, Promise<Unit> &&promise) {
if (!channel_id.is_valid()) {
return promise.set_error(Status::Error(6, "Invalid supergroup id"));
return promise.set_error(Status::Error(6, "Invalid supergroup identifier"));
}
have_channel_force(channel_id);
@ -12328,7 +12333,7 @@ ContactsManager::SecretChat *ContactsManager::get_secret_chat(SecretChatId secre
bool ContactsManager::get_secret_chat(SecretChatId secret_chat_id, bool force, Promise<Unit> &&promise) {
if (!secret_chat_id.is_valid()) {
promise.set_error(Status::Error(6, "Invalid secret chat id"));
promise.set_error(Status::Error(6, "Invalid secret chat identifier"));
return false;
}

View File

@ -240,6 +240,7 @@ class DialogDbImpl : public DialogDbSyncInterface {
return std::move(result);
}
Result<vector<NotificationGroupKey>> get_notification_groups_by_last_notification_date(
NotificationGroupKey notification_group_key, int32 limit) override {
auto &stmt = get_notification_groups_by_last_notification_date_stmt_;
@ -262,6 +263,7 @@ class DialogDbImpl : public DialogDbSyncInterface {
return std::move(notification_groups);
}
Status begin_transaction() override {
return db_.begin_transaction();
}

View File

@ -645,17 +645,33 @@ class MessagePoll : public MessageContent {
class MessageDice : public MessageContent {
public:
string emoji;
int32 dice_value = 0;
static constexpr const char *DEFAULT_EMOJI = "🎲";
MessageDice() = default;
explicit MessageDice(int32 dice_value) : dice_value(dice_value) {
MessageDice(string emoji, int32 dice_value)
: emoji(emoji.empty() ? string(DEFAULT_EMOJI) : std::move(emoji)), dice_value(dice_value) {
}
MessageContentType get_type() const override {
return MessageContentType::Dice;
}
bool is_valid() const {
if (dice_value < 0) {
return false;
}
if (emoji == "DEFAULT_EMOJI" || emoji == "🎯") {
return dice_value <= 6;
}
return dice_value <= 1000;
}
};
constexpr const char *MessageDice::DEFAULT_EMOJI;
template <class StorerT>
static void store(const MessageContent *content, StorerT &storer) {
CHECK(content != nullptr);
@ -915,6 +931,7 @@ static void store(const MessageContent *content, StorerT &storer) {
}
case MessageContentType::Dice: {
auto m = static_cast<const MessageDice *>(content);
store(m->emoji, storer);
store(m->dice_value, storer);
break;
}
@ -1262,8 +1279,13 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
}
case MessageContentType::Dice: {
auto m = make_unique<MessageDice>();
if (parser.version() >= static_cast<int32>(Version::AddDiceEmoji)) {
parse(m->emoji, parser);
} else {
m->emoji = MessageDice::DEFAULT_EMOJI;
}
parse(m->dice_value, parser);
is_bad = m->dice_value < 0 || m->dice_value > 6;
is_bad = !m->is_valid();
content = std::move(m);
break;
}
@ -1475,9 +1497,15 @@ static Result<InputMessageContent> create_input_message_content(
content = make_unique<MessageAudio>(file_id, std::move(caption));
break;
}
case td_api::inputMessageDice::ID:
content = make_unique<MessageDice>();
case td_api::inputMessageDice::ID: {
auto input_dice = static_cast<td_api::inputMessageDice *>(input_message_content.get());
if (!clean_input_string(input_dice->emoji_)) {
return Status::Error(400, "Dice emoji must be encoded in UTF-8");
}
content = td::make_unique<MessageDice>(std::move(input_dice->emoji_), 0);
clear_draft = input_dice->clear_draft_;
break;
}
case td_api::inputMessageDocument::ID:
td->documents_manager_->create_document(file_id, string(), thumbnail, std::move(file_name), std::move(mime_type),
false);
@ -1612,37 +1640,37 @@ static Result<InputMessageContent> create_input_message_content(
return Status::Error(400, "Invoices can be sent only by bots");
}
auto input_message_invoice = move_tl_object_as<td_api::inputMessageInvoice>(input_message_content);
if (!clean_input_string(input_message_invoice->title_)) {
auto input_invoice = move_tl_object_as<td_api::inputMessageInvoice>(input_message_content);
if (!clean_input_string(input_invoice->title_)) {
return Status::Error(400, "Invoice title must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->description_)) {
if (!clean_input_string(input_invoice->description_)) {
return Status::Error(400, "Invoice description must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->photo_url_)) {
if (!clean_input_string(input_invoice->photo_url_)) {
return Status::Error(400, "Invoice photo URL must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->start_parameter_)) {
if (!clean_input_string(input_invoice->start_parameter_)) {
return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->provider_token_)) {
if (!clean_input_string(input_invoice->provider_token_)) {
return Status::Error(400, "Invoice provider token must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->provider_data_)) {
if (!clean_input_string(input_invoice->provider_data_)) {
return Status::Error(400, "Invoice provider data must be encoded in UTF-8");
}
if (!clean_input_string(input_message_invoice->invoice_->currency_)) {
if (!clean_input_string(input_invoice->invoice_->currency_)) {
return Status::Error(400, "Invoice currency must be encoded in UTF-8");
}
auto message_invoice = make_unique<MessageInvoice>();
message_invoice->title = std::move(input_message_invoice->title_);
message_invoice->description = std::move(input_message_invoice->description_);
message_invoice->title = std::move(input_invoice->title_);
message_invoice->description = std::move(input_invoice->description_);
auto r_http_url = parse_url(input_message_invoice->photo_url_);
auto r_http_url = parse_url(input_invoice->photo_url_);
if (r_http_url.is_error()) {
if (!input_message_invoice->photo_url_.empty()) {
LOG(INFO) << "Can't register url " << input_message_invoice->photo_url_;
if (!input_invoice->photo_url_.empty()) {
LOG(INFO) << "Can't register url " << input_invoice->photo_url_;
}
message_invoice->photo.id = -2;
} else {
@ -1656,20 +1684,20 @@ static Result<InputMessageContent> create_input_message_content(
PhotoSize s;
s.type = 'u';
s.dimensions = get_dimensions(input_message_invoice->photo_width_, input_message_invoice->photo_height_);
s.size = input_message_invoice->photo_size_; // TODO use invoice_file_id size
s.dimensions = get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_);
s.size = input_invoice->photo_size_; // TODO use invoice_file_id size
s.file_id = invoice_file_id;
message_invoice->photo.photos.push_back(s);
}
}
message_invoice->start_parameter = std::move(input_message_invoice->start_parameter_);
message_invoice->start_parameter = std::move(input_invoice->start_parameter_);
message_invoice->invoice.currency = std::move(input_message_invoice->invoice_->currency_);
message_invoice->invoice.price_parts.reserve(input_message_invoice->invoice_->price_parts_.size());
message_invoice->invoice.currency = std::move(input_invoice->invoice_->currency_);
message_invoice->invoice.price_parts.reserve(input_invoice->invoice_->price_parts_.size());
int64 total_amount = 0;
const int64 MAX_AMOUNT = 9999'9999'9999;
for (auto &price : input_message_invoice->invoice_->price_parts_) {
for (auto &price : input_invoice->invoice_->price_parts_) {
if (!clean_input_string(price->label_)) {
return Status::Error(400, "Invoice price label must be encoded in UTF-8");
}
@ -1687,16 +1715,15 @@ static Result<InputMessageContent> create_input_message_content(
}
message_invoice->total_amount = total_amount;
message_invoice->invoice.is_test = input_message_invoice->invoice_->is_test_;
message_invoice->invoice.need_name = input_message_invoice->invoice_->need_name_;
message_invoice->invoice.need_phone_number = input_message_invoice->invoice_->need_phone_number_;
message_invoice->invoice.need_email_address = input_message_invoice->invoice_->need_email_address_;
message_invoice->invoice.need_shipping_address = input_message_invoice->invoice_->need_shipping_address_;
message_invoice->invoice.send_phone_number_to_provider =
input_message_invoice->invoice_->send_phone_number_to_provider_;
message_invoice->invoice.is_test = input_invoice->invoice_->is_test_;
message_invoice->invoice.need_name = input_invoice->invoice_->need_name_;
message_invoice->invoice.need_phone_number = input_invoice->invoice_->need_phone_number_;
message_invoice->invoice.need_email_address = input_invoice->invoice_->need_email_address_;
message_invoice->invoice.need_shipping_address = input_invoice->invoice_->need_shipping_address_;
message_invoice->invoice.send_phone_number_to_provider = input_invoice->invoice_->send_phone_number_to_provider_;
message_invoice->invoice.send_email_address_to_provider =
input_message_invoice->invoice_->send_email_address_to_provider_;
message_invoice->invoice.is_flexible = input_message_invoice->invoice_->is_flexible_;
input_invoice->invoice_->send_email_address_to_provider_;
message_invoice->invoice.is_flexible = input_invoice->invoice_->is_flexible_;
if (message_invoice->invoice.send_phone_number_to_provider) {
message_invoice->invoice.need_phone_number = true;
}
@ -1707,9 +1734,9 @@ static Result<InputMessageContent> create_input_message_content(
message_invoice->invoice.need_shipping_address = true;
}
message_invoice->payload = std::move(input_message_invoice->payload_);
message_invoice->provider_token = std::move(input_message_invoice->provider_token_);
message_invoice->provider_data = std::move(input_message_invoice->provider_data_);
message_invoice->payload = std::move(input_invoice->payload_);
message_invoice->provider_token = std::move(input_invoice->provider_token_);
message_invoice->provider_data = std::move(input_invoice->provider_data_);
content = std::move(message_invoice);
break;
@ -1749,6 +1776,7 @@ static Result<InputMessageContent> create_input_message_content(
bool allow_multiple_answers = false;
bool is_quiz = false;
int32 correct_option_id = -1;
FormattedText explanation;
if (input_poll->type_ == nullptr) {
return Status::Error(400, "Poll type must not be empty");
}
@ -1765,16 +1793,28 @@ static Result<InputMessageContent> create_input_message_content(
if (correct_option_id < 0 || correct_option_id >= static_cast<int32>(input_poll->options_.size())) {
return Status::Error(400, "Wrong correct option ID specified");
}
auto r_explanation =
process_input_caption(td->contacts_manager_.get(), dialog_id, std::move(type->explanation_), is_bot);
if (r_explanation.is_error()) {
return r_explanation.move_as_error();
}
explanation = r_explanation.move_as_ok();
break;
}
default:
UNREACHABLE();
}
int32 open_period = is_bot ? input_poll->open_period_ : 0;
int32 close_date = is_bot ? input_poll->close_date_ : 0;
if (open_period != 0) {
close_date = 0;
}
bool is_closed = is_bot ? input_poll->is_closed_ : false;
content = make_unique<MessagePoll>(td->poll_manager_->create_poll(
std::move(input_poll->question_), std::move(input_poll->options_), input_poll->is_anonymous_,
allow_multiple_answers, is_quiz, correct_option_id, is_closed));
content = make_unique<MessagePoll>(
td->poll_manager_->create_poll(std::move(input_poll->question_), std::move(input_poll->options_),
input_poll->is_anonymous_, allow_multiple_answers, is_quiz, correct_option_id,
std::move(explanation), open_period, close_date, is_closed));
break;
}
default:
@ -2143,8 +2183,10 @@ static tl_object_ptr<telegram_api::InputMedia> get_input_media_impl(
auto m = static_cast<const MessageContact *>(content);
return m->contact.get_input_media_contact();
}
case MessageContentType::Dice:
return make_tl_object<telegram_api::inputMediaDice>();
case MessageContentType::Dice: {
auto m = static_cast<const MessageDice *>(content);
return make_tl_object<telegram_api::inputMediaDice>(m->emoji);
}
case MessageContentType::Document: {
auto m = static_cast<const MessageDocument *>(content);
return td->documents_manager_->get_input_media(m->file_id, std::move(input_file), std::move(input_thumbnail));
@ -3076,7 +3118,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::Dice: {
auto old_ = static_cast<const MessageDice *>(old_content);
auto new_ = static_cast<const MessageDice *>(new_content);
if (old_->dice_value != new_->dice_value) {
if (old_->emoji != new_->emoji || old_->dice_value != new_->dice_value) {
need_update = true;
}
break;
@ -3231,6 +3273,10 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage
case MessageContentType::Poll:
return td->poll_manager_->register_poll(static_cast<const MessagePoll *>(content)->poll_id, full_message_id,
source);
case MessageContentType::Dice: {
auto dice = static_cast<const MessageDice *>(content);
return td->stickers_manager_->register_dice(dice->emoji, dice->dice_value, full_message_id, source);
}
default:
return;
}
@ -3254,6 +3300,14 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const
return;
}
break;
case MessageContentType::Dice:
if (static_cast<const MessageDice *>(old_content)->emoji ==
static_cast<const MessageDice *>(new_content)->emoji &&
static_cast<const MessageDice *>(old_content)->dice_value ==
static_cast<const MessageDice *>(new_content)->dice_value) {
return;
}
break;
default:
return;
}
@ -3271,6 +3325,10 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa
case MessageContentType::Poll:
return td->poll_manager_->unregister_poll(static_cast<const MessagePoll *>(content)->poll_id, full_message_id,
source);
case MessageContentType::Dice: {
auto dice = static_cast<const MessageDice *>(content);
return td->stickers_manager_->unregister_dice(dice->emoji, dice->dice_value, full_message_id, source);
}
default:
return;
}
@ -3711,8 +3769,8 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
case telegram_api::messageMediaDice::ID: {
auto message_dice = move_tl_object_as<telegram_api::messageMediaDice>(media);
auto m = make_unique<MessageDice>(message_dice->value_);
if (m->dice_value < 0 || m->dice_value > 6) {
auto m = td::make_unique<MessageDice>(message_dice->emoticon_, message_dice->value_);
if (!m->is_valid()) {
break;
}
@ -3900,12 +3958,13 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
}
case MessageContentType::Contact:
return make_unique<MessageContact>(*static_cast<const MessageContact *>(content));
case MessageContentType::Dice:
case MessageContentType::Dice: {
auto result = td::make_unique<MessageDice>(*static_cast<const MessageDice *>(content));
if (type != MessageContentDupType::Forward) {
return make_unique<MessageDice>();
} else {
return make_unique<MessageDice>(*static_cast<const MessageDice *>(content));
result->dice_value = 0;
}
return std::move(result);
}
case MessageContentType::Document: {
auto result = make_unique<MessageDocument>(*static_cast<const MessageDocument *>(content));
if (remove_caption) {
@ -4437,7 +4496,13 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
}
case MessageContentType::Dice: {
const MessageDice *m = static_cast<const MessageDice *>(content);
return make_tl_object<td_api::messageDice>(m->dice_value);
auto initial_state = td->stickers_manager_->get_dice_sticker_object(m->emoji, 0);
auto final_state =
m->dice_value == 0 ? nullptr : td->stickers_manager_->get_dice_sticker_object(m->emoji, m->dice_value);
auto success_animation_frame_number =
td->stickers_manager_->get_dice_success_animation_frame_number(m->emoji, m->dice_value);
return make_tl_object<td_api::messageDice>(std::move(initial_state), std::move(final_state), m->emoji,
m->dice_value, success_animation_frame_number);
}
default:
UNREACHABLE();
@ -4778,7 +4843,7 @@ void update_expired_message_content(unique_ptr<MessageContent> &content) {
content = make_unique<MessageExpiredVideo>();
break;
case MessageContentType::Unsupported:
// can happen if message content file id is broken
// can happen if message content file identifier is broken
break;
case MessageContentType::ExpiredPhoto:
case MessageContentType::ExpiredVideo:

View File

@ -2148,7 +2148,7 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
}
vector<MessageEntity> result;
size_t splittable_entity_offset[SPLITTABLE_ENTITY_TYPE_COUNT] = {};
int32 splittable_entity_offset[SPLITTABLE_ENTITY_TYPE_COUNT] = {};
int32 utf16_offset = 0;
for (size_t i = 0; i + 1 < text.size(); i++) {
auto c = static_cast<unsigned char>(text[i]);
@ -3818,6 +3818,9 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
merge_new_entities(entities, find_entities(text, skip_bot_commands));
}
// new whitespace-only entities could be added after splitting of entities
remove_invalid_entities(text, entities);
// TODO MAX_MESSAGE_LENGTH and MAX_CAPTION_LENGTH
return Status::OK();
@ -3899,6 +3902,9 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted
}
bool need_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot) {
if (!dialog_id.is_valid()) {
return true;
}
if (is_bot) {
return false;
}

View File

@ -2230,12 +2230,10 @@ class SendMediaActor : public NetActorOnce {
flags |= telegram_api::messages_sendMedia::ENTITIES_MASK;
}
telegram_api::messages_sendMedia request(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/,
std::move(input_peer), reply_to_message_id.get_server_message_id().get(),
std::move(input_media), text, random_id, std::move(reply_markup),
std::move(entities), schedule_date);
LOG(INFO) << "Send media: " << to_string(request);
auto query = G()->net_query_creator().create(request);
auto query = G()->net_query_creator().create(telegram_api::messages_sendMedia(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer),
reply_to_message_id.get_server_message_id().get(), std::move(input_media), text, random_id,
std::move(reply_markup), std::move(entities), schedule_date));
if (G()->shared_config().get_option_boolean("use_quick_ack") && was_uploaded_) {
query->quick_ack_promise_ = PromiseCreator::lambda(
[random_id](Unit) {
@ -5952,46 +5950,6 @@ void MessagesManager::on_update_delete_scheduled_messages(DialogId dialog_id,
send_update_chat_has_scheduled_messages(d, true);
}
void MessagesManager::on_update_include_sponsored_dialog_to_unread_count() {
if (td_->auth_manager_->is_bot()) {
// just in case
return;
}
bool include_sponsored_dialog = G()->shared_config().get_option_boolean("include_sponsored_chat_to_unread_count");
if (include_sponsored_dialog_to_unread_count_ == include_sponsored_dialog) {
return;
}
if (sponsored_dialog_id_.is_valid()) {
// preload sponsored dialog
get_dialog_force(sponsored_dialog_id_);
}
include_sponsored_dialog_to_unread_count_ = include_sponsored_dialog;
if (!sponsored_dialog_id_.is_valid()) {
// nothing has changed
return;
}
if (!G()->parameters().use_message_db) {
// there is no support for unread count updates without message database
return;
}
auto folder_id = FolderId::main();
auto &list = get_dialog_list(folder_id);
const Dialog *d = get_dialog(sponsored_dialog_id_);
CHECK(d != nullptr);
auto unread_count = d->server_unread_count + d->local_unread_count;
if (unread_count != 0 && list.is_message_unread_count_inited_) {
send_update_unread_message_count(folder_id, d->dialog_id, true,
"on_update_include_sponsored_dialog_to_unread_count");
}
if ((unread_count != 0 || d->is_marked_as_unread) && list.is_dialog_unread_count_inited_) {
send_update_unread_chat_count(folder_id, d->dialog_id, true, "on_update_include_sponsored_dialog_to_unread_count");
}
}
bool MessagesManager::need_cancel_user_dialog_action(int32 action_id, MessageContentType message_content_type) {
if (message_content_type == MessageContentType::None) {
return true;
@ -8526,12 +8484,11 @@ bool MessagesManager::can_delete_message(DialogId dialog_id, const Message *m) c
if (m == nullptr) {
return true;
}
if (m->message_id.is_local()) {
return true;
}
switch (dialog_id.get_type()) {
case DialogType::User:
if (G()->unix_time_cached() < m->date + 86400 && m->content->get_type() == MessageContentType::Dice &&
dialog_id != get_my_dialog_id()) {
return false;
}
return true;
case DialogType::Chat:
return true;
@ -8574,6 +8531,9 @@ bool MessagesManager::can_revoke_message(DialogId dialog_id, const Message *m) c
int32 revoke_time_limit =
G()->shared_config().get_option_integer("revoke_pm_time_limit", DEFAULT_REVOKE_TIME_LIMIT);
if (G()->unix_time_cached() - m->date < 86400 && content_type == MessageContentType::Dice) {
return false;
}
return ((m->is_outgoing && !is_service_message_content(content_type)) ||
(can_revoke_incoming && content_type != MessageContentType::ScreenshotTaken)) &&
G()->unix_time_cached() - m->date <= revoke_time_limit;
@ -8851,7 +8811,7 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from
auto last_new_message_id = d->last_new_message_id;
if (dialog_type != DialogType::SecretChat && last_new_message_id == MessageId()) {
// TODO get dialog from the server and delete history from last message id
// TODO get dialog from the server and delete history from last message identifier
}
bool allow_error = d->messages == nullptr;
@ -8983,6 +8943,20 @@ void MessagesManager::find_old_messages(const Message *m, MessageId max_message_
}
}
void MessagesManager::find_new_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids) {
if (m == nullptr) {
return;
}
if (m->message_id > min_message_id) {
find_new_messages(m->left.get(), min_message_id, message_ids);
message_ids.push_back(m->message_id);
}
find_new_messages(m->right.get(), min_message_id, message_ids);
}
void MessagesManager::find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m,
vector<MessageId> &message_ids, int32 &left_to_unload) const {
if (m == nullptr) {
@ -9456,6 +9430,7 @@ int32 MessagesManager::calc_new_unread_count_from_the_end(Dialog *d, MessageId m
}
// hint_unread_count is definitely wrong, ignore it
if (need_unread_counter(d->order)) {
LOG(ERROR) << "Receive hint_unread_count = " << hint_unread_count << ", but found " << unread_count
<< " unread messages in " << d->dialog_id;
@ -9786,7 +9761,7 @@ void MessagesManager::recalc_unread_count(FolderId folder_id) {
}
}
}
if (d->order != DEFAULT_ORDER) {
if (d->order != DEFAULT_ORDER || is_dialog_sponsored(d)) {
if (dialog_id.get_type() == DialogType::SecretChat) {
secret_chat_total_count++;
} else {
@ -9875,7 +9850,8 @@ void MessagesManager::set_dialog_last_read_inbox_message_id(Dialog *d, MessageId
}
send_update_unread_chat_count(d->folder_id, d->dialog_id, force_update, source);
}
if (message_id != MessageId::min() && d->last_read_inbox_message_id.is_valid() && d->order != DEFAULT_ORDER) {
if (message_id != MessageId::min() && d->last_read_inbox_message_id.is_valid() &&
(d->order != DEFAULT_ORDER || is_dialog_sponsored(d))) {
VLOG(notifications) << "Remove some notifications in " << d->dialog_id
<< " after updating last read inbox message to " << message_id
<< " and unread message count to " << server_unread_count << " + " << local_unread_count
@ -10335,9 +10311,6 @@ void MessagesManager::init() {
start_time_ = Time::now();
include_sponsored_dialog_to_unread_count_ =
G()->shared_config().get_option_boolean("include_sponsored_chat_to_unread_count");
if (G()->parameters().use_message_db) {
// erase old keys
G()->td_db()->get_binlog_pmc()->erase("last_server_dialog_date");
@ -10346,7 +10319,7 @@ void MessagesManager::init() {
auto last_database_server_dialog_dates = G()->td_db()->get_binlog_pmc()->prefix_get("last_server_dialog_date");
for (auto &it : last_database_server_dialog_dates) {
auto r_folder_id = to_integer_safe<int32>(Slice(it.first).substr(Slice("last_server_dialog_date").size()));
auto r_folder_id = to_integer_safe<int32>(it.first);
if (r_folder_id.is_error()) {
LOG(ERROR) << "Can't parse folder ID from " << it.first;
continue;
@ -10395,7 +10368,7 @@ void MessagesManager::init() {
auto unread_message_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_message_count");
for (auto &it : unread_message_counts) {
auto r_folder_id = to_integer_safe<int32>(Slice(it.first).substr(Slice("unread_message_count").size()));
auto r_folder_id = to_integer_safe<int32>(it.first);
if (r_folder_id.is_error()) {
LOG(ERROR) << "Can't parse folder ID from " << it.first;
continue;
@ -10420,13 +10393,13 @@ void MessagesManager::init() {
auto unread_dialog_counts = G()->td_db()->get_binlog_pmc()->prefix_get("unread_dialog_count");
for (auto &it : unread_dialog_counts) {
auto r_folder_id = to_integer_safe<int32>(Slice(it.first).substr(Slice("unread_dialog_count").size()));
auto r_folder_id = to_integer_safe<int32>(it.first);
if (r_folder_id.is_error()) {
LOG(ERROR) << "Can't parse folder ID from " << it.first;
continue;
}
auto counts = transform(full_split(it.second), [](Slice str) { return to_integer_safe<int32>(str); });
auto counts = transform(full_split(Slice(it.second)), [](Slice str) { return to_integer_safe<int32>(str); });
if ((counts.size() != 4 && counts.size() != 6) ||
std::any_of(counts.begin(), counts.end(), [](auto &c) { return c.is_error(); })) {
LOG(ERROR) << "Can't parse " << it.second;
@ -11664,21 +11637,12 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n
first_message_id = last_new_message_id;
}
MessagesConstIterator it(d, MessageId::max());
vector<MessageId> to_delete_message_ids;
while (*it != nullptr) {
auto message_id = (*it)->message_id;
if (message_id <= last_new_message_id) {
break;
}
if (!message_id.is_yet_unsent()) {
to_delete_message_ids.push_back(message_id);
}
--it;
}
find_new_messages(d->messages.get(), last_new_message_id, to_delete_message_ids);
td::remove_if(to_delete_message_ids, [](MessageId message_id) { return message_id.is_yet_unsent(); });
if (!to_delete_message_ids.empty()) {
LOG(ERROR) << "Delete " << format::as_array(to_delete_message_ids) << " because of received last new "
<< last_new_message_id << " in " << d->dialog_id << " from " << source;
LOG(WARNING) << "Delete " << format::as_array(to_delete_message_ids) << " because of received last new "
<< last_new_message_id << " in " << d->dialog_id << " from " << source;
vector<int64> deleted_message_ids;
bool need_update_dialog_pos = false;
@ -11830,7 +11794,7 @@ void MessagesManager::set_dialog_is_pinned(Dialog *d, bool is_pinned) {
LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in set_dialog_is_pinned";
update_dialog_pos(d, "set_dialog_is_pinned", false);
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateChatIsPinned>(d->dialog_id.get(), is_pinned, get_dialog_public_order(d)));
make_tl_object<td_api::updateChatIsPinned>(d->dialog_id.get(), is_pinned, get_dialog_order_object(d)));
}
// there is no need to call update_dialog_pos, it will be called by the caller
}
@ -12372,7 +12336,7 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vector<tl_object_ptr<te
}
}
if (from_pinned_dialog_list) {
auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialogs(folder_id));
auto pinned_dialog_ids = remove_secret_chat_dialog_ids(get_pinned_dialog_ids(folder_id));
std::reverse(pinned_dialog_ids.begin(), pinned_dialog_ids.end());
if (pinned_dialog_ids != added_dialog_ids) {
LOG(INFO) << "Repair pinned dialogs order from " << format::as_array(pinned_dialog_ids) << " to "
@ -13125,7 +13089,7 @@ vector<DialogId> MessagesManager::get_dialogs(FolderId folder_id, DialogDate off
auto d = get_dialog(sponsored_dialog_id_);
CHECK(d != nullptr);
if (is_dialog_sponsored(d)) {
DialogDate date(get_dialog_public_order(d), d->dialog_id);
DialogDate date(get_dialog_public_order(&list, d), d->dialog_id);
if (offset < date) {
result.push_back(sponsored_dialog_id_);
offset = date;
@ -13287,12 +13251,6 @@ void MessagesManager::preload_dialog_list(FolderId folder_id) {
return;
}
if (list.ordered_dialogs_.size() > MAX_PRELOADED_DIALOGS) {
// do nothing if there are more than MAX_PRELOADED_DIALOGS dialogs already loaded
recalc_unread_count(folder_id);
return;
}
if (list.last_loaded_database_dialog_date_ < list.last_database_server_dialog_date_) {
// if there are some dialogs in database, preload some of them
load_dialog_list(folder_id, 20, true, Auto());
@ -13309,7 +13267,7 @@ void MessagesManager::preload_dialog_list(FolderId folder_id) {
}
}
vector<DialogId> MessagesManager::get_pinned_dialogs(FolderId folder_id) const {
vector<DialogId> MessagesManager::get_pinned_dialog_ids(FolderId folder_id) const {
vector<DialogId> result;
auto *list = get_dialog_list(folder_id);
@ -13421,7 +13379,7 @@ vector<DialogId> MessagesManager::sort_dialogs_by_order(const vector<DialogId> &
auto dialog_dates = transform(dialog_ids, [this, &fake_order](DialogId dialog_id) {
const Dialog *d = get_dialog(dialog_id);
CHECK(d != nullptr);
auto order = get_dialog_public_order(d);
auto order = get_dialog_public_order(d->folder_id, d);
if (is_dialog_inited(d) || order != DEFAULT_ORDER) {
return DialogDate(order, dialog_id);
}
@ -14451,7 +14409,7 @@ Status MessagesManager::toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinn
}
if (is_pinned) {
auto pinned_dialog_ids = get_pinned_dialogs(d->folder_id);
auto pinned_dialog_ids = get_pinned_dialog_ids(d->folder_id);
auto pinned_dialog_count = pinned_dialog_ids.size();
auto secret_pinned_dialog_count =
std::count_if(pinned_dialog_ids.begin(), pinned_dialog_ids.end(),
@ -14546,7 +14504,7 @@ Status MessagesManager::set_pinned_dialogs(FolderId folder_id, vector<DialogId>
return Status::Error(400, "Duplicate chats in the list of pinned chats");
}
auto pinned_dialog_ids = get_pinned_dialogs(folder_id);
auto pinned_dialog_ids = get_pinned_dialog_ids(folder_id);
if (pinned_dialog_ids == dialog_ids) {
return Status::OK();
}
@ -15503,7 +15461,7 @@ td_api::object_ptr<td_api::chat> MessagesManager::get_chat_object(const Dialog *
d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_chat_list_object(d), get_dialog_title(d->dialog_id),
get_chat_photo_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)),
get_dialog_permissions(d->dialog_id).get_chat_permissions_object(),
get_message_object(d->dialog_id, get_message(d, d->last_message_id)), get_dialog_public_order(d),
get_message_object(d->dialog_id, get_message(d, d->last_message_id)), get_dialog_order_object(d),
d->pinned_order != DEFAULT_ORDER, d->is_marked_as_unread, is_dialog_sponsored(d),
get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users,
can_report_dialog(d->dialog_id), d->notification_settings.silent_send_message,
@ -16653,6 +16611,10 @@ FileSourceId MessagesManager::get_message_file_source_id(FullMessageId full_mess
}
void MessagesManager::add_message_file_sources(DialogId dialog_id, const Message *m) {
if (dialog_id.get_type() != DialogType::SecretChat && m->is_content_secret) {
return;
}
auto file_ids = get_message_content_file_ids(m->content.get(), td_);
if (file_ids.empty()) {
return;
@ -16683,6 +16645,10 @@ void MessagesManager::remove_message_file_sources(DialogId dialog_id, const Mess
}
void MessagesManager::change_message_files(DialogId dialog_id, const Message *m, const vector<FileId> &old_file_ids) {
if (dialog_id.get_type() != DialogType::SecretChat && m->is_content_secret) {
return;
}
auto new_file_ids = get_message_content_file_ids(m->content.get(), td_);
if (new_file_ids == old_file_ids) {
return;
@ -17931,7 +17897,6 @@ int64 MessagesManager::begin_send_message(DialogId dialog_id, const Message *m)
CHECK(m->random_id != 0 && being_sent_messages_.find(m->random_id) == being_sent_messages_.end());
CHECK(m->message_id.is_yet_unsent());
being_sent_messages_[m->random_id] = FullMessageId(dialog_id, m->message_id);
debug_being_sent_messages_[m->random_id] = dialog_id;
return m->random_id;
}
@ -19497,6 +19462,9 @@ bool MessagesManager::can_edit_message(DialogId dialog_id, const Message *m, boo
if (!channel_status.can_edit_messages() && !(channel_status.can_post_messages() && m->is_outgoing)) {
return false;
}
if (channel_status.can_edit_messages()) {
has_edit_time_limit = false;
}
}
if (is_bot && only_reply_markup) {
has_edit_time_limit = false;
@ -21371,22 +21339,6 @@ bool MessagesManager::on_update_message_id(int64 random_id, MessageId new_messag
if (!new_message_id.is_valid()) {
LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " from "
<< source;
auto it = debug_being_sent_messages_.find(random_id);
if (it == debug_being_sent_messages_.end()) {
LOG(ERROR) << "Message with random_id " << random_id << " was not sent";
return false;
}
auto dialog_id = it->second;
if (!dialog_id.is_valid()) {
LOG(ERROR) << "Sent message is in invalid " << dialog_id;
return false;
}
if (!have_dialog(dialog_id)) {
LOG(ERROR) << "Sent message is in not found " << dialog_id;
return false;
}
LOG(ERROR) << "Receive " << new_message_id << " in updateMessageId with random_id " << random_id << " in "
<< dialog_id;
return false;
}
@ -22679,7 +22631,9 @@ void MessagesManager::remove_message_dialog_notifications(Dialog *d, MessageId m
void MessagesManager::send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const {
CHECK(m != nullptr);
CHECK(d->is_update_new_chat_sent);
d->yet_unsent_message_id_to_persistent_message_id.emplace(old_message_id, m->message_id);
if (!td_->auth_manager_->is_bot()) {
d->yet_unsent_message_id_to_persistent_message_id.emplace(old_message_id, m->message_id);
}
send_closure(
G()->td(), &Td::send_update,
make_tl_object<td_api::updateMessageSendSucceeded>(get_message_object(d->dialog_id, m), old_message_id.get()));
@ -22746,7 +22700,7 @@ void MessagesManager::send_update_chat_draft_message(const Dialog *d) {
if (d->draft_message == nullptr || can_send_message(d->dialog_id).is_ok()) {
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateChatDraftMessage>(
d->dialog_id.get(), get_draft_message_object(d->draft_message), get_dialog_public_order(d)));
d->dialog_id.get(), get_draft_message_object(d->draft_message), get_dialog_order_object(d)));
}
}
@ -22762,7 +22716,7 @@ void MessagesManager::send_update_chat_last_message_impl(const Dialog *d, const
LOG(INFO) << "Send updateChatLastMessage in " << d->dialog_id << " to " << d->last_message_id << " from " << source;
auto update = make_tl_object<td_api::updateChatLastMessage>(
d->dialog_id.get(), get_message_object(d->dialog_id, get_message(d, d->last_message_id)),
get_dialog_public_order(d));
get_dialog_order_object(d));
send_closure(G()->td(), &Td::send_update, std::move(update));
}
@ -22914,7 +22868,7 @@ void MessagesManager::send_update_chat_is_sponsored(const Dialog *d) const {
LOG(INFO) << "Update chat is sponsored for " << d->dialog_id;
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateChatIsSponsored>(d->dialog_id.get(), is_dialog_sponsored(d),
get_dialog_public_order(d)));
get_dialog_order_object(d)));
}
void MessagesManager::send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const {
@ -23066,31 +23020,6 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI
if (it == being_sent_messages_.end()) {
LOG(ERROR) << "Result from sendMessage for " << new_message_id << " with random_id " << random_id << " sent at "
<< date << " comes from " << source << " after updateNewMessageId, but was not discarded by pts";
if (debug_being_sent_messages_.count(random_id) == 0) {
LOG(ERROR) << "Message with random_id " << random_id << " was mot sent";
return {};
}
auto dialog_id = debug_being_sent_messages_[random_id];
if (!dialog_id.is_valid()) {
LOG(ERROR) << "Sent message is in invalid " << dialog_id;
return {};
}
auto d = get_dialog(dialog_id);
if (d == nullptr) {
LOG(ERROR) << "Sent message is in not found " << dialog_id;
return {};
}
dump_debug_message_op(d, 7);
auto m = get_message_force(d, new_message_id, "on_send_message_success");
if (m == nullptr) {
LOG(ERROR) << new_message_id << " in " << dialog_id << " not found";
return {};
}
LOG(ERROR) << "Result from sent " << (m->is_outgoing ? "outgoing" : "incoming")
<< (m->forward_info == nullptr ? " not" : "") << " forwarded " << new_message_id
<< " with content of the type " << m->content->get_type() << " in " << dialog_id
<< " comes after updateNewMessageId, current last new is " << d->last_new_message_id << ", last is "
<< d->last_message_id;
return {};
}
@ -23341,23 +23270,7 @@ void MessagesManager::on_send_message_fail(int64 random_id, Status error) {
// we can't receive fail more than once
// but message can be successfully sent before
if (error.code() != NetQuery::Cancelled) {
auto debug_it = debug_being_sent_messages_.find(random_id);
if (debug_it == debug_being_sent_messages_.end()) {
LOG(ERROR) << "Message with random_id " << random_id << " was not sent";
return;
}
auto dialog_id = debug_it->second;
if (!dialog_id.is_valid()) {
LOG(ERROR) << "Sent message is in invalid " << dialog_id;
return;
}
if (!have_dialog(dialog_id)) {
LOG(ERROR) << "Sent message is in not found " << dialog_id;
return;
}
LOG(ERROR) << "Receive error " << error << " about successfully sent message with random_id = " << random_id
<< " in " << dialog_id;
dump_debug_message_op(get_dialog(dialog_id), 7);
LOG(ERROR) << "Receive error " << error << " about successfully sent message with random_id = " << random_id;
}
return;
}
@ -23586,6 +23499,7 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error
d->last_assigned_message_id = new_message_id;
}
} else {
// check deleted_message_ids, because the new_message_id is not a server scheduled
while (get_message_force(d, new_message_id, "fail_send_message") != nullptr ||
d->deleted_message_ids.count(new_message_id)) {
new_message_id = new_message_id.get_next_message_id(MessageType::Local);
@ -23622,7 +23536,9 @@ void MessagesManager::fail_send_message(FullMessageId full_message_id, int error
<< debug_add_message_to_dialog_fail_reason_;
LOG(INFO) << "Send updateMessageSendFailed for " << full_message_id;
d->yet_unsent_message_id_to_persistent_message_id.emplace(old_message_id, m->message_id);
if (!td_->auth_manager_->is_bot()) {
d->yet_unsent_message_id_to_persistent_message_id.emplace(old_message_id, m->message_id);
}
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateMessageSendFailed>(get_message_object(dialog_id, m), old_message_id.get(),
error_code, error_message));
@ -25912,7 +25828,6 @@ const MessagesManager::Message *MessagesManager::get_message(const Dialog *d, Me
}
CHECK(d != nullptr);
LOG(DEBUG) << "Search for " << message_id << " in " << d->dialog_id;
bool is_scheduled = message_id.is_scheduled();
if (is_scheduled && message_id.is_scheduled_server()) {
auto server_message_id = message_id.get_scheduled_server_message_id();
@ -25927,6 +25842,7 @@ const MessagesManager::Message *MessagesManager::get_message(const Dialog *d, Me
if (result != nullptr && !is_scheduled) {
result->last_access_date = G()->unix_time_cached();
}
LOG(INFO) << "Search for " << message_id << " in " << d->dialog_id << " found " << result;
return result;
}
@ -26052,7 +25968,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(DialogId dialog
MessageId message_id = message->message_id;
if (!message_id.is_valid() && !message_id.is_valid_scheduled()) {
LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " from " << source;
debug_add_message_to_dialog_fail_reason_ = "invalid message id";
debug_add_message_to_dialog_fail_reason_ = "invalid message identifier";
return nullptr;
}
@ -26105,7 +26021,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
}
LOG(ERROR) << "Receive " << message_id << " in " << dialog_id << " from " << source;
CHECK(!message->from_database);
debug_add_message_to_dialog_fail_reason_ = "invalid message id";
debug_add_message_to_dialog_fail_reason_ = "invalid message identifier";
return nullptr;
}
@ -26503,8 +26419,10 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq
if (next_message != nullptr) {
CHECK(!next_message->have_previous);
LOG(INFO) << "Attach " << message_id << " to the next " << next_message->message_id;
LOG_IF(ERROR, from_update) << "Attach " << message_id << " from " << source << " to the next "
<< next_message->message_id << " in " << dialog_id;
if (from_update && !next_message->message_id.is_yet_unsent()) {
LOG(ERROR) << "Attach " << message_id << " from " << source << " to the next " << next_message->message_id
<< " in " << dialog_id;
}
message->have_next = true;
message->have_previous = next_message->have_previous;
next_message->have_previous = true;
@ -27052,16 +26970,22 @@ void MessagesManager::delete_message_from_database(Dialog *d, MessageId message_
return;
}
if (message_id.is_yet_unsent()) {
return;
}
if (is_permanently_deleted) {
if (message_id.is_scheduled() && message_id.is_scheduled_server()) {
d->deleted_scheduled_server_message_ids.insert(message_id.get_scheduled_server_message_id());
} else {
d->deleted_message_ids.insert(message_id);
if (m == nullptr || !td_->auth_manager_->is_bot() || !m->is_failed_to_send) {
d->deleted_message_ids.insert(message_id);
}
}
}
if (message_id.is_yet_unsent()) {
return;
if (m != nullptr && m->random_id != 0 && (m->is_outgoing || d->dialog_id == get_my_dialog_id())) {
message_random_ids_.erase(m->random_id);
}
if (m != nullptr && m->notification_id.is_valid()) {
@ -27540,10 +27464,16 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
auto old_file_id = get_message_content_any_file_id(old_content.get());
bool need_finish_upload = old_file_id.is_valid() && need_merge_files;
if (old_content_type != new_content_type) {
need_update = true;
LOG(INFO) << "Message content has changed its type from " << old_content_type << " to " << new_content_type;
if (old_message->ttl > 0 && old_message->ttl_expires_at > 0 &&
((new_content_type == MessageContentType::ExpiredPhoto && old_content_type == MessageContentType::Photo) ||
(new_content_type == MessageContentType::ExpiredVideo && old_content_type == MessageContentType::Video))) {
LOG(INFO) << "Do not apply expired message content early";
} else {
need_update = true;
LOG(INFO) << "Message content has changed its type from " << old_content_type << " to " << new_content_type;
old_message->is_content_secret = is_secret_message_content(old_message->ttl, new_content->get_type());
old_message->is_content_secret = is_secret_message_content(old_message->ttl, new_content->get_type());
}
if (need_merge_files && old_file_id.is_valid()) {
auto new_file_id = get_message_content_any_file_id(new_content.get());
@ -27959,7 +27889,7 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
VLOG(notifications) << "Fix removing new secret chat " << d->new_secret_chat_notification_id << " in "
<< dialog_id;
d->new_secret_chat_notification_id = NotificationId();
on_dialog_updated(dialog_id, "fix new secret chat notification id");
on_dialog_updated(dialog_id, "fix new secret chat notification identifier");
}
}
@ -28231,11 +28161,22 @@ bool MessagesManager::is_dialog_sponsored(const Dialog *d) const {
return d->order == DEFAULT_ORDER && d->dialog_id == sponsored_dialog_id_;
}
int64 MessagesManager::get_dialog_public_order(const Dialog *d) const {
auto order = is_dialog_sponsored(d) ? SPONSORED_DIALOG_ORDER : d->order;
int64 MessagesManager::get_dialog_public_order(FolderId folder_id, const Dialog *d) const {
return get_dialog_public_order(get_dialog_list(folder_id), d);
}
int64 MessagesManager::get_dialog_public_order(const DialogList *list, const Dialog *d) const {
if (list == nullptr) {
return 0;
}
auto order = is_dialog_sponsored(d) && list->folder_id == FolderId::main() ? SPONSORED_DIALOG_ORDER : d->order;
DialogDate dialog_date(order, d->dialog_id);
auto *list = get_dialog_list(d->folder_id);
return list != nullptr && dialog_date <= list->last_dialog_date_ ? order : 0;
return dialog_date <= list->last_dialog_date_ ? order : 0;
}
int64 MessagesManager::get_dialog_order_object(const Dialog *d) const {
return get_dialog_public_order(d->folder_id, d);
}
int64 MessagesManager::get_next_pinned_dialog_order() {
@ -29358,7 +29299,7 @@ void MessagesManager::speculatively_update_channel_participants(DialogId dialog_
void MessagesManager::update_sent_message_contents(DialogId dialog_id, const Message *m) {
CHECK(m != nullptr);
if (td_->auth_manager_->is_bot() || (!m->is_outgoing && dialog_id != get_my_dialog_id()) ||
dialog_id.get_type() == DialogType::SecretChat || !m->message_id.is_local() || m->forward_info != nullptr ||
dialog_id.get_type() == DialogType::SecretChat || m->message_id.is_local() || m->forward_info != nullptr ||
m->had_forward_info) {
return;
}
@ -30595,20 +30536,6 @@ td_api::object_ptr<td_api::updateUnreadMessageCount> MessagesManager::get_update
CHECK(list.is_message_unread_count_inited_);
int32 unread_count = list.unread_message_total_count_;
int32 unread_unmuted_count = list.unread_message_total_count_ - list.unread_message_muted_count_;
if (include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() &&
list.folder_id == FolderId::main()) {
const Dialog *d = get_dialog(sponsored_dialog_id_);
CHECK(d != nullptr);
auto sponsored_unread_count = d->server_unread_count + d->local_unread_count;
if (is_dialog_sponsored(d) && sponsored_unread_count != 0) {
unread_count += sponsored_unread_count;
if (!is_dialog_muted(d)) {
unread_unmuted_count += sponsored_unread_count;
}
}
}
CHECK(unread_count >= 0);
CHECK(unread_unmuted_count >= 0);
return td_api::make_object<td_api::updateUnreadMessageCount>(get_chat_list_object(list.folder_id), unread_count,
@ -30626,26 +30553,6 @@ td_api::object_ptr<td_api::updateUnreadChatCount> MessagesManager::get_update_un
CHECK(unread_unmuted_count >= 0);
CHECK(unread_marked_count >= 0);
CHECK(unread_unmuted_marked_count >= 0);
if (include_sponsored_dialog_to_unread_count_ && sponsored_dialog_id_.is_valid() &&
list.folder_id == FolderId::main()) {
const Dialog *d = get_dialog(sponsored_dialog_id_);
CHECK(d != nullptr);
auto sponsored_unread_count = d->server_unread_count + d->local_unread_count;
if (is_dialog_sponsored(d) && (sponsored_unread_count != 0 || d->is_marked_as_unread)) {
unread_count++;
if (sponsored_unread_count == 0 && d->is_marked_as_unread) {
unread_marked_count++;
}
if (!is_dialog_muted(d)) {
unread_unmuted_count++;
if (sponsored_unread_count == 0 && d->is_marked_as_unread) {
unread_unmuted_marked_count++;
}
}
}
}
return td_api::make_object<td_api::updateUnreadChatCount>(
get_chat_list_object(list.folder_id), get_dialog_total_count(list), unread_count, unread_unmuted_count,
unread_marked_count, unread_unmuted_marked_count);
@ -30682,7 +30589,7 @@ void MessagesManager::get_current_state(vector<td_api::object_ptr<td_api::Update
auto update = td_api::make_object<td_api::updateNewChat>(get_chat_object(d));
if (update->chat_->last_message_ != nullptr && update->chat_->last_message_->forward_info_ != nullptr) {
last_message_updates.push_back(td_api::make_object<td_api::updateChatLastMessage>(
d->dialog_id.get(), std::move(update->chat_->last_message_), get_dialog_public_order(d)));
d->dialog_id.get(), std::move(update->chat_->last_message_), get_dialog_order_object(d)));
}
updates.push_back(std::move(update));

View File

@ -331,8 +331,6 @@ class MessagesManager : public Actor {
void on_update_delete_scheduled_messages(DialogId dialog_id, vector<ScheduledServerMessageId> &&server_message_ids);
void on_update_include_sponsored_dialog_to_unread_count();
void on_user_dialog_action(DialogId dialog_id, UserId user_id, tl_object_ptr<td_api::ChatAction> &&action, int32 date,
MessageContentType message_content_type = MessageContentType::None);
@ -1462,8 +1460,6 @@ class MessagesManager : public Actor {
static constexpr int32 MAX_RESEND_DELAY = 86400; // seconds, some resonable limit
static constexpr int32 MAX_PRELOADED_DIALOGS = 1000;
static constexpr int32 SCHEDULE_WHEN_ONLINE_DATE = 2147483646;
static constexpr double DIALOG_ACTION_TIMEOUT = 5.5;
@ -1678,6 +1674,8 @@ class MessagesManager : public Actor {
static void find_old_messages(const Message *m, MessageId max_message_id, vector<MessageId> &message_ids);
static void find_new_messages(const Message *m, MessageId min_message_id, vector<MessageId> &message_ids);
void find_unloadable_messages(const Dialog *d, int32 unload_before_date, const Message *m,
vector<MessageId> &message_ids, int32 &left_to_unload) const;
@ -2105,7 +2103,7 @@ class MessagesManager : public Actor {
void send_search_public_dialogs_query(const string &query, Promise<Unit> &&promise);
vector<DialogId> get_pinned_dialogs(FolderId folder_id) const;
vector<DialogId> get_pinned_dialog_ids(FolderId folder_id) const;
void reload_pinned_dialogs(FolderId folder_id, Promise<Unit> &&promise);
@ -2264,7 +2262,11 @@ class MessagesManager : public Actor {
bool is_dialog_sponsored(const Dialog *d) const;
int64 get_dialog_public_order(const Dialog *d) const;
int64 get_dialog_public_order(FolderId folder_id, const Dialog *d) const;
int64 get_dialog_public_order(const DialogList *list, const Dialog *d) const;
int64 get_dialog_order_object(const Dialog *d) const;
bool update_dialog_draft_message(Dialog *d, unique_ptr<DraftMessage> &&draft_message, bool from_update,
bool need_update_dialog_pos);
@ -2559,8 +2561,7 @@ class MessagesManager : public Actor {
update_message_ids_; // new_message_id -> temporary_id
std::unordered_map<DialogId, std::unordered_map<ScheduledServerMessageId, MessageId, ScheduledServerMessageIdHash>,
DialogIdHash>
update_scheduled_message_ids_; // new_message_id -> temporary_id
std::unordered_map<int64, DialogId> debug_being_sent_messages_; // message_random_id -> dialog_id
update_scheduled_message_ids_; // new_message_id -> temporary_id
const char *debug_add_message_to_dialog_fail_reason_ = "";
@ -2668,8 +2669,6 @@ class MessagesManager : public Actor {
uint64 current_message_edit_generation_ = 0;
bool include_sponsored_dialog_to_unread_count_ = false;
std::unordered_set<FolderId, FolderIdHash> postponed_unread_message_count_updates_;
std::unordered_set<FolderId, FolderIdHash> postponed_unread_chat_count_updates_;

View File

@ -69,7 +69,7 @@ void parse(PhotoSizeSource::DialogPhoto &source, ParserT &parser) {
switch (source.dialog_id.get_type()) {
case DialogType::SecretChat:
case DialogType::None:
return parser.set_error("Invalid chat id");
return parser.set_error("Invalid chat identifier");
default:
break;
}

View File

@ -15,6 +15,7 @@
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/logevent/LogEventHelper.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/PollId.hpp"
#include "td/telegram/PollManager.hpp"
@ -201,8 +202,8 @@ class StopPollActor : public NetActorOnce {
auto message_id = full_message_id.get_message_id().get_server_message_id().get();
auto poll = telegram_api::make_object<telegram_api::poll>();
poll->flags_ |= telegram_api::poll::CLOSED_MASK;
auto input_media =
telegram_api::make_object<telegram_api::inputMediaPoll>(0, std::move(poll), vector<BufferSlice>());
auto input_media = telegram_api::make_object<telegram_api::inputMediaPoll>(0, std::move(poll),
vector<BufferSlice>(), string(), Auto());
auto query = G()->net_query_creator().create(telegram_api::messages_editMessage(
flags, false /*ignored*/, std::move(input_peer), message_id, string(), std::move(input_media),
std::move(input_reply_markup), vector<tl_object_ptr<telegram_api::MessageEntity>>(), 0));
@ -236,6 +237,9 @@ class StopPollActor : public NetActorOnce {
PollManager::PollManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
update_poll_timeout_.set_callback(on_update_poll_timeout_callback);
update_poll_timeout_.set_callback_data(static_cast<void *>(this));
close_poll_timeout_.set_callback(on_close_poll_timeout_callback);
close_poll_timeout_.set_callback_data(static_cast<void *>(this));
}
void PollManager::start_up() {
@ -271,6 +275,15 @@ void PollManager::on_update_poll_timeout_callback(void *poll_manager_ptr, int64
send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_update_poll_timeout, PollId(poll_id_int));
}
void PollManager::on_close_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) {
if (G()->close_flag()) {
return;
}
auto poll_manager = static_cast<PollManager *>(poll_manager_ptr);
send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_close_poll_timeout, PollId(poll_id_int));
}
bool PollManager::is_local_poll_id(PollId poll_id) {
return poll_id.get() < 0 && poll_id.get() > std::numeric_limits<int32>::min();
}
@ -334,15 +347,23 @@ void PollManager::on_load_poll_from_database(PollId poll_id, string value) {
CHECK(!have_poll(poll_id));
if (!value.empty()) {
auto result = make_unique<Poll>();
auto status = log_event_parse(*result, value);
auto poll = make_unique<Poll>();
auto status = log_event_parse(*poll, value);
if (status.is_error()) {
LOG(FATAL) << status << ": " << format::as_hex_dump<4>(Slice(value));
}
for (auto &user_id : result->recent_voter_user_ids) {
for (auto &user_id : poll->recent_voter_user_ids) {
td_->contacts_manager_->have_user_force(user_id);
}
polls_[poll_id] = std::move(result);
if (!poll->is_closed && poll->close_date != 0) {
if (poll->close_date <= G()->server_time()) {
poll->is_closed = true;
} else {
CHECK(!is_local_poll_id(poll_id));
close_poll_timeout_.set_timeout_in(poll_id.get(), poll->close_date - G()->server_time() + 1e-3);
}
}
polls_[poll_id] = std::move(poll);
}
}
@ -531,15 +552,29 @@ td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id, co
td_api::object_ptr<td_api::PollType> poll_type;
if (poll->is_quiz) {
auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id;
poll_type = td_api::make_object<td_api::pollTypeQuiz>(correct_option_id);
poll_type = td_api::make_object<td_api::pollTypeQuiz>(
correct_option_id, get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation));
} else {
poll_type = td_api::make_object<td_api::pollTypeRegular>(poll->allow_multiple_answers);
}
auto open_period = poll->open_period;
auto close_date = poll->close_date;
if (open_period != 0 && close_date == 0) {
close_date = G()->unix_time() + open_period;
}
if (open_period == 0 && close_date != 0) {
auto now = G()->unix_time();
if (close_date < now + 5) {
close_date = 0;
} else {
open_period = close_date - now;
}
}
return td_api::make_object<td_api::poll>(
poll_id.get(), poll->question, std::move(poll_options), total_voter_count,
td_->contacts_manager_->get_user_ids_object(poll->recent_voter_user_ids, "get_poll_object"), poll->is_anonymous,
std::move(poll_type), poll->is_closed);
std::move(poll_type), open_period, close_date, poll->is_closed);
}
telegram_api::object_ptr<telegram_api::pollAnswer> PollManager::get_input_poll_option(const PollOption &poll_option) {
@ -547,7 +582,8 @@ telegram_api::object_ptr<telegram_api::pollAnswer> PollManager::get_input_poll_o
}
PollId PollManager::create_poll(string &&question, vector<string> &&options, bool is_anonymous,
bool allow_multiple_answers, bool is_quiz, int32 correct_option_id, bool is_closed) {
bool allow_multiple_answers, bool is_quiz, int32 correct_option_id,
FormattedText &&explanation, int32 open_period, int32 close_date, bool is_closed) {
auto poll = make_unique<Poll>();
poll->question = std::move(question);
int pos = '0';
@ -561,6 +597,9 @@ PollId PollManager::create_poll(string &&question, vector<string> &&options, boo
poll->allow_multiple_answers = allow_multiple_answers;
poll->is_quiz = is_quiz;
poll->correct_option_id = correct_option_id;
poll->explanation = std::move(explanation);
poll->open_period = open_period;
poll->close_date = close_date;
poll->is_closed = is_closed;
PollId poll_id(--current_local_poll_id_);
@ -582,7 +621,10 @@ void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id, c
LOG(INFO) << "Register " << poll_id << " from " << full_message_id << " from " << source;
bool is_inserted = poll_messages_[poll_id].insert(full_message_id).second;
LOG_CHECK(is_inserted) << source << " " << poll_id << " " << full_message_id;
if (!td_->auth_manager_->is_bot() && !is_local_poll_id(poll_id) && !get_poll_is_closed(poll_id)) {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
if (!td_->auth_manager_->is_bot() && !is_local_poll_id(poll_id) &&
!(poll->is_closed && poll->is_updated_after_close)) {
update_poll_timeout_.add_timeout_in(poll_id.get(), 0);
}
}
@ -807,7 +849,7 @@ void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation,
if (poll != nullptr && !poll->was_saved) {
// no updates was sent during updates processing, so send them
// poll wasn't changed, so there is no reason to actually save it
if (!poll->is_closed) {
if (!(poll->is_closed && poll->is_updated_after_close)) {
LOG(INFO) << "Schedule updating of " << poll_id << " soon";
update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0);
}
@ -892,7 +934,7 @@ void PollManager::get_poll_voters(PollId poll_id, FullMessageId full_message_id,
auto cur_offset = narrow_cast<int32>(voters.voter_user_ids.size());
if (offset > cur_offset) {
return promise.set_error(Status::Error(400, "Too big offset specified, voters can be received only consequently"));
return promise.set_error(Status::Error(400, "Too big offset specified; voters can be received only consequently"));
}
if (offset < cur_offset) {
vector<UserId> result;
@ -1082,7 +1124,9 @@ void PollManager::on_update_poll_timeout(PollId poll_id) {
if (G()->close_flag()) {
return;
}
if (get_poll_is_closed(poll_id)) {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
if (poll->is_closed && poll->is_updated_after_close) {
return;
}
if (pending_answers_.find(poll_id) != pending_answers_.end()) {
@ -1104,10 +1148,41 @@ void PollManager::on_update_poll_timeout(PollId poll_id) {
td_->create_handler<GetPollResultsQuery>(std::move(query_promise))->send(poll_id, full_message_id);
}
void PollManager::on_close_poll_timeout(PollId poll_id) {
CHECK(!is_local_poll_id(poll_id));
if (G()->close_flag()) {
return;
}
auto poll = get_poll_editable(poll_id);
CHECK(poll != nullptr);
if (poll->is_closed || poll->close_date == 0) {
return;
}
LOG(INFO) << "Trying to close " << poll_id << " by timer";
if (poll->close_date <= G()->server_time()) {
poll->is_closed = true;
notify_on_poll_update(poll_id);
save_poll(poll, poll_id);
// don't send updatePoll for bots, because there is no way to guarantee it
if (!td_->auth_manager_->is_bot()) {
update_poll_timeout_.set_timeout_in(poll_id.get(), 1.0);
}
} else {
close_poll_timeout_.set_timeout_in(poll_id.get(), poll->close_date - G()->server_time() + 1e-3);
}
}
void PollManager::on_get_poll_results(PollId poll_id, uint64 generation,
Result<tl_object_ptr<telegram_api::Updates>> result) {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
if (result.is_error()) {
if (!get_poll_is_closed(poll_id) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
if (!(poll->is_closed && poll->is_updated_after_close) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
auto timeout = get_polling_timeout();
LOG(INFO) << "Schedule updating of " << poll_id << " in " << timeout;
update_poll_timeout_.add_timeout_in(poll_id.get(), timeout);
@ -1119,7 +1194,7 @@ void PollManager::on_get_poll_results(PollId poll_id, uint64 generation,
}
if (generation != current_generation_) {
LOG(INFO) << "Receive possibly outdated result of " << poll_id << ", reget it";
if (!get_poll_is_closed(poll_id) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
if (!(poll->is_closed && poll->is_updated_after_close) && !G()->close_flag() && !td_->auth_manager_->is_bot()) {
update_poll_timeout_.set_timeout_in(poll_id.get(), 0.0);
}
return;
@ -1163,6 +1238,12 @@ tl_object_ptr<telegram_api::InputMedia> PollManager::get_input_media(PollId poll
if (poll->is_quiz) {
poll_flags |= telegram_api::poll::QUIZ_MASK;
}
if (poll->open_period != 0) {
poll_flags |= telegram_api::poll::CLOSE_PERIOD_MASK;
}
if (poll->close_date != 0) {
poll_flags |= telegram_api::poll::CLOSE_DATE_MASK;
}
if (poll->is_closed) {
poll_flags |= telegram_api::poll::CLOSED_MASK;
}
@ -1174,13 +1255,18 @@ tl_object_ptr<telegram_api::InputMedia> PollManager::get_input_media(PollId poll
CHECK(poll->correct_option_id >= 0);
CHECK(static_cast<size_t>(poll->correct_option_id) < poll->options.size());
correct_answers.push_back(BufferSlice(poll->options[poll->correct_option_id].data));
if (!poll->explanation.text.empty()) {
flags |= telegram_api::inputMediaPoll::SOLUTION_MASK;
}
}
return telegram_api::make_object<telegram_api::inputMediaPoll>(
flags,
telegram_api::make_object<telegram_api::poll>(0, poll_flags, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, poll->question,
transform(poll->options, get_input_poll_option)),
std::move(correct_answers));
telegram_api::make_object<telegram_api::poll>(
0, poll_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, poll->question,
transform(poll->options, get_input_poll_option), poll->open_period, poll->close_date),
std::move(correct_answers), poll->explanation.text,
get_input_message_entities(td_->contacts_manager_.get(), poll->explanation.entities, "get_input_media_poll"));
}
vector<PollManager::PollOption> PollManager::get_poll_options(
@ -1224,6 +1310,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
}
CHECK(poll != nullptr);
bool poll_server_is_closed = false;
if (poll_server != nullptr) {
if (poll->question != poll_server->question_) {
poll->question = std::move(poll_server->question_);
@ -1246,11 +1333,42 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
}
}
}
bool is_closed = (poll_server->flags_ & telegram_api::poll::CLOSED_MASK) != 0;
if (is_closed && !poll->is_closed) {
poll->is_closed = is_closed;
poll_server_is_closed = (poll_server->flags_ & telegram_api::poll::CLOSED_MASK) != 0;
if (poll_server_is_closed && !poll->is_closed) {
poll->is_closed = poll_server_is_closed;
is_changed = true;
}
if (poll_server_is_closed && !poll->is_updated_after_close) {
poll->is_updated_after_close = true;
is_changed = true;
}
int32 open_period =
(poll_server->flags_ & telegram_api::poll::CLOSE_PERIOD_MASK) != 0 ? poll_server->close_period_ : 0;
int32 close_date = (poll_server->flags_ & telegram_api::poll::CLOSE_DATE_MASK) != 0 ? poll_server->close_date_ : 0;
if (close_date == 0 || open_period == 0) {
close_date = 0;
open_period = 0;
}
if (open_period != poll->open_period) {
poll->open_period = open_period;
is_changed = true;
}
if (close_date != poll->close_date) {
poll->close_date = close_date;
is_changed = true;
if (!poll->is_closed) {
if (close_date != 0) {
if (close_date <= G()->server_time()) {
poll->is_closed = true;
} else {
close_poll_timeout_.set_timeout_in(poll_id.get(), close_date - G()->server_time() + 1e-3);
}
} else {
close_poll_timeout_.cancel_timeout(poll_id.get());
}
}
}
bool is_anonymous = (poll_server->flags_ & telegram_api::poll::PUBLIC_VOTERS_MASK) == 0;
if (is_anonymous != poll->is_anonymous) {
poll->is_anonymous = is_anonymous;
@ -1299,7 +1417,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
is_changed = true;
}
}
if (!is_min || poll->is_closed) {
if (!is_min || poll_server_is_closed) {
bool is_correct = (poll_result->flags_ & telegram_api::pollAnswerVoters::CORRECT_MASK) != 0;
if (is_correct) {
if (correct_option_id != -1) {
@ -1347,14 +1465,45 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
poll->total_voter_count = max_total_voter_count;
}
}
auto entities =
get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll");
auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, false);
if (status.is_error()) {
if (!clean_input_string(poll_results->solution_)) {
poll_results->solution_.clear();
}
entities = find_entities(poll_results->solution_, true);
}
FormattedText explanation{std::move(poll_results->solution_), std::move(entities)};
if (poll->is_quiz) {
if (poll->correct_option_id != correct_option_id) {
poll->correct_option_id = correct_option_id;
is_changed = true;
if (correct_option_id == -1 && poll->correct_option_id != -1) {
LOG(ERROR) << "Can't change correct option of " << poll_id << " from " << poll->correct_option_id << " to "
<< correct_option_id;
} else {
poll->correct_option_id = correct_option_id;
is_changed = true;
}
}
if (poll->explanation != explanation && (!is_min || poll_server_is_closed)) {
if (explanation.text.empty() && !poll->explanation.text.empty()) {
LOG(ERROR) << "Can't change known " << poll_id << " explanation to empty";
} else {
poll->explanation = std::move(explanation);
is_changed = true;
}
}
} else {
if (correct_option_id != -1) {
LOG(ERROR) << "Receive correct option " << correct_option_id << " in non-quiz " << poll_id;
}
if (!explanation.text.empty()) {
LOG(ERROR) << "Receive explanation " << explanation << " in non-quiz " << poll_id;
}
} else if (correct_option_id != -1) {
LOG(ERROR) << "Receive correct option " << correct_option_id << " in non-quiz " << poll_id;
}
vector<UserId> recent_voter_user_ids;
if (!is_bot) {
for (auto &user_id_int : poll_results->recent_voters_) {

View File

@ -7,6 +7,7 @@
#pragma once
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/PollId.h"
#include "td/telegram/ReplyMarkup.h"
@ -45,7 +46,8 @@ class PollManager : public Actor {
static bool is_local_poll_id(PollId poll_id);
PollId create_poll(string &&question, vector<string> &&options, bool is_anonymous, bool allow_multiple_answers,
bool is_quiz, int32 correct_option_id, bool is_closed);
bool is_quiz, int32 correct_option_id, FormattedText &&explanation, int32 open_period,
int32 close_date, bool is_closed);
void register_poll(PollId poll_id, FullMessageId full_message_id, const char *source);
@ -106,12 +108,16 @@ class PollManager : public Actor {
string question;
vector<PollOption> options;
vector<UserId> recent_voter_user_ids;
FormattedText explanation;
int32 total_voter_count = 0;
int32 correct_option_id = -1;
int32 open_period = 0;
int32 close_date = 0;
bool is_anonymous = true;
bool allow_multiple_answers = false;
bool is_quiz = false;
bool is_closed = false;
bool is_updated_after_close = false;
mutable bool was_saved = false;
template <class StorerT>
@ -137,6 +143,8 @@ class PollManager : public Actor {
static void on_update_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int);
static void on_close_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int);
static td_api::object_ptr<td_api::pollOption> get_poll_option_object(const PollOption &poll_option);
static telegram_api::object_ptr<telegram_api::pollAnswer> get_input_poll_option(const PollOption &poll_option);
@ -163,6 +171,8 @@ class PollManager : public Actor {
void on_update_poll_timeout(PollId poll_id);
void on_close_poll_timeout(PollId poll_id);
void on_online();
Poll *get_poll_force(PollId poll_id);
@ -189,6 +199,7 @@ class PollManager : public Actor {
uint64 logevent_id, Promise<Unit> &&promise);
MultiTimeout update_poll_timeout_{"UpdatePollTimeout"};
MultiTimeout close_poll_timeout_{"ClosePollTimeout"};
Td *td_;
ActorShared<> parent_;

View File

@ -44,12 +44,19 @@ void PollManager::Poll::store(StorerT &storer) const {
using ::td::store;
bool is_public = !is_anonymous;
bool has_recent_voters = !recent_voter_user_ids.empty();
bool has_open_period = open_period != 0;
bool has_close_date = close_date != 0;
bool has_explanation = !explanation.text.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_closed);
STORE_FLAG(is_public);
STORE_FLAG(allow_multiple_answers);
STORE_FLAG(is_quiz);
STORE_FLAG(has_recent_voters);
STORE_FLAG(has_open_period);
STORE_FLAG(has_close_date);
STORE_FLAG(has_explanation);
STORE_FLAG(is_updated_after_close);
END_STORE_FLAGS();
store(question, storer);
@ -61,6 +68,15 @@ void PollManager::Poll::store(StorerT &storer) const {
if (has_recent_voters) {
store(recent_voter_user_ids, storer);
}
if (has_open_period) {
store(open_period, storer);
}
if (has_close_date) {
store(close_date, storer);
}
if (has_explanation) {
store(explanation, storer);
}
}
template <class ParserT>
@ -68,12 +84,19 @@ void PollManager::Poll::parse(ParserT &parser) {
using ::td::parse;
bool is_public;
bool has_recent_voters;
bool has_open_period;
bool has_close_date;
bool has_explanation;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_closed);
PARSE_FLAG(is_public);
PARSE_FLAG(allow_multiple_answers);
PARSE_FLAG(is_quiz);
PARSE_FLAG(has_recent_voters);
PARSE_FLAG(has_open_period);
PARSE_FLAG(has_close_date);
PARSE_FLAG(has_explanation);
PARSE_FLAG(is_updated_after_close);
END_PARSE_FLAGS();
is_anonymous = !is_public;
@ -89,6 +112,15 @@ void PollManager::Poll::parse(ParserT &parser) {
if (has_recent_voters) {
parse(recent_voter_user_ids, parser);
}
if (has_open_period) {
parse(open_period, parser);
}
if (has_close_date) {
parse(close_date, parser);
}
if (has_explanation) {
parse(explanation, parser);
}
}
template <class StorerT>
@ -97,11 +129,17 @@ void PollManager::store_poll(PollId poll_id, StorerT &storer) const {
if (is_local_poll_id(poll_id)) {
auto poll = get_poll(poll_id);
CHECK(poll != nullptr);
bool has_open_period = poll->open_period != 0;
bool has_close_date = poll->close_date != 0;
bool has_explanation = !poll->explanation.text.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(poll->is_closed);
STORE_FLAG(poll->is_anonymous);
STORE_FLAG(poll->allow_multiple_answers);
STORE_FLAG(poll->is_quiz);
STORE_FLAG(has_open_period);
STORE_FLAG(has_close_date);
STORE_FLAG(has_explanation);
END_STORE_FLAGS();
store(poll->question, storer);
vector<string> options = transform(poll->options, [](const PollOption &option) { return option.text; });
@ -109,6 +147,15 @@ void PollManager::store_poll(PollId poll_id, StorerT &storer) const {
if (poll->is_quiz) {
store(poll->correct_option_id, storer);
}
if (has_open_period) {
store(poll->open_period, storer);
}
if (has_close_date) {
store(poll->close_date, storer);
}
if (has_explanation) {
store(poll->explanation, storer);
}
}
}
@ -120,10 +167,16 @@ PollId PollManager::parse_poll(ParserT &parser) {
if (is_local_poll_id(poll_id)) {
string question;
vector<string> options;
FormattedText explanation;
int32 open_period = 0;
int32 close_date = 0;
bool is_closed = false;
bool is_anonymous = true;
bool allow_multiple_answers = false;
bool is_quiz = false;
bool has_open_period = false;
bool has_close_date = false;
bool has_explanation = false;
int32 correct_option_id = -1;
if (parser.version() >= static_cast<int32>(Version::SupportPolls2_0)) {
@ -132,6 +185,9 @@ PollId PollManager::parse_poll(ParserT &parser) {
PARSE_FLAG(is_anonymous);
PARSE_FLAG(allow_multiple_answers);
PARSE_FLAG(is_quiz);
PARSE_FLAG(has_open_period);
PARSE_FLAG(has_close_date);
PARSE_FLAG(has_explanation);
END_PARSE_FLAGS();
}
parse(question, parser);
@ -142,11 +198,20 @@ PollId PollManager::parse_poll(ParserT &parser) {
parser.set_error("Wrong correct_option_id");
}
}
if (has_open_period) {
parse(open_period, parser);
}
if (has_close_date) {
parse(close_date, parser);
}
if (has_explanation) {
parse(explanation, parser);
}
if (parser.get_error() != nullptr) {
return PollId();
}
return create_poll(std::move(question), std::move(options), is_anonymous, allow_multiple_answers, is_quiz,
correct_option_id, is_closed);
correct_option_id, std::move(explanation), open_period, close_date, is_closed);
}
auto poll = get_poll_force(poll_id);

View File

@ -22,6 +22,9 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include <algorithm>
#include <iterator>
namespace td {
Result<PrivacyManager::UserPrivacySetting> PrivacyManager::UserPrivacySetting::from_td_api(
@ -344,6 +347,13 @@ vector<int64> PrivacyManager::UserPrivacySettingRule::chat_ids_as_dialog_ids() c
return result;
}
vector<int32> PrivacyManager::UserPrivacySettingRule::get_restricted_user_ids() const {
if (type_ == Type::RestrictUsers) {
return user_ids_;
}
return {};
}
Result<PrivacyManager::UserPrivacySettingRules> PrivacyManager::UserPrivacySettingRules::from_telegram_api(
tl_object_ptr<telegram_api::account_privacyRules> rules) {
G()->td().get_actor_unsafe()->contacts_manager_->on_get_users(std::move(rules->users_), "on get privacy rules");
@ -393,6 +403,16 @@ vector<tl_object_ptr<telegram_api::InputPrivacyRule>> PrivacyManager::UserPrivac
return result;
}
vector<int32> PrivacyManager::UserPrivacySettingRules::get_restricted_user_ids() const {
vector<int32> result;
for (auto &rule : rules_) {
combine(result, rule.get_restricted_user_ids());
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
return result;
}
void PrivacyManager::get_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
Promise<tl_object_ptr<td_api::userPrivacySettingRules>> promise) {
auto r_user_privacy_setting = UserPrivacySetting::from_td_api(std::move(key));
@ -499,14 +519,27 @@ void PrivacyManager::do_update_privacy(UserPrivacySetting user_privacy_setting,
info.is_synchronized = true;
if (!(info.rules == privacy_rules)) {
if ((from_update || was_synchronized) && user_privacy_setting.type() == UserPrivacySetting::Type::UserStatus &&
!G()->close_flag()) {
send_closure_later(G()->contacts_manager(), &ContactsManager::on_update_online_status_privacy);
auto old_restricted = info.rules.get_restricted_user_ids();
auto new_restricted = privacy_rules.get_restricted_user_ids();
if (old_restricted != new_restricted) {
// if a user was unrestricted, it is not received from the server anymore
// we need to reget their online status manually
std::vector<int32> unrestricted;
std::set_difference(old_restricted.begin(), old_restricted.end(), new_restricted.begin(), new_restricted.end(),
std::back_inserter(unrestricted));
for (auto &user_id : unrestricted) {
send_closure_later(G()->contacts_manager(), &ContactsManager::reload_user, UserId(user_id), Promise<Unit>());
}
}
}
info.rules = std::move(privacy_rules);
send_closure(G()->td(), &Td::send_update,
make_tl_object<td_api::updateUserPrivacySettingRules>(user_privacy_setting.as_td_api(),
info.rules.as_td_api()));
if ((from_update || was_synchronized) && user_privacy_setting.type() == UserPrivacySetting::Type::UserStatus) {
send_closure(G()->contacts_manager(), &ContactsManager::on_update_online_status_privacy);
}
}
}

View File

@ -76,6 +76,8 @@ class PrivacyManager : public NetQueryCallback {
return type_ == other.type_ && user_ids_ == other.user_ids_ && chat_ids_ == other.chat_ids_;
}
vector<int32> get_restricted_user_ids() const;
private:
enum class Type : int32 {
AllowContacts,
@ -113,6 +115,8 @@ class PrivacyManager : public NetQueryCallback {
return rules_ == other.rules_;
}
vector<int32> get_restricted_user_ids() const;
private:
vector<UserPrivacySettingRule> rules_;
};

View File

@ -0,0 +1,60 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/SpecialStickerSetType.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
namespace td {
string SpecialStickerSetType::animated_emoji() {
return "animated_emoji_sticker_set";
}
string SpecialStickerSetType::animated_dice(const string &emoji) {
CHECK(!emoji.empty());
return PSTRING() << "animated_dice_sticker_set#" << emoji;
}
SpecialStickerSetType::SpecialStickerSetType(
const telegram_api::object_ptr<telegram_api::InputStickerSet> &input_sticker_set) {
CHECK(input_sticker_set != nullptr);
switch (input_sticker_set->get_id()) {
case telegram_api::inputStickerSetAnimatedEmoji::ID:
type_ = animated_emoji();
break;
case telegram_api::inputStickerSetDice::ID:
type_ = animated_dice(static_cast<const telegram_api::inputStickerSetDice *>(input_sticker_set.get())->emoticon_);
break;
default:
UNREACHABLE();
break;
}
}
string SpecialStickerSetType::get_dice_emoji() const {
if (begins_with(type_, "animated_dice_sticker_set#")) {
return type_.substr(Slice("animated_dice_sticker_set#").size());
}
return string();
}
telegram_api::object_ptr<telegram_api::InputStickerSet> SpecialStickerSetType::get_input_sticker_set() const {
if (type_ == "animated_emoji_sticker_set") {
return telegram_api::make_object<telegram_api::inputStickerSetAnimatedEmoji>();
}
auto emoji = get_dice_emoji();
if (!emoji.empty()) {
return telegram_api::make_object<telegram_api::inputStickerSetDice>(emoji);
}
UNREACHABLE();
return nullptr;
}
} // namespace td

View File

@ -0,0 +1,31 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
namespace td {
struct SpecialStickerSetType {
string type_;
static string animated_emoji();
static string animated_dice(const string &emoji);
string get_dice_emoji() const;
SpecialStickerSetType() = default;
explicit SpecialStickerSetType(const telegram_api::object_ptr<telegram_api::InputStickerSet> &input_sticker_set);
telegram_api::object_ptr<telegram_api::InputStickerSet> get_input_sticker_set() const;
};
} // namespace td

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,16 @@
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/SpecialStickerSetType.h"
#include "td/telegram/StickerSetId.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/Hints.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/telegram/td_api.h"
@ -44,10 +47,16 @@ class StickersManager : public Actor {
StickersManager(Td *td, ActorShared<> parent);
void init();
tl_object_ptr<td_api::sticker> get_sticker_object(FileId file_id) const;
tl_object_ptr<td_api::stickers> get_stickers_object(const vector<FileId> &sticker_ids) const;
tl_object_ptr<td_api::sticker> get_dice_sticker_object(const string &emoji, int32 value) const;
int32 get_dice_success_animation_frame_number(const string &emoji, int32 value) const;
tl_object_ptr<td_api::stickerSet> get_sticker_set_object(StickerSetId sticker_set_id) const;
tl_object_ptr<td_api::stickerSets> get_sticker_sets_object(int32 total_count,
@ -56,6 +65,10 @@ class StickersManager : public Actor {
tl_object_ptr<telegram_api::InputStickerSet> get_input_sticker_set(StickerSetId sticker_set_id) const;
void register_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source);
void unregister_dice(const string &emoji, int32 value, FullMessageId full_message_id, const char *source);
void create_sticker(FileId file_id, PhotoSize thumbnail, Dimensions dimensions,
tl_object_ptr<telegram_api::documentAttributeSticker> sticker, bool is_animated,
MultiPromiseActor *load_data_multipromise_ptr);
@ -108,9 +121,9 @@ class StickersManager : public Actor {
StickerSetId on_get_sticker_set_covered(tl_object_ptr<telegram_api::StickerSetCovered> &&set_ptr, bool is_changed,
const char *source);
enum SpecialStickerSetType : int32 { AnimatedEmoji, AnimatedDice };
void on_get_special_sticker_set(const SpecialStickerSetType &type, StickerSetId sticker_set_id);
void on_get_special_sticker_set(StickerSetId sticker_set_id, SpecialStickerSetType type);
void on_load_special_sticker_set(const SpecialStickerSetType &type, Status result);
void on_load_sticker_set_fail(StickerSetId sticker_set_id, const Status &error);
@ -119,6 +132,10 @@ class StickersManager : public Actor {
void on_uninstall_sticker_set(StickerSetId set_id);
void on_update_dice_emojis();
void on_update_dice_success_values();
void on_update_sticker_sets();
void on_update_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids);
@ -130,11 +147,12 @@ class StickersManager : public Actor {
vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets,
int32 total_count);
vector<StickerSetId> get_featured_sticker_sets(Promise<Unit> &&promise);
std::pair<int32, vector<StickerSetId>> get_featured_sticker_sets(int32 offset, int32 limit, Promise<Unit> &&promise);
void on_get_featured_sticker_sets(tl_object_ptr<telegram_api::messages_FeaturedStickers> &&sticker_sets_ptr);
void on_get_featured_sticker_sets(int32 offset, int32 limit, uint32 generation,
tl_object_ptr<telegram_api::messages_FeaturedStickers> &&sticker_sets_ptr);
void on_get_featured_sticker_sets_failed(Status error);
void on_get_featured_sticker_sets_failed(int32 offset, int32 limit, uint32 generation, Status error);
vector<StickerSetId> get_attached_sticker_sets(FileId file_id, Promise<Unit> &&promise);
@ -210,8 +228,8 @@ class StickersManager : public Actor {
vector<string> get_sticker_emojis(const tl_object_ptr<td_api::InputFile> &input_file, Promise<Unit> &&promise);
vector<string> search_emojis(const string &text, bool exact_match, const string &input_language_code, bool force,
Promise<Unit> &&promise);
vector<string> search_emojis(const string &text, bool exact_match, const vector<string> &input_language_codes,
bool force, Promise<Unit> &&promise);
int64 get_emoji_suggestions_url(const string &language_code, Promise<Unit> &&promise);
@ -269,6 +287,7 @@ class StickersManager : public Actor {
private:
static constexpr int32 MAX_FEATURED_STICKER_SET_VIEW_DELAY = 5;
static constexpr int32 OLD_FEATURED_STICKER_SET_SLICE_SIZE = 20;
static constexpr int32 MAX_FOUND_STICKERS = 100; // server side limit
static constexpr int64 MAX_STICKER_FILE_SIZE = 1 << 19; // server side limit
@ -300,7 +319,7 @@ class StickersManager : public Actor {
class StickerSet {
public:
bool is_inited = false;
bool is_inited = false; // basic information about the set
bool was_loaded = false;
bool is_loaded = false;
@ -325,7 +344,9 @@ class StickersManager : public Actor {
bool is_masks = false;
bool is_viewed = true;
bool is_thumbnail_reloaded = false;
bool is_changed = true;
mutable bool was_update_sent = false; // does the sticker set is known to the client
bool is_changed = true; // have new changes that need to be sent to the client and database
bool need_save_to_database = true; // have new changes that need only to be saved to the database
vector<uint32> load_requests;
vector<uint32> load_without_stickers_requests;
@ -359,8 +380,9 @@ class StickersManager : public Actor {
struct SpecialStickerSet {
StickerSetId id_;
int64 access_hash_ = 0;
string name_;
string type_;
string short_name_;
SpecialStickerSetType type_;
bool is_being_loaded_ = false;
};
class StickerListLogEvent;
@ -431,6 +453,8 @@ class StickersManager : public Actor {
void load_featured_sticker_sets(Promise<Unit> &&promise);
void load_old_featured_sticker_sets(Promise<Unit> &&promise);
void load_recent_stickers(bool is_attached, Promise<Unit> &&promise);
void on_load_installed_sticker_sets_from_database(bool is_masks, string value);
@ -442,6 +466,11 @@ class StickersManager : public Actor {
void on_load_featured_sticker_sets_finished(vector<StickerSetId> &&featured_sticker_set_ids);
void on_load_old_featured_sticker_sets_from_database(uint32 generation, string value);
void on_load_old_featured_sticker_sets_finished(uint32 generation,
vector<StickerSetId> &&old_featured_sticker_set_ids);
void on_load_recent_stickers_from_database(bool is_attached, string value);
void on_load_recent_stickers_finished(bool is_attached, vector<FileId> &&recent_sticker_ids,
@ -451,6 +480,18 @@ class StickersManager : public Actor {
void send_update_installed_sticker_sets(bool from_database = false);
void reload_old_featured_sticker_sets(uint32 generation = 0);
void on_old_featured_sticker_sets_invalidated();
void invalidate_old_featured_sticker_sets();
void set_old_featured_sticker_set_count(int32 count);
// must be called after every call to set_old_featured_sticker_set_count or
// any change of old_featured_sticker_set_ids_ size
void fix_old_featured_sticker_set_count();
td_api::object_ptr<td_api::updateTrendingStickerSets> get_update_trending_sticker_sets_object() const;
void send_update_featured_sticker_sets();
@ -515,15 +556,25 @@ class StickersManager : public Actor {
bool update_sticker_set_cache(const StickerSet *sticker_set, Promise<Unit> &promise);
td_api::object_ptr<td_api::updateDiceEmojis> get_update_dice_emojis_object() const;
void start_up() override;
void tear_down() override;
SpecialStickerSet &add_special_sticker_set(const string &type);
static void init_special_sticker_set(SpecialStickerSet &sticker_set, int64 sticker_set_id, int64 access_hash,
string name);
void load_special_sticker_set_info_from_binlog(SpecialStickerSet &sticker_set);
void load_special_sticker_set_by_type(const SpecialStickerSetType &type);
void load_special_sticker_set(SpecialStickerSet &sticker_set);
void reload_special_sticker_set(SpecialStickerSet &sticker_set);
static void add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail);
static string get_sticker_mime_type(const Sticker *s);
@ -540,7 +591,8 @@ class StickersManager : public Actor {
double get_emoji_language_code_last_difference_time(const string &language_code);
vector<string> get_emoji_language_codes(const string &input_language_code, Promise<Unit> &promise);
vector<string> get_emoji_language_codes(const vector<string> &input_language_codes, Slice text,
Promise<Unit> &promise);
void load_language_codes(vector<string> language_codes, string key, Promise<Unit> &&promise);
@ -572,6 +624,7 @@ class StickersManager : public Actor {
vector<StickerSetId> installed_sticker_set_ids_[2];
vector<StickerSetId> featured_sticker_set_ids_;
vector<StickerSetId> old_featured_sticker_set_ids_;
vector<FileId> recent_sticker_ids_[2];
vector<FileId> favorite_sticker_ids_;
@ -584,6 +637,9 @@ class StickersManager : public Actor {
int32 featured_sticker_sets_hash_ = 0;
int32 recent_stickers_hash_[2] = {0, 0};
int32 old_featured_sticker_set_count_ = -1;
uint32 old_featured_sticker_set_generation_ = 1;
bool need_update_installed_sticker_sets_[2] = {false, false};
bool need_update_featured_sticker_sets_ = false;
bool need_update_recent_stickers_[2] = {false, false};
@ -593,8 +649,11 @@ class StickersManager : public Actor {
bool are_recent_stickers_loaded_[2] = {false, false};
bool are_favorite_stickers_loaded_ = false;
bool are_old_featured_sticker_sets_invalidated_ = false;
vector<Promise<Unit>> load_installed_sticker_sets_queries_[2];
vector<Promise<Unit>> load_featured_sticker_sets_queries_;
vector<Promise<Unit>> load_old_featured_sticker_sets_queries_;
vector<Promise<Unit>> load_recent_stickers_queries_[2];
vector<Promise<Unit>> repair_recent_stickers_queries_[2];
vector<Promise<Unit>> load_favorite_stickers_queries_;
@ -624,7 +683,7 @@ class StickersManager : public Actor {
int32 recent_stickers_limit_ = 200;
int32 favorite_stickers_limit_ = 5;
SpecialStickerSet special_sticker_sets_[2];
std::unordered_map<string, SpecialStickerSet> special_sticker_sets_;
struct StickerSetLoadRequest {
Promise<Unit> promise;
@ -652,6 +711,14 @@ class StickersManager : public Actor {
std::unordered_map<string, vector<Promise<Unit>>> load_emoji_keywords_queries_;
std::unordered_map<string, vector<Promise<Unit>>> load_language_codes_queries_;
std::unordered_map<int64, string> emoji_suggestions_urls_;
std::unordered_map<string, std::unordered_set<FullMessageId, FullMessageIdHash>> dice_messages_;
string dice_emojis_str_;
vector<string> dice_emojis_;
string dice_success_values_str_;
vector<std::pair<int32, int32>> dice_success_values_;
};
} // namespace td

View File

@ -72,9 +72,8 @@ FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
Slice data = parser.template fetch_string_raw<Slice>(parser.get_left_len());
for (auto c : data) {
if (c != '\0') {
LOG_CHECK(in_sticker_set_stored == in_sticker_set)
<< in_sticker_set << " " << in_sticker_set_stored << " " << parser.version() << " " << sticker->is_mask
<< " " << has_sticker_set_access_hash << " " << format::as_hex_dump<4>(data);
parser.set_error("Invalid sticker set is stored in the database");
break;
}
}
parser.set_error("Zero sticker set is stored in the database");

View File

@ -2337,18 +2337,22 @@ class GetArchivedStickerSetsRequest : public RequestActor<> {
};
class GetTrendingStickerSetsRequest : public RequestActor<> {
vector<StickerSetId> sticker_set_ids_;
std::pair<int32, vector<StickerSetId>> sticker_set_ids_;
int32 offset_;
int32 limit_;
void do_run(Promise<Unit> &&promise) override {
sticker_set_ids_ = td->stickers_manager_->get_featured_sticker_sets(std::move(promise));
sticker_set_ids_ = td->stickers_manager_->get_featured_sticker_sets(offset_, limit_, std::move(promise));
}
void do_send_result() override {
send_result(td->stickers_manager_->get_sticker_sets_object(-1, sticker_set_ids_, 5));
send_result(td->stickers_manager_->get_sticker_sets_object(sticker_set_ids_.first, sticker_set_ids_.second, 5));
}
public:
GetTrendingStickerSetsRequest(ActorShared<Td> td, uint64 request_id) : RequestActor(std::move(td), request_id) {
GetTrendingStickerSetsRequest(ActorShared<Td> td, uint64 request_id, int32 offset, int32 limit)
: RequestActor(std::move(td), request_id), offset_(offset), limit_(limit) {
set_tries(3);
}
};
@ -2710,12 +2714,12 @@ class GetStickerEmojisRequest : public RequestActor<> {
class SearchEmojisRequest : public RequestActor<> {
string text_;
bool exact_match_;
string input_language_code_;
vector<string> input_language_codes_;
vector<string> emojis_;
void do_run(Promise<Unit> &&promise) override {
emojis_ = td->stickers_manager_->search_emojis(text_, exact_match_, input_language_code_, get_tries() < 2,
emojis_ = td->stickers_manager_->search_emojis(text_, exact_match_, input_language_codes_, get_tries() < 2,
std::move(promise));
}
@ -2725,11 +2729,11 @@ class SearchEmojisRequest : public RequestActor<> {
public:
SearchEmojisRequest(ActorShared<Td> td, uint64 request_id, string &&text, bool exact_match,
string &&input_language_code)
vector<string> &&input_language_codes)
: RequestActor(std::move(td), request_id)
, text_(std::move(text))
, exact_match_(exact_match)
, input_language_code_(std::move(input_language_code)) {
, input_language_codes_(std::move(input_language_codes)) {
set_tries(3);
}
};
@ -3453,7 +3457,7 @@ bool Td::is_internal_config_option(Slice name) {
return name == "call_ring_timeout_ms" || name == "call_receive_timeout_ms" ||
name == "channels_read_media_period";
case 'd':
return name == "dc_txt_domain_name";
return name == "dc_txt_domain_name" || name == "dice_emojis" || name == "dice_success_values";
case 'e':
return name == "edit_time_limit";
case 'i':
@ -3490,8 +3494,6 @@ void Td::on_config_option_updated(const string &name) {
return stickers_manager_->on_update_recent_stickers_limit(G()->shared_config().get_option_integer(name));
} else if (name == "favorite_stickers_limit") {
stickers_manager_->on_update_favorite_stickers_limit(G()->shared_config().get_option_integer(name));
} else if (name == "include_sponsored_chat_to_unread_count") {
messages_manager_->on_update_include_sponsored_dialog_to_unread_count();
} else if (name == "my_id") {
G()->set_my_id(G()->shared_config().get_option_integer(name));
} else if (name == "session_count") {
@ -3542,6 +3544,10 @@ void Td::on_config_option_updated(const string &name) {
return send_closure(notification_manager_actor_, &NotificationManager::on_notification_default_delay_changed);
} else if (name == "ignored_restriction_reasons") {
return send_closure(contacts_manager_actor_, &ContactsManager::on_ignored_restriction_reasons_changed);
} else if (name == "dice_emojis") {
return send_closure(stickers_manager_actor_, &StickersManager::on_update_dice_emojis);
} else if (name == "dice_success_values") {
return send_closure(stickers_manager_actor_, &StickersManager::on_update_dice_success_values);
} else if (is_internal_config_option(name)) {
return;
}
@ -4337,15 +4343,19 @@ void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
case td_api::updateUserStatus::ID:
VLOG(td_requests) << "Sending update: " << oneline(to_string(object));
break;
case td_api::updateTrendingStickerSets::ID:
VLOG(td_requests) << "Sending update: updateTrendingStickerSets { ... }";
case td_api::updateTrendingStickerSets::ID: {
auto sticker_sets = static_cast<const td_api::updateTrendingStickerSets *>(object.get())->sticker_sets_.get();
VLOG(td_requests) << "Sending update: updateTrendingStickerSets { total_count = " << sticker_sets->total_count_
<< ", count = " << sticker_sets->sets_.size() << " }";
break;
}
case td_api::updateOption::ID / 2:
case td_api::updateChatReadInbox::ID / 2:
case td_api::updateUnreadMessageCount::ID / 2:
case td_api::updateUnreadChatCount::ID / 2:
case td_api::updateChatOnlineMemberCount::ID / 2:
case td_api::updateUserChatAction::ID / 2:
case td_api::updateDiceEmojis::ID / 2:
LOG(ERROR) << "Sending update: " << oneline(to_string(object));
break;
default:
@ -5024,7 +5034,7 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) {
for (auto chat_id : request.chat_ids_) {
DialogId dialog_id(chat_id);
if (!dialog_id.is_valid() && dialog_id != DialogId()) {
return send_error_raw(id, 400, "Wrong chat id");
return send_error_raw(id, 400, "Wrong chat identifier");
}
owner_dialog_ids.push_back(dialog_id);
}
@ -5032,7 +5042,7 @@ void Td::on_request(uint64 id, td_api::optimizeStorage &request) {
for (auto chat_id : request.exclude_chat_ids_) {
DialogId dialog_id(chat_id);
if (!dialog_id.is_valid() && dialog_id != DialogId()) {
return send_error_raw(id, 400, "Wrong chat id");
return send_error_raw(id, 400, "Wrong chat identifier");
}
exclude_owner_dialog_ids.push_back(dialog_id);
}
@ -5840,8 +5850,19 @@ void Td::on_request(uint64 id, const td_api::joinChat &request) {
void Td::on_request(uint64 id, const td_api::leaveChat &request) {
CREATE_OK_REQUEST_PROMISE();
messages_manager_->set_dialog_participant_status(DialogId(request.chat_id_), contacts_manager_->get_my_id(),
td_api::make_object<td_api::chatMemberStatusLeft>(),
DialogId dialog_id(request.chat_id_);
td_api::object_ptr<td_api::ChatMemberStatus> new_status = td_api::make_object<td_api::chatMemberStatusLeft>();
if (dialog_id.get_type() == DialogType::Channel && messages_manager_->have_dialog_force(dialog_id)) {
auto status = contacts_manager_->get_channel_status(dialog_id.get_channel_id());
if (status.is_creator()) {
if (!status.is_member()) {
return promise.set_value(Unit());
}
new_status = td_api::make_object<td_api::chatMemberStatusCreator>(status.get_rank(), false);
}
}
messages_manager_->set_dialog_participant_status(dialog_id, contacts_manager_->get_my_id(), std::move(new_status),
std::move(promise));
}
@ -5950,7 +5971,7 @@ void Td::on_request(uint64 id, const td_api::downloadFile &request) {
FileId file_id(request.file_id_, 0);
auto file_view = file_manager_->get_file_view(file_id);
if (file_view.empty()) {
return send_error_raw(id, 400, "Invalid file id");
return send_error_raw(id, 400, "Invalid file identifier");
}
auto info_it = pending_file_downloads_.find(file_id);
@ -6302,7 +6323,7 @@ void Td::on_request(uint64 id, const td_api::getArchivedStickerSets &request) {
void Td::on_request(uint64 id, const td_api::getTrendingStickerSets &request) {
CHECK_IS_USER();
CREATE_NO_ARGS_REQUEST(GetTrendingStickerSetsRequest);
CREATE_REQUEST(GetTrendingStickerSetsRequest, request.offset_, request.limit_);
}
void Td::on_request(uint64 id, const td_api::getAttachedStickerSets &request) {
@ -6428,9 +6449,11 @@ void Td::on_request(uint64 id, td_api::getStickerEmojis &request) {
void Td::on_request(uint64 id, td_api::searchEmojis &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.text_);
CLEAN_INPUT_STRING(request.input_language_code_);
for (auto &input_language_code : request.input_language_codes_) {
CLEAN_INPUT_STRING(input_language_code);
}
CREATE_REQUEST(SearchEmojisRequest, std::move(request.text_), request.exact_match_,
std::move(request.input_language_code_));
std::move(request.input_language_codes_));
}
void Td::on_request(uint64 id, td_api::getEmojiSuggestionsUrl &request) {
@ -6806,12 +6829,6 @@ void Td::on_request(uint64 id, td_api::setOption &request) {
if (set_boolean_option("is_emulator")) {
return;
}
// this option currently can't be set, because unread count doesn't work for channels,
// in which user have never been a member
if (false && !is_bot && set_boolean_option("include_sponsored_chat_to_unread_count")) {
return;
}
if (!is_bot && request.name_ == "ignore_sensitive_content_restrictions") {
if (!G()->shared_config().get_option_boolean("can_ignore_sensitive_content_restrictions")) {
return send_error_raw(id, 3, "Option \"ignore_sensitive_content_restrictions\" can't be changed by the user");

View File

@ -227,7 +227,7 @@ class Td final : public NetQueryCallback {
static td_api::object_ptr<td_api::Object> static_request(td_api::object_ptr<td_api::Function> function);
private:
static constexpr const char *TDLIB_VERSION = "1.6.2";
static constexpr const char *TDLIB_VERSION = "1.6.3";
static constexpr int64 ONLINE_ALARM_ID = 0;
static constexpr int64 PING_SERVER_ALARM_ID = -1;
static constexpr int32 PING_SERVER_TIMEOUT = 300;

View File

@ -8,7 +8,7 @@
namespace td {
constexpr int32 MTPROTO_LAYER = 111;
constexpr int32 MTPROTO_LAYER = 112;
enum class Version : int32 {
Initial,
@ -36,6 +36,7 @@ enum class Version : int32 {
AddPhotoSizeSource,
AddFolders,
SupportPolls2_0,
AddDiceEmoji,
Next
};

View File

@ -626,8 +626,22 @@ class CliClient final : public Actor {
}
void on_result(uint64 generation, uint64 id, td_api::object_ptr<td_api::Object> result) {
auto result_str = to_string(result);
if (result != nullptr) {
switch (result->get_id()) {
case td_api::stickerSets::ID: {
auto sticker_sets = static_cast<const td_api::stickerSets *>(result.get());
result_str = PSTRING() << "StickerSets { total_count = " << sticker_sets->total_count_
<< ", count = " << sticker_sets->sets_.size() << "}";
break;
}
default:
break;
}
}
if (id > 0 && GET_VERBOSITY_LEVEL() < VERBOSITY_NAME(td_requests)) {
LOG(ERROR) << "Receive result [" << generation << "][id=" << id << "] " << to_string(result);
LOG(ERROR) << "Receive result [" << generation << "][id=" << id << "] " << result_str;
}
auto as_json_str = json_encode<std::string>(ToJson(result));
@ -642,7 +656,7 @@ class CliClient final : public Actor {
// LOG(INFO) << "Receive result [" << generation << "][id=" << id << "] " << as_json_str;
if (generation != generation_) {
LOG(INFO) << "Drop received from previous Client " << to_string(result);
LOG(INFO) << "Drop received from previous Client " << result_str;
return;
}
@ -728,7 +742,7 @@ class CliClient final : public Actor {
on_get_file(*static_cast<const td_api::updateFile *>(result.get())->file_);
break;
case td_api::updateConnectionState::ID:
LOG(WARNING) << to_string(result);
LOG(WARNING) << result_str;
break;
}
}
@ -2243,7 +2257,15 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::getArchivedStickerSets>(
as_bool(is_masks), to_integer<int64>(offset_sticker_set_id), to_integer<int32>(limit)));
} else if (op == "gtss") {
send_request(td_api::make_object<td_api::getTrendingStickerSets>());
string offset;
string limit;
std::tie(offset, limit) = split(args);
if (limit.empty()) {
limit = "1000";
}
send_request(
td_api::make_object<td_api::getTrendingStickerSets>(to_integer<int32>(offset), to_integer<int32>(limit)));
} else if (op == "gatss") {
send_request(td_api::make_object<td_api::getAttachedStickerSets>(as_file_id(args)));
} else if (op == "storage") {
@ -2383,11 +2405,11 @@ class CliClient final : public Actor {
} else if (op == "gse") {
send_request(td_api::make_object<td_api::getStickerEmojis>(as_input_file_id(args)));
} else if (op == "se") {
send_request(td_api::make_object<td_api::searchEmojis>(args, false, ""));
send_request(td_api::make_object<td_api::searchEmojis>(args, false, vector<string>()));
} else if (op == "see") {
send_request(td_api::make_object<td_api::searchEmojis>(args, true, ""));
send_request(td_api::make_object<td_api::searchEmojis>(args, true, vector<string>()));
} else if (op == "seru") {
send_request(td_api::make_object<td_api::searchEmojis>(args, false, "ru_RU"));
send_request(td_api::make_object<td_api::searchEmojis>(args, false, vector<string>{"ru_RU"}));
} else if (op == "gesu") {
send_request(td_api::make_object<td_api::getEmojiSuggestionsUrl>(args));
} else {
@ -3123,8 +3145,12 @@ class CliClient final : public Actor {
send_message(chat_id, td_api::make_object<td_api::inputMessageForwarded>(as_chat_id(from_chat_id),
as_message_id(from_message_id), true,
op == "scopy", Random::fast(0, 1) == 0));
} else if (op == "sdice") {
send_message(args, td_api::make_object<td_api::inputMessageDice>());
} else if (op == "sdice" || op == "sdicecd") {
string chat_id;
string emoji;
std::tie(chat_id, emoji) = split(args);
send_message(chat_id, td_api::make_object<td_api::inputMessageDice>(emoji, op == "sdicecd"));
} else if (op == "sd") {
string chat_id;
string document_path;
@ -3227,12 +3253,13 @@ class CliClient final : public Actor {
td_api::object_ptr<td_api::PollType> poll_type;
if (op == "squiz") {
poll_type = td_api::make_object<td_api::pollTypeQuiz>(narrow_cast<int32>(options.size() - 1));
poll_type = td_api::make_object<td_api::pollTypeQuiz>(narrow_cast<int32>(options.size() - 1),
as_formatted_text("_te*st*_"));
} else {
poll_type = td_api::make_object<td_api::pollTypeRegular>(op == "spollm");
}
send_message(chat_id, td_api::make_object<td_api::inputMessagePoll>(question, std::move(options), op != "spollp",
std::move(poll_type), false));
std::move(poll_type), 0, 0, false));
} else if (op == "sp" || op == "spcaption" || op == "spttl") {
string chat_id;
string photo_path;

View File

@ -25,6 +25,7 @@
#include "td/actor/SleepActor.h"
#include "td/utils/base64.h"
#include "td/utils/filesystem.h"
#include "td/utils/format.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
@ -1400,6 +1401,11 @@ Result<FileId> FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy
LOG(ERROR) << "File remote location was changed from " << y_node->remote_.full.value() << " to "
<< x_node->remote_.full.value();
}
bool drop_last_successful_force_reupload_time = x_node->last_successful_force_reupload_time_ <= 0 &&
x_node->remote_.full &&
x_node->remote_.full_source == FileLocationSource::FromServer;
auto count_local = [](auto &node) {
return std::accumulate(node->file_ids_.begin(), node->file_ids_.end(), 0,
[](const auto &x, const auto &y) { return x + (y.get_remote() != 0); });
@ -1548,7 +1554,9 @@ Result<FileId> FileManager::merge(FileId x_file_id, FileId y_file_id, bool no_sy
node->need_load_from_pmc_ |= other_node->need_load_from_pmc_;
node->can_search_locally_ &= other_node->can_search_locally_;
if (other_node->last_successful_force_reupload_time_ > node->last_successful_force_reupload_time_) {
if (drop_last_successful_force_reupload_time) {
node->last_successful_force_reupload_time_ = -1e10;
} else if (other_node->last_successful_force_reupload_time_ > node->last_successful_force_reupload_time_) {
node->last_successful_force_reupload_time_ = other_node->last_successful_force_reupload_time_;
}
@ -1936,7 +1944,7 @@ void FileManager::read_file_part(FileId file_id, int32 offset, int32 count, int
}
if (!file_id.is_valid()) {
return promise.set_error(Status::Error(400, "File ID is invalid"));
return promise.set_error(Status::Error(400, "File identifier is invalid"));
}
auto node = get_sync_file_node(file_id);
if (!node) {
@ -2113,6 +2121,25 @@ void FileManager::download(FileId file_id, std::shared_ptr<DownloadCallback> cal
}
void FileManager::run_download(FileNodePtr node) {
int8 priority = 0;
for (auto id : node->file_ids_) {
auto *info = get_file_id_info(id);
if (info->download_priority_ > priority) {
priority = info->download_priority_;
}
}
auto old_priority = node->download_priority_;
if (priority == 0) {
node->set_download_priority(priority);
LOG(INFO) << "Cancel downloading of file " << node->main_file_id_;
if (old_priority != 0) {
do_cancel_download(node);
}
return;
}
if (node->need_load_from_pmc_) {
LOG(INFO) << "Skip run_download, because file " << node->main_file_id_ << " needs to be loaded from PMC";
return;
@ -2126,24 +2153,7 @@ void FileManager::run_download(FileNodePtr node) {
LOG(INFO) << "Skip run_download, because file " << node->main_file_id_ << " can't be downloaded from server";
return;
}
int8 priority = 0;
for (auto id : node->file_ids_) {
auto *info = get_file_id_info(id);
if (info->download_priority_ > priority) {
priority = info->download_priority_;
}
}
auto old_priority = node->download_priority_;
node->set_download_priority(priority);
if (priority == 0) {
LOG(INFO) << "Cancel downloading of file " << node->main_file_id_;
if (old_priority != 0) {
do_cancel_download(node);
}
return;
}
bool need_update_offset = node->is_download_offset_dirty_;
node->is_download_offset_dirty_ = false;
@ -2348,7 +2358,9 @@ class FileManager::ForceUploadActor : public Actor {
void FileManager::on_force_reupload_success(FileId file_id) {
auto node = get_sync_file_node(file_id);
CHECK(node);
node->last_successful_force_reupload_time_ = Time::now();
if (!node->remote_.is_full_alive) { // do not update for multiple simultaneous uploads
node->last_successful_force_reupload_time_ = Time::now();
}
}
void FileManager::resume_upload(FileId file_id, std::vector<int> bad_parts, std::shared_ptr<UploadCallback> callback,
@ -2434,7 +2446,7 @@ void FileManager::resume_upload(FileId file_id, std::vector<int> bad_parts, std:
bool FileManager::delete_partial_remote_location(FileId file_id) {
auto node = get_sync_file_node(file_id);
if (!node) {
LOG(INFO) << "Wrong file id " << file_id;
LOG(INFO) << "Wrong file identifier " << file_id;
return false;
}
if (node->upload_pause_ == file_id) {
@ -2470,7 +2482,7 @@ void FileManager::delete_file_reference(FileId file_id, string file_reference) {
<< tag("reference_base64", base64_encode(file_reference));
auto node = get_sync_file_node(file_id);
if (!node) {
LOG(ERROR) << "Wrong file id " << file_id;
LOG(ERROR) << "Wrong file identifier " << file_id;
return;
}
node->delete_file_reference(file_reference);
@ -2586,6 +2598,29 @@ void FileManager::run_generate(FileNodePtr node) {
}
void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
int8 priority = 0;
FileId file_id = node->main_file_id_;
for (auto id : node->file_ids_) {
auto *info = get_file_id_info(id);
if (info->upload_priority_ > priority) {
priority = info->upload_priority_;
file_id = id;
}
}
auto old_priority = node->upload_priority_;
if (priority == 0) {
node->set_upload_priority(priority);
if (old_priority != 0) {
LOG(INFO) << "Cancel file " << file_id << " uploading";
do_cancel_upload(node);
} else {
LOG(INFO) << "File " << file_id << " upload priority is still 0";
}
return;
}
if (node->need_load_from_pmc_) {
LOG(INFO) << "File " << node->main_file_id_ << " needs to be loaded from database before upload";
return;
@ -2594,6 +2629,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
LOG(INFO) << "File " << node->main_file_id_ << " upload is paused: " << node->upload_pause_;
return;
}
FileView file_view(node);
if (!file_view.has_local_location() && !file_view.has_remote_location()) {
if (node->get_by_hash_ || node->generate_id_ == 0 || !node->generate_was_update_) {
@ -2607,29 +2643,9 @@ void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
return;
}
}
int8 priority = 0;
FileId file_id = node->main_file_id_;
for (auto id : node->file_ids_) {
auto *info = get_file_id_info(id);
if (info->upload_priority_ > priority) {
priority = info->upload_priority_;
file_id = id;
}
}
auto old_priority = node->upload_priority_;
node->set_upload_priority(priority);
if (priority == 0) {
if (old_priority != 0) {
LOG(INFO) << "Cancel file " << file_id << " uploading";
do_cancel_upload(node);
} else {
LOG(INFO) << "File " << file_id << " upload priority is still 0";
}
return;
}
// create encryption key if necessary
if (((file_view.has_generate_location() && file_view.generate_location().file_type_ == FileType::Encrypted) ||
(file_view.has_local_location() && file_view.local_location().file_type_ == FileType::Encrypted)) &&
@ -2749,11 +2765,11 @@ Result<FileId> FileManager::from_persistent_id(CSlice persistent_id, FileType fi
auto r_binary = base64url_decode(persistent_id);
if (r_binary.is_error()) {
return Status::Error(10, PSLICE() << "Wrong remote file id specified: " << r_binary.error().message());
return Status::Error(10, PSLICE() << "Wrong remote file identifier specified: " << r_binary.error().message());
}
auto binary = r_binary.move_as_ok();
if (binary.empty()) {
return Status::Error(10, "Remote file id can't be empty");
return Status::Error(10, "Remote file identifier can't be empty");
}
if (binary.back() == PERSISTENT_ID_VERSION_OLD) {
return from_persistent_id_v2(binary, file_type);
@ -2764,7 +2780,7 @@ Result<FileId> FileManager::from_persistent_id(CSlice persistent_id, FileType fi
if (binary.back() == PERSISTENT_ID_VERSION_MAP) {
return from_persistent_id_map(binary, file_type);
}
return Status::Error(10, "Wrong remote file id specified: can't unserialize it. Wrong last symbol");
return Status::Error(10, "Wrong remote file identifier specified: can't unserialize it. Wrong last symbol");
}
Result<FileId> FileManager::from_persistent_id_map(Slice binary, FileType file_type) {
@ -2773,7 +2789,7 @@ Result<FileId> FileManager::from_persistent_id_map(Slice binary, FileType file_t
FullGenerateFileLocation generate_location;
auto status = unserialize(generate_location, decoded_binary);
if (status.is_error()) {
return Status::Error(10, "Wrong remote file id specified: can't unserialize it");
return Status::Error(10, "Wrong remote file identifier specified: can't unserialize it");
}
auto real_file_type = generate_location.file_type_;
if ((real_file_type != file_type && file_type != FileType::Temp) ||
@ -2790,7 +2806,7 @@ Result<FileId> FileManager::from_persistent_id_map(Slice binary, FileType file_t
Result<FileId> FileManager::from_persistent_id_v23(Slice binary, FileType file_type, int32 version) {
if (version < 0 || version >= static_cast<int32>(Version::Next)) {
return Status::Error("Invalid remote id");
return Status::Error("Invalid remote file identifier");
}
auto decoded_binary = zero_decode(binary);
FullRemoteFileLocation remote_location;
@ -2800,7 +2816,7 @@ Result<FileId> FileManager::from_persistent_id_v23(Slice binary, FileType file_t
parser.fetch_end();
auto status = parser.get_status();
if (status.is_error()) {
return Status::Error(10, "Wrong remote file id specified: can't unserialize it");
return Status::Error(10, "Wrong remote file identifier specified: can't unserialize it");
}
auto &real_file_type = remote_location.file_type_;
if (is_document_type(real_file_type) && is_document_type(file_type)) {
@ -2825,7 +2841,7 @@ Result<FileId> FileManager::from_persistent_id_v2(Slice binary, FileType file_ty
Result<FileId> FileManager::from_persistent_id_v3(Slice binary, FileType file_type) {
binary.remove_suffix(1);
if (binary.empty()) {
return Status::Error("Invalid remote id");
return Status::Error("Invalid remote file identifier");
}
int32 version = static_cast<uint8>(binary.back());
binary.remove_suffix(1);
@ -3010,7 +3026,25 @@ Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr
if (allow_zero && path.empty()) {
return FileId();
}
return register_local(FullLocalFileLocation(new_type, path, 0), owner_dialog_id, 0, get_by_hash);
string hash;
if (false && new_type == FileType::Photo) {
auto r_stat = stat(path);
if (r_stat.is_ok() && r_stat.ok().size_ > 0 && r_stat.ok().size_ < 1000000) {
auto r_file_content = read_file_str(path, r_stat.ok().size_);
if (r_file_content.is_ok()) {
hash = sha256(r_file_content.ok());
auto it = file_hash_to_file_id_.find(hash);
if (it != file_hash_to_file_id_.end()) {
return it->second;
}
}
}
}
TRY_RESULT(file_id, register_local(FullLocalFileLocation(new_type, path, 0), owner_dialog_id, 0, get_by_hash));
if (!hash.empty()) {
file_hash_to_file_id_[hash] = file_id;
}
return file_id;
}
case td_api::inputFileId::ID: {
FileId file_id(static_cast<const td_api::inputFileId *>(file.get())->id_, 0);

View File

@ -57,7 +57,7 @@ struct NewRemoteFileLocation {
unique_ptr<PartialRemoteFileLocation> partial;
//TODO: use RemoteId
// hardest part is to determine wether we should flush this location to db.
// hardest part is to determine whether we should flush this location to db.
// probably, will need some generation in RemoteInfo
optional<FullRemoteFileLocation> full;
bool is_full_alive{false}; // if false, then we may try to upload this file
@ -546,6 +546,8 @@ class FileManager : public FileLoadManager::Callback {
};
Enumerator<RemoteInfo> remote_location_info_;
std::unordered_map<string, FileId> file_hash_to_file_id_;
std::map<FullLocalFileLocation, FileId> local_location_to_file_id_;
std::map<FullGenerateFileLocation, FileId> generate_location_to_file_id_;
std::map<FileDbId, int32> pmc_id_to_file_node_id_;

View File

@ -1078,26 +1078,26 @@ void ConnectionCreator::start_up() {
}
auto proxy_info = G()->td_db()->get_binlog_pmc()->prefix_get("proxy");
auto it = proxy_info.find("proxy_max_id");
auto it = proxy_info.find("_max_id");
if (it != proxy_info.end()) {
max_proxy_id_ = to_integer<int32>(it->second);
proxy_info.erase(it);
}
it = proxy_info.find("proxy_active_id");
it = proxy_info.find("_active_id");
if (it != proxy_info.end()) {
set_active_proxy_id(to_integer<int32>(it->second), true);
proxy_info.erase(it);
}
for (auto &info : proxy_info) {
if (begins_with(info.first, "proxy_used")) {
int32 proxy_id = to_integer_safe<int32>(Slice(info.first).substr(10)).move_as_ok();
if (begins_with(info.first, "_used")) {
int32 proxy_id = to_integer_safe<int32>(Slice(info.first).substr(5)).move_as_ok();
int32 last_used = to_integer_safe<int32>(info.second).move_as_ok();
proxy_last_used_date_[proxy_id] = last_used;
proxy_last_used_saved_date_[proxy_id] = last_used;
} else {
LOG_CHECK(!ends_with(info.first, "_max_id")) << info.first;
int32 proxy_id = info.first == "proxy" ? 1 : to_integer_safe<int32>(Slice(info.first).substr(5)).move_as_ok();
int32 proxy_id = info.first == "" ? 1 : to_integer_safe<int32>(info.first).move_as_ok();
CHECK(proxies_.count(proxy_id) == 0);
log_event_parse(proxies_[proxy_id], info.second).ensure();
if (proxies_[proxy_id].type() == Proxy::Type::None) {

View File

@ -8,10 +8,12 @@
#include "td/telegram/JsonValue.h"
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/Version.h"
#include "td/tl/tl_object_store.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/tl_helpers.h"
namespace td {
@ -37,7 +39,7 @@ class HeaderStorer {
if (have_proxy) {
flags |= 1 << 0;
}
if (!options.parameters.empty()) {
if (!is_anonymous) {
flags |= 1 << 1;
}
if (options.is_emulator) {
@ -72,10 +74,29 @@ class HeaderStorer {
store(Slice(options.proxy.server()), storer);
store(options.proxy.port(), storer);
}
if (!options.parameters.empty()) {
auto parameters_copy = options.parameters;
auto json_value = get_input_json_value(parameters_copy).move_as_ok();
if (!is_anonymous) {
telegram_api::object_ptr<telegram_api::JSONValue> json_value;
if (options.parameters.empty()) {
json_value = make_tl_object<telegram_api::jsonObject>(vector<tl_object_ptr<telegram_api::jsonObjectValue>>());
} else {
auto parameters_copy = options.parameters;
json_value = get_input_json_value(parameters_copy).move_as_ok();
}
CHECK(json_value != nullptr);
if (json_value->get_id() == telegram_api::jsonObject::ID) {
auto &values = static_cast<telegram_api::jsonObject *>(json_value.get())->value_;
bool has_tz_offset = false;
for (auto &value : values) {
if (value->key_ == "tz_offset") {
has_tz_offset = true;
break;
}
}
if (!has_tz_offset) {
values.push_back(make_tl_object<telegram_api::jsonObjectValue>(
"tz_offset", make_tl_object<telegram_api::jsonNumber>(Clocks::tz_offset())));
}
}
TlStoreBoxedUnknown<TlStoreObject>::store(json_value, storer);
}
}

View File

@ -122,7 +122,7 @@ Status NetQueryDispatcher::wait_dc_init(DcId dc_id, bool force) {
}
size_t pos = static_cast<size_t>(dc_id.get_raw_id() - 1);
if (pos >= dcs_.size()) {
return Status::Error("Too big DC id");
return Status::Error("Too big DC ID");
}
auto &dc = dcs_[pos];

View File

@ -839,7 +839,7 @@ void Session::on_message_info(uint64 id, int32 state, uint64 answer_id, int32 an
case 2:
case 3:
// message not received by server
return on_message_failed(id, Status::Error("Unknown message id"));
return on_message_failed(id, Status::Error("Unknown message identifier"));
case 0:
if (answer_id == 0) {
LOG(ERROR) << "Unexpected message_info.state == 0 " << tag("id", id) << tag("state", state)

View File

@ -408,6 +408,7 @@ class PromiseActor final : public PromiseInterface<T> {
template <class T>
class FutureActor final : public Actor {
friend class PromiseActor<T>;
public:
enum State { Waiting, Ready };

View File

@ -219,7 +219,7 @@ class Scheduler {
bool has_guard_ = false;
bool close_flag_ = false;
uint32 wait_generation_ = 0;
uint32 wait_generation_ = 1;
int32 sched_id_ = 0;
int32 sched_n_ = 0;
std::shared_ptr<MpscPollableQueue<EventFull>> inbound_queue_;

View File

@ -190,7 +190,7 @@ inline void Scheduler::before_tail_send(const ActorId<> &actor_id) {
}
inline void Scheduler::inc_wait_generation() {
wait_generation_++;
wait_generation_ += 2;
}
template <ActorSendType send_type, class RunFuncT, class EventFuncT>

View File

@ -191,7 +191,7 @@ class BinlogKeyValue : public KeyValueSyncInterface {
std::unordered_map<string, string> res;
for (const auto &kv : map_) {
if (begins_with(kv.first, prefix)) {
res[kv.first] = kv.second.first;
res[kv.first.substr(prefix.size())] = kv.second.first;
}
}
return res;

View File

@ -29,6 +29,9 @@ class SqliteKeyValueAsync : public SqliteKeyValueAsyncInterface {
void erase(string key, Promise<> promise) override {
send_closure_later(impl_, &Impl::erase, std::move(key), std::move(promise));
}
void erase_by_prefix(string key_prefix, Promise<> promise) override {
send_closure_later(impl_, &Impl::erase_by_prefix, std::move(key_prefix), std::move(promise));
}
void get(string key, Promise<string> promise) override {
send_closure_later(impl_, &Impl::get, std::move(key), std::move(promise));
}
@ -67,6 +70,11 @@ class SqliteKeyValueAsync : public SqliteKeyValueAsyncInterface {
cnt_++;
do_flush(false /*force*/);
}
void erase_by_prefix(string key_prefix, Promise<> promise) {
do_flush(true /*force*/);
kv_->erase_by_prefix(key_prefix);
promise.set_value(Unit());
}
void get(const string &key, Promise<string> promise) {
auto it = buffer_.find(key);

View File

@ -20,6 +20,7 @@ class SqliteKeyValueAsyncInterface {
virtual void set(string key, string value, Promise<> promise) = 0;
virtual void erase(string key, Promise<> promise) = 0;
virtual void erase_by_prefix(string key_prefix, Promise<> promise) = 0;
virtual void get(string key, Promise<string> promise) = 0;
virtual void close(Promise<> promise) = 0;

View File

@ -27,11 +27,11 @@ char *str_dup(Slice str) {
string implode(const vector<string> &v, char delimiter) {
string result;
for (auto &str : v) {
if (!result.empty()) {
for (size_t i = 0; i < v.size(); i++) {
if (i != 0) {
result += delimiter;
}
result += str;
result += v[i];
}
return result;
}

View File

@ -167,6 +167,9 @@ void combine(vector<T> &destination, vector<T> &&source) {
if (destination.size() < source.size()) {
destination.swap(source);
}
if (source.empty()) {
return;
}
destination.reserve(destination.size() + source.size());
for (auto &elem : source) {
destination.push_back(std::move(elem));

View File

@ -815,6 +815,11 @@ TEST(MessageEntities, fix_formatted_text) {
"abc", {{td::MessageEntity::Type::Italic, 0, 2}, {td::MessageEntity::Type::Bold, 2, 1}});
check_fix_formatted_text("abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Bold, 2, 1}},
"abc", {{td::MessageEntity::Type::Italic, 0, 1}, {td::MessageEntity::Type::Bold, 2, 1}});
check_fix_formatted_text("@tests @tests", {{td::MessageEntity::Type::Italic, 0, 13}}, "@tests @tests",
{{td::MessageEntity::Type::Mention, 0, 6},
{td::MessageEntity::Type::Italic, 0, 6},
{td::MessageEntity::Type::Mention, 7, 6},
{td::MessageEntity::Type::Italic, 7, 6}});
// _a*b*_
check_fix_formatted_text(