Merge commit '44689f81ff8b9bc4583c1c889d8034c47f0c5270'
Conflicts: td/telegram/DialogDb.cpp td/telegram/MessagesManager.cpp td/telegram/StickersManager.cpp td/telegram/TdDb.cpp td/telegram/WebPagesManager.cpp
This commit is contained in:
commit
205fa52f8d
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||
|
||||
project(TDLib VERSION 1.6.4 LANGUAGES CXX C)
|
||||
project(TDLib VERSION 1.6.6 LANGUAGES CXX C)
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR "lib")
|
||||
@ -395,6 +395,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/DhCache.cpp
|
||||
td/telegram/DialogAdministrator.cpp
|
||||
td/telegram/DialogDb.cpp
|
||||
td/telegram/DialogFilter.cpp
|
||||
td/telegram/DialogId.cpp
|
||||
td/telegram/DialogLocation.cpp
|
||||
td/telegram/DialogParticipant.cpp
|
||||
@ -425,6 +426,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/Global.cpp
|
||||
td/telegram/HashtagHints.cpp
|
||||
td/telegram/InlineQueriesManager.cpp
|
||||
td/telegram/InputDialogId.cpp
|
||||
td/telegram/InputMessageText.cpp
|
||||
td/telegram/JsonValue.cpp
|
||||
td/telegram/LanguagePackManager.cpp
|
||||
@ -547,7 +549,10 @@ set(TDLIB_SOURCE
|
||||
td/telegram/DialogAdministrator.h
|
||||
td/telegram/DialogDate.h
|
||||
td/telegram/DialogDb.h
|
||||
td/telegram/DialogFilter.h
|
||||
td/telegram/DialogFilterId.h
|
||||
td/telegram/DialogId.h
|
||||
td/telegram/DialogListId.h
|
||||
td/telegram/DialogLocation.h
|
||||
td/telegram/DialogParticipant.h
|
||||
td/telegram/DialogSource.h
|
||||
@ -587,6 +592,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/Global.h
|
||||
td/telegram/HashtagHints.h
|
||||
td/telegram/InlineQueriesManager.h
|
||||
td/telegram/InputDialogId.h
|
||||
td/telegram/InputMessageText.h
|
||||
td/telegram/JsonValue.h
|
||||
td/telegram/LanguagePackManager.h
|
||||
@ -687,6 +693,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/AudiosManager.hpp
|
||||
td/telegram/AuthManager.hpp
|
||||
td/telegram/BackgroundType.hpp
|
||||
td/telegram/DialogFilter.hpp
|
||||
td/telegram/Document.hpp
|
||||
td/telegram/DocumentsManager.hpp
|
||||
td/telegram/DraftMessage.hpp
|
||||
|
@ -194,7 +194,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.4 REQUIRED)
|
||||
find_package(Td 1.6.6 REQUIRED)
|
||||
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||
```
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt).
|
||||
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
|
||||
|
||||
project(TdExample VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
find_package(Td 1.6.4 REQUIRED)
|
||||
find_package(Td 1.6.6 REQUIRED)
|
||||
|
||||
add_executable(tdjson_example tdjson_example.cpp)
|
||||
target_link_libraries(tdjson_example PRIVATE Td::TdJson)
|
||||
|
@ -71,27 +71,27 @@ public final class Example {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setChatOrder(TdApi.Chat chat, long order) {
|
||||
private static void setChatPositions(TdApi.Chat chat, TdApi.ChatPosition[] positions) {
|
||||
synchronized (mainChatList) {
|
||||
synchronized (chat) {
|
||||
if (chat.chatList == null || chat.chatList.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chat.order != 0) {
|
||||
boolean isRemoved = mainChatList.remove(new OrderedChat(chat.order, chat.id));
|
||||
for (TdApi.ChatPosition position : chat.positions) {
|
||||
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
boolean isRemoved = mainChatList.remove(new OrderedChat(chat.id, position));
|
||||
assert isRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
chat.order = order;
|
||||
chat.positions = positions;
|
||||
|
||||
if (chat.order != 0) {
|
||||
boolean isAdded = mainChatList.add(new OrderedChat(chat.order, chat.id));
|
||||
for (TdApi.ChatPosition position : chat.positions) {
|
||||
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
boolean isAdded = mainChatList.add(new OrderedChat(chat.id, position));
|
||||
assert isAdded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void onAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) {
|
||||
if (authorizationState != null) {
|
||||
@ -251,7 +251,7 @@ public final class Example {
|
||||
long offsetChatId = 0;
|
||||
if (!mainChatList.isEmpty()) {
|
||||
OrderedChat last = mainChatList.last();
|
||||
offsetOrder = last.order;
|
||||
offsetOrder = last.position.order;
|
||||
offsetChatId = last.chatId;
|
||||
}
|
||||
client.send(new TdApi.GetChats(new TdApi.ChatListMain(), offsetOrder, offsetChatId, limit - mainChatList.size()), new Client.ResultHandler() {
|
||||
@ -335,18 +335,18 @@ public final class Example {
|
||||
}
|
||||
|
||||
private static class OrderedChat implements Comparable<OrderedChat> {
|
||||
final long order;
|
||||
final long chatId;
|
||||
final TdApi.ChatPosition position;
|
||||
|
||||
OrderedChat(long order, long chatId) {
|
||||
this.order = order;
|
||||
OrderedChat(long chatId, TdApi.ChatPosition position) {
|
||||
this.chatId = chatId;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OrderedChat o) {
|
||||
if (this.order != o.order) {
|
||||
return o.order < this.order ? -1 : 1;
|
||||
if (this.position.order != o.position.order) {
|
||||
return o.position.order < this.position.order ? -1 : 1;
|
||||
}
|
||||
if (this.chatId != o.chatId) {
|
||||
return o.chatId < this.chatId ? -1 : 1;
|
||||
@ -357,7 +357,7 @@ public final class Example {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
OrderedChat o = (OrderedChat) obj;
|
||||
return this.order == o.order && this.chatId == o.chatId;
|
||||
return this.chatId == o.chatId && this.position.order == o.position.order;
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,9 +407,9 @@ public final class Example {
|
||||
synchronized (chat) {
|
||||
chats.put(chat.id, chat);
|
||||
|
||||
long order = chat.order;
|
||||
chat.order = 0;
|
||||
setChatOrder(chat, order);
|
||||
TdApi.ChatPosition[] positions = chat.positions;
|
||||
chat.positions = new TdApi.ChatPosition[0];
|
||||
setChatPositions(chat, positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -429,40 +429,42 @@ public final class Example {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatChatList.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatChatList updateChat = (TdApi.UpdateChatChatList) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (mainChatList) { // to not change Chat.chatList while mainChatList is locked
|
||||
synchronized (chat) {
|
||||
assert chat.order == 0; // guaranteed by TDLib
|
||||
chat.chatList = updateChat.chatList;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatLastMessage.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.lastMessage = updateChat.lastMessage;
|
||||
setChatOrder(chat, updateChat.order);
|
||||
setChatPositions(chat, updateChat.positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatOrder.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatOrder updateChat = (TdApi.UpdateChatOrder) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
setChatOrder(chat, updateChat.order);
|
||||
}
|
||||
case TdApi.UpdateChatPosition.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPosition updateChat = (TdApi.UpdateChatPosition) object;
|
||||
if (updateChat.position.list.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatIsPinned.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatIsPinned updateChat = (TdApi.UpdateChatIsPinned) object;
|
||||
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.isPinned = updateChat.isPinned;
|
||||
setChatOrder(chat, updateChat.order);
|
||||
int i;
|
||||
for (i = 0; i < chat.positions.length; i++) {
|
||||
if (chat.positions[i].list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TdApi.ChatPosition[] new_positions = new TdApi.ChatPosition[chat.positions.length + (updateChat.position.order == 0 ? 0 : 1) - (i < chat.positions.length ? 1 : 0)];
|
||||
int pos = 0;
|
||||
if (updateChat.position.order != 0) {
|
||||
new_positions[pos++] = updateChat.position;
|
||||
}
|
||||
for (int j = 0; j < chat.positions.length; j++) {
|
||||
if (j != i) {
|
||||
new_positions[pos++] = chat.positions[j];
|
||||
}
|
||||
}
|
||||
assert pos == new_positions.length;
|
||||
|
||||
setChatPositions(chat, new_positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -512,7 +514,15 @@ public final class Example {
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
synchronized (chat) {
|
||||
chat.draftMessage = updateChat.draftMessage;
|
||||
setChatOrder(chat, updateChat.order);
|
||||
setChatPositions(chat, updateChat.positions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatPermissions.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPermissions update = (TdApi.UpdateChatPermissions) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.permissions = update.permissions;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -540,12 +550,11 @@ public final class Example {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TdApi.UpdateChatIsSponsored.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatIsSponsored updateChat = (TdApi.UpdateChatIsSponsored) object;
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
case TdApi.UpdateChatHasScheduledMessages.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatHasScheduledMessages update = (TdApi.UpdateChatHasScheduledMessages) object;
|
||||
TdApi.Chat chat = chats.get(update.chatId);
|
||||
synchronized (chat) {
|
||||
chat.isSponsored = updateChat.isSponsored;
|
||||
setChatOrder(chat, updateChat.order);
|
||||
chat.hasScheduledMessages = update.hasScheduledMessages;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -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.4" Language="en-US" Publisher="Telegram LLC" />
|
||||
<Identity Id="Telegram.Td.UWP" Version="1.6.6" 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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tdweb",
|
||||
"version": "1.6.0",
|
||||
"version": "1.6.6",
|
||||
"description": "Javascript interface for TDLib (telegram library)",
|
||||
"main": "dist/tdweb.js",
|
||||
"repository": {
|
||||
|
@ -774,8 +774,8 @@ class TdClient {
|
||||
try {
|
||||
//const file_size = this.FS.stat(query.path).size;
|
||||
const stream = this.FS.open(query.path, 'r');
|
||||
const buf = new Uint8Array(query.size);
|
||||
this.FS.read(stream, buf, 0, query.size, query.offset);
|
||||
const buf = new Uint8Array(query.count);
|
||||
this.FS.read(stream, buf, 0, query.count, query.offset);
|
||||
this.FS.close(stream);
|
||||
res = buf;
|
||||
} catch (e) {
|
||||
|
@ -185,6 +185,31 @@ photoSize type:string photo:file width:int32 height:int32 = PhotoSize;
|
||||
minithumbnail width:int32 height:int32 data:bytes = Minithumbnail;
|
||||
|
||||
|
||||
//@class ThumbnailFormat @description Describes format of the thumbnail
|
||||
|
||||
//@description The thumbnail is in JPEG format
|
||||
thumbnailFormatJpeg = ThumbnailFormat;
|
||||
|
||||
//@description The thumbnail is in PNG format. It will be used only for background patterns
|
||||
thumbnailFormatPng = ThumbnailFormat;
|
||||
|
||||
//@description The thumbnail is in WEBP format. It will be used only for some stickers
|
||||
thumbnailFormatWebp = ThumbnailFormat;
|
||||
|
||||
//@description The thumbnail is in static GIF format. It will be used only for some bot inline results
|
||||
thumbnailFormatGif = ThumbnailFormat;
|
||||
|
||||
//@description The thumbnail is in TGS format. It will be used only for animated sticker sets
|
||||
thumbnailFormatTgs = ThumbnailFormat;
|
||||
|
||||
//@description The thumbnail is in MPEG4 format. It will be used only for some animations and videos
|
||||
thumbnailFormatMpeg4 = ThumbnailFormat;
|
||||
|
||||
|
||||
//@description Represents a thumbnail @format Thumbnail format @width Thumbnail width @height Thumbnail height @file The thumbnail
|
||||
thumbnail format:ThumbnailFormat width:int32 height:int32 file:file = Thumbnail;
|
||||
|
||||
|
||||
//@class MaskPoint @description Part of the face, relative to which a mask should be placed
|
||||
|
||||
//@description A mask should be placed relatively to the forehead
|
||||
@ -224,31 +249,39 @@ 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
|
||||
//@file_name Original name of the file; as defined by the sender @mime_type MIME type of the file, usually "image/gif" or "video/mp4"
|
||||
//@minithumbnail Animation minithumbnail; may be null @thumbnail Animation thumbnail; may be null @animation File containing the animation
|
||||
animation duration:int32 width:int32 height:int32 file_name:string mime_type:string minithumbnail:minithumbnail thumbnail:photoSize animation:file = Animation;
|
||||
//@has_stickers True, if stickers were added to the animation. The list of corresponding sticker set can be received using getAttachedStickerSets
|
||||
//@minithumbnail Animation minithumbnail; may be null @thumbnail Animation thumbnail in JPEG or MPEG4 format; may be null @animation File containing the animation
|
||||
animation duration:int32 width:int32 height:int32 file_name:string mime_type:string has_stickers:Bool minithumbnail:minithumbnail thumbnail:thumbnail animation:file = Animation;
|
||||
|
||||
//@description Describes an audio file. Audio is usually in MP3 or M4A format @duration Duration of the audio, in seconds; as defined by the sender @title Title of the audio; as defined by the sender @performer Performer of the audio; as defined by the sender
|
||||
//@file_name Original name of the file; as defined by the sender @mime_type The MIME type of the file; as defined by the sender @album_cover_minithumbnail The minithumbnail of the album cover; may be null @album_cover_thumbnail The thumbnail of the album cover; as defined by the sender. The full size thumbnail should be extracted from the downloaded file; may be null @audio File containing the audio
|
||||
audio duration:int32 title:string performer:string file_name:string mime_type:string album_cover_minithumbnail:minithumbnail album_cover_thumbnail:photoSize audio:file = Audio;
|
||||
//@file_name Original name of the file; as defined by the sender @mime_type The MIME type of the file; as defined by the sender @album_cover_minithumbnail The minithumbnail of the album cover; may be null
|
||||
//@album_cover_thumbnail The thumbnail of the album cover in JPEG format; as defined by the sender. The full size thumbnail should be extracted from the downloaded file; may be null @audio File containing the audio
|
||||
audio duration:int32 title:string performer:string file_name:string mime_type:string album_cover_minithumbnail:minithumbnail album_cover_thumbnail:thumbnail audio:file = Audio;
|
||||
|
||||
//@description Describes a document of any type @file_name Original name of the file; as defined by the sender @mime_type MIME type of the file; as defined by the sender
|
||||
//@minithumbnail Document minithumbnail; may be null @thumbnail Document thumbnail in JPEG or PNG format (PNG will be used only for background patterns); as defined by the sender; may be null @document File containing the document
|
||||
document file_name:string mime_type:string minithumbnail:minithumbnail thumbnail:photoSize document:file = Document;
|
||||
document file_name:string mime_type:string minithumbnail:minithumbnail thumbnail:thumbnail document:file = Document;
|
||||
|
||||
//@description Describes a photo @has_stickers True, if stickers were added to the photo @minithumbnail Photo minithumbnail; may be null @sizes Available variants of the photo, in different sizes
|
||||
//@description Describes a photo @has_stickers True, if stickers were added to the photo. The list of corresponding sticker sets can be received using getAttachedStickerSets
|
||||
//@minithumbnail Photo minithumbnail; may be null @sizes Available variants of the photo, in different sizes
|
||||
photo has_stickers:Bool minithumbnail:minithumbnail sizes:vector<photoSize> = Photo;
|
||||
|
||||
//@description Describes a sticker @set_id The identifier of the sticker set to which the sticker belongs; 0 if none @width Sticker width; as defined by the sender @height Sticker height; as defined by the sender
|
||||
//@emoji Emoji corresponding to the sticker @is_animated True, if the sticker is an animated sticker in TGS format @is_mask True, if the sticker is a mask @mask_position Position where the mask should be placed; may be null @thumbnail Sticker thumbnail in WEBP or JPEG format; may be null @sticker File containing the sticker
|
||||
sticker set_id:int64 width:int32 height:int32 emoji:string is_animated:Bool is_mask:Bool mask_position:maskPosition thumbnail:photoSize sticker:file = Sticker;
|
||||
//@emoji Emoji corresponding to the sticker @is_animated True, if the sticker is an animated sticker in TGS format @is_mask True, if the sticker is a mask @mask_position Position where the mask should be placed; may be null
|
||||
//@thumbnail Sticker thumbnail in WEBP or JPEG format; may be null @sticker File containing the sticker
|
||||
sticker set_id:int64 width:int32 height:int32 emoji:string is_animated:Bool is_mask:Bool mask_position:maskPosition thumbnail:thumbnail sticker:file = Sticker;
|
||||
|
||||
//@description Describes a video file @duration Duration of the video, in seconds; as defined by the sender @width Video width; as defined by the sender @height Video height; as defined by the sender
|
||||
//@file_name Original name of the file; as defined by the sender @mime_type MIME type of the file; as defined by the sender @has_stickers True, if stickers were added to the video
|
||||
//@supports_streaming True, if the video should be tried to be streamed @minithumbnail Video minithumbnail; may be null @thumbnail Video thumbnail; as defined by the sender; may be null @video File containing the video
|
||||
video duration:int32 width:int32 height:int32 file_name:string mime_type:string has_stickers:Bool supports_streaming:Bool minithumbnail:minithumbnail thumbnail:photoSize video:file = Video;
|
||||
//@file_name Original name of the file; as defined by the sender @mime_type MIME type of the file; as defined by the sender
|
||||
//@has_stickers True, if stickers were added to the video. The list of corresponding sticker sets can be received using getAttachedStickerSets
|
||||
//@supports_streaming True, if the video should be tried to be streamed @minithumbnail Video minithumbnail; may be null
|
||||
//@thumbnail Video thumbnail in JPEG or MPEG4 format; as defined by the sender; may be null @video File containing the video
|
||||
video duration:int32 width:int32 height:int32 file_name:string mime_type:string has_stickers:Bool supports_streaming:Bool minithumbnail:minithumbnail thumbnail:thumbnail video:file = Video;
|
||||
|
||||
//@description Describes a video note. The video must be equal in width and height, cropped to a circle, and stored in MPEG4 format @duration Duration of the video, in seconds; as defined by the sender @length Video width and height; as defined by the sender @minithumbnail Video minithumbnail; may be null @thumbnail Video thumbnail; as defined by the sender; may be null @video File containing the video
|
||||
videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:photoSize video:file = VideoNote;
|
||||
//@description Describes a video note. The video must be equal in width and height, cropped to a circle, and stored in MPEG4 format @duration Duration of the video, in seconds; as defined by the sender
|
||||
//@length Video width and height; as defined by the sender @minithumbnail Video minithumbnail; may be null
|
||||
//@thumbnail Video thumbnail in JPEG format; as defined by the sender; may be null @video File containing the video
|
||||
videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:thumbnail video:file = VideoNote;
|
||||
|
||||
//@description Describes a voice note. The voice note must be encoded with the Opus codec, and stored inside an OGG container. Voice notes can have only a single audio channel @duration Duration of the voice note, in seconds; as defined by the sender
|
||||
//@waveform A waveform representation of the voice note in 5-bit format @mime_type MIME type of the file; as defined by the sender @voice File containing the voice note
|
||||
@ -463,7 +496,7 @@ basicGroupFullInfo description:string creator_user_id:int32 members:vector<chatM
|
||||
//@username Username of the supergroup or channel; empty for private supergroups or channels
|
||||
//@date Point in time (Unix timestamp) when the current user joined, or the point in time when the supergroup or channel was created, in case the user is not a member
|
||||
//@status Status of the current user in the supergroup or channel; custom title will be always empty
|
||||
//@member_count Number of members in the supergroup or channel; 0 if unknown. Currently it is guaranteed to be known only if the supergroup or channel was found through SearchPublicChats
|
||||
//@member_count Number of members in the supergroup or channel; 0 if unknown. Currently it is guaranteed to be known only if the supergroup or channel was received through searchPublicChats, searchChatsNearby, getInactiveSupergroupChats, getSuitableDiscussionChats, getGroupsInCommon, or getUserPrivacySettingRules
|
||||
//@has_linked_chat True, if the channel has a discussion group, or the supergroup is the designated discussion group for a channel
|
||||
//@has_location True, if the supergroup is connected to a location, i.e. the supergroup is a location-based supergroup
|
||||
//@sign_messages True, if messages sent to the channel should contain information about the sender. This field is only applicable to channels
|
||||
@ -641,6 +674,36 @@ chatTypeSupergroup supergroup_id:int32 is_channel:Bool = ChatType;
|
||||
chatTypeSecret secret_chat_id:int32 user_id:int32 = ChatType;
|
||||
|
||||
|
||||
//@description Represents a filter of user chats
|
||||
//@title The title of the filter; 1-12 characters without line feeds
|
||||
//@icon_name The icon name for short filter representation. If non-empty, must be one of "All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom", "Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love", "Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work".
|
||||
//-If empty, use getChatFilterDefaultIconName to get default icon name for the filter
|
||||
//@pinned_chat_ids The chat identifiers of pinned chats in the filtered chat list
|
||||
//@included_chat_ids The chat identifiers of always included chats in the filtered chat list
|
||||
//@excluded_chat_ids The chat identifiers of always excluded chats in the filtered chat list
|
||||
//@exclude_muted True, if the muted chats need to be excluded
|
||||
//@exclude_read True, if read chats need to be excluded
|
||||
//@exclude_archived True, if archived chats need to be excluded
|
||||
//@include_contacts True, if contacts need to be included
|
||||
//@include_non_contacts True, if non-contact users need to be included
|
||||
//@include_bots True, if bots need to be included
|
||||
//@include_groups True, if basic groups and supergroups need to be included
|
||||
//@include_channels True, if channels need to be included
|
||||
chatFilter title:string icon_name:string pinned_chat_ids:vector<int53> included_chat_ids:vector<int53> excluded_chat_ids:vector<int53> exclude_muted:Bool exclude_read:Bool exclude_archived:Bool include_contacts:Bool include_non_contacts:Bool include_bots:Bool include_groups:Bool include_channels:Bool = ChatFilter;
|
||||
|
||||
//@description Contains basic information about a chat filter
|
||||
//@id Unique chat filter identifier
|
||||
//@title The title of the filter; 1-12 characters without line feeds
|
||||
//@icon_name The icon name for short filter representation. One of "All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom", "Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love", "Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work"
|
||||
chatFilterInfo id:int32 title:string icon_name:string = ChatFilterInfo;
|
||||
|
||||
//@description Describes a recommended chat filter @filter The chat filter @param_description Chat filter description
|
||||
recommendedChatFilter filter:chatFilter description:string = RecommendedChatFilter;
|
||||
|
||||
//@description Contains a list of recommended chat filters @chat_filters List of recommended chat filters
|
||||
recommendedChatFilters chat_filters:vector<recommendedChatFilter> = RecommendedChatFilters;
|
||||
|
||||
|
||||
//@class ChatList @description Describes a list of chats
|
||||
|
||||
//@description A main list of chats
|
||||
@ -649,8 +712,14 @@ chatListMain = ChatList;
|
||||
//@description A list of chats usually located at the top of the main chat list. Unmuted chats are automatically moved from the Archive to the Main chat list when a new message arrives
|
||||
chatListArchive = ChatList;
|
||||
|
||||
//@description A list of chats belonging to a chat filter @chat_filter_id Chat filter identifier
|
||||
chatListFilter chat_filter_id:int32 = ChatList;
|
||||
|
||||
//@class ChatSource @description Describes a reason why the chat is shown in a chat list
|
||||
//@description Contains a list of chat lists @chat_lists List of chat lists
|
||||
chatLists chat_lists:vector<ChatList> = ChatLists;
|
||||
|
||||
|
||||
//@class ChatSource @description Describes a reason why an external chat is shown in a chat list
|
||||
|
||||
//@description The chat is sponsored by the user's MTProxy server
|
||||
chatSourceMtprotoProxy = ChatSource;
|
||||
@ -659,17 +728,22 @@ chatSourceMtprotoProxy = ChatSource;
|
||||
chatSourcePublicServiceAnnouncement type:string text:string = ChatSource;
|
||||
|
||||
|
||||
//@description Describes a position of a chat in a chat list
|
||||
//@list The chat list
|
||||
//@order A parameter used to determine order of the chat in the chat list. Chats must be sorted by the pair (order, chat.id) in descending order
|
||||
//@is_pinned True, if the chat is pinned in the chat list
|
||||
//@source Source of the chat in the chat list; may be null
|
||||
chatPosition list:ChatList order:int64 is_pinned:Bool source:ChatSource = ChatPosition;
|
||||
|
||||
|
||||
//@description A chat. (Can be a private chat, basic group, supergroup, or secret chat)
|
||||
//@id Chat unique identifier
|
||||
//@type Type of the chat
|
||||
//@chat_list A chat list to which the chat belongs; may be null
|
||||
//@title Chat title
|
||||
//@photo Chat photo; may be null
|
||||
//@permissions Actions that non-administrator chat members are allowed to take in the chat
|
||||
//@last_message Last message in the chat; may be null
|
||||
//@order Descending parameter by which chats are sorted in the main chat list. If the order number of two chats is the same, they must be sorted in descending order by ID. If 0, the position of the chat in the list is undetermined
|
||||
//@source Source of the chat in a chat list; may be null
|
||||
//@is_pinned True, if the chat is pinned
|
||||
//@positions Positions of the chat in chat lists
|
||||
//@is_marked_as_unread True, if the chat is marked as unread
|
||||
//@has_scheduled_messages True, if the chat has scheduled messages
|
||||
//@can_be_deleted_only_for_self True, if the chat messages can be deleted only for the current user while other users will continue to see the messages
|
||||
@ -685,8 +759,8 @@ chatSourcePublicServiceAnnouncement type:string text:string = ChatSource;
|
||||
//@pinned_message_id Identifier of the pinned message in the chat; 0 if none
|
||||
//@reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat
|
||||
//@draft_message A draft of a message in the chat; may be null
|
||||
//@client_data Contains client-specific data associated with the chat. (For example, the chat position or local chat notification settings can be stored here.) Persistent if the message database is used
|
||||
chat id:int53 type:ChatType chat_list:ChatList title:string photo:chatPhoto permissions:chatPermissions last_message:message order:int64 source:ChatSource is_pinned:Bool is_marked_as_unread:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings action_bar:ChatActionBar pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
|
||||
//@client_data Contains client-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used
|
||||
chat id:int53 type:ChatType title:string photo:chatPhoto permissions:chatPermissions last_message:message positions:vector<chatPosition> is_marked_as_unread:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 notification_settings:chatNotificationSettings action_bar:ChatActionBar pinned_message_id:int53 reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
|
||||
|
||||
//@description Represents a list of chats @chat_ids List of chat identifiers
|
||||
chats chat_ids:vector<int53> = Chats;
|
||||
@ -1562,8 +1636,9 @@ sendMessageOptions disable_notification:Bool from_background:Bool scheduling_sta
|
||||
//@disable_web_page_preview True, if rich web page previews for URLs in the message text should be disabled @clear_draft True, if a chat message draft should be deleted
|
||||
inputMessageText text:formattedText disable_web_page_preview:Bool clear_draft:Bool = InputMessageContent;
|
||||
|
||||
//@description An animation message (GIF-style). @animation Animation file to be sent @thumbnail Animation thumbnail, if available @duration Duration of the animation, in seconds @width Width of the animation; may be replaced by the server @height Height of the animation; may be replaced by the server @caption Animation caption; 0-GetOption("message_caption_length_max") characters
|
||||
inputMessageAnimation animation:InputFile thumbnail:inputThumbnail duration:int32 width:int32 height:int32 caption:formattedText = InputMessageContent;
|
||||
//@description An animation message (GIF-style). @animation Animation file to be sent @thumbnail Animation thumbnail, if available @added_sticker_file_ids File identifiers of the stickers added to the animation, if applicable
|
||||
//@duration Duration of the animation, in seconds @width Width of the animation; may be replaced by the server @height Height of the animation; may be replaced by the server @caption Animation caption; 0-GetOption("message_caption_length_max") characters
|
||||
inputMessageAnimation animation:InputFile thumbnail:inputThumbnail added_sticker_file_ids:vector<int32> duration:int32 width:int32 height:int32 caption:formattedText = InputMessageContent;
|
||||
|
||||
//@description An audio message @audio Audio file to be sent @album_cover_thumbnail Thumbnail of the cover for the album, if available @duration Duration of the audio, in seconds; may be replaced by the server @title Title of the audio; 0-64 characters; may be replaced by the server
|
||||
//@performer Performer of the audio; 0-64 characters, may be replaced by the server @caption Audio caption; 0-GetOption("message_caption_length_max") characters
|
||||
@ -1735,18 +1810,18 @@ stickers stickers:vector<sticker> = Stickers;
|
||||
emojis emojis:vector<string> = Emojis;
|
||||
|
||||
//@description Represents a sticker set
|
||||
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP format with width and height 100; may be null. The file can be downloaded only before the thumbnail is changed
|
||||
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP or TGS format with width and height 100; may be null. The file can be downloaded only before the thumbnail is changed
|
||||
//@is_installed True, if the sticker set has been installed by the current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously
|
||||
//@is_official True, if the sticker set is official @is_animated True, is the stickers in the set are animated @is_masks True, if the stickers in the set are masks @is_viewed True for already viewed trending sticker sets
|
||||
//@stickers List of stickers in this set @emojis A list of emoji corresponding to the stickers in the same order. The list is only for informational purposes, because a sticker is always sent with a fixed emoji from the corresponding Sticker object
|
||||
stickerSet id:int64 title:string name:string thumbnail:photoSize is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool stickers:vector<sticker> emojis:vector<emojis> = StickerSet;
|
||||
stickerSet id:int64 title:string name:string thumbnail:thumbnail is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool stickers:vector<sticker> emojis:vector<emojis> = StickerSet;
|
||||
|
||||
//@description Represents short information about a sticker set
|
||||
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP format with width and height 100; may be null
|
||||
//@id Identifier of the sticker set @title Title of the sticker set @name Name of the sticker set @thumbnail Sticker set thumbnail in WEBP or TGS format with width and height 100; may be null
|
||||
//@is_installed True, if the sticker set has been installed by current user @is_archived True, if the sticker set has been archived. A sticker set can't be installed and archived simultaneously
|
||||
//@is_official True, if the sticker set is official @is_animated True, is the stickers in the set are animated @is_masks True, if the stickers in the set are masks @is_viewed True for already viewed trending sticker sets
|
||||
//@size Total number of stickers in the set @covers Contains up to the first 5 stickers from the set, depending on the context. If the client needs more stickers the full set should be requested
|
||||
stickerSetInfo id:int64 title:string name:string thumbnail:photoSize is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool size:int32 covers:vector<sticker> = StickerSetInfo;
|
||||
stickerSetInfo id:int64 title:string name:string thumbnail:thumbnail is_installed:Bool is_archived:Bool is_official:Bool is_animated:Bool is_masks:Bool is_viewed:Bool size:int32 covers:vector<sticker> = StickerSetInfo;
|
||||
|
||||
//@description Represents a list of sticker sets @total_count Approximate total number of sticker sets found @sets List of sticker sets
|
||||
stickerSets total_count:int32 sets:vector<stickerSetInfo> = StickerSets;
|
||||
@ -1931,17 +2006,17 @@ inputInlineQueryResultVoiceNote id:string title:string voice_note_url:string voi
|
||||
//@class InlineQueryResult @description Represents a single result of an inline query
|
||||
|
||||
//@description Represents a link to an article or web page @id Unique identifier of the query result @url URL of the result, if it exists @hide_url True, if the URL must be not shown @title Title of the result
|
||||
//@param_description A short description of the result @thumbnail Result thumbnail; may be null
|
||||
inlineQueryResultArticle id:string url:string hide_url:Bool title:string description:string thumbnail:photoSize = InlineQueryResult;
|
||||
//@param_description A short description of the result @thumbnail Result thumbnail in JPEG format; may be null
|
||||
inlineQueryResultArticle id:string url:string hide_url:Bool title:string description:string thumbnail:thumbnail = InlineQueryResult;
|
||||
|
||||
//@description Represents a user contact @id Unique identifier of the query result @contact A user contact @thumbnail Result thumbnail; may be null
|
||||
inlineQueryResultContact id:string contact:contact thumbnail:photoSize = InlineQueryResult;
|
||||
//@description Represents a user contact @id Unique identifier of the query result @contact A user contact @thumbnail Result thumbnail in JPEG format; may be null
|
||||
inlineQueryResultContact id:string contact:contact thumbnail:thumbnail = InlineQueryResult;
|
||||
|
||||
//@description Represents a point on the map @id Unique identifier of the query result @location Location result @title Title of the result @thumbnail Result thumbnail; may be null
|
||||
inlineQueryResultLocation id:string location:location title:string thumbnail:photoSize = InlineQueryResult;
|
||||
//@description Represents a point on the map @id Unique identifier of the query result @location Location result @title Title of the result @thumbnail Result thumbnail in JPEG format; may be null
|
||||
inlineQueryResultLocation id:string location:location title:string thumbnail:thumbnail = InlineQueryResult;
|
||||
|
||||
//@description Represents information about a venue @id Unique identifier of the query result @venue Venue result @thumbnail Result thumbnail; may be null
|
||||
inlineQueryResultVenue id:string venue:venue thumbnail:photoSize = InlineQueryResult;
|
||||
//@description Represents information about a venue @id Unique identifier of the query result @venue Venue result @thumbnail Result thumbnail in JPEG format; may be null
|
||||
inlineQueryResultVenue id:string venue:venue thumbnail:thumbnail = InlineQueryResult;
|
||||
|
||||
//@description Represents information about a game @id Unique identifier of the query result @game Game result
|
||||
inlineQueryResultGame id:string game:game = InlineQueryResult;
|
||||
@ -2870,9 +2945,6 @@ updateMessageLiveLocationViewed chat_id:int53 message_id:int53 = Update;
|
||||
//@description A new chat has been loaded/created. This update is guaranteed to come before the chat identifier is returned to the client. The chat field changes will be reported through separate updates @chat The chat
|
||||
updateNewChat chat:chat = Update;
|
||||
|
||||
//@description The list to which the chat belongs was changed. This update is guaranteed to be sent only when chat.order == 0 and the current or the new chat list is null @chat_id Chat identifier @chat_list The new chat's chat list; may be null
|
||||
updateChatChatList chat_id:int53 chat_list:ChatList = Update;
|
||||
|
||||
//@description The title of a chat was changed @chat_id Chat identifier @title The new chat title
|
||||
updateChatTitle chat_id:int53 title:string = Update;
|
||||
|
||||
@ -2882,21 +2954,15 @@ updateChatPhoto chat_id:int53 photo:chatPhoto = Update;
|
||||
//@description Chat permissions was changed @chat_id Chat identifier @permissions The new chat permissions
|
||||
updateChatPermissions chat_id:int53 permissions:chatPermissions = Update;
|
||||
|
||||
//@description The last message of a chat was changed. If last_message is null, then the last message in the chat became unknown. Some new unknown messages might be added to the chat in this case @chat_id Chat identifier @last_message The new last message in the chat; may be null @order New value of the chat order
|
||||
updateChatLastMessage chat_id:int53 last_message:message order:int64 = Update;
|
||||
//@description The last message of a chat was changed. If last_message is null, then the last message in the chat became unknown. Some new unknown messages might be added to the chat in this case @chat_id Chat identifier @last_message The new last message in the chat; may be null @positions The new chat positions in the chat lists
|
||||
updateChatLastMessage chat_id:int53 last_message:message positions:vector<chatPosition> = Update;
|
||||
|
||||
//@description The order of the chat in the chat list has changed. Instead of this update updateChatLastMessage, updateChatIsPinned, updateChatDraftMessage, or updateChatSource might be sent @chat_id Chat identifier @order New value of the order
|
||||
updateChatOrder chat_id:int53 order:int64 = Update;
|
||||
|
||||
//@description A chat was pinned or unpinned @chat_id Chat identifier @is_pinned New value of is_pinned @order New value of the chat order
|
||||
updateChatIsPinned chat_id:int53 is_pinned:Bool order:int64 = Update;
|
||||
//@description The position of a chat in a chat list has changed. Instead of this update updateChatLastMessage or updateChatDraftMessage might be sent @chat_id Chat identifier @position New chat position. If new order is 0, then the chat needs to be removed from the list
|
||||
updateChatPosition chat_id:int53 position:chatPosition = Update;
|
||||
|
||||
//@description A chat was marked as unread or was read @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread
|
||||
updateChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Update;
|
||||
|
||||
//@description A chat's source in the chat list has changed @chat_id Chat identifier @source New chat's source; may be null @order New value of chat order
|
||||
updateChatSource chat_id:int53 source:ChatSource order:int64 = Update;
|
||||
|
||||
//@description A chat's has_scheduled_messages field has changed @chat_id Chat identifier @has_scheduled_messages New value of has_scheduled_messages
|
||||
updateChatHasScheduledMessages chat_id:int53 has_scheduled_messages:Bool = Update;
|
||||
|
||||
@ -2928,8 +2994,11 @@ updateChatPinnedMessage chat_id:int53 pinned_message_id:int53 = Update;
|
||||
//@chat_id Chat identifier @reply_markup_message_id Identifier of the message from which reply markup needs to be used; 0 if there is no default custom reply markup in the chat
|
||||
updateChatReplyMarkup chat_id:int53 reply_markup_message_id:int53 = Update;
|
||||
|
||||
//@description A chat draft has changed. Be aware that the update may come in the currently opened chat but with old content of the draft. If the user has changed the content of the draft, this update shouldn't be applied @chat_id Chat identifier @draft_message The new draft message; may be null @order New value of the chat order
|
||||
updateChatDraftMessage chat_id:int53 draft_message:draftMessage order:int64 = Update;
|
||||
//@description A chat draft has changed. Be aware that the update may come in the currently opened chat but with old content of the draft. If the user has changed the content of the draft, this update shouldn't be applied @chat_id Chat identifier @draft_message The new draft message; may be null @positions The new chat positions in the chat lists
|
||||
updateChatDraftMessage chat_id:int53 draft_message:draftMessage positions:vector<chatPosition> = Update;
|
||||
|
||||
//@description The list of chat filters or a chat filter has changed @chat_filters The new list of chat filters
|
||||
updateChatFilters chat_filters:vector<chatFilterInfo> = Update;
|
||||
|
||||
//@description The number of online group members has changed. This update with non-zero count is sent only for currently opened chats. There is no guarantee that it will be sent just after the count has changed @chat_id Identifier of the chat @online_member_count New number of online members in the chat, or 0 if unknown
|
||||
updateChatOnlineMemberCount chat_id:int53 online_member_count:int32 = Update;
|
||||
@ -3061,6 +3130,9 @@ 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 The parameters of animation search through GetOption("animation_search_bot_username") bot has changed @provider Name of the animation search provider @emojis The new list of emojis suggested for searching
|
||||
updateAnimationSearchParameters provider:string 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;
|
||||
@ -3159,7 +3231,9 @@ resendAuthenticationCode = Ok;
|
||||
//@description Checks the authentication code. Works only when the current authorization state is authorizationStateWaitCode @code The verification code received via SMS, Telegram message, phone call, or flash call
|
||||
checkAuthenticationCode code:string = Ok;
|
||||
|
||||
//@description Requests QR code authentication by scanning a QR code on another logged in device. Works only when the current authorization state is authorizationStateWaitPhoneNumber @other_user_ids List of user identifiers of other users currently using the client
|
||||
//@description Requests QR code authentication by scanning a QR code on another logged in device. Works only when the current authorization state is authorizationStateWaitPhoneNumber,
|
||||
//-or if there is no pending authentication query and the current authorization state is authorizationStateWaitCode, authorizationStateWaitRegistration, or authorizationStateWaitPassword
|
||||
//@other_user_ids List of user identifiers of other users currently using the client
|
||||
requestQrCodeAuthentication other_user_ids:vector<int32> = Ok;
|
||||
|
||||
//@description Finishes user registration. Works only when the current authorization state is authorizationStateWaitRegistration
|
||||
@ -3283,7 +3357,7 @@ getFile file_id:int32 = File;
|
||||
//@remote_file_id Remote identifier of the file to get @file_type File type, if known
|
||||
getRemoteFile remote_file_id:string file_type:FileType = File;
|
||||
|
||||
//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (order, chat_id) in decreasing order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1).
|
||||
//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (chat.position.order, chat.id) in descending order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1).
|
||||
//-For optimal performance the number of returned chats is chosen by the library
|
||||
//@chat_list The chat list in which to return chats
|
||||
//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from
|
||||
@ -3296,10 +3370,10 @@ searchPublicChat username:string = Chat;
|
||||
//@description Searches public chats by looking for specified query in their username and title. Currently only private chats, supergroups and channels can be public. Returns a meaningful number of results. Returns nothing if the length of the searched username prefix is less than 5. Excludes private chats with contacts and chats from the chat list from the results @query Query to search for
|
||||
searchPublicChats query:string = Chats;
|
||||
|
||||
//@description Searches for the specified query in the title and username of already known chats, this is an offline request. Returns chats in the order seen in the chat list @query Query to search for. If the query is empty, returns up to 20 recently found chats @limit The maximum number of chats to be returned
|
||||
//@description Searches for the specified query in the title and username of already known chats, this is an offline request. Returns chats in the order seen in the main chat list @query Query to search for. If the query is empty, returns up to 20 recently found chats @limit The maximum number of chats to be returned
|
||||
searchChats query:string limit:int32 = Chats;
|
||||
|
||||
//@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the chat list @query Query to search for @limit The maximum number of chats to be returned
|
||||
//@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the main chat list @query Query to search for @limit The maximum number of chats to be returned
|
||||
searchChatsOnServer query:string limit:int32 = Chats;
|
||||
|
||||
//@description Returns a list of users and location-based supergroups nearby. The list of users nearby will be updated for 60 seconds after the request by the updates updateUsersNearby. The request should be sent again every 25 seconds with adjusted location to not miss new chats @location Current user location
|
||||
@ -3669,8 +3743,34 @@ createNewSecretChat user_id:int32 = Chat;
|
||||
upgradeBasicGroupChatToSupergroupChat chat_id:int53 = Chat;
|
||||
|
||||
|
||||
//@description Moves a chat to a different chat list. Current chat list of the chat must ne non-null @chat_id Chat identifier @chat_list New chat list of the chat. The chat with the current user (Saved Messages) and the chat 777000 (Telegram) can't be moved to the Archive chat list
|
||||
setChatChatList chat_id:int53 chat_list:ChatList = Ok;
|
||||
//@description Returns chat lists to which the chat can be added. This is an offline request @chat_id Chat identifier
|
||||
getChatListsToAddChat chat_id:int53 = ChatLists;
|
||||
|
||||
//@description Adds a chat to a chat list. A chat can't be simultaneously in Main and Archive chat lists, so it is automatically removed from another one if needed
|
||||
//@chat_id Chat identifier @chat_list The chat list. Use getChatListsToAddChat to get suitable chat lists
|
||||
addChatToList chat_id:int53 chat_list:ChatList = Ok;
|
||||
|
||||
//@description Returns information about a chat filter by its identifier @chat_filter_id Chat filter identifier
|
||||
getChatFilter chat_filter_id:int32 = ChatFilter;
|
||||
|
||||
//@description Creates new chat filter. Returns information about the created chat filter @filter Chat filter
|
||||
createChatFilter filter:chatFilter = ChatFilterInfo;
|
||||
|
||||
//@description Edits existing chat filter. Returns information about the edited chat filter @chat_filter_id Chat filter identifier @filter The edited chat filter
|
||||
editChatFilter chat_filter_id:int32 filter:chatFilter = ChatFilterInfo;
|
||||
|
||||
//@description Deletes existing chat filter @chat_filter_id Chat filter identifier
|
||||
deleteChatFilter chat_filter_id:int32 = Ok;
|
||||
|
||||
//@description Changes the order of chat filters @chat_filter_ids Identifiers of chat filters in the new correct order
|
||||
reorderChatFilters chat_filter_ids:vector<int32> = Ok;
|
||||
|
||||
//@description Returns recommended chat filters for the current user
|
||||
getRecommendedChatFilters = RecommendedChatFilters;
|
||||
|
||||
//@description Returns default icon name for a filter. This is an offline method. Can be called before authorization. Can be called synchronously @filter Chat filter
|
||||
getChatFilterDefaultIconName filter:chatFilter = Text;
|
||||
|
||||
|
||||
//@description Changes the chat title. Supported only for basic groups, supergroups and channels. Requires can_change_info rights. The title will not be changed until the request to the server has been completed
|
||||
//@chat_id Chat identifier @title New title of the chat; 1-128 characters
|
||||
@ -3691,9 +3791,6 @@ setChatDraftMessage chat_id:int53 draft_message:draftMessage = Ok;
|
||||
//@chat_id Chat identifier @notification_settings New notification settings for the chat. If the chat is muted for more than 1 week, it is considered to be muted forever
|
||||
setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok;
|
||||
|
||||
//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") non-secret chats and the same number of secret chats in the main/archive chat list @chat_id Chat identifier @is_pinned New value of is_pinned
|
||||
toggleChatIsPinned chat_id:int53 is_pinned:Bool = Ok;
|
||||
|
||||
//@description Changes the marked as unread state of a chat @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread
|
||||
toggleChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Ok;
|
||||
|
||||
@ -3775,6 +3872,10 @@ setScopeNotificationSettings scope:NotificationSettingsScope notification_settin
|
||||
resetAllNotificationSettings = Ok;
|
||||
|
||||
|
||||
//@description Changes the pinned state of a chat. You can pin up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") non-secret chats and the same number of secret chats in the main/arhive chat list
|
||||
//@chat_list Chat list in which to change the pinned state of the chat @chat_id Chat identifier @is_pinned True, if the chat is pinned
|
||||
toggleChatIsPinned chat_list:ChatList chat_id:int53 is_pinned:Bool = Ok;
|
||||
|
||||
//@description Changes the order of pinned chats @chat_list Chat list in which to change the order of pinned chats @chat_ids The new list of pinned chats
|
||||
setPinnedChats chat_list:ChatList chat_ids:vector<int53> = Ok;
|
||||
|
||||
@ -4383,7 +4484,7 @@ answerCustomQuery custom_query_id:int64 data:string = Ok;
|
||||
setAlarm seconds:double = Ok;
|
||||
|
||||
|
||||
//@description Uses current user IP address to found their country. Returns two-letter ISO 3166-1 alpha-2 country code. Can be called before authorization
|
||||
//@description Uses current user IP address to find their country. Returns two-letter ISO 3166-1 alpha-2 country code. Can be called before authorization
|
||||
getCountryCode = Text;
|
||||
|
||||
//@description Returns the default text for invitation messages to be used as a placeholder when the current user invites friends to Telegram
|
||||
|
Binary file not shown.
@ -52,7 +52,6 @@ inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:strin
|
||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
||||
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
|
||||
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||
@ -344,6 +343,7 @@ updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> =
|
||||
updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
|
||||
updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
|
||||
updateDialogFilters#3504914f = Update;
|
||||
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@ -408,7 +408,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
|
||||
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
|
||||
|
||||
documentEmpty#36f8c871 id:long = Document;
|
||||
document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
|
||||
document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
|
||||
|
||||
help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
||||
|
||||
@ -605,11 +605,6 @@ channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector
|
||||
|
||||
help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector<MessageEntity> min_age_confirm:flags.1?int = help.TermsOfService;
|
||||
|
||||
foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif;
|
||||
foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif;
|
||||
|
||||
messages.foundGifs#450a1c0a next_offset:int results:Vector<FoundGif> = messages.FoundGifs;
|
||||
|
||||
messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs;
|
||||
messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
|
||||
|
||||
@ -1127,6 +1122,8 @@ stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueA
|
||||
help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
|
||||
help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
|
||||
|
||||
videoSize#435bb987 type:string location:FileLocation w:int h:int size:int = VideoSize;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@ -1296,7 +1293,6 @@ messages.migrateChat#15a3b8e3 chat_id:int = Updates;
|
||||
messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
|
||||
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
|
||||
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
|
||||
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
@ -1472,6 +1468,7 @@ phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
|
||||
phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
|
||||
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
|
||||
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
||||
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
|
||||
|
||||
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
|
||||
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
|
||||
|
Binary file not shown.
@ -7,6 +7,7 @@
|
||||
#include "td/telegram/AnimationsManager.h"
|
||||
|
||||
#include "td/telegram/AuthManager.h"
|
||||
#include "td/telegram/ConfigShared.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/Document.h"
|
||||
#include "td/telegram/DocumentsManager.h"
|
||||
@ -166,11 +167,14 @@ tl_object_ptr<td_api::animation> AnimationsManager::get_animation_object(FileId
|
||||
<< static_cast<int32>(td_->file_manager_->get_file_view(file_id).get_type());
|
||||
// TODO can we make that function const?
|
||||
animation->is_changed = false;
|
||||
auto thumbnail =
|
||||
animation->animated_thumbnail.file_id.is_valid()
|
||||
? get_thumbnail_object(td_->file_manager_.get(), animation->animated_thumbnail, PhotoFormat::Mpeg4)
|
||||
: get_thumbnail_object(td_->file_manager_.get(), animation->thumbnail, PhotoFormat::Jpeg);
|
||||
return make_tl_object<td_api::animation>(animation->duration, animation->dimensions.width,
|
||||
animation->dimensions.height, animation->file_name, animation->mime_type,
|
||||
get_minithumbnail_object(animation->minithumbnail),
|
||||
get_photo_size_object(td_->file_manager_.get(), &animation->thumbnail),
|
||||
td_->file_manager_->get_file_object(file_id));
|
||||
animation->has_stickers, get_minithumbnail_object(animation->minithumbnail),
|
||||
std::move(thumbnail), td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation, bool replace) {
|
||||
@ -217,6 +221,24 @@ FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation,
|
||||
a->thumbnail = new_animation->thumbnail;
|
||||
a->is_changed = true;
|
||||
}
|
||||
if (a->animated_thumbnail != new_animation->animated_thumbnail) {
|
||||
if (!a->animated_thumbnail.file_id.is_valid()) {
|
||||
LOG(DEBUG) << "Animation " << file_id << " animated thumbnail has changed";
|
||||
} else {
|
||||
LOG(INFO) << "Animation " << file_id << " animated thumbnail has changed from " << a->animated_thumbnail
|
||||
<< " to " << new_animation->animated_thumbnail;
|
||||
}
|
||||
a->animated_thumbnail = new_animation->animated_thumbnail;
|
||||
a->is_changed = true;
|
||||
}
|
||||
if (a->has_stickers != new_animation->has_stickers && new_animation->has_stickers) {
|
||||
a->has_stickers = new_animation->has_stickers;
|
||||
a->is_changed = true;
|
||||
}
|
||||
if (a->sticker_file_ids != new_animation->sticker_file_ids && !new_animation->sticker_file_ids.empty()) {
|
||||
a->sticker_file_ids = std::move(new_animation->sticker_file_ids);
|
||||
a->is_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return file_id;
|
||||
@ -240,12 +262,21 @@ FileId AnimationsManager::get_animation_thumbnail_file_id(FileId file_id) const
|
||||
return animation->thumbnail.file_id;
|
||||
}
|
||||
|
||||
FileId AnimationsManager::get_animation_animated_thumbnail_file_id(FileId file_id) const {
|
||||
auto animation = get_animation(file_id);
|
||||
if (animation == nullptr) {
|
||||
return FileId();
|
||||
}
|
||||
return animation->animated_thumbnail.file_id;
|
||||
}
|
||||
|
||||
void AnimationsManager::delete_animation_thumbnail(FileId file_id) {
|
||||
auto &animation = animations_[file_id];
|
||||
if (animation == nullptr) {
|
||||
return;
|
||||
}
|
||||
animation->thumbnail = PhotoSize();
|
||||
animation->animated_thumbnail = PhotoSize();
|
||||
}
|
||||
|
||||
FileId AnimationsManager::dup_animation(FileId new_id, FileId old_id) {
|
||||
@ -257,6 +288,8 @@ FileId AnimationsManager::dup_animation(FileId new_id, FileId old_id) {
|
||||
new_animation = make_unique<Animation>(*old_animation);
|
||||
new_animation->file_id = new_id;
|
||||
new_animation->thumbnail.file_id = td_->file_manager_->dup_file_id(new_animation->thumbnail.file_id);
|
||||
new_animation->animated_thumbnail.file_id =
|
||||
td_->file_manager_->dup_file_id(new_animation->animated_thumbnail.file_id);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
@ -299,8 +332,10 @@ bool AnimationsManager::merge_animations(FileId new_id, FileId old_id, bool can_
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationsManager::create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail, string file_name,
|
||||
string mime_type, int32 duration, Dimensions dimensions, bool replace) {
|
||||
void AnimationsManager::create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail,
|
||||
PhotoSize animated_thumbnail, bool has_stickers,
|
||||
vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
|
||||
int32 duration, Dimensions dimensions, bool replace) {
|
||||
auto a = make_unique<Animation>();
|
||||
a->file_id = file_id;
|
||||
a->file_name = std::move(file_name);
|
||||
@ -309,6 +344,9 @@ void AnimationsManager::create_animation(FileId file_id, string minithumbnail, P
|
||||
a->dimensions = dimensions;
|
||||
a->minithumbnail = std::move(minithumbnail);
|
||||
a->thumbnail = std::move(thumbnail);
|
||||
a->animated_thumbnail = std::move(animated_thumbnail);
|
||||
a->has_stickers = has_stickers;
|
||||
a->sticker_file_ids = std::move(sticker_file_ids);
|
||||
on_get_animation(std::move(a), replace);
|
||||
}
|
||||
|
||||
@ -411,6 +449,42 @@ SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file
|
||||
BufferSlice(encryption_key.iv_slice()), std::move(attributes), caption)};
|
||||
}
|
||||
|
||||
void AnimationsManager::on_update_animation_search_emojis(string animation_search_emojis) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (td_->auth_manager_->is_bot()) {
|
||||
G()->shared_config().set_option_empty("animation_search_emojis");
|
||||
return;
|
||||
}
|
||||
|
||||
is_animation_search_emojis_inited_ = true;
|
||||
if (animation_search_emojis_ == animation_search_emojis) {
|
||||
return;
|
||||
}
|
||||
animation_search_emojis_ = std::move(animation_search_emojis);
|
||||
|
||||
try_send_update_animation_search_parameters();
|
||||
}
|
||||
|
||||
void AnimationsManager::on_update_animation_search_provider(string animation_search_provider) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
if (td_->auth_manager_->is_bot()) {
|
||||
G()->shared_config().set_option_empty("animation_search_provider");
|
||||
return;
|
||||
}
|
||||
|
||||
is_animation_search_provider_inited_ = true;
|
||||
if (animation_search_provider_ == animation_search_provider) {
|
||||
return;
|
||||
}
|
||||
animation_search_provider_ = std::move(animation_search_provider);
|
||||
|
||||
try_send_update_animation_search_parameters();
|
||||
}
|
||||
|
||||
void AnimationsManager::on_update_saved_animations_limit(int32 saved_animations_limit) {
|
||||
if (saved_animations_limit != saved_animations_limit_) {
|
||||
if (saved_animations_limit > 0) {
|
||||
@ -669,6 +743,15 @@ void AnimationsManager::send_save_gif_query(FileId animation_id, bool unsave, Pr
|
||||
}
|
||||
|
||||
void AnimationsManager::add_saved_animation_by_id(FileId animation_id) {
|
||||
auto animation = get_animation(animation_id);
|
||||
if (animation == nullptr) {
|
||||
return;
|
||||
}
|
||||
CHECK(animation != nullptr);
|
||||
if (animation->has_stickers) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO log event
|
||||
add_saved_animation_impl(animation_id, false, Auto());
|
||||
}
|
||||
@ -779,6 +862,22 @@ void AnimationsManager::remove_saved_animation(const tl_object_ptr<td_api::Input
|
||||
send_update_saved_animations();
|
||||
}
|
||||
|
||||
void AnimationsManager::try_send_update_animation_search_parameters() const {
|
||||
auto update_animation_search_parameters = get_update_animation_search_parameters_object();
|
||||
if (update_animation_search_parameters != nullptr) {
|
||||
send_closure(G()->td(), &Td::send_update, std::move(update_animation_search_parameters));
|
||||
}
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::updateAnimationSearchParameters>
|
||||
AnimationsManager::get_update_animation_search_parameters_object() const {
|
||||
if (!is_animation_search_emojis_inited_ || !is_animation_search_provider_inited_) {
|
||||
return nullptr;
|
||||
}
|
||||
return td_api::make_object<td_api::updateAnimationSearchParameters>(animation_search_provider_,
|
||||
full_split(animation_search_emojis_, ','));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::updateSavedAnimations> AnimationsManager::get_update_saved_animations_object() const {
|
||||
return td_api::make_object<td_api::updateSavedAnimations>(
|
||||
td_->file_manager_->get_file_ids_object(saved_animation_ids_));
|
||||
@ -788,9 +887,15 @@ void AnimationsManager::send_update_saved_animations(bool from_database) {
|
||||
if (are_saved_animations_loaded_) {
|
||||
vector<FileId> new_saved_animation_file_ids = saved_animation_ids_;
|
||||
for (auto &animation_id : saved_animation_ids_) {
|
||||
auto thumbnail_file_id = get_animation_thumbnail_file_id(animation_id);
|
||||
if (thumbnail_file_id.is_valid()) {
|
||||
new_saved_animation_file_ids.push_back(thumbnail_file_id);
|
||||
auto animation = get_animation(animation_id);
|
||||
if (animation != nullptr) {
|
||||
CHECK(animation != nullptr);
|
||||
if (animation->thumbnail.file_id.is_valid()) {
|
||||
new_saved_animation_file_ids.push_back(animation->thumbnail.file_id);
|
||||
}
|
||||
if (animation->animated_thumbnail.file_id.is_valid()) {
|
||||
new_saved_animation_file_ids.push_back(animation->animated_thumbnail.file_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(new_saved_animation_file_ids.begin(), new_saved_animation_file_ids.end());
|
||||
@ -845,6 +950,10 @@ void AnimationsManager::get_current_state(vector<td_api::object_ptr<td_api::Upda
|
||||
if (are_saved_animations_loaded_) {
|
||||
updates.push_back(get_update_saved_animations_object());
|
||||
}
|
||||
auto update_animation_search_parameters = get_update_animation_search_parameters_object();
|
||||
if (update_animation_search_parameters != nullptr) {
|
||||
updates.push_back(std::move(update_animation_search_parameters));
|
||||
}
|
||||
}
|
||||
void AnimationsManager::memory_cleanup() {
|
||||
animations_.clear();
|
||||
|
@ -37,7 +37,8 @@ class AnimationsManager : public Actor {
|
||||
|
||||
tl_object_ptr<td_api::animation> get_animation_object(FileId file_id, const char *source);
|
||||
|
||||
void create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail, string file_name, string mime_type,
|
||||
void create_animation(FileId file_id, string minithumbnail, PhotoSize thumbnail, PhotoSize animated_thumbnail,
|
||||
bool has_stickers, vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
|
||||
int32 duration, Dimensions dimensions, bool replace);
|
||||
|
||||
tl_object_ptr<telegram_api::InputMedia> get_input_media(FileId file_id,
|
||||
@ -50,12 +51,18 @@ class AnimationsManager : public Actor {
|
||||
|
||||
FileId get_animation_thumbnail_file_id(FileId file_id) const;
|
||||
|
||||
FileId get_animation_animated_thumbnail_file_id(FileId file_id) const;
|
||||
|
||||
void delete_animation_thumbnail(FileId file_id);
|
||||
|
||||
FileId dup_animation(FileId new_id, FileId old_id);
|
||||
|
||||
bool merge_animations(FileId new_id, FileId old_id, bool can_delete_old);
|
||||
|
||||
void on_update_animation_search_emojis(string animation_search_emojis);
|
||||
|
||||
void on_update_animation_search_provider(string animation_search_provider);
|
||||
|
||||
void on_update_saved_animations_limit(int32 saved_animations_limit);
|
||||
|
||||
void reload_saved_animations(bool force);
|
||||
@ -99,6 +106,10 @@ class AnimationsManager : public Actor {
|
||||
Dimensions dimensions;
|
||||
string minithumbnail;
|
||||
PhotoSize thumbnail;
|
||||
PhotoSize animated_thumbnail;
|
||||
|
||||
bool has_stickers = false;
|
||||
vector<FileId> sticker_file_ids;
|
||||
|
||||
FileId file_id;
|
||||
|
||||
@ -119,6 +130,10 @@ class AnimationsManager : public Actor {
|
||||
|
||||
void on_load_saved_animations_finished(vector<FileId> &&saved_animation_ids, bool from_database = false);
|
||||
|
||||
void try_send_update_animation_search_parameters() const;
|
||||
|
||||
td_api::object_ptr<td_api::updateAnimationSearchParameters> get_update_animation_search_parameters_object() const;
|
||||
|
||||
td_api::object_ptr<td_api::updateSavedAnimations> get_update_saved_animations_object() const;
|
||||
|
||||
void send_update_saved_animations(bool from_database = false);
|
||||
@ -142,6 +157,11 @@ class AnimationsManager : public Actor {
|
||||
vector<Promise<Unit>> load_saved_animations_queries_;
|
||||
vector<Promise<Unit>> repair_saved_animations_queries_;
|
||||
FileSourceId saved_animations_file_source_id_;
|
||||
|
||||
string animation_search_emojis_;
|
||||
string animation_search_provider_;
|
||||
bool is_animation_search_emojis_inited_ = false;
|
||||
bool is_animation_search_provider_inited_ = false;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -24,6 +24,11 @@ void AnimationsManager::store_animation(FileId file_id, StorerT &storer) const {
|
||||
return;
|
||||
}
|
||||
const Animation *animation = it->second.get();
|
||||
bool has_animated_thumbnail = animation->animated_thumbnail.file_id.is_valid();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(animation->has_stickers);
|
||||
STORE_FLAG(has_animated_thumbnail);
|
||||
END_STORE_FLAGS();
|
||||
store(animation->duration, storer);
|
||||
store(animation->dimensions, storer);
|
||||
store(animation->file_name, storer);
|
||||
@ -31,11 +36,24 @@ void AnimationsManager::store_animation(FileId file_id, StorerT &storer) const {
|
||||
store(animation->minithumbnail, storer);
|
||||
store(animation->thumbnail, storer);
|
||||
store(file_id, storer);
|
||||
if (animation->has_stickers) {
|
||||
store(animation->sticker_file_ids, storer);
|
||||
}
|
||||
if (has_animated_thumbnail) {
|
||||
store(animation->animated_thumbnail, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
FileId AnimationsManager::parse_animation(ParserT &parser) {
|
||||
auto animation = make_unique<Animation>();
|
||||
bool has_animated_thumbnail = false;
|
||||
if (parser.version() >= static_cast<int32>(Version::AddAnimationStickers)) {
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(animation->has_stickers);
|
||||
PARSE_FLAG(has_animated_thumbnail);
|
||||
END_PARSE_FLAGS();
|
||||
}
|
||||
if (parser.version() >= static_cast<int32>(Version::AddDurationToAnimation)) {
|
||||
parse(animation->duration, parser);
|
||||
}
|
||||
@ -47,6 +65,12 @@ FileId AnimationsManager::parse_animation(ParserT &parser) {
|
||||
}
|
||||
parse(animation->thumbnail, parser);
|
||||
parse(animation->file_id, parser);
|
||||
if (animation->has_stickers) {
|
||||
parse(animation->sticker_file_ids, parser);
|
||||
}
|
||||
if (has_animated_thumbnail) {
|
||||
parse(animation->animated_thumbnail, parser);
|
||||
}
|
||||
if (parser.get_error() != nullptr || !animation->file_id.is_valid()) {
|
||||
return FileId();
|
||||
}
|
||||
|
@ -40,9 +40,10 @@ tl_object_ptr<td_api::audio> AudiosManager::get_audio_object(FileId file_id) {
|
||||
return nullptr;
|
||||
}
|
||||
audio->is_changed = false;
|
||||
return make_tl_object<td_api::audio>(audio->duration, audio->title, audio->performer, audio->file_name,
|
||||
audio->mime_type, get_minithumbnail_object(audio->minithumbnail),
|
||||
get_photo_size_object(td_->file_manager_.get(), &audio->thumbnail),
|
||||
return make_tl_object<td_api::audio>(
|
||||
audio->duration, audio->title, audio->performer, audio->file_name, audio->mime_type,
|
||||
get_minithumbnail_object(audio->minithumbnail),
|
||||
get_thumbnail_object(td_->file_manager_.get(), audio->thumbnail, PhotoFormat::Jpeg),
|
||||
td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/logevent/LogEvent.h"
|
||||
#include "td/telegram/MessagesManager.h"
|
||||
#include "td/telegram/misc.h"
|
||||
#include "td/telegram/net/DcId.h"
|
||||
#include "td/telegram/net/NetQueryDispatcher.h"
|
||||
@ -143,11 +144,11 @@ void AuthManager::check_bot_token(uint64 query_id, string bot_token) {
|
||||
}
|
||||
if (state_ != State::WaitPhoneNumber && state_ != State::Ok) {
|
||||
// TODO do not allow State::Ok
|
||||
return on_query_error(query_id, Status::Error(8, "Call to checkAuthenticationBotToken unexpected"));
|
||||
return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationBotToken unexpected"));
|
||||
}
|
||||
if (!send_code_helper_.phone_number().empty() || was_qr_code_request_) {
|
||||
return on_query_error(
|
||||
query_id, Status::Error(8, "Cannot set bot token after authentication beginning. You need to log out first"));
|
||||
query_id, Status::Error(400, "Cannot set bot token after authentication beginning. You need to log out first"));
|
||||
}
|
||||
if (was_check_bot_token_ && bot_token_ != bot_token) {
|
||||
return on_query_error(query_id, Status::Error(8, "Cannot change bot token. You need to log out first"));
|
||||
@ -719,6 +720,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
|
||||
if ((auth->flags_ & telegram_api::auth_authorization::TMP_SESSIONS_MASK) != 0) {
|
||||
G()->shared_config().set_option_integer("session_count", auth->tmp_sessions_);
|
||||
}
|
||||
td->messages_manager_->on_authorization_success();
|
||||
td->notification_manager_->init();
|
||||
td->stickers_manager_->init();
|
||||
send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up);
|
||||
|
@ -1060,7 +1060,8 @@ td_api::object_ptr<td_api::background> BackgroundManager::get_background_object(
|
||||
}
|
||||
return td_api::make_object<td_api::background>(
|
||||
background->id.get(), background->is_default, background->is_dark, background->name,
|
||||
td_->documents_manager_->get_document_object(background->file_id), get_background_type_object(*type));
|
||||
td_->documents_manager_->get_document_object(background->file_id, PhotoFormat::Png),
|
||||
get_background_type_object(*type));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::backgrounds> BackgroundManager::get_backgrounds_object(bool for_dark_theme) const {
|
||||
|
@ -138,6 +138,10 @@ void CallActor::create_call(UserId user_id, tl_object_ptr<telegram_api::InputUse
|
||||
promise.set_value(CallId(local_call_id_));
|
||||
}
|
||||
|
||||
void CallActor::update_call_signaling_data(string data) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void CallActor::discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id,
|
||||
Promise<> promise) {
|
||||
promise.set_value(Unit());
|
||||
@ -578,7 +582,7 @@ void CallActor::try_send_request_query() {
|
||||
double timeout = call_receive_timeout_ms * 0.001;
|
||||
LOG(INFO) << "Set call timeout to " << timeout;
|
||||
set_timeout_in(timeout);
|
||||
query->total_timeout_limit = timeout;
|
||||
query->total_timeout_limit_ = max(timeout, 10.0);
|
||||
request_query_ref_ = query.get_weak();
|
||||
send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](NetQueryPtr net_query) {
|
||||
send_closure(actor_id, &CallActor::on_request_query_result, std::move(net_query));
|
||||
@ -726,7 +730,7 @@ void CallActor::on_get_call_config_result(NetQueryPtr net_query) {
|
||||
}
|
||||
|
||||
void CallActor::loop() {
|
||||
LOG(DEBUG) << "Enter loop for call " << call_id_ << " in state " << static_cast<int32>(state_) << '/'
|
||||
LOG(DEBUG) << "Enter loop for " << call_id_ << " in state " << static_cast<int32>(state_) << '/'
|
||||
<< static_cast<int32>(call_state_.type);
|
||||
flush_call_state();
|
||||
switch (state_) {
|
||||
@ -747,7 +751,7 @@ void CallActor::loop() {
|
||||
(call_state_.need_rating || call_state_.need_debug_information)) {
|
||||
break;
|
||||
}
|
||||
LOG(INFO) << "Close call " << local_call_id_;
|
||||
LOG(INFO) << "Close " << local_call_id_;
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ class CallActor : public NetQueryCallback {
|
||||
|
||||
void create_call(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, CallProtocol &&protocol,
|
||||
bool is_video, Promise<CallId> &&promise);
|
||||
void update_call_signaling_data(string data);
|
||||
void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise<> promise);
|
||||
void accept_call(CallProtocol &&protocol, Promise<> promise);
|
||||
void rate_call(int32 rating, string comment, vector<td_api::object_ptr<td_api::CallProblem>> &&problems,
|
||||
|
@ -53,7 +53,7 @@ struct CallIdHash {
|
||||
};
|
||||
|
||||
inline StringBuilder &operator<<(StringBuilder &sb, const CallId call_id) {
|
||||
return sb << "CallId(" << call_id.get() << ")";
|
||||
return sb << "call " << call_id.get();
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -23,7 +23,7 @@ CallManager::CallManager(ActorShared<> parent) : parent_(std::move(parent)) {
|
||||
void CallManager::update_call(Update call) {
|
||||
int64 call_id = 0;
|
||||
downcast_call(*call->phone_call_, [&](auto &update) { call_id = update.id_; });
|
||||
LOG(DEBUG) << "Receive UpdateCall for call " << call_id;
|
||||
LOG(DEBUG) << "Receive UpdateCall for " << call_id;
|
||||
|
||||
auto &info = call_info_[call_id];
|
||||
|
||||
@ -32,7 +32,7 @@ void CallManager::update_call(Update call) {
|
||||
}
|
||||
|
||||
if (!info.call_id.is_valid()) {
|
||||
LOG(INFO) << "Call_id is not valid for call " << call_id << ", postpone update " << to_string(call);
|
||||
LOG(INFO) << "Call_id is not valid for " << call_id << ", postpone update " << to_string(call);
|
||||
info.updates.push_back(std::move(call));
|
||||
return;
|
||||
}
|
||||
@ -44,6 +44,20 @@ void CallManager::update_call(Update call) {
|
||||
send_closure(actor, &CallActor::update_call, std::move(call->phone_call_));
|
||||
}
|
||||
|
||||
void CallManager::update_call_signaling_data(int64 call_id, string data) {
|
||||
auto info_it = call_info_.find(call_id);
|
||||
if (info_it == call_info_.end() || !info_it->second.call_id.is_valid()) {
|
||||
LOG(INFO) << "Ignore signaling data for " << call_id;
|
||||
}
|
||||
|
||||
auto actor = get_call_actor(info_it->second.call_id);
|
||||
if (actor.empty()) {
|
||||
LOG(INFO) << "Ignore signaling data for " << info_it->second.call_id;
|
||||
return;
|
||||
}
|
||||
send_closure(actor, &CallActor::update_call_signaling_data, std::move(data));
|
||||
}
|
||||
|
||||
void CallManager::create_call(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user,
|
||||
CallProtocol &&protocol, bool is_video, Promise<CallId> promise) {
|
||||
LOG(INFO) << "Create call with " << user_id;
|
||||
|
@ -27,6 +27,7 @@ class CallManager : public Actor {
|
||||
using Update = telegram_api::object_ptr<telegram_api::updatePhoneCall>;
|
||||
explicit CallManager(ActorShared<> parent);
|
||||
void update_call(Update call);
|
||||
void update_call_signaling_data(int64 call_id, string data);
|
||||
|
||||
void create_call(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, CallProtocol &&protocol,
|
||||
bool is_video, Promise<CallId> promise);
|
||||
|
@ -65,7 +65,7 @@ class GetBotCallbackAnswerQuery : public Td::ResultHandler {
|
||||
|
||||
auto net_query = G()->net_query_creator().create(telegram_api::messages_getBotCallbackAnswer(
|
||||
flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), std::move(data)));
|
||||
net_query->need_resend_on_503 = false;
|
||||
net_query->need_resend_on_503_ = false;
|
||||
send_query(std::move(net_query));
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,17 @@
|
||||
// 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 managed(push, off)
|
||||
#include "td/telegram/Client.h"
|
||||
#pragma managed(pop)
|
||||
|
||||
#include "td/telegram/TdDotNetApi.h"
|
||||
|
||||
#include "td/utils/port/CxCli.h"
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include <cstdint>
|
||||
#pragma managed(pop)
|
||||
|
||||
namespace Telegram {
|
||||
namespace Td {
|
||||
|
@ -506,9 +506,9 @@ ActorOwn<> get_full_config(DcOption option, Promise<FullConfig> promise, ActorSh
|
||||
false /*need_destroy_auth_key*/, mtproto::AuthKey(),
|
||||
std::vector<mtproto::ServerSalt>());
|
||||
auto query = G()->net_query_creator().create_unauth(telegram_api::help_getConfig(), DcId::empty());
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
query->set_callback(actor_shared(this));
|
||||
query->dispatch_ttl = 0;
|
||||
query->dispatch_ttl_ = 0;
|
||||
send_closure(session_, &Session::send, std::move(query));
|
||||
set_timeout_in(10);
|
||||
}
|
||||
@ -579,7 +579,7 @@ class ConfigRecoverer : public Actor {
|
||||
loop();
|
||||
}
|
||||
void on_connecting(bool is_connecting) {
|
||||
VLOG(config_recoverer) << "ON CONNECTING " << is_connecting;
|
||||
VLOG(config_recoverer) << "On connecting " << is_connecting;
|
||||
if (is_connecting && !is_connecting_) {
|
||||
connecting_since_ = Time::now_cached();
|
||||
}
|
||||
@ -726,7 +726,7 @@ class ConfigRecoverer : public Actor {
|
||||
|
||||
uint32 ref_cnt_{1};
|
||||
bool close_flag_{false};
|
||||
uint8 simple_config_turn_{0};
|
||||
uint32 simple_config_turn_{0};
|
||||
|
||||
ActorShared<> parent_;
|
||||
|
||||
@ -757,9 +757,9 @@ class ConfigRecoverer : public Actor {
|
||||
}
|
||||
|
||||
if (is_connecting_) {
|
||||
VLOG(config_recoverer) << "Failed to connect for " << Time::now_cached() - connecting_since_;
|
||||
VLOG(config_recoverer) << "Failed to connect for " << Time::now() - connecting_since_;
|
||||
} else {
|
||||
VLOG(config_recoverer) << "Successfully connected";
|
||||
VLOG(config_recoverer) << "Successfully connected in " << Time::now() - connecting_since_;
|
||||
}
|
||||
|
||||
Timestamp wakeup_timestamp;
|
||||
@ -786,24 +786,28 @@ class ConfigRecoverer : public Actor {
|
||||
check_timeout(Timestamp::at(dc_options_at_ + (expect_blocking() ? 5 : 10)));
|
||||
if (need_simple_config) {
|
||||
ref_cnt_++;
|
||||
VLOG(config_recoverer) << "ASK SIMPLE CONFIG";
|
||||
VLOG(config_recoverer) << "Ask simple config with turn " << simple_config_turn_;
|
||||
auto promise =
|
||||
PromiseCreator::lambda([actor_id = actor_shared(this)](Result<SimpleConfigResult> r_simple_config) {
|
||||
send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false);
|
||||
});
|
||||
auto get_simple_config = [&] {
|
||||
switch (simple_config_turn_ % 4) {
|
||||
case 2:
|
||||
switch (simple_config_turn_ % 10) {
|
||||
case 6:
|
||||
return get_simple_config_azure;
|
||||
case 3:
|
||||
case 2:
|
||||
return get_simple_config_firebase_remote_config;
|
||||
case 4:
|
||||
return get_simple_config_firebase_realtime;
|
||||
case 5:
|
||||
case 9:
|
||||
return get_simple_config_firebase_firestore;
|
||||
case 0:
|
||||
case 3:
|
||||
case 8:
|
||||
return get_simple_config_google_dns;
|
||||
case 1:
|
||||
case 5:
|
||||
case 7:
|
||||
default:
|
||||
return get_simple_config_mozilla_dns;
|
||||
}
|
||||
@ -815,7 +819,7 @@ class ConfigRecoverer : public Actor {
|
||||
|
||||
if (need_full_config) {
|
||||
ref_cnt_++;
|
||||
VLOG(config_recoverer) << "ASK FULL CONFIG";
|
||||
VLOG(config_recoverer) << "Ask full config with dc_options_i_ = " << dc_options_i_;
|
||||
full_config_query_ =
|
||||
get_full_config(dc_options_.dc_options[dc_options_i_],
|
||||
PromiseCreator::lambda([actor_id = actor_id(this)](Result<FullConfig> r_full_config) {
|
||||
@ -829,7 +833,7 @@ class ConfigRecoverer : public Actor {
|
||||
VLOG(config_recoverer) << "Wakeup in " << format::as_time(wakeup_timestamp.in());
|
||||
set_timeout_at(wakeup_timestamp.at());
|
||||
} else {
|
||||
VLOG(config_recoverer) << "Wakeup NEVER";
|
||||
VLOG(config_recoverer) << "Wakeup never";
|
||||
}
|
||||
}
|
||||
|
||||
@ -933,7 +937,7 @@ void ConfigManager::get_app_config(Promise<td_api::object_ptr<td_api::JsonValue>
|
||||
get_app_config_queries_.push_back(std::move(promise));
|
||||
if (get_app_config_queries_.size() == 1) {
|
||||
auto query = G()->net_query_creator().create_unauth(telegram_api::help_getAppConfig());
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 1));
|
||||
}
|
||||
}
|
||||
@ -989,7 +993,7 @@ void ConfigManager::on_dc_options_update(DcOptions dc_options) {
|
||||
void ConfigManager::request_config_from_dc_impl(DcId dc_id) {
|
||||
config_sent_cnt_++;
|
||||
auto query = G()->net_query_creator().create_unauth(telegram_api::help_getConfig(), dc_id);
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, 0));
|
||||
}
|
||||
|
||||
@ -1283,33 +1287,17 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
LOG(INFO) << "Receive app config " << to_string(config);
|
||||
|
||||
vector<tl_object_ptr<telegram_api::jsonObjectValue>> new_values;
|
||||
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;
|
||||
string animation_search_provider;
|
||||
string animation_search_emojis;
|
||||
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_;
|
||||
telegram_api::JSONValue *value = key_value->value_.get();
|
||||
if (key == "test" || key == "wallet_enabled") {
|
||||
continue;
|
||||
}
|
||||
if (key == "wallet_blockchain_name") {
|
||||
if (value->get_id() == telegram_api::jsonString::ID) {
|
||||
wallet_blockchain_name = std::move(static_cast<telegram_api::jsonString *>(value)->value_);
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected wallet_blockchain_name " << to_string(*value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (key == "wallet_config") {
|
||||
if (value->get_id() == telegram_api::jsonString::ID) {
|
||||
wallet_config = std::move(static_cast<telegram_api::jsonString *>(value)->value_);
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected wallet_config " << to_string(*value);
|
||||
}
|
||||
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config") {
|
||||
continue;
|
||||
}
|
||||
if (key == "ignore_restriction_reasons") {
|
||||
@ -1394,6 +1382,38 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (key == "gif_search_branding") {
|
||||
if (value->get_id() == telegram_api::jsonString::ID) {
|
||||
animation_search_provider = std::move(static_cast<telegram_api::jsonString *>(value)->value_);
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected gif_search_branding " << to_string(*value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (key == "gif_search_emojies") {
|
||||
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_str = static_cast<telegram_api::jsonString *>(emoji.get())->value_;
|
||||
if (!emoji_str.empty() && emoji_str.find(',') == Slice::npos) {
|
||||
if (!animation_search_emojis.empty()) {
|
||||
animation_search_emojis += ',';
|
||||
}
|
||||
animation_search_emojis.append(emoji_str.begin(), emoji_str.end());
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected animation search emoji " << emoji_str;
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected animation search emoji " << to_string(emoji);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Receive unexpected gif_search_emojies " << to_string(*value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
new_values.push_back(std::move(key_value));
|
||||
}
|
||||
@ -1403,13 +1423,6 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
config = make_tl_object<telegram_api::jsonObject>(std::move(new_values));
|
||||
|
||||
ConfigShared &shared_config = G()->shared_config();
|
||||
if (wallet_config.empty()) {
|
||||
shared_config.set_option_empty("default_ton_blockchain_config");
|
||||
shared_config.set_option_empty("default_ton_blockchain_name");
|
||||
} else {
|
||||
shared_config.set_option_string("default_ton_blockchain_name", wallet_blockchain_name);
|
||||
shared_config.set_option_string("default_ton_blockchain_config", wallet_config);
|
||||
}
|
||||
|
||||
if (ignored_restriction_reasons.empty()) {
|
||||
shared_config.set_option_empty("ignored_restriction_reasons");
|
||||
@ -1437,6 +1450,20 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
||||
shared_config.set_option_string("dice_success_values", implode(dice_success_values, ','));
|
||||
shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01'));
|
||||
}
|
||||
|
||||
if (animation_search_provider.empty()) {
|
||||
shared_config.set_option_empty("animation_search_provider");
|
||||
} else {
|
||||
shared_config.set_option_string("animation_search_provider", animation_search_provider);
|
||||
}
|
||||
if (animation_search_emojis.empty()) {
|
||||
shared_config.set_option_empty("animation_search_emojis");
|
||||
} else {
|
||||
shared_config.set_option_string("animation_search_emojis", animation_search_emojis);
|
||||
}
|
||||
|
||||
shared_config.set_option_empty("default_ton_blockchain_config");
|
||||
shared_config.set_option_empty("default_ton_blockchain_name");
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -12025,6 +12025,7 @@ bool ContactsManager::is_chat_full_outdated(const ChatFull *chat_full, const Cha
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Full " << chat_id << " is up-to-date with version " << chat_full->version;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,16 @@
|
||||
#include "td/db/SqliteDb.h"
|
||||
#include "td/db/SqliteStatement.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
namespace td {
|
||||
// NB: must happen inside a transaction
|
||||
Status init_dialog_db(SqliteDb &db, int32 version, bool &was_created) {
|
||||
Status init_dialog_db(SqliteDb &db, int32 version, KeyValueSyncInterface &binlog_pmc, bool &was_created) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
|
||||
#include "td/db/KeyValueSyncInterface.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Status.h"
|
||||
@ -94,7 +96,8 @@ class DialogDbAsyncInterface {
|
||||
virtual void close(Promise<> promise) = 0;
|
||||
};
|
||||
|
||||
Status init_dialog_db(SqliteDb &db, int version, bool &was_created) TD_WARN_UNUSED_RESULT;
|
||||
Status init_dialog_db(SqliteDb &db, int version, KeyValueSyncInterface &binlog_pmc,
|
||||
bool &was_created) TD_WARN_UNUSED_RESULT;
|
||||
Status drop_dialog_db(SqliteDb &db, int version) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
std::shared_ptr<DialogDbSyncSafeInterface> create_dialog_db_sync(
|
||||
|
422
td/telegram/DialogFilter.cpp
Normal file
422
td/telegram/DialogFilter.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
//
|
||||
// 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/DialogFilter.h"
|
||||
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/misc.h"
|
||||
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace td {
|
||||
|
||||
unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
|
||||
bool with_id) {
|
||||
DialogFilterId dialog_filter_id(filter->id_);
|
||||
if (with_id && !dialog_filter_id.is_valid()) {
|
||||
LOG(ERROR) << "Receive invalid " << to_string(filter);
|
||||
return nullptr;
|
||||
}
|
||||
auto dialog_filter = make_unique<DialogFilter>();
|
||||
dialog_filter->dialog_filter_id = dialog_filter_id;
|
||||
dialog_filter->title = std::move(filter->title_);
|
||||
dialog_filter->emoji = std::move(filter->emoticon_);
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
dialog_filter->pinned_dialog_ids = InputDialogId::get_input_dialog_ids(filter->pinned_peers_, &added_dialog_ids);
|
||||
dialog_filter->included_dialog_ids = InputDialogId::get_input_dialog_ids(filter->include_peers_, &added_dialog_ids);
|
||||
dialog_filter->excluded_dialog_ids = InputDialogId::get_input_dialog_ids(filter->exclude_peers_, &added_dialog_ids);
|
||||
auto flags = filter->flags_;
|
||||
dialog_filter->exclude_muted = (flags & telegram_api::dialogFilter::EXCLUDE_MUTED_MASK) != 0;
|
||||
dialog_filter->exclude_read = (flags & telegram_api::dialogFilter::EXCLUDE_READ_MASK) != 0;
|
||||
dialog_filter->exclude_archived = (flags & telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK) != 0;
|
||||
dialog_filter->include_contacts = (flags & telegram_api::dialogFilter::CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_non_contacts = (flags & telegram_api::dialogFilter::NON_CONTACTS_MASK) != 0;
|
||||
dialog_filter->include_bots = (flags & telegram_api::dialogFilter::BOTS_MASK) != 0;
|
||||
dialog_filter->include_groups = (flags & telegram_api::dialogFilter::GROUPS_MASK) != 0;
|
||||
dialog_filter->include_channels = (flags & telegram_api::dialogFilter::BROADCASTS_MASK) != 0;
|
||||
return dialog_filter;
|
||||
}
|
||||
|
||||
void DialogFilter::remove_secret_chat_dialog_ids() {
|
||||
auto remove_secret_chats = [](vector<InputDialogId> &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [](InputDialogId input_dialog_id) {
|
||||
return input_dialog_id.get_dialog_id().get_type() == DialogType::SecretChat;
|
||||
});
|
||||
};
|
||||
remove_secret_chats(pinned_dialog_ids);
|
||||
remove_secret_chats(included_dialog_ids);
|
||||
remove_secret_chats(excluded_dialog_ids);
|
||||
}
|
||||
|
||||
bool DialogFilter::is_empty(bool for_server) const {
|
||||
if (include_contacts || include_non_contacts || include_bots || include_groups || include_channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (for_server) {
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
return InputDialogId::are_equivalent(pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(included_dialog_ids, empty_input_dialog_ids);
|
||||
} else {
|
||||
return pinned_dialog_ids.empty() && included_dialog_ids.empty();
|
||||
}
|
||||
}
|
||||
|
||||
Status DialogFilter::check_limits() const {
|
||||
auto get_server_dialog_count = [](const vector<InputDialogId> &input_dialog_ids) {
|
||||
int32 result = 0;
|
||||
for (auto &input_dialog_id : input_dialog_ids) {
|
||||
if (input_dialog_id.get_dialog_id().get_type() != DialogType::SecretChat) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto excluded_server_dialog_count = get_server_dialog_count(excluded_dialog_ids);
|
||||
auto included_server_dialog_count = get_server_dialog_count(included_dialog_ids);
|
||||
auto pinned_server_dialog_count = get_server_dialog_count(pinned_dialog_ids);
|
||||
|
||||
auto excluded_secret_dialog_count = static_cast<int32>(excluded_dialog_ids.size()) - excluded_server_dialog_count;
|
||||
auto included_secret_dialog_count = static_cast<int32>(included_dialog_ids.size()) - included_server_dialog_count;
|
||||
auto pinned_secret_dialog_count = static_cast<int32>(pinned_dialog_ids.size()) - pinned_server_dialog_count;
|
||||
|
||||
if (excluded_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
excluded_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of excluded chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of included chats exceeded");
|
||||
}
|
||||
if (included_server_dialog_count + pinned_server_dialog_count > MAX_INCLUDED_FILTER_DIALOGS ||
|
||||
included_secret_dialog_count + pinned_secret_dialog_count > MAX_INCLUDED_FILTER_DIALOGS) {
|
||||
return Status::Error(400, "Maximum number of pinned chats exceeded");
|
||||
}
|
||||
|
||||
if (is_empty(false)) {
|
||||
return Status::Error(400, "Folder must contain at least 1 chat");
|
||||
}
|
||||
|
||||
if (include_contacts && include_non_contacts && include_bots && include_groups && include_channels &&
|
||||
exclude_archived && !exclude_read && !exclude_muted) {
|
||||
return Status::Error(400, "Folder must be different from the main chat list");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
string DialogFilter::get_emoji_by_icon_name(const string &icon_name) {
|
||||
init_icon_names();
|
||||
auto it = icon_name_to_emoji_.find(icon_name);
|
||||
if (it != icon_name_to_emoji_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
string DialogFilter::get_icon_name() const {
|
||||
init_icon_names();
|
||||
auto it = emoji_to_icon_name_.find(emoji);
|
||||
if (it != emoji_to_icon_name_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
string DialogFilter::get_default_icon_name(const td_api::chatFilter *filter) {
|
||||
if (!filter->icon_name_.empty() && !get_emoji_by_icon_name(filter->icon_name_).empty()) {
|
||||
return filter->icon_name_;
|
||||
}
|
||||
|
||||
if (!filter->pinned_chat_ids_.empty() || !filter->included_chat_ids_.empty() || !filter->excluded_chat_ids_.empty()) {
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
if (filter->include_contacts_ || filter->include_non_contacts_) {
|
||||
if (!filter->include_bots_ && !filter->include_groups_ && !filter->include_channels_) {
|
||||
return "Private";
|
||||
}
|
||||
} else {
|
||||
if (!filter->include_bots_ && !filter->include_channels_) {
|
||||
if (!filter->include_groups_) {
|
||||
// just in case
|
||||
return "Custom";
|
||||
}
|
||||
return "Groups";
|
||||
}
|
||||
if (!filter->include_bots_ && !filter->include_groups_) {
|
||||
return "Channels";
|
||||
}
|
||||
if (!filter->include_groups_ && !filter->include_channels_) {
|
||||
return "Bots";
|
||||
}
|
||||
}
|
||||
if (filter->exclude_read_ && !filter->exclude_muted_) {
|
||||
return "Unread";
|
||||
}
|
||||
if (filter->exclude_muted_ && !filter->exclude_read_) {
|
||||
return "Unmuted";
|
||||
}
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
telegram_api::object_ptr<telegram_api::dialogFilter> DialogFilter::get_input_dialog_filter() const {
|
||||
int32 flags = 0;
|
||||
if (!emoji.empty()) {
|
||||
flags |= telegram_api::dialogFilter::EMOTICON_MASK;
|
||||
}
|
||||
if (exclude_muted) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_MUTED_MASK;
|
||||
}
|
||||
if (exclude_read) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_READ_MASK;
|
||||
}
|
||||
if (exclude_archived) {
|
||||
flags |= telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK;
|
||||
}
|
||||
if (include_contacts) {
|
||||
flags |= telegram_api::dialogFilter::CONTACTS_MASK;
|
||||
}
|
||||
if (include_non_contacts) {
|
||||
flags |= telegram_api::dialogFilter::NON_CONTACTS_MASK;
|
||||
}
|
||||
if (include_bots) {
|
||||
flags |= telegram_api::dialogFilter::BOTS_MASK;
|
||||
}
|
||||
if (include_groups) {
|
||||
flags |= telegram_api::dialogFilter::GROUPS_MASK;
|
||||
}
|
||||
if (include_channels) {
|
||||
flags |= telegram_api::dialogFilter::BROADCASTS_MASK;
|
||||
}
|
||||
|
||||
return telegram_api::make_object<telegram_api::dialogFilter>(
|
||||
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
|
||||
false /*ignored*/, false /*ignored*/, false /*ignored*/, dialog_filter_id.get(), title, emoji,
|
||||
InputDialogId::get_input_peers(pinned_dialog_ids), InputDialogId::get_input_peers(included_dialog_ids),
|
||||
InputDialogId::get_input_peers(excluded_dialog_ids));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::chatFilterInfo> DialogFilter::get_chat_filter_info_object() const {
|
||||
return td_api::make_object<td_api::chatFilterInfo>(dialog_filter_id.get(), title, get_icon_name());
|
||||
}
|
||||
|
||||
// merges changes from old_server_filter to new_server_filter in old_filter
|
||||
unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogFilter *old_filter,
|
||||
const DialogFilter *old_server_filter,
|
||||
const DialogFilter *new_server_filter) {
|
||||
CHECK(old_filter != nullptr);
|
||||
CHECK(old_server_filter != nullptr);
|
||||
CHECK(new_server_filter != nullptr);
|
||||
CHECK(old_filter->dialog_filter_id == old_server_filter->dialog_filter_id);
|
||||
CHECK(old_filter->dialog_filter_id == new_server_filter->dialog_filter_id);
|
||||
auto dialog_filter_id = old_filter->dialog_filter_id;
|
||||
auto new_filter = make_unique<DialogFilter>(*old_filter);
|
||||
new_filter->dialog_filter_id = dialog_filter_id;
|
||||
|
||||
auto merge_ordered_changes = [dialog_filter_id](auto &new_dialog_ids, auto old_server_dialog_ids,
|
||||
auto new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
LOG(INFO) << "Pinned chats was not changed remotely in " << dialog_filter_id << ", keep local changes";
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputDialogId::are_equivalent(new_dialog_ids, old_server_dialog_ids)) {
|
||||
LOG(INFO) << "Pinned chats was not changed locally in " << dialog_filter_id << ", keep remote changes";
|
||||
|
||||
size_t kept_server_dialogs = 0;
|
||||
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids;
|
||||
auto old_it = old_server_dialog_ids.rbegin();
|
||||
for (auto &input_dialog_id : reversed(new_server_dialog_ids)) {
|
||||
auto dialog_id = input_dialog_id.get_dialog_id();
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
if (old_it->get_dialog_id() == dialog_id) {
|
||||
kept_server_dialogs++;
|
||||
++old_it;
|
||||
break;
|
||||
}
|
||||
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
}
|
||||
while (old_it < old_server_dialog_ids.rend()) {
|
||||
// remove the dialog, it could be added back later
|
||||
removed_dialog_ids.insert(old_it->get_dialog_id());
|
||||
++old_it;
|
||||
}
|
||||
td::remove_if(new_dialog_ids, [&removed_dialog_ids](auto input_dialog_id) {
|
||||
return removed_dialog_ids.count(input_dialog_id.get_dialog_id()) > 0;
|
||||
});
|
||||
new_dialog_ids.insert(new_dialog_ids.begin(), new_server_dialog_ids.begin(),
|
||||
new_server_dialog_ids.end() - kept_server_dialogs);
|
||||
} else {
|
||||
LOG(WARNING) << "Ignore remote changes of pinned chats in " << dialog_filter_id;
|
||||
// there are both local and remote changes; ignore remote changes for now
|
||||
}
|
||||
};
|
||||
|
||||
auto merge_changes = [](auto &new_dialog_ids, const auto &old_server_dialog_ids, const auto &new_server_dialog_ids) {
|
||||
if (old_server_dialog_ids == new_server_dialog_ids) {
|
||||
// fast path
|
||||
return;
|
||||
}
|
||||
|
||||
// merge additions and deletions from other clients to the local changes
|
||||
std::unordered_set<DialogId, DialogIdHash> deleted_dialog_ids;
|
||||
for (auto old_dialog_id : old_server_dialog_ids) {
|
||||
deleted_dialog_ids.insert(old_dialog_id.get_dialog_id());
|
||||
}
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
auto dialog_id = new_dialog_id.get_dialog_id();
|
||||
if (deleted_dialog_ids.erase(dialog_id) == 0) {
|
||||
added_dialog_ids.insert(dialog_id);
|
||||
}
|
||||
}
|
||||
vector<InputDialogId> result;
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
// do not add dialog twice
|
||||
added_dialog_ids.erase(input_dialog_id.get_dialog_id());
|
||||
}
|
||||
for (auto new_dialog_id : new_server_dialog_ids) {
|
||||
if (added_dialog_ids.count(new_dialog_id.get_dialog_id()) == 1) {
|
||||
result.push_back(new_dialog_id);
|
||||
}
|
||||
}
|
||||
for (auto input_dialog_id : new_dialog_ids) {
|
||||
if (deleted_dialog_ids.count(input_dialog_id.get_dialog_id()) == 0) {
|
||||
result.push_back(input_dialog_id);
|
||||
}
|
||||
}
|
||||
new_dialog_ids = std::move(result);
|
||||
};
|
||||
|
||||
merge_ordered_changes(new_filter->pinned_dialog_ids, old_server_filter->pinned_dialog_ids,
|
||||
new_server_filter->pinned_dialog_ids);
|
||||
merge_changes(new_filter->included_dialog_ids, old_server_filter->included_dialog_ids,
|
||||
new_server_filter->included_dialog_ids);
|
||||
merge_changes(new_filter->excluded_dialog_ids, old_server_filter->excluded_dialog_ids,
|
||||
new_server_filter->excluded_dialog_ids);
|
||||
|
||||
{
|
||||
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
|
||||
auto remove_duplicates = [&added_dialog_ids](auto &input_dialog_ids) {
|
||||
td::remove_if(input_dialog_ids, [&added_dialog_ids](auto input_dialog_id) {
|
||||
return !added_dialog_ids.insert(input_dialog_id.get_dialog_id()).second;
|
||||
});
|
||||
};
|
||||
remove_duplicates(new_filter->pinned_dialog_ids);
|
||||
remove_duplicates(new_filter->included_dialog_ids);
|
||||
remove_duplicates(new_filter->excluded_dialog_ids);
|
||||
}
|
||||
|
||||
auto update_value = [](auto &new_value, const auto &old_server_value, const auto &new_server_value) {
|
||||
// if the value was changed from other client and wasn't changed from the current client, update it
|
||||
if (new_server_value != old_server_value && old_server_value == new_value) {
|
||||
new_value = new_server_value;
|
||||
}
|
||||
};
|
||||
|
||||
update_value(new_filter->exclude_muted, old_server_filter->exclude_muted, new_server_filter->exclude_muted);
|
||||
update_value(new_filter->exclude_read, old_server_filter->exclude_read, new_server_filter->exclude_read);
|
||||
update_value(new_filter->exclude_archived, old_server_filter->exclude_archived, new_server_filter->exclude_archived);
|
||||
update_value(new_filter->include_contacts, old_server_filter->include_contacts, new_server_filter->include_contacts);
|
||||
update_value(new_filter->include_non_contacts, old_server_filter->include_non_contacts,
|
||||
new_server_filter->include_non_contacts);
|
||||
update_value(new_filter->include_bots, old_server_filter->include_bots, new_server_filter->include_bots);
|
||||
update_value(new_filter->include_groups, old_server_filter->include_groups, new_server_filter->include_groups);
|
||||
update_value(new_filter->include_channels, old_server_filter->include_channels, new_server_filter->include_channels);
|
||||
|
||||
if (new_filter->check_limits().is_error()) {
|
||||
LOG(WARNING) << "Failed to merge local and remote changes in " << new_filter->dialog_filter_id
|
||||
<< ", keep only local changes";
|
||||
*new_filter = *old_filter;
|
||||
}
|
||||
|
||||
update_value(new_filter->title, old_server_filter->title, new_server_filter->title);
|
||||
update_value(new_filter->emoji, old_server_filter->emoji, new_server_filter->emoji);
|
||||
return new_filter;
|
||||
}
|
||||
|
||||
bool DialogFilter::are_similar(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
if (lhs.title == rhs.title) {
|
||||
return true;
|
||||
}
|
||||
if (!are_flags_equal(lhs, rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<InputDialogId> empty_input_dialog_ids;
|
||||
if (InputDialogId::are_equivalent(lhs.excluded_dialog_ids, empty_input_dialog_ids) !=
|
||||
InputDialogId::are_equivalent(rhs.excluded_dialog_ids, empty_input_dialog_ids)) {
|
||||
return false;
|
||||
}
|
||||
if ((InputDialogId::are_equivalent(lhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, empty_input_dialog_ids)) !=
|
||||
(InputDialogId::are_equivalent(rhs.pinned_dialog_ids, empty_input_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(rhs.included_dialog_ids, empty_input_dialog_ids))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialogFilter::are_equivalent(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
InputDialogId::are_equivalent(lhs.pinned_dialog_ids, rhs.pinned_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.included_dialog_ids, rhs.included_dialog_ids) &&
|
||||
InputDialogId::are_equivalent(lhs.excluded_dialog_ids, rhs.excluded_dialog_ids) && are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogFilter &filter) {
|
||||
return string_builder << filter.dialog_filter_id << " (pinned " << filter.pinned_dialog_ids << ", included "
|
||||
<< filter.included_dialog_ids << ", excluded " << filter.excluded_dialog_ids << ", "
|
||||
<< filter.exclude_muted << ' ' << filter.exclude_read << ' ' << filter.exclude_archived << '/'
|
||||
<< filter.include_contacts << ' ' << filter.include_non_contacts << ' ' << filter.include_bots
|
||||
<< ' ' << filter.include_groups << ' ' << filter.include_channels << ')';
|
||||
}
|
||||
|
||||
void DialogFilter::init_icon_names() {
|
||||
static bool is_inited = [&] {
|
||||
vector<string> emojis{"\xF0\x9F\x92\xAC", "\xE2\x9C\x85", "\xF0\x9F\x94\x94",
|
||||
"\xF0\x9F\xA4\x96", "\xF0\x9F\x93\xA2", "\xF0\x9F\x91\xA5",
|
||||
"\xF0\x9F\x91\xA4", "\xF0\x9F\x93\x81", "\xF0\x9F\x93\x8B",
|
||||
"\xF0\x9F\x90\xB1", "\xF0\x9F\x91\x91", "\xE2\xAD\x90\xEF\xB8\x8F",
|
||||
"\xF0\x9F\x8C\xB9", "\xF0\x9F\x8E\xAE", "\xF0\x9F\x8F\xA0",
|
||||
"\xE2\x9D\xA4\xEF\xB8\x8F", "\xF0\x9F\x8E\xAD", "\xF0\x9F\x8D\xB8",
|
||||
"\xE2\x9A\xBD\xEF\xB8\x8F", "\xF0\x9F\x8E\x93", "\xF0\x9F\x93\x88",
|
||||
"\xE2\x9C\x88\xEF\xB8\x8F", "\xF0\x9F\x92\xBC"};
|
||||
vector<string> icon_names{"All", "Unread", "Unmuted", "Bots", "Channels", "Groups", "Private", "Custom",
|
||||
"Setup", "Cat", "Crown", "Favorite", "Flower", "Game", "Home", "Love",
|
||||
"Mask", "Party", "Sport", "Study", "Trade", "Travel", "Work"};
|
||||
CHECK(emojis.size() == icon_names.size());
|
||||
for (size_t i = 0; i < emojis.size(); i++) {
|
||||
emoji_to_icon_name_[remove_emoji_modifiers(emojis[i])] = icon_names[i];
|
||||
icon_name_to_emoji_[icon_names[i]] = remove_emoji_modifiers(emojis[i]);
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
bool DialogFilter::are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.exclude_muted == rhs.exclude_muted && lhs.exclude_read == rhs.exclude_read &&
|
||||
lhs.exclude_archived == rhs.exclude_archived && lhs.include_contacts == rhs.include_contacts &&
|
||||
lhs.include_non_contacts == rhs.include_non_contacts && lhs.include_bots == rhs.include_bots &&
|
||||
lhs.include_groups == rhs.include_groups && lhs.include_channels == rhs.include_channels;
|
||||
}
|
||||
|
||||
std::unordered_map<string, string> DialogFilter::emoji_to_icon_name_;
|
||||
std::unordered_map<string, string> DialogFilter::icon_name_to_emoji_;
|
||||
|
||||
} // namespace td
|
104
td/telegram/DialogFilter.h
Normal file
104
td/telegram/DialogFilter.h
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// 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/DialogFilterId.h"
|
||||
#include "td/telegram/InputDialogId.h"
|
||||
#include "td/telegram/td_api.h"
|
||||
#include "td/telegram/telegram_api.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace td {
|
||||
|
||||
class DialogFilter {
|
||||
public:
|
||||
static constexpr int32 MAX_INCLUDED_FILTER_DIALOGS = 100; // server side limit
|
||||
|
||||
DialogFilterId dialog_filter_id;
|
||||
string title;
|
||||
string emoji;
|
||||
vector<InputDialogId> pinned_dialog_ids;
|
||||
vector<InputDialogId> included_dialog_ids;
|
||||
vector<InputDialogId> excluded_dialog_ids;
|
||||
bool exclude_muted = false;
|
||||
bool exclude_read = false;
|
||||
bool exclude_archived = false;
|
||||
bool include_contacts = false;
|
||||
bool include_non_contacts = false;
|
||||
bool include_bots = false;
|
||||
bool include_groups = false;
|
||||
bool include_channels = false;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const;
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser);
|
||||
|
||||
static unique_ptr<DialogFilter> get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
|
||||
bool with_id);
|
||||
|
||||
void remove_secret_chat_dialog_ids();
|
||||
|
||||
bool is_empty(bool for_server) const;
|
||||
|
||||
Status check_limits() const;
|
||||
|
||||
static string get_emoji_by_icon_name(const string &icon_name);
|
||||
|
||||
string get_icon_name() const;
|
||||
|
||||
static string get_default_icon_name(const td_api::chatFilter *filter);
|
||||
|
||||
telegram_api::object_ptr<telegram_api::dialogFilter> get_input_dialog_filter() const;
|
||||
|
||||
td_api::object_ptr<td_api::chatFilterInfo> get_chat_filter_info_object() const;
|
||||
|
||||
// merges changes from old_server_filter to new_server_filter in old_filter
|
||||
static unique_ptr<DialogFilter> merge_dialog_filter_changes(const DialogFilter *old_filter,
|
||||
const DialogFilter *old_server_filter,
|
||||
const DialogFilter *new_server_filter);
|
||||
|
||||
static bool are_similar(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
static bool are_equivalent(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
static bool are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs);
|
||||
|
||||
private:
|
||||
static std::unordered_map<string, string> emoji_to_icon_name_;
|
||||
static std::unordered_map<string, string> icon_name_to_emoji_;
|
||||
|
||||
static void init_icon_names();
|
||||
};
|
||||
|
||||
inline bool operator==(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return lhs.dialog_filter_id == rhs.dialog_filter_id && lhs.title == rhs.title && lhs.emoji == rhs.emoji &&
|
||||
lhs.pinned_dialog_ids == rhs.pinned_dialog_ids && lhs.included_dialog_ids == rhs.included_dialog_ids &&
|
||||
lhs.excluded_dialog_ids == rhs.excluded_dialog_ids && DialogFilter::are_flags_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const DialogFilter &lhs, const DialogFilter &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator==(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const unique_ptr<DialogFilter> &lhs, const unique_ptr<DialogFilter> &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogFilter &filter);
|
||||
|
||||
} // namespace td
|
84
td/telegram/DialogFilter.hpp
Normal file
84
td/telegram/DialogFilter.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// 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/DialogFilter.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/tl_helpers.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class StorerT>
|
||||
void DialogFilter::store(StorerT &storer) const {
|
||||
using td::store;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(exclude_muted);
|
||||
STORE_FLAG(exclude_read);
|
||||
STORE_FLAG(exclude_archived);
|
||||
STORE_FLAG(include_contacts);
|
||||
STORE_FLAG(include_non_contacts);
|
||||
STORE_FLAG(include_bots);
|
||||
STORE_FLAG(include_groups);
|
||||
STORE_FLAG(include_channels);
|
||||
STORE_FLAG(has_pinned_dialog_ids);
|
||||
STORE_FLAG(has_included_dialog_ids);
|
||||
STORE_FLAG(has_excluded_dialog_ids);
|
||||
END_STORE_FLAGS();
|
||||
|
||||
store(dialog_filter_id, storer);
|
||||
store(title, storer);
|
||||
store(emoji, storer);
|
||||
if (has_pinned_dialog_ids) {
|
||||
store(pinned_dialog_ids, storer);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
store(included_dialog_ids, storer);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
store(excluded_dialog_ids, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void DialogFilter::parse(ParserT &parser) {
|
||||
using td::parse;
|
||||
bool has_pinned_dialog_ids = !pinned_dialog_ids.empty();
|
||||
bool has_included_dialog_ids = !included_dialog_ids.empty();
|
||||
bool has_excluded_dialog_ids = !excluded_dialog_ids.empty();
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(exclude_muted);
|
||||
PARSE_FLAG(exclude_read);
|
||||
PARSE_FLAG(exclude_archived);
|
||||
PARSE_FLAG(include_contacts);
|
||||
PARSE_FLAG(include_non_contacts);
|
||||
PARSE_FLAG(include_bots);
|
||||
PARSE_FLAG(include_groups);
|
||||
PARSE_FLAG(include_channels);
|
||||
PARSE_FLAG(has_pinned_dialog_ids);
|
||||
PARSE_FLAG(has_included_dialog_ids);
|
||||
PARSE_FLAG(has_excluded_dialog_ids);
|
||||
END_PARSE_FLAGS();
|
||||
|
||||
parse(dialog_filter_id, parser);
|
||||
parse(title, parser);
|
||||
parse(emoji, parser);
|
||||
if (has_pinned_dialog_ids) {
|
||||
parse(pinned_dialog_ids, parser);
|
||||
}
|
||||
if (has_included_dialog_ids) {
|
||||
parse(included_dialog_ids, parser);
|
||||
}
|
||||
if (has_excluded_dialog_ids) {
|
||||
parse(excluded_dialog_ids, parser);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
73
td/telegram/DialogFilterId.h
Normal file
73
td/telegram/DialogFilterId.h
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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/utils/common.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
class DialogFilterId {
|
||||
int32 id = 0;
|
||||
|
||||
public:
|
||||
DialogFilterId() = default;
|
||||
|
||||
explicit constexpr DialogFilterId(int32 dialog_filter_id) : id(dialog_filter_id) {
|
||||
}
|
||||
template <class T, typename = std::enable_if_t<std::is_convertible<T, int32>::value>>
|
||||
DialogFilterId(T dialog_filter_id) = delete;
|
||||
|
||||
static constexpr DialogFilterId min() {
|
||||
return DialogFilterId(static_cast<int32>(2));
|
||||
}
|
||||
static constexpr DialogFilterId max() {
|
||||
return DialogFilterId(static_cast<int32>(255));
|
||||
}
|
||||
|
||||
bool is_valid() const {
|
||||
// don't check max() for greater future flexibility
|
||||
return id >= min().get();
|
||||
}
|
||||
|
||||
int32 get() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
bool operator==(const DialogFilterId &other) const {
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
bool operator!=(const DialogFilterId &other) const {
|
||||
return id != other.id;
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
storer.store_int(id);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
id = parser.fetch_int();
|
||||
}
|
||||
};
|
||||
|
||||
struct DialogFilterIdHash {
|
||||
std::size_t operator()(DialogFilterId dialog_filter_id) const {
|
||||
return std::hash<int32>()(dialog_filter_id.get());
|
||||
}
|
||||
};
|
||||
|
||||
inline StringBuilder &operator<<(StringBuilder &string_builder, DialogFilterId dialog_filter_id) {
|
||||
return string_builder << "filter " << dialog_filter_id.get();
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -14,18 +14,14 @@ namespace td {
|
||||
|
||||
bool DialogId::is_valid() const {
|
||||
switch (get_type()) {
|
||||
case DialogType::User: {
|
||||
case DialogType::User:
|
||||
return get_user_id().is_valid();
|
||||
}
|
||||
case DialogType::Chat: {
|
||||
case DialogType::Chat:
|
||||
return get_chat_id().is_valid();
|
||||
}
|
||||
case DialogType::Channel: {
|
||||
case DialogType::Channel:
|
||||
return get_channel_id().is_valid();
|
||||
}
|
||||
case DialogType::SecretChat: {
|
||||
case DialogType::SecretChat:
|
||||
return get_secret_chat_id().is_valid();
|
||||
}
|
||||
case DialogType::None:
|
||||
return false;
|
||||
default:
|
||||
|
@ -37,7 +37,6 @@ class DialogId {
|
||||
static int64 get_peer_id(const tl_object_ptr<telegram_api::Peer> &peer);
|
||||
|
||||
public:
|
||||
using UnderlyingType = decltype(id);
|
||||
DialogId() = default;
|
||||
|
||||
explicit DialogId(int64 dialog_id) : id(dialog_id) {
|
||||
|
131
td/telegram/DialogListId.h
Normal file
131
td/telegram/DialogListId.h
Normal file
@ -0,0 +1,131 @@
|
||||
//
|
||||
// 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/DialogFilterId.h"
|
||||
#include "td/telegram/FolderId.h"
|
||||
#include "td/telegram/td_api.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
class DialogListId {
|
||||
int64 id = 0;
|
||||
|
||||
static constexpr int64 FILTER_ID_SHIFT = static_cast<int64>(1) << 32;
|
||||
|
||||
public:
|
||||
DialogListId() = default;
|
||||
|
||||
explicit DialogListId(int64 dialog_list_id) : id(dialog_list_id) {
|
||||
}
|
||||
template <class T, typename = std::enable_if_t<std::is_convertible<T, int32>::value>>
|
||||
DialogListId(T dialog_list_id) = delete;
|
||||
|
||||
explicit DialogListId(DialogFilterId dialog_filter_id) : id(dialog_filter_id.get() + FILTER_ID_SHIFT) {
|
||||
}
|
||||
explicit DialogListId(FolderId folder_id) : id(folder_id.get()) {
|
||||
}
|
||||
|
||||
explicit DialogListId(const td_api::object_ptr<td_api::ChatList> &chat_list) {
|
||||
if (chat_list == nullptr) {
|
||||
CHECK(id == FolderId::main().get());
|
||||
return;
|
||||
}
|
||||
switch (chat_list->get_id()) {
|
||||
case td_api::chatListArchive::ID:
|
||||
id = FolderId::archive().get();
|
||||
break;
|
||||
case td_api::chatListMain::ID:
|
||||
CHECK(id == FolderId::main().get());
|
||||
break;
|
||||
case td_api::chatListFilter::ID: {
|
||||
DialogFilterId filter_id(static_cast<const td_api::chatListFilter *>(chat_list.get())->chat_filter_id_);
|
||||
if (filter_id.is_valid()) {
|
||||
*this = DialogListId(filter_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::ChatList> get_chat_list_object() const {
|
||||
if (is_folder()) {
|
||||
auto folder_id = get_folder_id();
|
||||
if (folder_id == FolderId::archive()) {
|
||||
return td_api::make_object<td_api::chatListArchive>();
|
||||
}
|
||||
if (folder_id == FolderId::main()) {
|
||||
return td_api::make_object<td_api::chatListMain>();
|
||||
}
|
||||
return td_api::make_object<td_api::chatListMain>();
|
||||
}
|
||||
if (is_filter()) {
|
||||
return td_api::make_object<td_api::chatListFilter>(get_filter_id().get());
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64 get() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
bool operator==(const DialogListId &other) const {
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
bool operator!=(const DialogListId &other) const {
|
||||
return id != other.id;
|
||||
}
|
||||
|
||||
bool is_folder() const {
|
||||
return std::numeric_limits<int32>::min() <= id && id <= std::numeric_limits<int32>::max();
|
||||
}
|
||||
|
||||
bool is_filter() const {
|
||||
return std::numeric_limits<int32>::min() + FILTER_ID_SHIFT <= id &&
|
||||
id <= std::numeric_limits<int32>::max() + FILTER_ID_SHIFT;
|
||||
}
|
||||
|
||||
FolderId get_folder_id() const {
|
||||
CHECK(is_folder());
|
||||
return FolderId(static_cast<int32>(id));
|
||||
}
|
||||
|
||||
DialogFilterId get_filter_id() const {
|
||||
CHECK(is_filter());
|
||||
return DialogFilterId(static_cast<int32>(id - FILTER_ID_SHIFT));
|
||||
}
|
||||
};
|
||||
|
||||
struct DialogListIdHash {
|
||||
std::size_t operator()(DialogListId dialog_list_id) const {
|
||||
return std::hash<int64>()(dialog_list_id.get());
|
||||
}
|
||||
};
|
||||
|
||||
inline StringBuilder &operator<<(StringBuilder &string_builder, DialogListId dialog_list_id) {
|
||||
if (dialog_list_id.is_folder()) {
|
||||
return string_builder << "chat list " << dialog_list_id.get_folder_id();
|
||||
}
|
||||
if (dialog_list_id.is_filter()) {
|
||||
return string_builder << "chat list " << dialog_list_id.get_filter_id();
|
||||
}
|
||||
return string_builder << "chat list " << dialog_list_id.get();
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -35,6 +35,7 @@ void Document::append_file_ids(const Td *td, vector<FileId> &file_ids) const {
|
||||
}
|
||||
|
||||
file_ids.push_back(file_id);
|
||||
|
||||
FileId thumbnail_file_id = [&] {
|
||||
switch (type) {
|
||||
case Type::Animation:
|
||||
@ -54,6 +55,20 @@ void Document::append_file_ids(const Td *td, vector<FileId> &file_ids) const {
|
||||
if (thumbnail_file_id.is_valid()) {
|
||||
file_ids.push_back(thumbnail_file_id);
|
||||
}
|
||||
|
||||
FileId animated_thumbnail_file_id = [&] {
|
||||
switch (type) {
|
||||
case Type::Animation:
|
||||
return td->animations_manager_->get_animation_animated_thumbnail_file_id(file_id);
|
||||
case Type::Video:
|
||||
return td->videos_manager_->get_video_animated_thumbnail_file_id(file_id);
|
||||
default:
|
||||
return FileId();
|
||||
}
|
||||
}();
|
||||
if (animated_thumbnail_file_id.is_valid()) {
|
||||
file_ids.push_back(animated_thumbnail_file_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Document &lhs, const Document &rhs) {
|
||||
|
@ -48,7 +48,7 @@ namespace td {
|
||||
DocumentsManager::DocumentsManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId file_id) {
|
||||
tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId file_id, PhotoFormat thumbnail_format) {
|
||||
if (!file_id.is_valid()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -60,9 +60,9 @@ tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId fil
|
||||
}
|
||||
LOG_CHECK(document != nullptr) << tag("file_id", file_id);
|
||||
document->is_changed = false;
|
||||
return make_tl_object<td_api::document>(document->file_name, document->mime_type,
|
||||
get_minithumbnail_object(document->minithumbnail),
|
||||
get_photo_size_object(td_->file_manager_.get(), &document->thumbnail),
|
||||
return make_tl_object<td_api::document>(
|
||||
document->file_name, document->mime_type, get_minithumbnail_object(document->minithumbnail),
|
||||
get_thumbnail_object(td_->file_manager_.get(), document->thumbnail, thumbnail_format),
|
||||
td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
@ -217,6 +217,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
string file_reference;
|
||||
string minithumbnail;
|
||||
PhotoSize thumbnail;
|
||||
PhotoSize animated_thumbnail;
|
||||
FileEncryptionKey encryption_key;
|
||||
bool is_animated_sticker = false;
|
||||
bool is_web = false;
|
||||
@ -236,6 +237,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
default_extension = Slice("tgs");
|
||||
owner_dialog_id = DialogId();
|
||||
file_name.clear();
|
||||
thumbnail_format = PhotoFormat::Webp;
|
||||
}
|
||||
};
|
||||
|
||||
@ -272,6 +274,16 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &thumb : document->video_thumbs_) {
|
||||
if (thumb->type_ == "v") {
|
||||
animated_thumbnail =
|
||||
get_video_photo_size(td_->file_manager_.get(), {FileType::Thumbnail, 0}, id, access_hash, file_reference,
|
||||
DcId::create(dc_id), owner_dialog_id, std::move(thumb));
|
||||
if (animated_thumbnail.file_id.is_valid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (remote_document.secret_file != nullptr) {
|
||||
CHECK(remote_document.secret_document != nullptr);
|
||||
auto file = std::move(remote_document.secret_file);
|
||||
@ -300,7 +312,14 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
id = Random::fast(0, std::numeric_limits<int32>::max());
|
||||
dc_id = 0;
|
||||
access_hash = 0;
|
||||
if (remote_document.thumbnail.type == 'v') {
|
||||
animated_thumbnail = std::move(remote_document.thumbnail);
|
||||
} else {
|
||||
thumbnail = std::move(remote_document.thumbnail);
|
||||
if (remote_document.thumbnail.type == 'g') {
|
||||
thumbnail_format = PhotoFormat::Gif;
|
||||
}
|
||||
}
|
||||
|
||||
auto web_document_ptr = std::move(remote_document.web_document);
|
||||
switch (web_document_ptr->get_id()) {
|
||||
@ -391,10 +410,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
|
||||
switch (document_type) {
|
||||
case Document::Type::Animation:
|
||||
// TODO use has_stickers
|
||||
td_->animations_manager_->create_animation(file_id, std::move(minithumbnail), std::move(thumbnail),
|
||||
std::move(file_name), std::move(mime_type), video_duration, dimensions,
|
||||
!is_web);
|
||||
td_->animations_manager_->create_animation(
|
||||
file_id, std::move(minithumbnail), std::move(thumbnail), std::move(animated_thumbnail), has_stickers,
|
||||
vector<FileId>(), std::move(file_name), std::move(mime_type), video_duration, dimensions, !is_web);
|
||||
break;
|
||||
case Document::Type::Audio: {
|
||||
int32 duration = 0;
|
||||
@ -419,9 +437,10 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
|
||||
is_animated_sticker, load_data_multipromise_ptr);
|
||||
break;
|
||||
case Document::Type::Video:
|
||||
td_->videos_manager_->create_video(file_id, std::move(minithumbnail), std::move(thumbnail), has_stickers,
|
||||
vector<FileId>(), std::move(file_name), std::move(mime_type), video_duration,
|
||||
dimensions, supports_streaming, !is_web);
|
||||
td_->videos_manager_->create_video(file_id, std::move(minithumbnail), std::move(thumbnail),
|
||||
std::move(animated_thumbnail), has_stickers, vector<FileId>(),
|
||||
std::move(file_name), std::move(mime_type), video_duration, dimensions,
|
||||
supports_streaming, !is_web);
|
||||
break;
|
||||
case Document::Type::VideoNote:
|
||||
td_->video_notes_manager_->create_video_note(file_id, std::move(minithumbnail), std::move(thumbnail),
|
||||
|
@ -74,7 +74,7 @@ class DocumentsManager {
|
||||
}
|
||||
};
|
||||
|
||||
tl_object_ptr<td_api::document> get_document_object(FileId file_id);
|
||||
tl_object_ptr<td_api::document> get_document_object(FileId file_id, PhotoFormat thumbnail_format);
|
||||
|
||||
void memory_cleanup();
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/telegram/td_api.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
@ -27,14 +25,6 @@ class FolderId {
|
||||
template <class T, typename = std::enable_if_t<std::is_convertible<T, int32>::value>>
|
||||
FolderId(T folder_id) = delete;
|
||||
|
||||
explicit FolderId(const td_api::object_ptr<td_api::ChatList> &chat_list) {
|
||||
if (chat_list != nullptr && chat_list->get_id() == td_api::chatListArchive::ID) {
|
||||
id = 1;
|
||||
} else {
|
||||
CHECK(id == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int32 get() const {
|
||||
return id;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ class GetInlineBotResultsQuery : public Td::ResultHandler {
|
||||
flags, std::move(bot_input_user), std::move(input_peer),
|
||||
user_location.empty() ? nullptr : user_location.get_input_geo_point(), query, offset));
|
||||
auto result = net_query.get_weak();
|
||||
net_query->need_resend_on_503 = false;
|
||||
net_query->need_resend_on_503_ = false;
|
||||
send_query(std::move(net_query));
|
||||
return result;
|
||||
}
|
||||
@ -865,6 +865,29 @@ tl_object_ptr<td_api::photoSize> copy(const td_api::photoSize &obj) {
|
||||
return make_tl_object<td_api::photoSize>(obj.type_, copy(obj.photo_), obj.width_, obj.height_);
|
||||
}
|
||||
|
||||
template <>
|
||||
tl_object_ptr<td_api::thumbnail> copy(const td_api::thumbnail &obj) {
|
||||
auto format = [&]() -> td_api::object_ptr<td_api::ThumbnailFormat> {
|
||||
switch (obj.format_->get_id()) {
|
||||
case td_api::thumbnailFormatJpeg::ID:
|
||||
return td_api::make_object<td_api::thumbnailFormatJpeg>();
|
||||
case td_api::thumbnailFormatPng::ID:
|
||||
return td_api::make_object<td_api::thumbnailFormatPng>();
|
||||
case td_api::thumbnailFormatWebp::ID:
|
||||
return td_api::make_object<td_api::thumbnailFormatWebp>();
|
||||
case td_api::thumbnailFormatTgs::ID:
|
||||
return td_api::make_object<td_api::thumbnailFormatTgs>();
|
||||
case td_api::thumbnailFormatMpeg4::ID:
|
||||
return td_api::make_object<td_api::thumbnailFormatMpeg4>();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
}();
|
||||
|
||||
return make_tl_object<td_api::thumbnail>(std::move(format), obj.width_, obj.height_, copy(obj.file_));
|
||||
}
|
||||
|
||||
static tl_object_ptr<td_api::photoSize> copy_photo_size(const tl_object_ptr<td_api::photoSize> &obj) {
|
||||
return copy(obj);
|
||||
}
|
||||
@ -894,7 +917,8 @@ tl_object_ptr<td_api::maskPosition> copy(const td_api::maskPosition &obj) {
|
||||
template <>
|
||||
tl_object_ptr<td_api::animation> copy(const td_api::animation &obj) {
|
||||
return make_tl_object<td_api::animation>(obj.duration_, obj.width_, obj.height_, obj.file_name_, obj.mime_type_,
|
||||
copy(obj.minithumbnail_), copy(obj.thumbnail_), copy(obj.animation_));
|
||||
obj.has_stickers_, copy(obj.minithumbnail_), copy(obj.thumbnail_),
|
||||
copy(obj.animation_));
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -1060,15 +1084,16 @@ tl_object_ptr<td_api::inlineQueryResults> InlineQueriesManager::decrease_pending
|
||||
return copy(it->second.results);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::photoSize> InlineQueriesManager::register_thumbnail(
|
||||
tl_object_ptr<td_api::thumbnail> InlineQueriesManager::register_thumbnail(
|
||||
tl_object_ptr<telegram_api::WebDocument> &&web_document_ptr) const {
|
||||
PhotoSize thumbnail = get_web_document_photo_size(td_->file_manager_.get(), FileType::Thumbnail, DialogId(),
|
||||
std::move(web_document_ptr));
|
||||
if (!thumbnail.file_id.is_valid()) {
|
||||
if (!thumbnail.file_id.is_valid() || thumbnail.type == 'v') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return get_photo_size_object(td_->file_manager_.get(), &thumbnail);
|
||||
return get_thumbnail_object(td_->file_manager_.get(), thumbnail,
|
||||
thumbnail.type == 'g' ? PhotoFormat::Gif : PhotoFormat::Jpeg);
|
||||
}
|
||||
|
||||
string InlineQueriesManager::get_web_document_url(const tl_object_ptr<telegram_api::WebDocument> &web_document_ptr) {
|
||||
@ -1201,7 +1226,8 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
|
||||
|
||||
auto document = make_tl_object<td_api::inlineQueryResultDocument>();
|
||||
document->id_ = std::move(result->id_);
|
||||
document->document_ = td_->documents_manager_->get_document_object(parsed_document.file_id);
|
||||
document->document_ =
|
||||
td_->documents_manager_->get_document_object(parsed_document.file_id, PhotoFormat::Jpeg);
|
||||
document->title_ = std::move(result->title_);
|
||||
document->description_ = std::move(result->description_);
|
||||
|
||||
@ -1389,7 +1415,7 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
|
||||
|
||||
PhotoSize photo_size = get_web_document_photo_size(td_->file_manager_.get(), FileType::Temp, DialogId(),
|
||||
std::move(result->content_));
|
||||
if (!photo_size.file_id.is_valid()) {
|
||||
if (!photo_size.file_id.is_valid() || photo_size.type == 'v' || photo_size.type == 'g') {
|
||||
LOG(ERROR) << "Receive invalid web document photo";
|
||||
continue;
|
||||
}
|
||||
@ -1397,7 +1423,7 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
|
||||
Photo new_photo;
|
||||
PhotoSize thumbnail = get_web_document_photo_size(td_->file_manager_.get(), FileType::Thumbnail, DialogId(),
|
||||
std::move(result->thumb_));
|
||||
if (thumbnail.file_id.is_valid()) {
|
||||
if (thumbnail.file_id.is_valid() && thumbnail.type != 'v' && thumbnail.type != 'g') {
|
||||
new_photo.photos.push_back(std::move(thumbnail));
|
||||
}
|
||||
new_photo.photos.push_back(std::move(photo_size));
|
||||
@ -1467,7 +1493,7 @@ void InlineQueriesManager::on_get_inline_query_results(UserId bot_user_id, uint6
|
||||
} else if (result->type_ == "file" && parsed_document.type == Document::Type::General) {
|
||||
auto document = make_tl_object<td_api::inlineQueryResultDocument>();
|
||||
document->id_ = std::move(result->id_);
|
||||
document->document_ = td_->documents_manager_->get_document_object(file_id);
|
||||
document->document_ = td_->documents_manager_->get_document_object(file_id, PhotoFormat::Jpeg);
|
||||
document->title_ = std::move(result->title_);
|
||||
document->description_ = std::move(result->description_);
|
||||
if (!register_inline_message_content(results->query_id_, document->id_, file_id,
|
||||
|
@ -92,7 +92,7 @@ class InlineQueriesManager : public Actor {
|
||||
tl_object_ptr<telegram_api::BotInlineMessage> &&inline_message,
|
||||
int32 allowed_media_content_id, Photo *photo = nullptr, Game *game = nullptr);
|
||||
|
||||
tl_object_ptr<td_api::photoSize> register_thumbnail(
|
||||
tl_object_ptr<td_api::thumbnail> register_thumbnail(
|
||||
tl_object_ptr<telegram_api::WebDocument> &&web_document_ptr) const;
|
||||
|
||||
static string get_web_document_url(const tl_object_ptr<telegram_api::WebDocument> &web_document_ptr);
|
||||
|
146
td/telegram/InputDialogId.cpp
Normal file
146
td/telegram/InputDialogId.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
//
|
||||
// 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/InputDialogId.h"
|
||||
|
||||
#include "td/telegram/ChannelId.h"
|
||||
#include "td/telegram/ChatId.h"
|
||||
#include "td/telegram/UserId.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
InputDialogId::InputDialogId(const tl_object_ptr<telegram_api::InputPeer> &input_peer) {
|
||||
CHECK(input_peer != nullptr);
|
||||
switch (input_peer->get_id()) {
|
||||
case telegram_api::inputPeerUser::ID: {
|
||||
auto input_user = static_cast<const telegram_api::inputPeerUser *>(input_peer.get());
|
||||
UserId user_id(input_user->user_id_);
|
||||
if (user_id.is_valid()) {
|
||||
dialog_id = DialogId(user_id);
|
||||
access_hash = input_user->access_hash_;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case telegram_api::inputPeerChat::ID: {
|
||||
auto input_chat = static_cast<const telegram_api::inputPeerChat *>(input_peer.get());
|
||||
ChatId chat_id(input_chat->chat_id_);
|
||||
if (chat_id.is_valid()) {
|
||||
dialog_id = DialogId(chat_id);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case telegram_api::inputPeerChannel::ID: {
|
||||
auto input_channel = static_cast<const telegram_api::inputPeerChannel *>(input_peer.get());
|
||||
ChannelId channel_id(input_channel->channel_id_);
|
||||
if (channel_id.is_valid()) {
|
||||
dialog_id = DialogId(channel_id);
|
||||
access_hash = input_channel->access_hash_;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LOG(ERROR) << "Receive " << to_string(input_peer);
|
||||
}
|
||||
|
||||
vector<InputDialogId> InputDialogId::get_input_dialog_ids(
|
||||
const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
|
||||
std::unordered_set<DialogId, DialogIdHash> *added_dialog_ids) {
|
||||
std::unordered_set<DialogId, DialogIdHash> temp_added_dialog_ids;
|
||||
if (added_dialog_ids == nullptr) {
|
||||
added_dialog_ids = &temp_added_dialog_ids;
|
||||
}
|
||||
vector<InputDialogId> result;
|
||||
result.reserve(input_peers.size());
|
||||
for (auto &input_peer : input_peers) {
|
||||
InputDialogId input_dialog_id(input_peer);
|
||||
if (input_dialog_id.is_valid() && added_dialog_ids->insert(input_dialog_id.get_dialog_id()).second) {
|
||||
result.push_back(input_dialog_id);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> InputDialogId::get_input_dialog_peers(
|
||||
const vector<InputDialogId> &input_dialog_ids) {
|
||||
vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> result;
|
||||
result.reserve(input_dialog_ids.size());
|
||||
for (auto input_dialog_id : input_dialog_ids) {
|
||||
auto input_peer = input_dialog_id.get_input_peer();
|
||||
if (input_peer != nullptr) {
|
||||
result.push_back(telegram_api::make_object<telegram_api::inputDialogPeer>(std::move(input_peer)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<telegram_api::object_ptr<telegram_api::InputPeer>> InputDialogId::get_input_peers(
|
||||
const vector<InputDialogId> &input_dialog_ids) {
|
||||
vector<telegram_api::object_ptr<telegram_api::InputPeer>> result;
|
||||
result.reserve(input_dialog_ids.size());
|
||||
for (auto input_dialog_id : input_dialog_ids) {
|
||||
auto input_peer = input_dialog_id.get_input_peer();
|
||||
CHECK(input_peer != nullptr);
|
||||
result.push_back(std::move(input_peer));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
tl_object_ptr<telegram_api::InputPeer> InputDialogId::get_input_peer() const {
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
return make_tl_object<telegram_api::inputPeerUser>(dialog_id.get_user_id().get(), access_hash);
|
||||
case DialogType::Chat:
|
||||
return make_tl_object<telegram_api::inputPeerChat>(dialog_id.get_chat_id().get());
|
||||
case DialogType::Channel:
|
||||
return make_tl_object<telegram_api::inputPeerChannel>(dialog_id.get_channel_id().get(), access_hash);
|
||||
case DialogType::SecretChat:
|
||||
case DialogType::None:
|
||||
return nullptr;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDialogId::are_equivalent(const vector<InputDialogId> &lhs, const vector<InputDialogId> &rhs) {
|
||||
auto lhs_it = lhs.begin();
|
||||
auto rhs_it = rhs.begin();
|
||||
while (lhs_it != lhs.end() || rhs_it != rhs.end()) {
|
||||
while (lhs_it != lhs.end() && lhs_it->get_dialog_id().get_type() == DialogType::SecretChat) {
|
||||
++lhs_it;
|
||||
}
|
||||
while (rhs_it != rhs.end() && rhs_it->get_dialog_id().get_type() == DialogType::SecretChat) {
|
||||
++rhs_it;
|
||||
}
|
||||
if (lhs_it == lhs.end() || rhs_it == rhs.end()) {
|
||||
break;
|
||||
}
|
||||
if (lhs_it->get_dialog_id() != rhs_it->get_dialog_id()) {
|
||||
return false;
|
||||
}
|
||||
++lhs_it;
|
||||
++rhs_it;
|
||||
}
|
||||
return lhs_it == lhs.end() && rhs_it == rhs.end();
|
||||
}
|
||||
|
||||
bool InputDialogId::contains(const vector<InputDialogId> &input_dialog_ids, DialogId dialog_id) {
|
||||
for (auto &input_dialog_id : input_dialog_ids) {
|
||||
if (input_dialog_id.get_dialog_id() == dialog_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace td
|
80
td/telegram/InputDialogId.h
Normal file
80
td/telegram/InputDialogId.h
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// 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/DialogId.h"
|
||||
#include "td/telegram/telegram_api.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace td {
|
||||
|
||||
class InputDialogId {
|
||||
DialogId dialog_id;
|
||||
int64 access_hash = 0;
|
||||
|
||||
public:
|
||||
InputDialogId() = default;
|
||||
|
||||
explicit InputDialogId(DialogId dialog_id) : dialog_id(dialog_id) {
|
||||
}
|
||||
|
||||
explicit InputDialogId(const tl_object_ptr<telegram_api::InputPeer> &input_peer);
|
||||
|
||||
static vector<InputDialogId> get_input_dialog_ids(
|
||||
const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
|
||||
std::unordered_set<DialogId, DialogIdHash> *added_dialog_ids = nullptr);
|
||||
|
||||
static vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> get_input_dialog_peers(
|
||||
const vector<InputDialogId> &input_dialog_ids);
|
||||
|
||||
static vector<telegram_api::object_ptr<telegram_api::InputPeer>> get_input_peers(
|
||||
const vector<InputDialogId> &input_dialog_ids);
|
||||
|
||||
static bool are_equivalent(const vector<InputDialogId> &lhs, const vector<InputDialogId> &rhs);
|
||||
|
||||
static bool contains(const vector<InputDialogId> &input_dialog_ids, DialogId dialog_id);
|
||||
|
||||
bool operator==(const InputDialogId &other) const {
|
||||
return dialog_id == other.dialog_id && access_hash == other.access_hash;
|
||||
}
|
||||
|
||||
bool operator!=(const InputDialogId &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool is_valid() const {
|
||||
return dialog_id.is_valid();
|
||||
}
|
||||
|
||||
DialogId get_dialog_id() const {
|
||||
return dialog_id;
|
||||
}
|
||||
|
||||
tl_object_ptr<telegram_api::InputPeer> get_input_peer() const;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
dialog_id.store(storer);
|
||||
storer.store_long(access_hash);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
dialog_id.parse(parser);
|
||||
access_hash = parser.fetch_long();
|
||||
}
|
||||
};
|
||||
|
||||
inline StringBuilder &operator<<(StringBuilder &string_builder, InputDialogId input_dialog_id) {
|
||||
return string_builder << "input " << input_dialog_id.get_dialog_id();
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -4,11 +4,15 @@
|
||||
// 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 managed(push, off)
|
||||
#include "td/telegram/Log.h"
|
||||
#pragma managed(pop)
|
||||
|
||||
#include "td/utils/port/CxCli.h"
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include <cstdint>
|
||||
#pragma managed(pop)
|
||||
|
||||
namespace Telegram {
|
||||
namespace Td {
|
||||
|
@ -1473,8 +1473,10 @@ static Result<InputMessageContent> create_input_message_content(
|
||||
case td_api::inputMessageAnimation::ID: {
|
||||
auto input_animation = static_cast<td_api::inputMessageAnimation *>(input_message_content.get());
|
||||
|
||||
bool has_stickers = !sticker_file_ids.empty();
|
||||
td->animations_manager_->create_animation(
|
||||
file_id, string(), thumbnail, std::move(file_name), std::move(mime_type), input_animation->duration_,
|
||||
file_id, string(), thumbnail, PhotoSize(), has_stickers, std::move(sticker_file_ids), std::move(file_name),
|
||||
std::move(mime_type), input_animation->duration_,
|
||||
get_dimensions(input_animation->width_, input_animation->height_), false);
|
||||
|
||||
content = make_unique<MessageAnimation>(file_id, std::move(caption));
|
||||
@ -1562,9 +1564,8 @@ static Result<InputMessageContent> create_input_message_content(
|
||||
}
|
||||
case td_api::inputMessageSticker::ID: {
|
||||
auto input_sticker = static_cast<td_api::inputMessageSticker *>(input_message_content.get());
|
||||
td->stickers_manager_->create_sticker(file_id, thumbnail,
|
||||
get_dimensions(input_sticker->width_, input_sticker->height_), nullptr,
|
||||
mime_type == "application/x-tgsticker", nullptr);
|
||||
td->stickers_manager_->create_sticker(
|
||||
file_id, thumbnail, get_dimensions(input_sticker->width_, input_sticker->height_), nullptr, false, nullptr);
|
||||
|
||||
content = make_unique<MessageSticker>(file_id);
|
||||
break;
|
||||
@ -1575,9 +1576,9 @@ static Result<InputMessageContent> create_input_message_content(
|
||||
ttl = input_video->ttl_;
|
||||
|
||||
bool has_stickers = !sticker_file_ids.empty();
|
||||
td->videos_manager_->create_video(file_id, string(), thumbnail, has_stickers, std::move(sticker_file_ids),
|
||||
std::move(file_name), std::move(mime_type), input_video->duration_,
|
||||
get_dimensions(input_video->width_, input_video->height_),
|
||||
td->videos_manager_->create_video(
|
||||
file_id, string(), thumbnail, PhotoSize(), has_stickers, std::move(sticker_file_ids), std::move(file_name),
|
||||
std::move(mime_type), input_video->duration_, get_dimensions(input_video->width_, input_video->height_),
|
||||
input_video->supports_streaming_, false);
|
||||
|
||||
content = make_unique<MessageVideo>(file_id, std::move(caption));
|
||||
@ -1840,6 +1841,9 @@ Result<InputMessageContent> get_input_message_content(
|
||||
r_file_id = td->file_manager_->get_input_file_id(FileType::Animation, input_message->animation_, dialog_id, false,
|
||||
is_secret, true);
|
||||
input_thumbnail = std::move(input_message->thumbnail_);
|
||||
if (!input_message->added_sticker_file_ids_.empty()) {
|
||||
sticker_file_ids = td->stickers_manager_->get_attached_sticker_file_ids(input_message->added_sticker_file_ids_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case td_api::inputMessageAudio::ID: {
|
||||
@ -3507,7 +3511,7 @@ static auto secret_to_telegram_document(secret_api::decryptedMessageMediaExterna
|
||||
thumbnails.push_back(secret_to_telegram<telegram_api::PhotoSize>(*from.thumb_));
|
||||
return make_tl_object<telegram_api::document>(
|
||||
telegram_api::document::THUMBS_MASK, from.id_, from.access_hash_, BufferSlice(), from.date_, from.mime_type_,
|
||||
from.size_, std::move(thumbnails), from.dc_id_, secret_to_telegram(from.attributes_));
|
||||
from.size_, std::move(thumbnails), Auto(), from.dc_id_, secret_to_telegram(from.attributes_));
|
||||
}
|
||||
|
||||
template <class ToT, class FromT>
|
||||
@ -4362,7 +4366,8 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
||||
}
|
||||
case MessageContentType::Document: {
|
||||
const MessageDocument *m = static_cast<const MessageDocument *>(content);
|
||||
return make_tl_object<td_api::messageDocument>(td->documents_manager_->get_document_object(m->file_id),
|
||||
return make_tl_object<td_api::messageDocument>(
|
||||
td->documents_manager_->get_document_object(m->file_id, PhotoFormat::Jpeg),
|
||||
get_formatted_text_object(m->caption));
|
||||
}
|
||||
case MessageContentType::Game: {
|
||||
@ -4700,6 +4705,20 @@ FileId get_message_content_thumbnail_file_id(const MessageContent *content, cons
|
||||
return FileId();
|
||||
}
|
||||
|
||||
FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td) {
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Animation:
|
||||
return td->animations_manager_->get_animation_animated_thumbnail_file_id(
|
||||
static_cast<const MessageAnimation *>(content)->file_id);
|
||||
case MessageContentType::Video:
|
||||
return td->videos_manager_->get_video_animated_thumbnail_file_id(
|
||||
static_cast<const MessageVideo *>(content)->file_id);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FileId();
|
||||
}
|
||||
|
||||
vector<FileId> get_message_content_file_ids(const MessageContent *content, const Td *td) {
|
||||
switch (content->get_type()) {
|
||||
case MessageContentType::Photo:
|
||||
@ -4720,6 +4739,10 @@ vector<FileId> get_message_content_file_ids(const MessageContent *content, const
|
||||
if (thumbnail_file_id.is_valid()) {
|
||||
result.push_back(thumbnail_file_id);
|
||||
}
|
||||
FileId animated_thumbnail_file_id = get_message_content_animated_thumbnail_file_id(content, td);
|
||||
if (animated_thumbnail_file_id.is_valid()) {
|
||||
result.push_back(animated_thumbnail_file_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case MessageContentType::Sticker:
|
||||
|
@ -199,6 +199,8 @@ void update_message_content_file_id_remote(MessageContent *content, FileId file_
|
||||
|
||||
FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td);
|
||||
|
||||
FileId get_message_content_animated_thumbnail_file_id(const MessageContent *content, const Td *td);
|
||||
|
||||
vector<FileId> get_message_content_file_ids(const MessageContent *content, const Td *td);
|
||||
|
||||
string get_message_content_search_text(const Td *td, const MessageContent *content);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,9 @@
|
||||
#include "td/telegram/DialogAdministrator.h"
|
||||
#include "td/telegram/DialogDate.h"
|
||||
#include "td/telegram/DialogDb.h"
|
||||
#include "td/telegram/DialogFilterId.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/DialogListId.h"
|
||||
#include "td/telegram/DialogLocation.h"
|
||||
#include "td/telegram/DialogParticipant.h"
|
||||
#include "td/telegram/DialogSource.h"
|
||||
@ -25,6 +27,7 @@
|
||||
#include "td/telegram/FolderId.h"
|
||||
#include "td/telegram/FullMessageId.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/InputDialogId.h"
|
||||
#include "td/telegram/MessageContentType.h"
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/MessagesDb.h"
|
||||
@ -73,6 +76,8 @@ namespace td {
|
||||
|
||||
struct BinlogEvent;
|
||||
|
||||
class DialogFilter;
|
||||
|
||||
class DraftMessage;
|
||||
|
||||
struct InputMessageContent;
|
||||
@ -297,6 +302,8 @@ class MessagesManager : public Actor {
|
||||
|
||||
void on_update_dialog_folder_id(DialogId dialog_id, FolderId folder_id);
|
||||
|
||||
void on_update_dialog_filters();
|
||||
|
||||
void on_update_service_notification(tl_object_ptr<telegram_api::updateServiceNotification> &&update,
|
||||
bool skip_new_entities, Promise<Unit> &&promise);
|
||||
|
||||
@ -442,7 +449,9 @@ class MessagesManager : public Actor {
|
||||
|
||||
void send_dialog_action(DialogId dialog_id, const tl_object_ptr<td_api::ChatAction> &action, Promise<Unit> &&promise);
|
||||
|
||||
void set_dialog_folder_id(DialogId dialog_id, FolderId folder_id, Promise<Unit> &&promise);
|
||||
vector<DialogListId> get_dialog_lists_to_add_dialog(DialogId dialog_id);
|
||||
|
||||
void add_dialog_to_list(DialogId dialog_id, DialogListId dialog_list_id, Promise<Unit> &&promise);
|
||||
|
||||
void set_dialog_photo(DialogId dialog_id, const tl_object_ptr<td_api::InputFile> &photo, Promise<Unit> &&promise);
|
||||
|
||||
@ -496,7 +505,12 @@ class MessagesManager : public Actor {
|
||||
|
||||
void load_dialogs(vector<DialogId> dialog_ids, Promise<Unit> &&promise);
|
||||
|
||||
vector<DialogId> get_dialogs(FolderId folder_id, DialogDate offset, int32 limit, bool force, Promise<Unit> &&promise);
|
||||
void load_dialog_filter(DialogFilterId dialog_id, bool force, Promise<Unit> &&promise);
|
||||
|
||||
void get_recommended_dialog_filters(Promise<td_api::object_ptr<td_api::recommendedChatFilters>> &&promise);
|
||||
|
||||
vector<DialogId> get_dialogs(DialogListId dialog_list_id, DialogDate offset, int32 limit, bool force,
|
||||
Promise<Unit> &&promise);
|
||||
|
||||
vector<DialogId> search_public_dialogs(const string &query, Promise<Unit> &&promise);
|
||||
|
||||
@ -546,6 +560,16 @@ class MessagesManager : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::messageLinkInfo> get_message_link_info_object(const MessageLinkInfo &info) const;
|
||||
|
||||
void create_dialog_filter(td_api::object_ptr<td_api::chatFilter> filter,
|
||||
Promise<td_api::object_ptr<td_api::chatFilterInfo>> &&promise);
|
||||
|
||||
void edit_dialog_filter(DialogFilterId dialog_filter_id, td_api::object_ptr<td_api::chatFilter> filter,
|
||||
Promise<td_api::object_ptr<td_api::chatFilterInfo>> &&promise);
|
||||
|
||||
void delete_dialog_filter(DialogFilterId dialog_filter_id, Promise<Unit> &&promise);
|
||||
|
||||
void reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, Promise<Unit> &&promise);
|
||||
|
||||
Status delete_dialog_reply_markup(DialogId dialog_id, MessageId message_id) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status set_dialog_draft_message(DialogId dialog_id,
|
||||
@ -553,15 +577,13 @@ class MessagesManager : public Actor {
|
||||
|
||||
void clear_all_draft_messages(bool exclude_secret_chats, Promise<Unit> &&promise);
|
||||
|
||||
void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned);
|
||||
|
||||
Status toggle_dialog_is_pinned(DialogId dialog_id, bool is_pinned) TD_WARN_UNUSED_RESULT;
|
||||
Status toggle_dialog_is_pinned(DialogListId dialog_list_id, DialogId dialog_id, bool is_pinned) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status toggle_dialog_is_marked_as_unread(DialogId dialog_id, bool is_marked_as_unread) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status toggle_dialog_silent_send_message(DialogId dialog_id, bool silent_send_message) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status set_pinned_dialogs(FolderId folder_id, vector<DialogId> dialog_ids) TD_WARN_UNUSED_RESULT;
|
||||
Status set_pinned_dialogs(DialogListId dialog_list_id, vector<DialogId> dialog_ids) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status set_dialog_client_data(DialogId dialog_id, string &&client_data) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
@ -608,6 +630,8 @@ class MessagesManager : public Actor {
|
||||
|
||||
static tl_object_ptr<td_api::chats> get_chats_object(const vector<DialogId> &dialogs);
|
||||
|
||||
tl_object_ptr<td_api::chatFilter> get_chat_filter_object(DialogFilterId dialog_filter_id) const;
|
||||
|
||||
tl_object_ptr<td_api::messages> get_dialog_history(DialogId dialog_id, MessageId from_message_id, int32 offset,
|
||||
int32 limit, int left_tries, bool only_local,
|
||||
Promise<Unit> &&promise);
|
||||
@ -715,6 +739,8 @@ class MessagesManager : public Actor {
|
||||
void get_login_url(DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access,
|
||||
Promise<td_api::object_ptr<td_api::httpUrl>> &&promise);
|
||||
|
||||
void on_authorization_success();
|
||||
|
||||
void before_get_difference();
|
||||
|
||||
void after_get_difference();
|
||||
@ -1055,7 +1081,9 @@ class MessagesManager : public Actor {
|
||||
uint64 read_history_logevent_id_generation = 0;
|
||||
uint64 set_folder_id_logevent_id = 0;
|
||||
uint64 set_folder_id_logevent_id_generation = 0;
|
||||
|
||||
FolderId folder_id;
|
||||
vector<DialogListId> dialog_list_ids; // TODO replace with mask
|
||||
|
||||
MessageId
|
||||
last_read_all_mentions_message_id; // all mentions with a message identifier not greater than it are implicitly read
|
||||
@ -1065,7 +1093,6 @@ class MessagesManager : public Actor {
|
||||
int32 last_clear_history_date = 0;
|
||||
MessageId last_clear_history_message_id;
|
||||
int64 order = DEFAULT_ORDER;
|
||||
int64 pinned_order = DEFAULT_ORDER;
|
||||
int32 delete_last_message_date = 0;
|
||||
MessageId deleted_last_message_id;
|
||||
int32 pending_last_message_date = 0;
|
||||
@ -1215,8 +1242,13 @@ class MessagesManager : public Actor {
|
||||
void parse(ParserT &parser);
|
||||
};
|
||||
|
||||
struct RecommendedDialogFilter {
|
||||
unique_ptr<DialogFilter> dialog_filter;
|
||||
string description;
|
||||
};
|
||||
|
||||
struct DialogList {
|
||||
FolderId folder_id;
|
||||
DialogListId dialog_list_id;
|
||||
bool is_message_unread_count_inited_ = false;
|
||||
bool is_dialog_unread_count_inited_ = false;
|
||||
bool need_unread_count_recalc_ = true;
|
||||
@ -1230,22 +1262,97 @@ class MessagesManager : public Actor {
|
||||
int32 server_dialog_total_count_ = -1;
|
||||
int32 secret_chat_total_count_ = -1;
|
||||
|
||||
// date of the last dialog in loaded the dialog list prefix
|
||||
DialogDate last_dialog_date_ = MIN_DIALOG_DATE; // in memory
|
||||
std::set<DialogDate> ordered_dialogs_; // all dialogs with date <= last_dialog_date_
|
||||
vector<Promise<Unit>> load_list_queries_;
|
||||
|
||||
std::set<DialogDate> ordered_server_dialogs_; // all known dialogs, including with default order
|
||||
std::unordered_map<DialogId, int64, DialogIdHash> pinned_dialog_id_orders_;
|
||||
vector<DialogDate> pinned_dialogs_;
|
||||
bool are_pinned_dialogs_inited_ = false;
|
||||
|
||||
DialogDate last_pinned_dialog_date_ = MIN_DIALOG_DATE; // in memory
|
||||
|
||||
// date of the last loaded dialog
|
||||
// min(folder1_last_dialog_date_, folder2_last_dialog_date, last_pinned_dialog_date_)
|
||||
DialogDate list_last_dialog_date_ = MIN_DIALOG_DATE; // in memory
|
||||
};
|
||||
|
||||
struct DialogFolder {
|
||||
FolderId folder_id;
|
||||
// date of the last loaded dialog in the folder
|
||||
DialogDate folder_last_dialog_date_{MAX_ORDINARY_DIALOG_ORDER, DialogId()}; // in memory
|
||||
|
||||
std::set<DialogDate> ordered_dialogs_; // all known dialogs, including with default order
|
||||
|
||||
// date of last known user/group/channel dialog in the right order
|
||||
DialogDate last_server_dialog_date_ = MIN_DIALOG_DATE;
|
||||
DialogDate last_loaded_database_dialog_date_ = MIN_DIALOG_DATE;
|
||||
DialogDate last_database_server_dialog_date_ = MIN_DIALOG_DATE;
|
||||
DialogDate last_server_dialog_date_{MAX_ORDINARY_DIALOG_ORDER, DialogId()};
|
||||
DialogDate last_loaded_database_dialog_date_{MAX_ORDINARY_DIALOG_ORDER, DialogId()};
|
||||
DialogDate last_database_server_dialog_date_{MAX_ORDINARY_DIALOG_ORDER, DialogId()};
|
||||
|
||||
MultiPromiseActor load_dialog_list_multipromise_{
|
||||
"LoadDialogListMultiPromiseActor"}; // should be defined before pending_on_get_dialogs_
|
||||
MultiPromiseActor load_folder_dialog_list_multipromise_{
|
||||
"LoadDialogListMultiPromiseActor"}; // must be defined before pending_on_get_dialogs_
|
||||
int32 load_dialog_list_limit_max_ = 0;
|
||||
};
|
||||
|
||||
class DialogListViewIterator {
|
||||
MessagesManager *messages_manager_;
|
||||
const DialogListId *dialog_list_id_;
|
||||
|
||||
public:
|
||||
DialogListViewIterator(MessagesManager *messages_manager, const DialogListId *dialog_list_id)
|
||||
: messages_manager_(messages_manager), dialog_list_id_(dialog_list_id) {
|
||||
}
|
||||
|
||||
DialogList &operator*() const {
|
||||
auto dialog_list_ptr = messages_manager_->get_dialog_list(*dialog_list_id_);
|
||||
CHECK(dialog_list_ptr != nullptr);
|
||||
return *dialog_list_ptr;
|
||||
}
|
||||
|
||||
bool operator!=(const DialogListViewIterator &other) const {
|
||||
return dialog_list_id_ != other.dialog_list_id_;
|
||||
}
|
||||
|
||||
void operator++() {
|
||||
dialog_list_id_++;
|
||||
}
|
||||
};
|
||||
|
||||
class DialogListView {
|
||||
MessagesManager *messages_manager_;
|
||||
// TODO can be optimized to store only mask of dialog lists
|
||||
vector<DialogListId> dialog_list_ids_;
|
||||
|
||||
public:
|
||||
DialogListView(MessagesManager *messages_manager, vector<DialogListId> dialog_list_ids)
|
||||
: messages_manager_(messages_manager), dialog_list_ids_(std::move(dialog_list_ids)) {
|
||||
}
|
||||
|
||||
DialogListViewIterator begin() {
|
||||
return DialogListViewIterator(messages_manager_, dialog_list_ids_.empty() ? nullptr : &dialog_list_ids_[0]);
|
||||
}
|
||||
|
||||
DialogListViewIterator end() {
|
||||
return DialogListViewIterator(
|
||||
messages_manager_, dialog_list_ids_.empty() ? nullptr : &dialog_list_ids_[0] + dialog_list_ids_.size());
|
||||
}
|
||||
};
|
||||
|
||||
struct DialogPositionInList {
|
||||
int64 order = DEFAULT_ORDER;
|
||||
int64 private_order = 0;
|
||||
int64 public_order = 0;
|
||||
bool is_pinned = false;
|
||||
bool is_sponsored = false;
|
||||
|
||||
int32 total_dialog_count = 0;
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogPositionInList &order) {
|
||||
return string_builder << "order = " << order.order << ", private_order = " << order.private_order
|
||||
<< ", public_order = " << order.public_order << ", is_pinned = " << order.is_pinned
|
||||
<< ", is_sponsored = " << order.is_sponsored
|
||||
<< ", total_dialog_count = " << order.total_dialog_count;
|
||||
}
|
||||
};
|
||||
|
||||
class MessagesIteratorBase {
|
||||
vector<const Message *> stack_;
|
||||
|
||||
@ -1426,6 +1533,8 @@ class MessagesManager : public Actor {
|
||||
class UpdateDialogNotificationSettingsOnServerLogEvent;
|
||||
class UpdateScopeNotificationSettingsOnServerLogEvent;
|
||||
|
||||
class DialogFiltersLogEvent;
|
||||
|
||||
static constexpr size_t MAX_GROUPED_MESSAGES = 10; // server side limit
|
||||
static constexpr int32 MAX_GET_DIALOGS = 100; // server side limit
|
||||
static constexpr int32 MAX_GET_HISTORY = 100; // server side limit
|
||||
@ -1437,9 +1546,15 @@ class MessagesManager : public Actor {
|
||||
static constexpr int32 MAX_RECENT_FOUND_DIALOGS = 20; // some reasonable value
|
||||
static constexpr size_t MAX_TITLE_LENGTH = 128; // server side limit for chat title
|
||||
static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat description
|
||||
static constexpr size_t MAX_DIALOG_FILTER_TITLE_LENGTH = 12; // server side limit for dialog filter title
|
||||
static constexpr int32 MAX_PRIVATE_MESSAGE_TTL = 60; // server side limit
|
||||
static constexpr int32 MAX_DIALOG_FILTERS = 10; // server side limit
|
||||
static constexpr int32 DIALOG_FILTERS_CACHE_TIME = 86400;
|
||||
|
||||
static constexpr int64 SPONSORED_DIALOG_ORDER = static_cast<int64>(2147483647) << 32;
|
||||
static constexpr int32 MIN_PINNED_DIALOG_DATE = 2147000000; // some big date
|
||||
static constexpr int32 MAX_PRIVATE_MESSAGE_TTL = 60; // server side limit
|
||||
static constexpr int64 MAX_ORDINARY_DIALOG_ORDER =
|
||||
9221294780217032704; // == get_dialog_order(MessageId(), MIN_PINNED_DIALOG_DATE - 1)
|
||||
|
||||
static constexpr int32 UPDATE_CHANNEL_TO_LONG_FLAG_HAS_PTS = 1 << 0;
|
||||
|
||||
@ -1492,6 +1607,10 @@ class MessagesManager : public Actor {
|
||||
|
||||
bool is_dialog_mention_notifications_disabled(const Dialog *d) const;
|
||||
|
||||
int64 get_dialog_pinned_order(DialogListId dialog_list_id, DialogId dialog_id) const;
|
||||
|
||||
static int64 get_dialog_pinned_order(const DialogList *list, DialogId dialog_id);
|
||||
|
||||
void open_dialog(Dialog *d);
|
||||
|
||||
void close_dialog(Dialog *d);
|
||||
@ -1775,11 +1894,15 @@ class MessagesManager : public Actor {
|
||||
|
||||
void try_reuse_notification_group(NotificationGroupInfo &group_info);
|
||||
|
||||
void load_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise<Unit> &&promise);
|
||||
void load_dialog_list(DialogList &list, int32 limit, Promise<Unit> &&promise);
|
||||
|
||||
void load_dialog_list_from_database(FolderId folder_id, int32 limit, Promise<Unit> &&promise);
|
||||
void load_folder_dialog_list(FolderId folder_id, int32 limit, bool only_local, Promise<Unit> &&promise);
|
||||
|
||||
void preload_dialog_list(FolderId folderId);
|
||||
void on_load_folder_dialog_list_fail(FolderId folder_id, Status error);
|
||||
|
||||
void load_folder_dialog_list_from_database(FolderId folder_id, int32 limit, Promise<Unit> &&promise);
|
||||
|
||||
void preload_folder_dialog_list(FolderId folderId);
|
||||
|
||||
static void invalidate_message_indexes(Dialog *d);
|
||||
|
||||
@ -1918,9 +2041,13 @@ class MessagesManager : public Actor {
|
||||
|
||||
void send_update_chat_last_message_impl(const Dialog *d, const char *source) const;
|
||||
|
||||
void send_update_unread_message_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source);
|
||||
void send_update_chat_filters();
|
||||
|
||||
void send_update_unread_chat_count(FolderId folder_id, DialogId dialog_id, bool force, const char *source);
|
||||
void send_update_unread_message_count(DialogList &list, DialogId dialog_id, bool force, const char *source,
|
||||
bool from_database = false);
|
||||
|
||||
void send_update_unread_chat_count(DialogList &list, DialogId dialog_id, bool force, const char *source,
|
||||
bool from_database = false);
|
||||
|
||||
void send_update_chat_read_inbox(const Dialog *d, bool force, const char *source);
|
||||
|
||||
@ -1928,12 +2055,10 @@ class MessagesManager : public Actor {
|
||||
|
||||
void send_update_chat_unread_mention_count(const Dialog *d);
|
||||
|
||||
void send_update_chat_source(const Dialog *d) const;
|
||||
void send_update_chat_position(DialogListId dialog_list_id, const Dialog *d, const char *source) const;
|
||||
|
||||
void send_update_chat_online_member_count(DialogId dialog_id, int32 online_member_count) const;
|
||||
|
||||
void send_update_chat_chat_list(const Dialog *d) const;
|
||||
|
||||
void send_update_secret_chats_with_user_action_bar(const Dialog *d) const;
|
||||
|
||||
void send_update_chat_action_bar(const Dialog *d);
|
||||
@ -1964,19 +2089,23 @@ class MessagesManager : public Actor {
|
||||
|
||||
int32 get_dialog_total_count(const DialogList &list) const;
|
||||
|
||||
void repair_server_dialog_total_count(FolderId folder_id);
|
||||
void repair_server_dialog_total_count(DialogListId dialog_list_id);
|
||||
|
||||
void repair_secret_chat_total_count(FolderId folder_id);
|
||||
void repair_secret_chat_total_count(DialogListId dialog_list_id);
|
||||
|
||||
void on_get_secret_chat_total_count(FolderId folder_id, int32 total_count);
|
||||
void on_get_secret_chat_total_count(DialogListId dialog_list_id, int32 total_count);
|
||||
|
||||
void recalc_unread_count(FolderId folder_id);
|
||||
void recalc_unread_count(DialogListId dialog_list_id, int32 old_dialog_total_count = -1);
|
||||
|
||||
td_api::object_ptr<td_api::updateChatFilters> get_update_chat_filters_object() const;
|
||||
|
||||
td_api::object_ptr<td_api::updateUnreadMessageCount> get_update_unread_message_count_object(
|
||||
const DialogList &list) const;
|
||||
|
||||
td_api::object_ptr<td_api::updateUnreadChatCount> get_update_unread_chat_count_object(const DialogList &list) const;
|
||||
|
||||
void save_unread_chat_count(const DialogList &list);
|
||||
|
||||
void set_dialog_last_read_inbox_message_id(Dialog *d, MessageId message_id, int32 server_unread_count,
|
||||
int32 local_unread_count, bool force_update, const char *source);
|
||||
|
||||
@ -1996,11 +2125,14 @@ class MessagesManager : public Actor {
|
||||
|
||||
void set_dialog_is_empty(Dialog *d, const char *source);
|
||||
|
||||
static int32 get_pinned_dialogs_limit(FolderId folder_id);
|
||||
static int32 get_pinned_dialogs_limit(DialogListId dialog_list_id);
|
||||
|
||||
static vector<DialogId> remove_secret_chat_dialog_ids(vector<DialogId> dialog_ids);
|
||||
|
||||
void set_dialog_is_pinned(Dialog *d, bool is_pinned);
|
||||
void set_dialog_is_pinned(DialogId dialog_id, bool is_pinned);
|
||||
|
||||
bool set_dialog_is_pinned(DialogListId dialog_list_id, Dialog *d, bool is_pinned,
|
||||
bool need_update_dialog_lists = true);
|
||||
|
||||
void set_dialog_is_marked_as_unread(Dialog *d, bool is_marked_as_unread);
|
||||
|
||||
@ -2045,10 +2177,14 @@ class MessagesManager : public Actor {
|
||||
bool update_scope_notification_settings(NotificationSettingsScope scope, ScopeNotificationSettings *current_settings,
|
||||
const ScopeNotificationSettings &new_settings);
|
||||
|
||||
void update_dialog_unmute_timeout(Dialog *d, bool old_use_default, int32 old_mute_until, bool new_use_default,
|
||||
void schedule_dialog_unmute(DialogId dialog_id, bool use_default, int32 mute_until);
|
||||
|
||||
void update_dialog_unmute_timeout(Dialog *d, bool &old_use_default, int32 &old_mute_until, bool new_use_default,
|
||||
int32 new_mute_until);
|
||||
|
||||
void update_scope_unmute_timeout(NotificationSettingsScope scope, int32 old_mute_until, int32 new_mute_until);
|
||||
void schedule_scope_unmute(NotificationSettingsScope scope, int32 mute_until);
|
||||
|
||||
void update_scope_unmute_timeout(NotificationSettingsScope scope, int32 &old_mute_until, int32 new_mute_until);
|
||||
|
||||
void on_dialog_unmute(DialogId dialog_id);
|
||||
|
||||
@ -2086,10 +2222,6 @@ class MessagesManager : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::ChatType> get_chat_type_object(DialogId dialog_id) const;
|
||||
|
||||
td_api::object_ptr<td_api::ChatList> get_chat_list_object(const Dialog *d) const;
|
||||
|
||||
static td_api::object_ptr<td_api::ChatList> get_chat_list_object(FolderId folder_id);
|
||||
|
||||
td_api::object_ptr<td_api::ChatActionBar> get_chat_action_bar_object(const Dialog *d) const;
|
||||
|
||||
td_api::object_ptr<td_api::chat> get_chat_object(const Dialog *d, int64 real_order = DEFAULT_ORDER) const;
|
||||
@ -2111,15 +2243,107 @@ class MessagesManager : public Actor {
|
||||
|
||||
void send_search_public_dialogs_query(const string &query, Promise<Unit> &&promise);
|
||||
|
||||
vector<DialogId> get_pinned_dialog_ids(FolderId folder_id) const;
|
||||
vector<DialogId> get_pinned_dialog_ids(DialogListId dialog_list_id) const;
|
||||
|
||||
void reload_pinned_dialogs(FolderId folder_id, Promise<Unit> &&promise);
|
||||
void reload_pinned_dialogs(DialogListId dialog_list_id, Promise<Unit> &&promise);
|
||||
|
||||
double get_dialog_filters_cache_time() const;
|
||||
|
||||
void schedule_dialog_filters_reload(double timeout);
|
||||
|
||||
static void on_reload_dialog_filters_timeout(void *messages_manager_ptr);
|
||||
|
||||
void reload_dialog_filters();
|
||||
|
||||
void on_get_dialog_filters(Result<vector<tl_object_ptr<telegram_api::dialogFilter>>> r_filters, bool dummy);
|
||||
|
||||
bool need_synchronize_dialog_filters() const;
|
||||
|
||||
void synchronize_dialog_filters();
|
||||
|
||||
void update_dialogs_hints(const Dialog *d);
|
||||
void update_dialogs_hints_rating(const Dialog *d);
|
||||
|
||||
DialogList &get_dialog_list(FolderId folder_id);
|
||||
const DialogList *get_dialog_list(FolderId folder_id) const;
|
||||
td_api::object_ptr<td_api::chatFilter> get_chat_filter_object(const DialogFilter *filter) const;
|
||||
|
||||
void load_dialog_filter(const DialogFilter *filter, bool force, Promise<Unit> &&promise);
|
||||
|
||||
void on_get_recommended_dialog_filters(Result<vector<tl_object_ptr<telegram_api::dialogFilterSuggested>>> result,
|
||||
Promise<td_api::object_ptr<td_api::recommendedChatFilters>> &&promise);
|
||||
|
||||
void on_load_recommended_dialog_filters(Result<Unit> &&result, vector<RecommendedDialogFilter> &&filters,
|
||||
Promise<td_api::object_ptr<td_api::recommendedChatFilters>> &&promise);
|
||||
|
||||
InputDialogId get_input_dialog_id(DialogId dialog_id) const;
|
||||
|
||||
void sort_dialog_filter_input_dialog_ids(DialogFilter *dialog_filter) const;
|
||||
|
||||
Result<unique_ptr<DialogFilter>> create_dialog_filter(DialogFilterId dialog_filter_id,
|
||||
td_api::object_ptr<td_api::chatFilter> filter);
|
||||
|
||||
void update_dialog_filter_on_server(unique_ptr<DialogFilter> &&dialog_filter);
|
||||
|
||||
void on_update_dialog_filter(unique_ptr<DialogFilter> dialog_filter, Status result);
|
||||
|
||||
void delete_dialog_filter_on_server(DialogFilterId dialog_filter_id);
|
||||
|
||||
void on_delete_dialog_filter(DialogFilterId dialog_filter_id, Status result);
|
||||
|
||||
void reorder_dialog_filters_on_server(vector<DialogFilterId> dialog_filter_ids);
|
||||
|
||||
void on_reorder_dialog_filters(vector<DialogFilterId> dialog_filter_ids, Status result);
|
||||
|
||||
void save_dialog_filters();
|
||||
|
||||
void add_dialog_filter(unique_ptr<DialogFilter> dialog_filter, bool at_beginning, const char *source);
|
||||
|
||||
void edit_dialog_filter(unique_ptr<DialogFilter> new_dialog_filter, const char *source);
|
||||
|
||||
void delete_dialog_filter(DialogFilterId dialog_filter_id, const char *source);
|
||||
|
||||
static bool set_dialog_filters_order(vector<unique_ptr<DialogFilter>> &dialog_filters,
|
||||
vector<DialogFilterId> dialog_filter_ids);
|
||||
|
||||
const DialogFilter *get_server_dialog_filter(DialogFilterId dialog_filter_id) const;
|
||||
|
||||
DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id);
|
||||
const DialogFilter *get_dialog_filter(DialogFilterId dialog_filter_id) const;
|
||||
|
||||
static vector<DialogFilterId> get_dialog_filter_ids(const vector<unique_ptr<DialogFilter>> &dialog_filters);
|
||||
|
||||
static vector<FolderId> get_dialog_filter_folder_ids(const DialogFilter *filter);
|
||||
|
||||
vector<FolderId> get_dialog_list_folder_ids(const DialogList &list) const;
|
||||
|
||||
bool has_dialogs_from_folder(const DialogList &list, const DialogFolder &folder) const;
|
||||
|
||||
bool is_dialog_in_list(const Dialog *d, DialogListId dialog_list_id) const;
|
||||
|
||||
void add_dialog_to_list(Dialog *d, DialogListId dialog_list_id) const;
|
||||
|
||||
void remove_dialog_from_list(Dialog *d, DialogListId dialog_list_id) const;
|
||||
|
||||
bool need_dialog_in_filter(const Dialog *d, const DialogFilter *filter) const;
|
||||
|
||||
bool need_dialog_in_list(const Dialog *d, const DialogList &list) const;
|
||||
|
||||
static bool need_send_update_chat_position(const DialogPositionInList &old_position,
|
||||
const DialogPositionInList &new_position);
|
||||
|
||||
DialogPositionInList get_dialog_position_in_list(const DialogList *list, const Dialog *d, bool actual = false) const;
|
||||
|
||||
std::unordered_map<DialogListId, DialogPositionInList, DialogListIdHash> get_dialog_positions(const Dialog *d) const;
|
||||
|
||||
vector<DialogListId> get_dialog_list_ids(const Dialog *d) const;
|
||||
DialogListView get_dialog_lists(const Dialog *d);
|
||||
|
||||
DialogList &add_dialog_list(DialogListId dialog_list_id);
|
||||
|
||||
DialogList *get_dialog_list(DialogListId dialog_list_id);
|
||||
const DialogList *get_dialog_list(DialogListId dialog_list_id) const;
|
||||
|
||||
DialogFolder *get_dialog_folder(FolderId folder_id);
|
||||
const DialogFolder *get_dialog_folder(FolderId folder_id) const;
|
||||
|
||||
std::pair<int32, vector<DialogParticipant>> search_private_chat_participants(UserId my_user_id, UserId peer_user_id,
|
||||
const string &query, int32 limit,
|
||||
@ -2205,6 +2429,7 @@ class MessagesManager : public Actor {
|
||||
void loop() override;
|
||||
void tear_down() override;
|
||||
|
||||
void create_folders();
|
||||
void init();
|
||||
|
||||
void ttl_db_loop_start(double server_now);
|
||||
@ -2270,15 +2495,13 @@ class MessagesManager : public Actor {
|
||||
|
||||
bool is_dialog_sponsored(const Dialog *d) const;
|
||||
|
||||
int64 get_dialog_private_order(FolderId folder_id, const Dialog *d) const;
|
||||
int64 get_dialog_base_order(const Dialog *d) const;
|
||||
|
||||
int64 get_dialog_private_order(const DialogList *list, const Dialog *d) const;
|
||||
|
||||
int64 get_dialog_public_order(FolderId folder_id, const Dialog *d) const;
|
||||
td_api::object_ptr<td_api::chatPosition> get_chat_position_object(DialogListId dialog_list_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;
|
||||
vector<td_api::object_ptr<td_api::chatPosition>> get_chat_positions_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);
|
||||
@ -2308,14 +2531,26 @@ class MessagesManager : public Actor {
|
||||
|
||||
bool is_removed_from_dialog_list(const Dialog *d) const;
|
||||
|
||||
void update_dialog_pos(Dialog *d, const char *source, bool need_send_update_chat_order = true,
|
||||
void update_dialog_pos(Dialog *d, const char *source, bool need_send_update = true,
|
||||
bool is_loaded_from_database = false);
|
||||
|
||||
bool set_dialog_order(Dialog *d, int64 new_order, bool need_send_update_chat_order, bool is_loaded_from_database,
|
||||
bool set_dialog_order(Dialog *d, int64 new_order, bool need_send_update, bool is_loaded_from_database,
|
||||
const char *source);
|
||||
|
||||
void update_dialog_lists(Dialog *d,
|
||||
std::unordered_map<DialogListId, DialogPositionInList, DialogListIdHash> &&old_positions,
|
||||
bool need_send_update, bool is_loaded_from_database, const char *source);
|
||||
|
||||
void update_last_dialog_date(FolderId folder_id);
|
||||
|
||||
bool do_update_list_last_pinned_dialog_date(DialogList &list) const;
|
||||
|
||||
void update_list_last_pinned_dialog_date(DialogList &list);
|
||||
|
||||
bool do_update_list_last_dialog_date(DialogList &list, const vector<FolderId> &folder_ids) const;
|
||||
|
||||
void update_list_last_dialog_date(DialogList &list);
|
||||
|
||||
void load_notification_settings();
|
||||
|
||||
void set_get_difference_timeout(double timeout);
|
||||
@ -2375,7 +2610,7 @@ class MessagesManager : public Actor {
|
||||
|
||||
static void on_update_dialog_online_member_count_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int);
|
||||
|
||||
static void on_preload_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int);
|
||||
static void on_preload_folder_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int);
|
||||
|
||||
void load_secret_thumbnail(FileId thumbnail_file_id);
|
||||
|
||||
@ -2683,12 +2918,24 @@ class MessagesManager : public Actor {
|
||||
|
||||
uint64 current_message_edit_generation_ = 0;
|
||||
|
||||
std::unordered_set<FolderId, FolderIdHash> postponed_unread_message_count_updates_;
|
||||
std::unordered_set<FolderId, FolderIdHash> postponed_unread_chat_count_updates_;
|
||||
std::unordered_set<DialogListId, DialogListIdHash> postponed_unread_message_count_updates_;
|
||||
std::unordered_set<DialogListId, DialogListIdHash> postponed_unread_chat_count_updates_;
|
||||
|
||||
int64 current_pinned_dialog_order_ = DEFAULT_ORDER;
|
||||
int64 current_pinned_dialog_order_ = static_cast<int64>(MIN_PINNED_DIALOG_DATE) << 32;
|
||||
|
||||
std::unordered_map<FolderId, DialogList, FolderIdHash> dialog_lists_;
|
||||
std::unordered_map<DialogListId, DialogList, DialogListIdHash> dialog_lists_;
|
||||
std::unordered_map<FolderId, DialogFolder, FolderIdHash> dialog_folders_;
|
||||
|
||||
bool are_dialog_filters_being_synchronized_ = false;
|
||||
bool are_dialog_filters_being_reloaded_ = false;
|
||||
bool need_dialog_filters_reload_ = false;
|
||||
bool disable_get_dialog_filter_ = false;
|
||||
bool is_update_chat_filters_sent_ = false;
|
||||
int32 dialog_filters_updated_date_ = 0;
|
||||
vector<unique_ptr<DialogFilter>> server_dialog_filters_;
|
||||
vector<unique_ptr<DialogFilter>> dialog_filters_;
|
||||
vector<RecommendedDialogFilter> recommended_dialog_filters_;
|
||||
vector<Promise<Unit>> dialog_filter_reload_queries_;
|
||||
|
||||
std::unordered_map<DialogId, string, DialogIdHash> active_get_channel_differencies_;
|
||||
std::unordered_map<DialogId, uint64, DialogIdHash> get_channel_difference_to_logevent_id_;
|
||||
@ -2705,7 +2952,9 @@ class MessagesManager : public Actor {
|
||||
MultiTimeout pending_send_dialog_action_timeout_{"PendingSendDialogActionTimeout"};
|
||||
MultiTimeout active_dialog_action_timeout_{"ActiveDialogActionTimeout"};
|
||||
MultiTimeout update_dialog_online_member_count_timeout_{"UpdateDialogOnlineMemberCountTimeout"};
|
||||
MultiTimeout preload_dialog_list_timeout_{"PreloadDialogListTimeout"};
|
||||
MultiTimeout preload_folder_dialog_list_timeout_{"PreloadFolderDialogListTimeout"};
|
||||
|
||||
Timeout reload_dialog_filters_timeout_;
|
||||
|
||||
Hints dialogs_hints_; // search dialogs by title and username
|
||||
|
||||
|
@ -3907,8 +3907,9 @@ void NotificationManager::before_get_chat_difference(NotificationGroupId group_i
|
||||
|
||||
VLOG(notifications) << "Before get chat difference in " << group_id;
|
||||
CHECK(group_id.is_valid());
|
||||
running_get_chat_difference_.insert(group_id.get());
|
||||
if (running_get_chat_difference_.insert(group_id.get()).second) {
|
||||
on_unreceived_notification_update_count_changed(1, group_id.get(), "before_get_chat_difference");
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::after_get_chat_difference(NotificationGroupId group_id) {
|
||||
|
@ -216,7 +216,7 @@ class NotificationTypePushMessage : public NotificationType {
|
||||
if (key == "MESSAGE_DOCUMENT") {
|
||||
auto documents_manager = G()->td().get_actor_unsafe()->documents_manager_.get();
|
||||
return td_api::make_object<td_api::pushMessageContentDocument>(
|
||||
documents_manager->get_document_object(document.file_id), is_pinned);
|
||||
documents_manager->get_document_object(document.file_id, PhotoFormat::Jpeg), is_pinned);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
|
@ -732,41 +732,7 @@ void PasswordManager::drop_cached_secret() {
|
||||
LOG(INFO) << "Drop passport secret";
|
||||
secret_ = optional<secure_storage::Secret>();
|
||||
}
|
||||
/*
|
||||
void PasswordManager::get_ton_wallet_password_salt(Promise<td_api::object_ptr<td_api::tonWalletPasswordSalt>> promise) {
|
||||
if (!ton_wallet_password_salt_.empty()) {
|
||||
return promise.set_value(td_api::make_object<td_api::tonWalletPasswordSalt>(ton_wallet_password_salt_));
|
||||
}
|
||||
|
||||
get_ton_wallet_password_salt_queries_.push_back(std::move(promise));
|
||||
if (get_ton_wallet_password_salt_queries_.size() == 1) {
|
||||
send_with_promise(G()->net_query_creator().create(telegram_api::wallet_getKeySecretSalt(false)),
|
||||
PromiseCreator::lambda([actor_id = actor_id(this)](Result<NetQueryPtr> r_query) mutable {
|
||||
auto r_result = fetch_result<telegram_api::wallet_getKeySecretSalt>(std::move(r_query));
|
||||
send_closure(actor_id, &PasswordManager::on_get_ton_wallet_password_salt, std::move(r_result));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void PasswordManager::on_get_ton_wallet_password_salt(
|
||||
Result<telegram_api::object_ptr<telegram_api::wallet_secretSalt>> result) {
|
||||
auto promises = std::move(get_ton_wallet_password_salt_queries_);
|
||||
reset_to_empty(get_ton_wallet_password_salt_queries_);
|
||||
CHECK(!promises.empty());
|
||||
|
||||
if (result.is_ok()) {
|
||||
ton_wallet_password_salt_ = result.ok()->salt_.as_slice().str();
|
||||
|
||||
for (auto &promise : promises) {
|
||||
promise.set_value(td_api::make_object<td_api::tonWalletPasswordSalt>(ton_wallet_password_salt_));
|
||||
}
|
||||
} else {
|
||||
for (auto &promise : promises) {
|
||||
promise.set_error(result.error().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
void PasswordManager::timeout_expired() {
|
||||
if (Time::now() >= secret_expire_date_) {
|
||||
drop_cached_secret();
|
||||
|
@ -89,8 +89,6 @@ class PasswordManager : public NetQueryCallback {
|
||||
|
||||
static TempPasswordState get_temp_password_state_sync();
|
||||
|
||||
// void get_ton_wallet_password_salt(Promise<td_api::object_ptr<td_api::tonWalletPasswordSalt>> promise);
|
||||
|
||||
private:
|
||||
static constexpr size_t MIN_NEW_SALT_SIZE = 8;
|
||||
static constexpr size_t MIN_NEW_SECURE_SALT_SIZE = 8;
|
||||
@ -162,9 +160,6 @@ class PasswordManager : public NetQueryCallback {
|
||||
|
||||
int32 last_code_length_ = 0;
|
||||
|
||||
// string ton_wallet_password_salt_;
|
||||
// vector<Promise<td_api::object_ptr<td_api::tonWalletPasswordSalt>>> get_ton_wallet_password_salt_queries_;
|
||||
|
||||
static Result<secure_storage::Secret> decrypt_secure_secret(
|
||||
Slice password, tl_object_ptr<telegram_api::SecurePasswordKdfAlgo> algo_ptr, Slice secret, int64 secret_id);
|
||||
|
||||
@ -191,8 +186,6 @@ class PasswordManager : public NetQueryCallback {
|
||||
Promise<TempPasswordState> promise);
|
||||
void on_finish_create_temp_password(Result<TempPasswordState> result, bool dummy);
|
||||
|
||||
// void on_get_ton_wallet_password_salt(Result<telegram_api::object_ptr<telegram_api::wallet_secretSalt>> result);
|
||||
|
||||
void on_result(NetQueryPtr query) override;
|
||||
|
||||
void start_up() override;
|
||||
|
@ -514,34 +514,7 @@ class GetBankCardInfoQuery : public Td::ResultHandler {
|
||||
promise_.set_error(std::move(status));
|
||||
}
|
||||
};
|
||||
/*
|
||||
class SendLiteRequestQuery : public Td::ResultHandler {
|
||||
Promise<td_api::object_ptr<td_api::tonLiteServerResponse>> promise_;
|
||||
|
||||
public:
|
||||
explicit SendLiteRequestQuery(Promise<td_api::object_ptr<td_api::tonLiteServerResponse>> &&promise)
|
||||
: promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(BufferSlice request) {
|
||||
send_query(G()->net_query_creator().create_unauth(telegram_api::wallet_sendLiteRequest(std::move(request))));
|
||||
}
|
||||
|
||||
void on_result(uint64 id, BufferSlice packet) override {
|
||||
auto result_ptr = fetch_result<telegram_api::wallet_sendLiteRequest>(packet);
|
||||
if (result_ptr.is_error()) {
|
||||
return on_error(id, result_ptr.move_as_error());
|
||||
}
|
||||
|
||||
auto response = result_ptr.move_as_ok();
|
||||
promise_.set_value(td_api::make_object<td_api::tonLiteServerResponse>(response->response_.as_slice().str()));
|
||||
}
|
||||
|
||||
void on_error(uint64 id, Status status) override {
|
||||
promise_.set_error(std::move(status));
|
||||
}
|
||||
};
|
||||
*/
|
||||
bool operator==(const LabeledPricePart &lhs, const LabeledPricePart &rhs) {
|
||||
return lhs.label == rhs.label && lhs.amount == rhs.amount;
|
||||
}
|
||||
@ -927,9 +900,5 @@ void delete_saved_credentials(Promise<Unit> &&promise) {
|
||||
void get_bank_card_info(const string &bank_card_number, Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise) {
|
||||
G()->td().get_actor_unsafe()->create_handler<GetBankCardInfoQuery>(std::move(promise))->send(bank_card_number);
|
||||
}
|
||||
/*
|
||||
void send_ton_lite_server_request(Slice request, Promise<td_api::object_ptr<td_api::tonLiteServerResponse>> &&promise) {
|
||||
G()->td().get_actor_unsafe()->create_handler<SendLiteRequestQuery>(std::move(promise))->send(BufferSlice{request});
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace td
|
||||
|
@ -156,6 +156,4 @@ void delete_saved_credentials(Promise<Unit> &&promise);
|
||||
|
||||
void get_bank_card_info(const string &bank_card_number, Promise<td_api::object_ptr<td_api::bankCardInfo>> &&promise);
|
||||
|
||||
// void send_ton_lite_server_request(Slice request, Promise<td_api::object_ptr<td_api::tonLiteServerResponse>> &&promise);
|
||||
|
||||
} // namespace td
|
||||
|
@ -95,6 +95,26 @@ td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::ThumbnailFormat> get_thumbnail_format_object(PhotoFormat format) {
|
||||
switch (format) {
|
||||
case PhotoFormat::Jpeg:
|
||||
return td_api::make_object<td_api::thumbnailFormatJpeg>();
|
||||
case PhotoFormat::Png:
|
||||
return td_api::make_object<td_api::thumbnailFormatPng>();
|
||||
case PhotoFormat::Webp:
|
||||
return td_api::make_object<td_api::thumbnailFormatWebp>();
|
||||
case PhotoFormat::Gif:
|
||||
return td_api::make_object<td_api::thumbnailFormatGif>();
|
||||
case PhotoFormat::Tgs:
|
||||
return td_api::make_object<td_api::thumbnailFormatTgs>();
|
||||
case PhotoFormat::Mpeg4:
|
||||
return td_api::make_object<td_api::thumbnailFormatMpeg4>();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) {
|
||||
switch (format) {
|
||||
case PhotoFormat::Jpeg:
|
||||
@ -103,8 +123,12 @@ static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat form
|
||||
return string_builder << "png";
|
||||
case PhotoFormat::Webp:
|
||||
return string_builder << "webp";
|
||||
case PhotoFormat::Gif:
|
||||
return string_builder << "gif";
|
||||
case PhotoFormat::Tgs:
|
||||
return string_builder << "tgs";
|
||||
case PhotoFormat::Mpeg4:
|
||||
return string_builder << "mp4";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return string_builder;
|
||||
@ -348,6 +372,30 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
PhotoSize get_video_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
|
||||
std::string file_reference, DcId dc_id, DialogId owner_dialog_id,
|
||||
tl_object_ptr<telegram_api::videoSize> &&size) {
|
||||
if (size == nullptr) {
|
||||
return {};
|
||||
}
|
||||
CHECK(size != nullptr);
|
||||
PhotoSize res;
|
||||
if (size->type_ != "v") {
|
||||
LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size);
|
||||
}
|
||||
res.type = static_cast<uint8>('v');
|
||||
res.dimensions = get_dimensions(size->w_, size->h_);
|
||||
res.size = size->size_;
|
||||
|
||||
if (source.get_type() == PhotoSizeSource::Type::Thumbnail) {
|
||||
source.thumbnail().thumbnail_type = res.type;
|
||||
}
|
||||
|
||||
res.file_id = register_photo(file_manager, source, id, access_hash, file_reference, std::move(size->location_),
|
||||
owner_dialog_id, res.size, dc_id, PhotoFormat::Mpeg4);
|
||||
return res;
|
||||
}
|
||||
|
||||
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
|
||||
tl_object_ptr<telegram_api::WebDocument> web_document_ptr) {
|
||||
if (web_document_ptr == nullptr) {
|
||||
@ -357,6 +405,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
|
||||
FileId file_id;
|
||||
vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
|
||||
int32 size = 0;
|
||||
string mime_type;
|
||||
switch (web_document_ptr->get_id()) {
|
||||
case telegram_api::webDocument::ID: {
|
||||
auto web_document = move_tl_object_as<telegram_api::webDocument>(web_document_ptr);
|
||||
@ -371,6 +420,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
|
||||
FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_,
|
||||
get_url_query_file_name(http_url.query_));
|
||||
size = web_document->size_;
|
||||
mime_type = std::move(web_document->mime_type_);
|
||||
attributes = std::move(web_document->attributes_);
|
||||
break;
|
||||
}
|
||||
@ -389,6 +439,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
|
||||
file_id = r_file_id.move_as_ok();
|
||||
|
||||
size = web_document->size_;
|
||||
mime_type = std::move(web_document->mime_type_);
|
||||
attributes = std::move(web_document->attributes_);
|
||||
break;
|
||||
}
|
||||
@ -396,6 +447,8 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
|
||||
UNREACHABLE();
|
||||
}
|
||||
CHECK(file_id.is_valid());
|
||||
bool is_animation = mime_type == "video/mp4";
|
||||
bool is_gif = mime_type == "image/gif";
|
||||
|
||||
Dimensions dimensions;
|
||||
for (auto &attribute : attributes) {
|
||||
@ -420,14 +473,29 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t
|
||||
}
|
||||
|
||||
PhotoSize s;
|
||||
s.type = file_type == FileType::Thumbnail ? 't' : 'u';
|
||||
s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'u'));
|
||||
s.dimensions = dimensions;
|
||||
s.size = size;
|
||||
s.file_id = file_id;
|
||||
return s;
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size) {
|
||||
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
|
||||
PhotoFormat format) {
|
||||
if (!photo_size.file_id.is_valid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (format == PhotoFormat::Jpeg && photo_size.type == 'g') {
|
||||
format = PhotoFormat::Gif;
|
||||
}
|
||||
|
||||
return td_api::make_object<td_api::thumbnail>(get_thumbnail_format_object(format), photo_size.dimensions.width,
|
||||
photo_size.dimensions.height,
|
||||
file_manager->get_file_object(photo_size.file_id));
|
||||
}
|
||||
|
||||
static tl_object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size) {
|
||||
if (photo_size == nullptr || !photo_size->file_id.is_valid()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -438,7 +506,7 @@ tl_object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager
|
||||
file_manager->get_file_object(photo_size->file_id), photo_size->dimensions.width, photo_size->dimensions.height);
|
||||
}
|
||||
|
||||
vector<td_api::object_ptr<td_api::photoSize>> get_photo_sizes_object(FileManager *file_manager,
|
||||
static vector<td_api::object_ptr<td_api::photoSize>> get_photo_sizes_object(FileManager *file_manager,
|
||||
const vector<PhotoSize> &photo_sizes) {
|
||||
auto sizes = transform(photo_sizes, [file_manager](const PhotoSize &photo_size) {
|
||||
return get_photo_size_object(file_manager, &photo_size);
|
||||
@ -558,7 +626,7 @@ Photo get_web_document_photo(FileManager *file_manager, tl_object_ptr<telegram_a
|
||||
DialogId owner_dialog_id) {
|
||||
PhotoSize s = get_web_document_photo_size(file_manager, FileType::Photo, owner_dialog_id, std::move(web_document));
|
||||
Photo photo;
|
||||
if (!s.file_id.is_valid()) {
|
||||
if (!s.file_id.is_valid() || s.type == 'v' || s.type == 'g') {
|
||||
photo.id = -2;
|
||||
} else {
|
||||
photo.id = 0;
|
||||
|
@ -90,7 +90,7 @@ bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs);
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo);
|
||||
|
||||
enum class PhotoFormat : int32 { Jpeg, Png, Webp, Tgs };
|
||||
enum class PhotoFormat : int32 { Jpeg, Png, Webp, Gif, Tgs, Mpeg4 };
|
||||
|
||||
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
|
||||
int32 width, int32 height);
|
||||
@ -98,11 +98,13 @@ Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSo
|
||||
int64 access_hash, string file_reference, DcId dc_id,
|
||||
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
|
||||
PhotoFormat format);
|
||||
PhotoSize get_video_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
|
||||
string file_reference, DcId dc_id, DialogId owner_dialog_id,
|
||||
tl_object_ptr<telegram_api::videoSize> &&size);
|
||||
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
|
||||
tl_object_ptr<telegram_api::WebDocument> web_document_ptr);
|
||||
td_api::object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size);
|
||||
vector<td_api::object_ptr<td_api::photoSize>> get_photo_sizes_object(FileManager *file_manager,
|
||||
const vector<PhotoSize> &photo_sizes);
|
||||
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
|
||||
PhotoFormat format);
|
||||
|
||||
bool operator==(const PhotoSize &lhs, const PhotoSize &rhs);
|
||||
bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs);
|
||||
|
@ -963,15 +963,17 @@ void PollManager::get_poll_voters(PollId poll_id, FullMessageId full_message_id,
|
||||
return;
|
||||
}
|
||||
|
||||
auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), poll_id, option_id, limit](
|
||||
Result<tl_object_ptr<telegram_api::messages_votesList>> &&result) {
|
||||
send_closure(actor_id, &PollManager::on_get_poll_voters, poll_id, option_id, limit, std::move(result));
|
||||
auto query_promise =
|
||||
PromiseCreator::lambda([actor_id = actor_id(this), poll_id, option_id, offset = voters.next_offset,
|
||||
limit](Result<tl_object_ptr<telegram_api::messages_votesList>> &&result) {
|
||||
send_closure(actor_id, &PollManager::on_get_poll_voters, poll_id, option_id, std::move(offset), limit,
|
||||
std::move(result));
|
||||
});
|
||||
td_->create_handler<GetPollVotersQuery>(std::move(query_promise))
|
||||
->send(poll_id, full_message_id, BufferSlice(poll->options[option_id].data), voters.next_offset, max(limit, 15));
|
||||
->send(poll_id, full_message_id, BufferSlice(poll->options[option_id].data), voters.next_offset, max(limit, 10));
|
||||
}
|
||||
|
||||
void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, int32 limit,
|
||||
void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, string offset, int32 limit,
|
||||
Result<tl_object_ptr<telegram_api::messages_votesList>> &&result) {
|
||||
auto poll = get_poll(poll_id);
|
||||
CHECK(poll != nullptr);
|
||||
@ -986,8 +988,16 @@ void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, int32 limi
|
||||
}
|
||||
|
||||
auto &voters = get_poll_option_voters(poll, poll_id, option_id);
|
||||
if (voters.next_offset != offset) {
|
||||
LOG(ERROR) << "Expected results for option " << option_id << " in " << poll_id << " with offset "
|
||||
<< voters.next_offset << ", but received with " << offset;
|
||||
return;
|
||||
}
|
||||
auto promises = std::move(voters.pending_queries);
|
||||
CHECK(!promises.empty());
|
||||
if (promises.empty()) {
|
||||
LOG(ERROR) << "Have no waiting promises for option " << option_id << " in " << poll_id;
|
||||
return;
|
||||
}
|
||||
if (result.is_error()) {
|
||||
for (auto &promise : promises) {
|
||||
promise.set_error(result.error().clone());
|
||||
@ -1305,6 +1315,26 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
LOG(ERROR) << "Receive poll " << poll_server->id_ << " instead of " << poll_id;
|
||||
return PollId();
|
||||
}
|
||||
constexpr size_t MAX_POLL_OPTIONS = 10; // server-side limit
|
||||
if (poll_server != nullptr &&
|
||||
(poll_server->answers_.size() <= 1 || poll_server->answers_.size() > 10 * MAX_POLL_OPTIONS)) {
|
||||
LOG(ERROR) << "Receive " << poll_id << " with wrong number of answers: " << to_string(poll_server);
|
||||
return PollId();
|
||||
}
|
||||
if (poll_server != nullptr) {
|
||||
std::unordered_set<Slice, SliceHash> option_data;
|
||||
for (auto &answer : poll_server->answers_) {
|
||||
if (answer->option_.empty()) {
|
||||
LOG(ERROR) << "Receive " << poll_id << " with an empty option data: " << to_string(poll_server);
|
||||
return PollId();
|
||||
}
|
||||
option_data.insert(answer->option_.as_slice());
|
||||
}
|
||||
if (option_data.size() != poll_server->answers_.size()) {
|
||||
LOG(ERROR) << "Receive " << poll_id << " with duplicate options: " << to_string(poll_server);
|
||||
return PollId();
|
||||
}
|
||||
}
|
||||
|
||||
auto poll = get_poll_force(poll_id);
|
||||
bool is_changed = false;
|
||||
@ -1323,13 +1353,15 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
|
||||
bool poll_server_is_closed = false;
|
||||
if (poll_server != nullptr) {
|
||||
if (poll->question != poll_server->question_) {
|
||||
poll->question = std::move(poll_server->question_);
|
||||
is_changed = true;
|
||||
string correct_option_data;
|
||||
if (poll->correct_option_id != -1) {
|
||||
CHECK(0 <= poll->correct_option_id && poll->correct_option_id < static_cast<int32>(poll->options.size()));
|
||||
correct_option_data = poll->options[poll->correct_option_id].data;
|
||||
}
|
||||
bool are_options_changed = false;
|
||||
if (poll->options.size() != poll_server->answers_.size()) {
|
||||
poll->options = get_poll_options(std::move(poll_server->answers_));
|
||||
is_changed = true;
|
||||
are_options_changed = true;
|
||||
} else {
|
||||
for (size_t i = 0; i < poll->options.size(); i++) {
|
||||
if (poll->options[i].text != poll_server->answers_[i]->text_) {
|
||||
@ -1340,9 +1372,35 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
poll->options[i].data = poll_server->answers_[i]->option_.as_slice().str();
|
||||
poll->options[i].voter_count = 0;
|
||||
poll->options[i].is_chosen = false;
|
||||
are_options_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (are_options_changed) {
|
||||
if (!correct_option_data.empty()) {
|
||||
poll->correct_option_id = -1;
|
||||
for (size_t i = 0; i < poll->options.size(); i++) {
|
||||
if (poll->options[i].data == correct_option_data) {
|
||||
poll->correct_option_id = static_cast<int32>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto it = poll_voters_.find(poll_id);
|
||||
if (it != poll_voters_.end()) {
|
||||
for (auto &voters : it->second) {
|
||||
auto promises = std::move(voters.pending_queries);
|
||||
for (auto &promise : promises) {
|
||||
promise.set_error(Status::Error(500, "The poll was changed"));
|
||||
}
|
||||
}
|
||||
poll_voters_.erase(it);
|
||||
}
|
||||
is_changed = true;
|
||||
}
|
||||
}
|
||||
if (poll->question != poll_server->question_) {
|
||||
poll->question = std::move(poll_server->question_);
|
||||
is_changed = true;
|
||||
}
|
||||
poll_server_is_closed = (poll_server->flags_ & telegram_api::poll::CLOSED_MASK) != 0;
|
||||
if (poll_server_is_closed && !poll->is_closed) {
|
||||
@ -1413,8 +1471,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
is_changed = true;
|
||||
}
|
||||
int32 correct_option_id = -1;
|
||||
for (size_t i = 0; i < poll_results->results_.size(); i++) {
|
||||
auto &poll_result = poll_results->results_[i];
|
||||
for (auto &poll_result : poll_results->results_) {
|
||||
Slice data = poll_result->option_.as_slice();
|
||||
for (size_t option_index = 0; option_index < poll->options.size(); option_index++) {
|
||||
auto &option = poll->options[option_index];
|
||||
@ -1432,9 +1489,9 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
||||
bool is_correct = (poll_result->flags_ & telegram_api::pollAnswerVoters::CORRECT_MASK) != 0;
|
||||
if (is_correct) {
|
||||
if (correct_option_id != -1) {
|
||||
LOG(ERROR) << "Receive more than 1 correct answers " << correct_option_id << " and " << i;
|
||||
LOG(ERROR) << "Receive more than 1 correct answers " << correct_option_id << " and " << option_index;
|
||||
}
|
||||
correct_option_id = static_cast<int32>(i);
|
||||
correct_option_id = static_cast<int32>(option_index);
|
||||
}
|
||||
} else {
|
||||
correct_option_id = poll->correct_option_id;
|
||||
|
@ -192,7 +192,7 @@ class PollManager : public Actor {
|
||||
|
||||
PollOptionVoters &get_poll_option_voters(const Poll *poll, PollId poll_id, int32 option_id);
|
||||
|
||||
void on_get_poll_voters(PollId poll_id, int32 option_id, int32 limit,
|
||||
void on_get_poll_voters(PollId poll_id, int32 option_id, string offset, int32 limit,
|
||||
Result<tl_object_ptr<telegram_api::messages_votesList>> &&result);
|
||||
|
||||
void do_stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup,
|
||||
|
@ -342,7 +342,7 @@ void SecretChatActor::send_message_impl(tl_object_ptr<secret_api::DecryptedMessa
|
||||
binlog_event->encrypted_message =
|
||||
create_encrypted_message(current_layer(), binlog_event->my_in_seq_no, binlog_event->my_out_seq_no, message)
|
||||
.move_as_ok();
|
||||
binlog_event->is_service = (flags & SendFlag::Push) == 0;
|
||||
binlog_event->need_notify_user = (flags & SendFlag::Push) == 0;
|
||||
binlog_event->is_external = (flags & SendFlag::External) != 0;
|
||||
if (message->get_id() == secret_api::decryptedMessageService::ID) {
|
||||
binlog_event->is_rewritable = false;
|
||||
@ -1460,12 +1460,11 @@ void SecretChatActor::inbound_loop(InboundMessageState *state, uint64 state_id)
|
||||
|
||||
NetQueryPtr SecretChatActor::create_net_query(const logevent::OutboundSecretMessage &message) {
|
||||
NetQueryPtr query;
|
||||
if (message.is_service) {
|
||||
if (message.need_notify_user) {
|
||||
CHECK(message.file.empty());
|
||||
query = create_net_query(QueryType::Message,
|
||||
telegram_api::messages_sendEncryptedService(get_input_chat(), message.random_id,
|
||||
message.encrypted_message.clone()));
|
||||
query->total_timeout_limit = 1000000000; // inf. We will re-sent it immediately anyway
|
||||
} else if (message.file.empty()) {
|
||||
query = create_net_query(
|
||||
QueryType::Message,
|
||||
@ -1476,6 +1475,9 @@ NetQueryPtr SecretChatActor::create_net_query(const logevent::OutboundSecretMess
|
||||
telegram_api::messages_sendEncryptedFile(get_input_chat(), message.random_id, message.encrypted_message.clone(),
|
||||
message.file.as_input_encrypted_file()));
|
||||
}
|
||||
if (!message.is_rewritable) {
|
||||
query->total_timeout_limit_ = 1000000000; // inf. We will re-sent it immediately anyway
|
||||
}
|
||||
if (message.is_external && context_->get_config_option_boolean("use_quick_ack")) {
|
||||
query->quick_ack_promise_ =
|
||||
PromiseCreator::lambda([actor_id = actor_id(this), random_id = message.random_id](
|
||||
@ -1547,7 +1549,7 @@ Status SecretChatActor::outbound_rewrite_with_empty(uint64 state_id) {
|
||||
MutableSlice data = state->message->encrypted_message.as_slice();
|
||||
CHECK(is_aligned_pointer<4>(data.data()));
|
||||
|
||||
// Rewrite with delete itself.
|
||||
// Rewrite with delete itself
|
||||
tl_object_ptr<secret_api::DecryptedMessage> message = secret_api::make_object<secret_api::decryptedMessageService>(
|
||||
state->message->random_id, secret_api::make_object<secret_api::decryptedMessageActionDeleteMessages>(
|
||||
std::vector<int64>{static_cast<int64>(state->message->random_id)}));
|
||||
@ -1558,7 +1560,7 @@ Status SecretChatActor::outbound_rewrite_with_empty(uint64 state_id) {
|
||||
LOG(INFO) << tag("crc", crc64(state->message->encrypted_message.as_slice()));
|
||||
state->message->is_rewritable = false;
|
||||
state->message->is_external = false;
|
||||
state->message->is_service = true;
|
||||
state->message->need_notify_user = false;
|
||||
state->message->file = logevent::EncryptedInputFile::from_input_encrypted_file(nullptr);
|
||||
binlog_rewrite(context_->binlog(), state->message->logevent_id(), LogEvent::HandlerType::SecretChats,
|
||||
create_storer(*state->message));
|
||||
|
@ -49,12 +49,12 @@ void SequenceDispatcher::check_timeout(Data &data) {
|
||||
if (data.state_ != State::Start) {
|
||||
return;
|
||||
}
|
||||
data.query_->total_timeout += data.total_timeout_;
|
||||
data.query_->total_timeout_ += data.total_timeout_;
|
||||
data.total_timeout_ = 0;
|
||||
if (data.query_->total_timeout > data.query_->total_timeout_limit) {
|
||||
if (data.query_->total_timeout_ > data.query_->total_timeout_limit_) {
|
||||
LOG(WARNING) << "Fail " << data.query_ << " to " << data.query_->source_ << " because total_timeout "
|
||||
<< data.query_->total_timeout << " is greater than total_timeout_limit "
|
||||
<< data.query_->total_timeout_limit;
|
||||
<< data.query_->total_timeout_ << " is greater than total_timeout_limit "
|
||||
<< data.query_->total_timeout_limit_;
|
||||
data.query_->set_error(Status::Error(
|
||||
429, PSLICE() << "Too Many Requests: retry after " << static_cast<int32>(data.last_timeout_ + 0.999)));
|
||||
data.state_ = State::Dummy;
|
||||
@ -69,7 +69,7 @@ void SequenceDispatcher::try_resend_query(Data &data, NetQueryPtr query) {
|
||||
data.state_ = State::Wait;
|
||||
wait_cnt_++;
|
||||
auto token = pos + id_offset_;
|
||||
// TODO: is query is ok, use NetQueryCallback::on_result
|
||||
// TODO: if query is ok, use NetQueryCallback::on_result
|
||||
auto promise = PromiseCreator::lambda([&, self = actor_shared(this, token)](NetQueryPtr query) mutable {
|
||||
if (!query.empty()) {
|
||||
send_closure(std::move(self), &SequenceDispatcher::on_resend_ok, std::move(query));
|
||||
@ -129,10 +129,10 @@ void SequenceDispatcher::on_result(NetQueryPtr query) {
|
||||
size_t pos = &data - &data_[0];
|
||||
CHECK(pos < data_.size());
|
||||
|
||||
if (query->last_timeout != 0) {
|
||||
if (query->last_timeout_ != 0) {
|
||||
for (auto i = pos + 1; i < data_.size(); i++) {
|
||||
data_[i].total_timeout_ += query->last_timeout;
|
||||
data_[i].last_timeout_ = query->last_timeout;
|
||||
data_[i].total_timeout_ += query->last_timeout_;
|
||||
data_[i].last_timeout_ = query->last_timeout_;
|
||||
check_timeout(data_[i]);
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ void SequenceDispatcher::loop() {
|
||||
invoke_after = data_[last_sent_i_].net_query_ref_;
|
||||
}
|
||||
data_[next_i_].query_->set_invoke_after(invoke_after);
|
||||
data_[next_i_].query_->last_timeout = 0;
|
||||
data_[next_i_].query_->last_timeout_ = 0;
|
||||
|
||||
VLOG(net_query) << "Send " << data_[next_i_].query_;
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/PathView.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Time.h"
|
||||
@ -1144,6 +1145,8 @@ void StickersManager::init() {
|
||||
if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "Init StickersManager";
|
||||
is_inited_ = true;
|
||||
|
||||
{
|
||||
// add animated emoji sticker set
|
||||
@ -1158,10 +1161,15 @@ void StickersManager::init() {
|
||||
animated_emoji_sticker_set.short_name_);
|
||||
}
|
||||
|
||||
dice_emojis_str_ = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀");
|
||||
dice_emojis_ = full_split(dice_emojis_str_, '\x01');
|
||||
for (auto &dice_emoji : dice_emojis_) {
|
||||
auto &animated_dice_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(dice_emoji));
|
||||
load_special_sticker_set_info_from_binlog(animated_dice_sticker_set);
|
||||
}
|
||||
send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
|
||||
|
||||
on_update_dice_success_values();
|
||||
|
||||
if (G()->parameters().use_file_db) {
|
||||
auto old_featured_sticker_set_count_str = G()->td_db()->get_binlog_pmc()->get("old_featured_sticker_set_count");
|
||||
@ -1348,10 +1356,18 @@ tl_object_ptr<td_api::sticker> StickersManager::get_sticker_object(FileId file_i
|
||||
: nullptr;
|
||||
|
||||
const PhotoSize &thumbnail = sticker->m_thumbnail.file_id.is_valid() ? sticker->m_thumbnail : sticker->s_thumbnail;
|
||||
auto thumbnail_format = PhotoFormat::Webp;
|
||||
if (!sticker->set_id.is_valid()) {
|
||||
auto file_view = td_->file_manager_->get_file_view(sticker->file_id);
|
||||
if (file_view.is_encrypted()) {
|
||||
// uploaded to secret chats stickers have JPEG thumbnail instead of server-generated WEBP
|
||||
thumbnail_format = PhotoFormat::Jpeg;
|
||||
}
|
||||
}
|
||||
auto thumbnail_object = get_thumbnail_object(td_->file_manager_.get(), thumbnail, thumbnail_format);
|
||||
return make_tl_object<td_api::sticker>(sticker->set_id.get(), sticker->dimensions.width, sticker->dimensions.height,
|
||||
sticker->alt, sticker->is_animated, sticker->is_mask, std::move(mask_position),
|
||||
get_photo_size_object(td_->file_manager_.get(), &thumbnail),
|
||||
td_->file_manager_->get_file_object(file_id));
|
||||
std::move(thumbnail_object), td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::stickers> StickersManager::get_stickers_object(const vector<FileId> &sticker_ids) const {
|
||||
@ -1421,9 +1437,10 @@ tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(Sticke
|
||||
emojis.push_back(make_tl_object<td_api::emojis>(vector<string>(it->second)));
|
||||
}
|
||||
}
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail,
|
||||
sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
|
||||
return make_tl_object<td_api::stickerSet>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name,
|
||||
get_photo_size_object(td_->file_manager_.get(), &sticker_set->thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed, std::move(stickers), std::move(emojis));
|
||||
}
|
||||
@ -1465,9 +1482,10 @@ tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_obje
|
||||
}
|
||||
}
|
||||
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail,
|
||||
sticker_set->is_animated ? PhotoFormat::Tgs : PhotoFormat::Webp);
|
||||
return make_tl_object<td_api::stickerSetInfo>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name,
|
||||
get_photo_size_object(td_->file_manager_.get(), &sticker_set->thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
sticker_set->is_animated, sticker_set->is_masks, sticker_set->is_viewed,
|
||||
sticker_set->was_loaded ? narrow_cast<int32>(sticker_set->sticker_ids.size()) : sticker_set->sticker_count,
|
||||
@ -1601,7 +1619,7 @@ std::pair<int64, FileId> StickersManager::on_get_sticker_document(
|
||||
thumbnail = std::move(photo_size.get<0>());
|
||||
break;
|
||||
} else {
|
||||
LOG(ERROR) << "Receive minithumbnail for a sticker";
|
||||
LOG(ERROR) << "Receive minithumbnail for a sticker " << sticker_id << ": " << to_string(sticker);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1714,6 +1732,7 @@ StickersManager::StickerSet *StickersManager::add_sticker_set(StickerSetId stick
|
||||
} else {
|
||||
CHECK(s->id == sticker_set_id);
|
||||
if (s->access_hash != access_hash) {
|
||||
LOG(INFO) << "Access hash of " << sticker_set_id << " changed";
|
||||
s->access_hash = access_hash;
|
||||
s->need_save_to_database = true;
|
||||
}
|
||||
@ -1955,23 +1974,21 @@ void StickersManager::create_sticker(FileId file_id, PhotoSize thumbnail, Dimens
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s->set_id.is_valid()) {
|
||||
s->is_animated = is_animated;
|
||||
}
|
||||
on_get_sticker(std::move(s), sticker != nullptr);
|
||||
}
|
||||
|
||||
bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) const {
|
||||
auto file_view = td_->file_manager_->get_file_view(sticker_file_id);
|
||||
if (is_secret) {
|
||||
const Sticker *sticker = get_sticker(sticker_file_id);
|
||||
|
||||
if (sticker == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file_view = td_->file_manager_->get_file_view(sticker_file_id);
|
||||
if (is_secret) {
|
||||
CHECK(sticker != nullptr);
|
||||
if (file_view.is_encrypted_secret()) {
|
||||
if (file_view.has_remote_location() && !sticker->s_thumbnail.file_id.is_valid()) {
|
||||
if (!file_view.encryption_key().empty() && file_view.has_remote_location() &&
|
||||
!sticker->s_thumbnail.file_id.is_valid()) {
|
||||
return true;
|
||||
}
|
||||
} else if (!file_view.is_encrypted()) {
|
||||
@ -1984,7 +2001,13 @@ bool StickersManager::has_input_media(FileId sticker_file_id, bool is_secret) co
|
||||
if (file_view.is_encrypted()) {
|
||||
return false;
|
||||
}
|
||||
if (file_view.has_remote_location() || file_view.has_url()) {
|
||||
if (td_->auth_manager_->is_bot() && file_view.has_remote_location()) {
|
||||
return true;
|
||||
}
|
||||
// having remote location is not enough to have InputMedia, because the file may not have valid file_reference
|
||||
// also file_id needs to be duped, because upload can be called to repair the file_reference and every upload
|
||||
// request must have unique file_id
|
||||
if (/* file_view.has_remote_location() || */ file_view.has_url()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2094,9 +2117,17 @@ tl_object_ptr<telegram_api::InputMedia> StickersManager::get_input_media(
|
||||
if (input_thumbnail != nullptr) {
|
||||
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
|
||||
}
|
||||
auto mime_type = get_sticker_mime_type(s);
|
||||
if (!s->is_animated && !s->set_id.is_valid()) {
|
||||
auto suggested_name = file_view.suggested_name();
|
||||
const PathView path_view(suggested_name);
|
||||
if (path_view.extension() == "tgs") {
|
||||
mime_type = "application/x-tgsticker";
|
||||
}
|
||||
}
|
||||
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
|
||||
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), get_sticker_mime_type(s),
|
||||
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
|
||||
flags, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type, std::move(attributes),
|
||||
vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
|
||||
} else {
|
||||
CHECK(!file_view.has_remote_location());
|
||||
}
|
||||
@ -2128,6 +2159,7 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
|
||||
}
|
||||
}
|
||||
if (!s->is_inited) {
|
||||
LOG(INFO) << "Init " << set_id;
|
||||
s->is_inited = true;
|
||||
s->title = std::move(set->title_);
|
||||
s->short_name = std::move(set->short_name_);
|
||||
@ -2172,11 +2204,13 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
|
||||
s->is_changed = true;
|
||||
}
|
||||
if (!s->is_thumbnail_reloaded) {
|
||||
LOG(INFO) << "Thumbnail of " << set_id << " was reloaded";
|
||||
s->is_thumbnail_reloaded = true;
|
||||
s->need_save_to_database = true;
|
||||
}
|
||||
|
||||
if (s->sticker_count != set->count_ || s->hash != set->hash_) {
|
||||
LOG(INFO) << "Number of stickers in " << set_id << " changed from " << s->sticker_count << " to " << set->count_;
|
||||
s->is_loaded = false;
|
||||
|
||||
s->sticker_count = set->count_;
|
||||
@ -2189,6 +2223,7 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
|
||||
}
|
||||
|
||||
if (s->is_official != is_official) {
|
||||
LOG(INFO) << "Official flag of " << set_id << " changed to " << is_official;
|
||||
s->is_official = is_official;
|
||||
s->is_changed = true;
|
||||
}
|
||||
@ -2321,7 +2356,7 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
|
||||
}
|
||||
if (static_cast<int32>(s->sticker_ids.size()) != s->sticker_count) {
|
||||
LOG(ERROR) << "Wrong sticker set size " << s->sticker_count << " instead of " << s->sticker_ids.size()
|
||||
<< " specified in " << set_id << " from " << source;
|
||||
<< " specified in " << set_id << "/" << s->short_name << " from " << source;
|
||||
s->sticker_count = static_cast<int32>(s->sticker_ids.size());
|
||||
}
|
||||
|
||||
@ -2334,7 +2369,8 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
|
||||
for (int64 document_id : pack->documents_) {
|
||||
auto it = document_id_to_sticker_id.find(document_id);
|
||||
if (it == document_id_to_sticker_id.end()) {
|
||||
LOG(ERROR) << "Can't find document with id " << document_id << " in " << set_id << " from " << source;
|
||||
LOG(ERROR) << "Can't find document with id " << document_id << " in " << set_id << "/" << s->short_name
|
||||
<< " from " << source;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -3015,7 +3051,7 @@ void StickersManager::change_sticker_set(StickerSetId set_id, bool is_installed,
|
||||
void StickersManager::on_update_sticker_set(StickerSet *sticker_set, bool is_installed, bool is_archived,
|
||||
bool is_changed, bool from_database) {
|
||||
LOG(INFO) << "Update " << sticker_set->id << ": installed = " << is_installed << ", archived = " << is_archived
|
||||
<< ", changed = " << is_changed;
|
||||
<< ", changed = " << is_changed << ", from_database = " << from_database;
|
||||
CHECK(sticker_set->is_inited);
|
||||
if (is_archived) {
|
||||
is_installed = true;
|
||||
@ -3428,6 +3464,9 @@ void StickersManager::on_update_dice_emojis() {
|
||||
G()->shared_config().set_option_empty("dice_emojis");
|
||||
return;
|
||||
}
|
||||
if (!is_inited_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dice_emojis_str = G()->shared_config().get_option_string("dice_emojis", "🎲\x01🎯\x01🏀");
|
||||
if (dice_emojis_str == dice_emojis_str_) {
|
||||
@ -3440,10 +3479,12 @@ void StickersManager::on_update_dice_emojis() {
|
||||
auto &special_sticker_set = add_special_sticker_set(SpecialStickerSetType::animated_dice(emoji));
|
||||
CHECK(!special_sticker_set.id_.is_valid());
|
||||
|
||||
if (G()->parameters().use_file_db) {
|
||||
LOG(INFO) << "Load new dice sticker set for emoji " << emoji;
|
||||
load_special_sticker_set(special_sticker_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
dice_emojis_ = std::move(new_dice_emojis);
|
||||
|
||||
send_closure(G()->td(), &Td::send_update, get_update_dice_emojis_object());
|
||||
@ -3457,11 +3498,16 @@ void StickersManager::on_update_dice_success_values() {
|
||||
G()->shared_config().set_option_empty("dice_success_values");
|
||||
return;
|
||||
}
|
||||
if (!is_inited_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dice_success_values_str = G()->shared_config().get_option_string("dice_success_values", "0,0,0");
|
||||
if (dice_success_values_str == dice_success_values_str_) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Change dice success values to " << dice_success_values_str;
|
||||
dice_success_values_str_ = std::move(dice_success_values_str);
|
||||
dice_success_values_ = transform(full_split(dice_success_values_str_, ','), [](Slice value) {
|
||||
auto result = split(value, ':');
|
||||
@ -6198,29 +6244,6 @@ td_api::object_ptr<td_api::httpUrl> StickersManager::get_emoji_suggestions_url_r
|
||||
return result;
|
||||
}
|
||||
|
||||
string StickersManager::remove_emoji_modifiers(string emoji) {
|
||||
static const Slice modifiers[] = {u8"\uFE0E" /* variation selector-15 */,
|
||||
u8"\uFE0F" /* variation selector-16 */,
|
||||
u8"\u200D\u2640" /* zero width joiner + female sign */,
|
||||
u8"\u200D\u2642" /* zero width joiner + male sign */,
|
||||
u8"\U0001F3FB" /* emoji modifier fitzpatrick type-1-2 */,
|
||||
u8"\U0001F3FC" /* emoji modifier fitzpatrick type-3 */,
|
||||
u8"\U0001F3FD" /* emoji modifier fitzpatrick type-4 */,
|
||||
u8"\U0001F3FE" /* emoji modifier fitzpatrick type-5 */,
|
||||
u8"\U0001F3FF" /* emoji modifier fitzpatrick type-6 */};
|
||||
bool found = true;
|
||||
while (found) {
|
||||
found = false;
|
||||
for (auto &modifier : modifiers) {
|
||||
if (ends_with(emoji, modifier) && emoji.size() > modifier.size()) {
|
||||
emoji.resize(emoji.size() - modifier.size());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return emoji;
|
||||
}
|
||||
|
||||
void StickersManager::after_get_difference() {
|
||||
if (td_->auth_manager_->is_bot()) {
|
||||
return;
|
||||
|
@ -616,10 +616,11 @@ class StickersManager : public Actor {
|
||||
void on_get_emoji_suggestions_url(int64 random_id, Promise<Unit> &&promise,
|
||||
Result<telegram_api::object_ptr<telegram_api::emojiURL>> &&r_emoji_url);
|
||||
|
||||
static string remove_emoji_modifiers(string emoji);
|
||||
|
||||
Td *td_;
|
||||
ActorShared<> parent_;
|
||||
|
||||
bool is_inited_ = false;
|
||||
|
||||
std::unordered_map<FileId, unique_ptr<Sticker>, FileIdHash> stickers_; // file_id -> Sticker
|
||||
std::unordered_map<StickerSetId, unique_ptr<StickerSet>, StickerSetIdHash> sticker_sets_; // id -> StickerSet
|
||||
std::unordered_map<string, StickerSetId> short_name_to_sticker_set_id_;
|
||||
|
@ -24,7 +24,10 @@
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/DeviceTokenManager.h"
|
||||
#include "td/telegram/DialogAdministrator.h"
|
||||
#include "td/telegram/DialogFilter.h"
|
||||
#include "td/telegram/DialogFilterId.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/DialogListId.h"
|
||||
#include "td/telegram/DialogLocation.h"
|
||||
#include "td/telegram/DialogParticipant.h"
|
||||
#include "td/telegram/DialogSource.h"
|
||||
@ -830,15 +833,34 @@ class GetChatRequest : public RequestActor<> {
|
||||
}
|
||||
};
|
||||
|
||||
class GetChatFilterRequest : public RequestActor<> {
|
||||
DialogFilterId dialog_filter_id_;
|
||||
|
||||
void do_run(Promise<Unit> &&promise) override {
|
||||
td->messages_manager_->load_dialog_filter(dialog_filter_id_, get_tries() < 2, std::move(promise));
|
||||
}
|
||||
|
||||
void do_send_result() override {
|
||||
send_result(td->messages_manager_->get_chat_filter_object(dialog_filter_id_));
|
||||
}
|
||||
|
||||
public:
|
||||
GetChatFilterRequest(ActorShared<Td> td, uint64 request_id, int32 dialog_filter_id)
|
||||
: RequestActor(std::move(td), request_id), dialog_filter_id_(dialog_filter_id) {
|
||||
set_tries(3);
|
||||
}
|
||||
};
|
||||
|
||||
class GetChatsRequest : public RequestActor<> {
|
||||
FolderId folder_id_;
|
||||
DialogListId dialog_list_id_;
|
||||
DialogDate offset_;
|
||||
int32 limit_;
|
||||
|
||||
vector<DialogId> dialog_ids_;
|
||||
|
||||
void do_run(Promise<Unit> &&promise) override {
|
||||
dialog_ids_ = td->messages_manager_->get_dialogs(folder_id_, offset_, limit_, get_tries() < 2, std::move(promise));
|
||||
dialog_ids_ =
|
||||
td->messages_manager_->get_dialogs(dialog_list_id_, offset_, limit_, get_tries() < 2, std::move(promise));
|
||||
}
|
||||
|
||||
void do_send_result() override {
|
||||
@ -846,10 +868,10 @@ class GetChatsRequest : public RequestActor<> {
|
||||
}
|
||||
|
||||
public:
|
||||
GetChatsRequest(ActorShared<Td> td, uint64 request_id, FolderId folder_id, int64 offset_order, int64 offset_dialog_id,
|
||||
int32 limit)
|
||||
GetChatsRequest(ActorShared<Td> td, uint64 request_id, DialogListId dialog_list_id, int64 offset_order,
|
||||
int64 offset_dialog_id, int32 limit)
|
||||
: RequestActor(std::move(td), request_id)
|
||||
, folder_id_(folder_id)
|
||||
, dialog_list_id_(dialog_list_id)
|
||||
, offset_(offset_order, DialogId(offset_dialog_id))
|
||||
, limit_(limit) {
|
||||
// 1 for database + 1 for server request + 1 for server request at the end + 1 for return + 1 just in case
|
||||
@ -3147,6 +3169,7 @@ void Td::on_get_promo_data(Result<telegram_api::object_ptr<telegram_api::help_Pr
|
||||
}
|
||||
|
||||
void Td::schedule_get_promo_data(int32 expires_in) {
|
||||
LOG(INFO) << "Schedule getPromoData in " << expires_in;
|
||||
if (expires_in < 0) {
|
||||
LOG(ERROR) << "Receive wrong expires_in: " << expires_in;
|
||||
expires_in = 0;
|
||||
@ -3208,6 +3231,7 @@ bool Td::is_synchronous_request(int32 id) {
|
||||
case td_api::getFileExtension::ID:
|
||||
case td_api::cleanFileName::ID:
|
||||
case td_api::getLanguagePackString::ID:
|
||||
case td_api::getChatFilterDefaultIconName::ID:
|
||||
case td_api::getJsonValue::ID:
|
||||
case td_api::getJsonString::ID:
|
||||
case td_api::getPushReceiverId::ID:
|
||||
@ -3258,7 +3282,6 @@ bool Td::is_preauthentication_request(int32 id) {
|
||||
case td_api::setCustomLanguagePackString::ID:
|
||||
case td_api::deleteLanguagePack::ID:
|
||||
case td_api::processPushNotification::ID:
|
||||
// case td_api::sendTonLiteServerRequest::ID:
|
||||
case td_api::getOption::ID:
|
||||
case td_api::setOption::ID:
|
||||
case td_api::getStorageStatistics::ID:
|
||||
@ -3431,6 +3454,7 @@ td_api::object_ptr<td_api::Object> Td::static_request(td_api::object_ptr<td_api:
|
||||
case td_api::getFileMimeType::ID:
|
||||
case td_api::getFileExtension::ID:
|
||||
case td_api::cleanFileName::ID:
|
||||
case td_api::getChatFilterDefaultIconName::ID:
|
||||
case td_api::getJsonValue::ID:
|
||||
case td_api::getJsonString::ID:
|
||||
case td_api::testReturnError::ID:
|
||||
@ -3545,7 +3569,7 @@ void Td::on_result(NetQueryPtr query) {
|
||||
bool Td::is_internal_config_option(Slice name) {
|
||||
switch (name[0]) {
|
||||
case 'a':
|
||||
return name == "auth";
|
||||
return name == "animation_search_emojis" || name == "animation_search_provider" || name == "auth";
|
||||
case 'b':
|
||||
return name == "base_language_pack_version";
|
||||
case 'c':
|
||||
@ -3585,6 +3609,10 @@ void Td::on_config_option_updated(const string &name) {
|
||||
return on_authorization_lost();
|
||||
} else if (name == "saved_animations_limit") {
|
||||
return animations_manager_->on_update_saved_animations_limit(G()->shared_config().get_option_integer(name));
|
||||
} else if (name == "animation_search_emojis") {
|
||||
return animations_manager_->on_update_animation_search_emojis(G()->shared_config().get_option_string(name));
|
||||
} else if (name == "animation_search_provider") {
|
||||
return animations_manager_->on_update_animation_search_provider(G()->shared_config().get_option_string(name));
|
||||
} else if (name == "recent_stickers_limit") {
|
||||
return stickers_manager_->on_update_recent_stickers_limit(G()->shared_config().get_option_integer(name));
|
||||
} else if (name == "favorite_stickers_limit") {
|
||||
@ -4430,6 +4458,7 @@ void Td::send_get_nearest_dc_query(Promise<string> promise) {
|
||||
}
|
||||
|
||||
void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
|
||||
CHECK(object != nullptr);
|
||||
auto object_id = object->get_id();
|
||||
if (close_flag_ >= 5 && object_id != td_api::updateAuthorizationState::ID) {
|
||||
// just in case
|
||||
@ -4456,7 +4485,8 @@ void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
|
||||
case td_api::updateUnreadChatCount::ID / 2:
|
||||
case td_api::updateChatOnlineMemberCount::ID / 2:
|
||||
case td_api::updateUserChatAction::ID / 2:
|
||||
case td_api::updateDiceEmojis::ID / 2:
|
||||
case td_api::updateChatFilters::ID / 2:
|
||||
case td_api::updateChatPosition::ID / 2:
|
||||
LOG(ERROR) << "Sending update: " << oneline(to_string(object));
|
||||
break;
|
||||
default:
|
||||
@ -5306,7 +5336,7 @@ void Td::on_request(uint64 id, const td_api::removeTopChat &request) {
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getChats &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST(GetChatsRequest, FolderId(request.chat_list_), request.offset_order_, request.offset_chat_id_,
|
||||
CREATE_REQUEST(GetChatsRequest, DialogListId(request.chat_list_), request.offset_order_, request.offset_chat_id_,
|
||||
request.limit_);
|
||||
}
|
||||
|
||||
@ -5449,7 +5479,11 @@ void Td::on_request(uint64 id, td_api::searchSecretMessages &request) {
|
||||
void Td::on_request(uint64 id, td_api::searchMessages &request) {
|
||||
CHECK_IS_USER();
|
||||
CLEAN_INPUT_STRING(request.query_);
|
||||
CREATE_REQUEST(SearchMessagesRequest, FolderId(request.chat_list_), request.chat_list_ == nullptr,
|
||||
DialogListId dialog_list_id(request.chat_list_);
|
||||
if (!dialog_list_id.is_folder()) {
|
||||
return send_error_raw(id, 400, "Wrong chat list specified");
|
||||
}
|
||||
CREATE_REQUEST(SearchMessagesRequest, dialog_list_id.get_folder_id(), request.chat_list_ == nullptr,
|
||||
std::move(request.query_), request.offset_date_, request.offset_chat_id_, request.offset_message_id_,
|
||||
request.limit_);
|
||||
}
|
||||
@ -5859,10 +5893,66 @@ void Td::on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupCh
|
||||
CREATE_REQUEST(UpgradeGroupChatToSupergroupChatRequest, request.chat_id_);
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::setChatChatList &request) {
|
||||
void Td::on_request(uint64 id, const td_api::getChatListsToAddChat &request) {
|
||||
CHECK_IS_USER();
|
||||
auto dialog_lists = messages_manager_->get_dialog_lists_to_add_dialog(DialogId(request.chat_id_));
|
||||
auto chat_lists =
|
||||
transform(dialog_lists, [](DialogListId dialog_list_id) { return dialog_list_id.get_chat_list_object(); });
|
||||
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::chatLists>(std::move(chat_lists)));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::addChatToList &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
messages_manager_->set_dialog_folder_id(DialogId(request.chat_id_), FolderId(request.chat_list_), std::move(promise));
|
||||
messages_manager_->add_dialog_to_list(DialogId(request.chat_id_), DialogListId(request.chat_list_),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getChatFilter &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST(GetChatFilterRequest, request.chat_filter_id_);
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getRecommendedChatFilters &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST_PROMISE();
|
||||
messages_manager_->get_recommended_dialog_filters(std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, td_api::createChatFilter &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.filter_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Chat filter must be non-empty");
|
||||
}
|
||||
CLEAN_INPUT_STRING(request.filter_->title_);
|
||||
CLEAN_INPUT_STRING(request.filter_->icon_name_);
|
||||
CREATE_REQUEST_PROMISE();
|
||||
messages_manager_->create_dialog_filter(std::move(request.filter_), std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, td_api::editChatFilter &request) {
|
||||
CHECK_IS_USER();
|
||||
if (request.filter_ == nullptr) {
|
||||
return send_error_raw(id, 400, "Chat filter must be non-empty");
|
||||
}
|
||||
CLEAN_INPUT_STRING(request.filter_->title_);
|
||||
CLEAN_INPUT_STRING(request.filter_->icon_name_);
|
||||
CREATE_REQUEST_PROMISE();
|
||||
messages_manager_->edit_dialog_filter(DialogFilterId(request.chat_filter_id_), std::move(request.filter_),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::deleteChatFilter &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
messages_manager_->delete_dialog_filter(DialogFilterId(request.chat_filter_id_), std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::reorderChatFilters &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_OK_REQUEST_PROMISE();
|
||||
messages_manager_->reorder_dialog_filters(
|
||||
transform(request.chat_filter_ids_, [](int32 id) { return DialogFilterId(id); }), std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, td_api::setChatTitle &request) {
|
||||
@ -5889,7 +5979,8 @@ void Td::on_request(uint64 id, td_api::setChatDraftMessage &request) {
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::toggleChatIsPinned &request) {
|
||||
CHECK_IS_USER();
|
||||
answer_ok_query(id, messages_manager_->toggle_dialog_is_pinned(DialogId(request.chat_id_), request.is_pinned_));
|
||||
answer_ok_query(id, messages_manager_->toggle_dialog_is_pinned(DialogListId(request.chat_list_),
|
||||
DialogId(request.chat_id_), request.is_pinned_));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::toggleChatIsMarkedAsUnread &request) {
|
||||
@ -5907,7 +5998,7 @@ void Td::on_request(uint64 id, const td_api::toggleChatDefaultDisableNotificatio
|
||||
void Td::on_request(uint64 id, const td_api::setPinnedChats &request) {
|
||||
CHECK_IS_USER();
|
||||
answer_ok_query(id, messages_manager_->set_pinned_dialogs(
|
||||
FolderId(request.chat_list_),
|
||||
DialogListId(request.chat_list_),
|
||||
transform(request.chat_ids_, [](int64 chat_id) { return DialogId(chat_id); })));
|
||||
}
|
||||
|
||||
@ -7255,18 +7346,6 @@ void Td::on_request(uint64 id, const td_api::deleteSavedCredentials &request) {
|
||||
delete_saved_credentials(std::move(promise));
|
||||
}
|
||||
|
||||
// void Td::on_request(uint64 id, const td_api::sendTonLiteServerRequest &request) {
|
||||
// CHECK_IS_USER();
|
||||
// CREATE_REQUEST_PROMISE();
|
||||
// send_ton_lite_server_request(request.request_, std::move(promise));
|
||||
// }
|
||||
|
||||
// void Td::on_request(uint64 id, const td_api::getTonWalletPasswordSalt &request) {
|
||||
// CHECK_IS_USER();
|
||||
// CREATE_REQUEST_PROMISE();
|
||||
// send_closure(password_manager_, &PasswordManager::get_ton_wallet_password_salt, std::move(promise));
|
||||
// }
|
||||
|
||||
void Td::on_request(uint64 id, td_api::getPassportElement &request) {
|
||||
CHECK_IS_USER();
|
||||
CLEAN_INPUT_STRING(request.password_);
|
||||
@ -7689,6 +7768,10 @@ void Td::on_request(uint64 id, const td_api::getPushReceiverId &request) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getChatFilterDefaultIconName &request) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getJsonValue &request) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -7840,6 +7923,19 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getPushRe
|
||||
return td_api::make_object<td_api::pushReceiverId>(r_push_receiver_id.ok());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getChatFilterDefaultIconName &request) {
|
||||
if (request.filter_ == nullptr) {
|
||||
return make_error(400, "Chat filter must be non-empty");
|
||||
}
|
||||
if (!check_utf8(request.filter_->title_)) {
|
||||
return make_error(400, "Chat filter title must be encoded in UTF-8");
|
||||
}
|
||||
if (!check_utf8(request.filter_->icon_name_)) {
|
||||
return make_error(400, "Chat filter icon name must be encoded in UTF-8");
|
||||
}
|
||||
return td_api::make_object<td_api::text>(DialogFilter::get_default_icon_name(request.filter_.get()));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getJsonValue &request) {
|
||||
if (!check_utf8(request.json_)) {
|
||||
return make_error(400, "JSON has invalid encoding");
|
||||
|
@ -231,7 +231,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.4";
|
||||
static constexpr const char *TDLIB_VERSION = "1.6.6";
|
||||
static constexpr int64 ONLINE_ALARM_ID = 0;
|
||||
static constexpr int64 PING_SERVER_ALARM_ID = -1;
|
||||
static constexpr int32 PING_SERVER_TIMEOUT = 300;
|
||||
@ -674,7 +674,21 @@ class Td final : public NetQueryCallback {
|
||||
|
||||
void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::setChatChatList &request);
|
||||
void on_request(uint64 id, const td_api::getChatListsToAddChat &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::addChatToList &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getChatFilter &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getRecommendedChatFilters &request);
|
||||
|
||||
void on_request(uint64 id, td_api::createChatFilter &request);
|
||||
|
||||
void on_request(uint64 id, td_api::editChatFilter &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::deleteChatFilter &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::reorderChatFilters &request);
|
||||
|
||||
void on_request(uint64 id, td_api::setChatTitle &request);
|
||||
|
||||
@ -960,10 +974,6 @@ class Td final : public NetQueryCallback {
|
||||
|
||||
void on_request(uint64 id, const td_api::deleteSavedCredentials &request);
|
||||
|
||||
// void on_request(uint64 id, const td_api::sendTonLiteServerRequest &request);
|
||||
|
||||
// void on_request(uint64 id, const td_api::getTonWalletPasswordSalt &request);
|
||||
|
||||
void on_request(uint64 id, td_api::getPassportElement &request);
|
||||
|
||||
void on_request(uint64 id, td_api::getAllPassportElements &request);
|
||||
@ -1074,6 +1084,8 @@ class Td final : public NetQueryCallback {
|
||||
|
||||
void on_request(uint64 id, const td_api::getPushReceiverId &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getChatFilterDefaultIconName &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getJsonValue &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getJsonString &request);
|
||||
@ -1122,6 +1134,7 @@ class Td final : public NetQueryCallback {
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::cleanFileName &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getLanguagePackString &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getPushReceiverId &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getChatFilterDefaultIconName &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::getJsonValue &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getJsonString &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::setLogStream &request);
|
||||
|
@ -29,10 +29,13 @@
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace {
|
||||
@ -322,7 +325,7 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters ¶meters, DbK
|
||||
bool dialog_db_was_created = false;
|
||||
|
||||
if (use_dialog_db) {
|
||||
TRY_STATUS(init_dialog_db(db, user_version, dialog_db_was_created));
|
||||
TRY_STATUS(init_dialog_db(db, user_version, binlog_pmc, dialog_db_was_created));
|
||||
}
|
||||
|
||||
// init MessagesDb
|
||||
@ -347,6 +350,7 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters ¶meters, DbK
|
||||
}
|
||||
|
||||
if (dialog_db_was_created) {
|
||||
binlog_pmc.erase_by_prefix("pinned_dialog_ids");
|
||||
binlog_pmc.erase_by_prefix("last_server_dialog_date");
|
||||
binlog_pmc.erase_by_prefix("unread_message_count");
|
||||
binlog_pmc.erase_by_prefix("unread_dialog_count");
|
||||
@ -525,6 +529,45 @@ Result<string> TdDb::get_stats() {
|
||||
TRY_STATUS(run_kv_query("ch%"));
|
||||
TRY_STATUS(run_kv_query("ss%"));
|
||||
TRY_STATUS(run_kv_query("gr%"));
|
||||
|
||||
vector<int32> prev(1);
|
||||
size_t count = 0;
|
||||
int32 max_bad_to = 0;
|
||||
size_t bad_count = 0;
|
||||
file_db_->pmc().get_by_range("file0", "file:", [&](Slice key, Slice value) {
|
||||
if (value.substr(0, 2) != "@@") {
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
auto from = to_integer<int32>(key.substr(4));
|
||||
auto to = to_integer<int32>(value.substr(2));
|
||||
if (from <= to) {
|
||||
LOG(DEBUG) << "Have forward reference from " << from << " to " << to;
|
||||
if (to > max_bad_to) {
|
||||
max_bad_to = to;
|
||||
}
|
||||
bad_count++;
|
||||
return true;
|
||||
}
|
||||
if (static_cast<size_t>(from) >= prev.size()) {
|
||||
prev.resize(from + 1);
|
||||
}
|
||||
if (static_cast<size_t>(to) >= prev.size()) {
|
||||
prev.resize(to + 1);
|
||||
}
|
||||
prev[from] = to;
|
||||
return true;
|
||||
});
|
||||
for (size_t i = 1; i < prev.size(); i++) {
|
||||
if (!prev[i]) {
|
||||
continue;
|
||||
}
|
||||
prev[i] = prev[prev[i]] + 1;
|
||||
}
|
||||
sb << "Max file database depth out of " << prev.size() << '/' << count
|
||||
<< " elements: " << *std::max_element(prev.begin(), prev.end()) << "\n";
|
||||
sb << "Have " << bad_count << " forward references with maximum reference to " << max_bad_to;
|
||||
|
||||
return sb.as_cslice().str();
|
||||
}
|
||||
|
||||
|
@ -1857,6 +1857,18 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogUnreadMar
|
||||
DialogId(update->peer_), (update->flags_ & telegram_api::updateDialogUnreadMark::UNREAD_MASK) != 0);
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilter> update, bool /*force_apply*/) {
|
||||
td_->messages_manager_->on_update_dialog_filters();
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilters> update, bool /*force_apply*/) {
|
||||
td_->messages_manager_->on_update_dialog_filters();
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilterOrder> update, bool /*force_apply*/) {
|
||||
td_->messages_manager_->on_update_dialog_filters();
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDcOptions> update, bool /*force_apply*/) {
|
||||
send_closure(G()->config_manager(), &ConfigManager::on_dc_options_update, DcOptions(update->dc_options_));
|
||||
}
|
||||
@ -1982,6 +1994,11 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePhoneCall> upda
|
||||
send_closure(G()->call_manager(), &CallManager::update_call, std::move(update));
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updatePhoneCallSignalingData> update, bool /*force_apply*/) {
|
||||
send_closure(G()->call_manager(), &CallManager::update_call_signaling_data, update->phone_call_id_,
|
||||
update->data_.as_slice().str());
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateContactsReset> update, bool /*force_apply*/) {
|
||||
td_->contacts_manager_->on_update_contacts_reset();
|
||||
}
|
||||
@ -2032,13 +2049,4 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateLoginToken> upd
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateTheme> update, bool /*force_apply*/) {
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilter> update, bool /*force_apply*/) {
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilterOrder> update, bool /*force_apply*/) {
|
||||
}
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateDialogFilters> update, bool /*force_apply*/) {
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -239,6 +239,10 @@ class UpdatesManager : public Actor {
|
||||
void on_update(tl_object_ptr<telegram_api::updatePinnedDialogs> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogUnreadMark> update, bool /*force_apply*/);
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilter> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilters> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilterOrder> update, bool /*force_apply*/);
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateBotInlineQuery> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateBotInlineSend> update, bool /*force_apply*/);
|
||||
|
||||
@ -272,6 +276,7 @@ class UpdatesManager : public Actor {
|
||||
void on_update(tl_object_ptr<telegram_api::updateBotWebhookJSONQuery> update, bool /*force_apply*/);
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updatePhoneCall> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updatePhoneCallSignalingData> update, bool /*force_apply*/);
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateContactsReset> update, bool /*force_apply*/);
|
||||
|
||||
@ -291,10 +296,6 @@ class UpdatesManager : public Actor {
|
||||
// unsupported updates
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateTheme> update, bool /*force_apply*/);
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilter> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilterOrder> update, bool /*force_apply*/);
|
||||
void on_update(tl_object_ptr<telegram_api::updateDialogFilters> update, bool /*force_apply*/);
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
constexpr int32 MTPROTO_LAYER = 113;
|
||||
constexpr int32 MTPROTO_LAYER = 114;
|
||||
|
||||
enum class Version : int32 {
|
||||
Initial,
|
||||
@ -37,6 +37,7 @@ enum class Version : int32 {
|
||||
AddFolders,
|
||||
SupportPolls2_0,
|
||||
AddDiceEmoji,
|
||||
AddAnimationStickers,
|
||||
Next
|
||||
};
|
||||
|
||||
@ -50,6 +51,7 @@ enum class DbVersion : int32 {
|
||||
AddNotificationsSupport,
|
||||
AddFolders,
|
||||
AddScheduledMessages,
|
||||
StorePinnedDialogsInBinlog,
|
||||
Next
|
||||
};
|
||||
|
||||
|
@ -42,9 +42,9 @@ tl_object_ptr<td_api::videoNote> VideoNotesManager::get_video_note_object(FileId
|
||||
}
|
||||
video_note->is_changed = false;
|
||||
|
||||
return make_tl_object<td_api::videoNote>(video_note->duration, video_note->dimensions.width,
|
||||
get_minithumbnail_object(video_note->minithumbnail),
|
||||
get_photo_size_object(td_->file_manager_.get(), &video_note->thumbnail),
|
||||
return make_tl_object<td_api::videoNote>(
|
||||
video_note->duration, video_note->dimensions.width, get_minithumbnail_object(video_note->minithumbnail),
|
||||
get_thumbnail_object(td_->file_manager_.get(), video_note->thumbnail, PhotoFormat::Jpeg),
|
||||
td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
|
@ -41,10 +41,13 @@ tl_object_ptr<td_api::video> VideosManager::get_video_object(FileId file_id) {
|
||||
}
|
||||
video->is_changed = false;
|
||||
|
||||
return make_tl_object<td_api::video>(
|
||||
video->duration, video->dimensions.width, video->dimensions.height, video->file_name, video->mime_type,
|
||||
video->has_stickers, video->supports_streaming, get_minithumbnail_object(video->minithumbnail),
|
||||
get_photo_size_object(td_->file_manager_.get(), &video->thumbnail), td_->file_manager_->get_file_object(file_id));
|
||||
auto thumbnail = video->animated_thumbnail.file_id.is_valid()
|
||||
? get_thumbnail_object(td_->file_manager_.get(), video->animated_thumbnail, PhotoFormat::Mpeg4)
|
||||
: get_thumbnail_object(td_->file_manager_.get(), video->thumbnail, PhotoFormat::Jpeg);
|
||||
return make_tl_object<td_api::video>(video->duration, video->dimensions.width, video->dimensions.height,
|
||||
video->file_name, video->mime_type, video->has_stickers,
|
||||
video->supports_streaming, get_minithumbnail_object(video->minithumbnail),
|
||||
std::move(thumbnail), td_->file_manager_->get_file_object(file_id));
|
||||
}
|
||||
|
||||
FileId VideosManager::on_get_video(unique_ptr<Video> new_video, bool replace) {
|
||||
@ -88,12 +91,22 @@ FileId VideosManager::on_get_video(unique_ptr<Video> new_video, bool replace) {
|
||||
v->thumbnail = new_video->thumbnail;
|
||||
v->is_changed = true;
|
||||
}
|
||||
if (v->animated_thumbnail != new_video->animated_thumbnail) {
|
||||
if (!v->animated_thumbnail.file_id.is_valid()) {
|
||||
LOG(DEBUG) << "Video " << file_id << " animated thumbnail has changed";
|
||||
} else {
|
||||
LOG(INFO) << "Video " << file_id << " animated thumbnail has changed from " << v->animated_thumbnail << " to "
|
||||
<< new_video->animated_thumbnail;
|
||||
}
|
||||
v->animated_thumbnail = new_video->animated_thumbnail;
|
||||
v->is_changed = true;
|
||||
}
|
||||
if (v->has_stickers != new_video->has_stickers && new_video->has_stickers) {
|
||||
v->has_stickers = new_video->has_stickers;
|
||||
v->is_changed = true;
|
||||
}
|
||||
if (v->sticker_file_ids != new_video->sticker_file_ids && !new_video->sticker_file_ids.empty()) {
|
||||
v->sticker_file_ids = new_video->sticker_file_ids;
|
||||
v->sticker_file_ids = std::move(new_video->sticker_file_ids);
|
||||
v->is_changed = true;
|
||||
}
|
||||
}
|
||||
@ -120,12 +133,21 @@ FileId VideosManager::get_video_thumbnail_file_id(FileId file_id) const {
|
||||
return video->thumbnail.file_id;
|
||||
}
|
||||
|
||||
FileId VideosManager::get_video_animated_thumbnail_file_id(FileId file_id) const {
|
||||
auto video = get_video(file_id);
|
||||
if (video == nullptr) {
|
||||
return FileId();
|
||||
}
|
||||
return video->animated_thumbnail.file_id;
|
||||
}
|
||||
|
||||
void VideosManager::delete_video_thumbnail(FileId file_id) {
|
||||
auto &video = videos_[file_id];
|
||||
if (video == nullptr) {
|
||||
return;
|
||||
}
|
||||
video->thumbnail = PhotoSize();
|
||||
video->animated_thumbnail = PhotoSize();
|
||||
}
|
||||
|
||||
FileId VideosManager::dup_video(FileId new_id, FileId old_id) {
|
||||
@ -136,6 +158,7 @@ FileId VideosManager::dup_video(FileId new_id, FileId old_id) {
|
||||
new_video = make_unique<Video>(*old_video);
|
||||
new_video->file_id = new_id;
|
||||
new_video->thumbnail.file_id = td_->file_manager_->dup_file_id(new_video->thumbnail.file_id);
|
||||
new_video->animated_thumbnail.file_id = td_->file_manager_->dup_file_id(new_video->animated_thumbnail.file_id);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
@ -182,9 +205,10 @@ bool VideosManager::merge_videos(FileId new_id, FileId old_id, bool can_delete_o
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideosManager::create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail, bool has_stickers,
|
||||
vector<FileId> &&sticker_file_ids, string file_name, string mime_type, int32 duration,
|
||||
Dimensions dimensions, bool supports_streaming, bool replace) {
|
||||
void VideosManager::create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail,
|
||||
PhotoSize animated_thumbnail, bool has_stickers, vector<FileId> &&sticker_file_ids,
|
||||
string file_name, string mime_type, int32 duration, Dimensions dimensions,
|
||||
bool supports_streaming, bool replace) {
|
||||
auto v = make_unique<Video>();
|
||||
v->file_id = file_id;
|
||||
v->file_name = std::move(file_name);
|
||||
@ -193,6 +217,7 @@ void VideosManager::create_video(FileId file_id, string minithumbnail, PhotoSize
|
||||
v->dimensions = dimensions;
|
||||
v->minithumbnail = std::move(minithumbnail);
|
||||
v->thumbnail = std::move(thumbnail);
|
||||
v->animated_thumbnail = std::move(animated_thumbnail);
|
||||
v->supports_streaming = supports_streaming;
|
||||
v->has_stickers = has_stickers;
|
||||
v->sticker_file_ids = std::move(sticker_file_ids);
|
||||
|
@ -32,9 +32,9 @@ class VideosManager {
|
||||
|
||||
tl_object_ptr<td_api::video> get_video_object(FileId file_id);
|
||||
|
||||
void create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail, bool has_stickers,
|
||||
vector<FileId> &&sticker_file_ids, string file_name, string mime_type, int32 duration,
|
||||
Dimensions dimensions, bool supports_streaming, bool replace);
|
||||
void create_video(FileId file_id, string minithumbnail, PhotoSize thumbnail, PhotoSize animated_thumbnail,
|
||||
bool has_stickers, vector<FileId> &&sticker_file_ids, string file_name, string mime_type,
|
||||
int32 duration, Dimensions dimensions, bool supports_streaming, bool replace);
|
||||
|
||||
tl_object_ptr<telegram_api::InputMedia> get_input_media(FileId file_id,
|
||||
tl_object_ptr<telegram_api::InputFile> input_file,
|
||||
@ -47,6 +47,8 @@ class VideosManager {
|
||||
|
||||
FileId get_video_thumbnail_file_id(FileId file_id) const;
|
||||
|
||||
FileId get_video_animated_thumbnail_file_id(FileId file_id) const;
|
||||
|
||||
void delete_video_thumbnail(FileId file_id);
|
||||
|
||||
FileId dup_video(FileId new_id, FileId old_id);
|
||||
@ -70,6 +72,7 @@ class VideosManager {
|
||||
Dimensions dimensions;
|
||||
string minithumbnail;
|
||||
PhotoSize thumbnail;
|
||||
PhotoSize animated_thumbnail;
|
||||
|
||||
bool supports_streaming = false;
|
||||
|
||||
|
@ -24,9 +24,11 @@ void VideosManager::store_video(FileId file_id, StorerT &storer) const {
|
||||
return;
|
||||
}
|
||||
const Video *video = it->second.get();
|
||||
bool has_animated_thumbnail = video->animated_thumbnail.file_id.is_valid();
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(video->has_stickers);
|
||||
STORE_FLAG(video->supports_streaming);
|
||||
STORE_FLAG(has_animated_thumbnail);
|
||||
END_STORE_FLAGS();
|
||||
store(video->file_name, storer);
|
||||
store(video->mime_type, storer);
|
||||
@ -38,14 +40,19 @@ void VideosManager::store_video(FileId file_id, StorerT &storer) const {
|
||||
if (video->has_stickers) {
|
||||
store(video->sticker_file_ids, storer);
|
||||
}
|
||||
if (has_animated_thumbnail) {
|
||||
store(video->animated_thumbnail, storer);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
FileId VideosManager::parse_video(ParserT &parser) {
|
||||
auto video = make_unique<Video>();
|
||||
bool has_animated_thumbnail;
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(video->has_stickers);
|
||||
PARSE_FLAG(video->supports_streaming);
|
||||
PARSE_FLAG(has_animated_thumbnail);
|
||||
END_PARSE_FLAGS();
|
||||
parse(video->file_name, parser);
|
||||
parse(video->mime_type, parser);
|
||||
@ -59,6 +66,9 @@ FileId VideosManager::parse_video(ParserT &parser) {
|
||||
if (video->has_stickers) {
|
||||
parse(video->sticker_file_ids, parser);
|
||||
}
|
||||
if (has_animated_thumbnail) {
|
||||
parse(video->animated_thumbnail, parser);
|
||||
}
|
||||
if (parser.get_error() != nullptr || !video->file_id.is_valid()) {
|
||||
return FileId();
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ class RichText {
|
||||
auto width = static_cast<int32>(dimensions / 65536);
|
||||
auto height = static_cast<int32>(dimensions % 65536);
|
||||
return make_tl_object<td_api::richTextIcon>(
|
||||
context->td_->documents_manager_->get_document_object(document_file_id), width, height);
|
||||
context->td_->documents_manager_->get_document_object(document_file_id, PhotoFormat::Jpeg), width, height);
|
||||
}
|
||||
case RichText::Type::Anchor: {
|
||||
if (context->is_first_pass_) {
|
||||
|
@ -454,7 +454,8 @@ WebPageId WebPagesManager::on_get_web_page(tl_object_ptr<telegram_api::WebPage>
|
||||
}
|
||||
|
||||
auto web_page_date = web_page->date_;
|
||||
LOG(INFO) << "Got pending " << web_page_id << ", date = " << web_page_date << ", now = " << G()->server_time();
|
||||
LOG(INFO) << "Got pending " << web_page_id << ", force_get_date = " << web_page_date
|
||||
<< ", now = " << G()->server_time();
|
||||
|
||||
pending_web_pages_timeout_.add_timeout_in(web_page_id.get(), max(web_page_date - G()->server_time(), 1.0));
|
||||
return web_page_id;
|
||||
|
@ -467,10 +467,22 @@ class CliClient final : public Actor {
|
||||
return to_integer<int64>(str);
|
||||
}
|
||||
|
||||
static int32 as_chat_filter_id(Slice str) {
|
||||
return to_integer<int32>(trim(str));
|
||||
}
|
||||
|
||||
static vector<int32> as_chat_filter_ids(Slice chat_filter_ids) {
|
||||
return transform(full_split(trim(chat_filter_ids), get_delimiter(chat_filter_ids)),
|
||||
[](Slice str) { return as_chat_filter_id(str); });
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::ChatList> as_chat_list(string chat_list) {
|
||||
if (!chat_list.empty() && chat_list.back() == 'a') {
|
||||
return td_api::make_object<td_api::chatListArchive>();
|
||||
}
|
||||
if (chat_list.find('-') != string::npos) {
|
||||
return td_api::make_object<td_api::chatListFilter>(as_chat_filter_id(chat_list.substr(chat_list.find('-') + 1)));
|
||||
}
|
||||
return td_api::make_object<td_api::chatListMain>();
|
||||
}
|
||||
|
||||
@ -1077,6 +1089,27 @@ class CliClient final : public Actor {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::chatFilter> as_chat_filter(string filter) const {
|
||||
string title;
|
||||
string icon_name;
|
||||
string pinned_chat_ids;
|
||||
string included_chat_ids;
|
||||
string excluded_chat_ids;
|
||||
std::tie(title, filter) = split(filter);
|
||||
std::tie(icon_name, filter) = split(filter);
|
||||
std::tie(pinned_chat_ids, filter) = split(filter);
|
||||
std::tie(included_chat_ids, filter) = split(filter);
|
||||
std::tie(excluded_chat_ids, filter) = split(filter);
|
||||
|
||||
auto rand_bool = [] {
|
||||
return Random::fast(0, 1) == 1;
|
||||
};
|
||||
|
||||
return td_api::make_object<td_api::chatFilter>(
|
||||
title, icon_name, as_chat_ids(pinned_chat_ids), as_chat_ids(included_chat_ids), as_chat_ids(excluded_chat_ids),
|
||||
rand_bool(), rand_bool(), rand_bool(), rand_bool(), rand_bool(), rand_bool(), rand_bool(), rand_bool());
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::TopChatCategory> get_top_chat_category(MutableSlice category) {
|
||||
category = trim(category);
|
||||
to_lower_inplace(category);
|
||||
@ -1732,10 +1765,10 @@ class CliClient final : public Actor {
|
||||
op_not_found_count++;
|
||||
}
|
||||
|
||||
if (op == "gc" || op == "GetChats" || op == "gca") {
|
||||
if (op == "gc" || op == "GetChats" || op == "gca" || begins_with(op, "gc-")) {
|
||||
string limit;
|
||||
string offset_order_string;
|
||||
string offset_chat_id;
|
||||
string limit;
|
||||
|
||||
std::tie(limit, args) = split(args);
|
||||
std::tie(offset_order_string, offset_chat_id) = split(args);
|
||||
@ -2816,11 +2849,12 @@ class CliClient final : public Actor {
|
||||
send_request(td_api::make_object<td_api::setChatDraftMessage>(as_chat_id(chat_id), std::move(draft_message)));
|
||||
} else if (op == "cadm") {
|
||||
send_request(td_api::make_object<td_api::clearAllDraftMessages>());
|
||||
} else if (op == "tcip") {
|
||||
} else if (op == "tcip" || op == "tcipa" || begins_with(op, "tcip-")) {
|
||||
string chat_id;
|
||||
string is_pinned;
|
||||
std::tie(chat_id, is_pinned) = split(args);
|
||||
send_request(td_api::make_object<td_api::toggleChatIsPinned>(as_chat_id(chat_id), as_bool(is_pinned)));
|
||||
send_request(
|
||||
td_api::make_object<td_api::toggleChatIsPinned>(as_chat_list(op), as_chat_id(chat_id), as_bool(is_pinned)));
|
||||
} else if (op == "tcimar") {
|
||||
string chat_id;
|
||||
string is_marked_as_read;
|
||||
@ -2833,7 +2867,7 @@ class CliClient final : public Actor {
|
||||
std::tie(chat_id, default_disable_notification) = split(args);
|
||||
send_request(td_api::make_object<td_api::toggleChatDefaultDisableNotification>(
|
||||
as_chat_id(chat_id), as_bool(default_disable_notification)));
|
||||
} else if (op == "spchats" || op == "spchatsa") {
|
||||
} else if (op == "spchats" || op == "spchatsa" || begins_with(op, "spchats-")) {
|
||||
vector<string> chat_ids_str = full_split(args, ' ');
|
||||
vector<int64> chat_ids;
|
||||
for (auto &chat_id_str : chat_ids_str) {
|
||||
@ -2941,8 +2975,8 @@ class CliClient final : public Actor {
|
||||
std::tie(message_id, animation) = split(args);
|
||||
send_request(td_api::make_object<td_api::editMessageMedia>(
|
||||
as_chat_id(chat_id), as_message_id(message_id), nullptr,
|
||||
td_api::make_object<td_api::inputMessageAnimation>(as_input_file(animation), nullptr, 0, 0, 0,
|
||||
as_caption("animation"))));
|
||||
td_api::make_object<td_api::inputMessageAnimation>(as_input_file(animation), nullptr, vector<int32>(), 0, 0,
|
||||
0, as_caption("animation"))));
|
||||
} else if (op == "emc") {
|
||||
string chat_id;
|
||||
string message_id;
|
||||
@ -3074,7 +3108,7 @@ class CliClient final : public Actor {
|
||||
std::tie(height, caption) = split(args);
|
||||
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_input_file(animation_path), nullptr, 60, to_integer<int32>(width),
|
||||
as_input_file(animation_path), nullptr, vector<int32>(), 60, to_integer<int32>(width),
|
||||
to_integer<int32>(height), as_caption(caption)));
|
||||
} else if (op == "sang") {
|
||||
string chat_id;
|
||||
@ -3082,30 +3116,30 @@ class CliClient final : public Actor {
|
||||
string animation_conversion;
|
||||
std::tie(chat_id, args) = split(args);
|
||||
std::tie(animation_path, animation_conversion) = split(args);
|
||||
send_message(chat_id,
|
||||
td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_generated_file(animation_path, animation_conversion), nullptr, 60, 0, 0, as_caption("")));
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_generated_file(animation_path, animation_conversion), nullptr, vector<int32>(), 60,
|
||||
0, 0, as_caption("")));
|
||||
} else if (op == "sanid") {
|
||||
string chat_id;
|
||||
string file_id;
|
||||
std::tie(chat_id, file_id) = split(args);
|
||||
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(as_input_file_id(file_id), nullptr, 0, 0,
|
||||
0, as_caption("")));
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_input_file_id(file_id), nullptr, vector<int32>(), 0, 0, 0, as_caption("")));
|
||||
} else if (op == "sanurl") {
|
||||
string chat_id;
|
||||
string url;
|
||||
std::tie(chat_id, url) = split(args);
|
||||
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(as_generated_file(url, "#url#"), nullptr,
|
||||
0, 0, 0, as_caption("")));
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_generated_file(url, "#url#"), nullptr, vector<int32>(), 0, 0, 0, as_caption("")));
|
||||
} else if (op == "sanurl2") {
|
||||
string chat_id;
|
||||
string url;
|
||||
std::tie(chat_id, url) = split(args);
|
||||
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(as_remote_file(url), nullptr, 0, 0, 0,
|
||||
as_caption("")));
|
||||
send_message(chat_id, td_api::make_object<td_api::inputMessageAnimation>(
|
||||
as_remote_file(url), nullptr, vector<int32>(), 0, 0, 0, as_caption("")));
|
||||
} else if (op == "sau") {
|
||||
string chat_id;
|
||||
string audio_path;
|
||||
@ -3480,9 +3514,36 @@ class CliClient final : public Actor {
|
||||
|
||||
std::tie(supergroup_id, force) = split(args);
|
||||
send_request(td_api::make_object<td_api::createSupergroupChat>(as_supergroup_id(supergroup_id), as_bool(force)));
|
||||
} else if (op == "sccl" || op == "sccla") {
|
||||
} else if (op == "gcltac") {
|
||||
string chat_id = args;
|
||||
send_request(td_api::make_object<td_api::setChatChatList>(as_chat_id(chat_id), as_chat_list(op)));
|
||||
send_request(td_api::make_object<td_api::getChatListsToAddChat>(as_chat_id(chat_id)));
|
||||
} else if (op == "actl" || op == "actla" || begins_with(op, "actl-")) {
|
||||
string chat_id = args;
|
||||
send_request(td_api::make_object<td_api::addChatToList>(as_chat_id(chat_id), as_chat_list(op)));
|
||||
} else if (op == "gcf") {
|
||||
send_request(td_api::make_object<td_api::getChatFilter>(as_chat_filter_id(args)));
|
||||
} else if (op == "ccf") {
|
||||
send_request(td_api::make_object<td_api::createChatFilter>(as_chat_filter(args)));
|
||||
} else if (op == "ccfe") {
|
||||
auto chat_filter = td_api::make_object<td_api::chatFilter>();
|
||||
chat_filter->title_ = "empty";
|
||||
chat_filter->included_chat_ids_ = as_chat_ids(args);
|
||||
send_request(td_api::make_object<td_api::createChatFilter>(std::move(chat_filter)));
|
||||
} else if (op == "ecf") {
|
||||
string chat_filter_id;
|
||||
string filter;
|
||||
|
||||
std::tie(chat_filter_id, filter) = split(args);
|
||||
send_request(
|
||||
td_api::make_object<td_api::editChatFilter>(as_chat_filter_id(chat_filter_id), as_chat_filter(filter)));
|
||||
} else if (op == "dcf") {
|
||||
send_request(td_api::make_object<td_api::deleteChatFilter>(as_chat_filter_id(args)));
|
||||
} else if (op == "rcf") {
|
||||
send_request(td_api::make_object<td_api::reorderChatFilters>(as_chat_filter_ids(args)));
|
||||
} else if (op == "grcf") {
|
||||
send_request(td_api::make_object<td_api::getRecommendedChatFilters>());
|
||||
} else if (op == "gcfdin") {
|
||||
execute(td_api::make_object<td_api::getChatFilterDefaultIconName>(as_chat_filter(args)));
|
||||
} else if (op == "sct") {
|
||||
string chat_id;
|
||||
string title;
|
||||
|
@ -874,8 +874,8 @@ bool FileManager::are_modification_times_equal(int64 old_mtime, int64 new_mtime)
|
||||
return false;
|
||||
}
|
||||
|
||||
Status FileManager::check_local_location(FullLocalFileLocation &location, int64 &size) {
|
||||
constexpr int64 MAX_THUMBNAIL_SIZE = 200 * (1 << 10) /* 200 KB */;
|
||||
Status FileManager::check_local_location(FullLocalFileLocation &location, int64 &size, bool skip_file_size_checks) {
|
||||
constexpr int64 MAX_THUMBNAIL_SIZE = 200 * (1 << 10) - 1 /* 200 KB - 1 B */;
|
||||
constexpr int64 MAX_PHOTO_SIZE = 10 * (1 << 20) /* 10 MB */;
|
||||
|
||||
if (location.path_.empty()) {
|
||||
@ -909,16 +909,19 @@ Status FileManager::check_local_location(FullLocalFileLocation &location, int64
|
||||
<< ", new mtime = " << stat.mtime_nsec_;
|
||||
return Status::Error(PSLICE() << "File \"" << location.path_ << "\" was modified");
|
||||
}
|
||||
if (skip_file_size_checks) {
|
||||
return Status::OK();
|
||||
}
|
||||
if ((location.file_type_ == FileType::Thumbnail || location.file_type_ == FileType::EncryptedThumbnail) &&
|
||||
size >= MAX_THUMBNAIL_SIZE && !begins_with(PathView(location.path_).file_name(), "map")) {
|
||||
size > MAX_THUMBNAIL_SIZE && !begins_with(PathView(location.path_).file_name(), "map")) {
|
||||
return Status::Error(PSLICE() << "File \"" << location.path_ << "\" is too big for a thumbnail "
|
||||
<< tag("size", format::as_size(size)));
|
||||
}
|
||||
if (location.file_type_ == FileType::Photo && size >= MAX_PHOTO_SIZE) {
|
||||
if (location.file_type_ == FileType::Photo && size > MAX_PHOTO_SIZE) {
|
||||
return Status::Error(PSLICE() << "File \"" << location.path_ << "\" is too big for a photo "
|
||||
<< tag("size", format::as_size(size)));
|
||||
}
|
||||
if (size >= MAX_FILE_SIZE) {
|
||||
if (size > MAX_FILE_SIZE) {
|
||||
return Status::Error(PSLICE() << "File \"" << location.path_ << "\" is too big "
|
||||
<< tag("size", format::as_size(size)));
|
||||
}
|
||||
@ -940,7 +943,7 @@ static Status check_partial_local_location(const PartialLocalFileLocation &locat
|
||||
Status FileManager::check_local_location(FileNodePtr node) {
|
||||
Status status;
|
||||
if (node->local_.type() == LocalFileLocation::Type::Full) {
|
||||
status = check_local_location(node->local_.full(), node->size_);
|
||||
status = check_local_location(node->local_.full(), node->size_, false);
|
||||
} else if (node->local_.type() == LocalFileLocation::Type::Partial) {
|
||||
status = check_partial_local_location(node->local_.partial());
|
||||
}
|
||||
@ -1042,13 +1045,14 @@ void FileManager::on_file_unlink(const FullLocalFileLocation &location) {
|
||||
}
|
||||
|
||||
Result<FileId> FileManager::register_local(FullLocalFileLocation location, DialogId owner_dialog_id, int64 size,
|
||||
bool get_by_hash, bool force) {
|
||||
bool get_by_hash, bool force, bool skip_file_size_checks) {
|
||||
// TODO: use get_by_hash
|
||||
FileData data;
|
||||
data.local_ = LocalFileLocation(std::move(location));
|
||||
data.owner_dialog_id_ = owner_dialog_id;
|
||||
data.size_ = size;
|
||||
return register_file(std::move(data), FileLocationSource::None /*won't be used*/, "register_local", force);
|
||||
return register_file(std::move(data), FileLocationSource::None /*won't be used*/, "register_local", force,
|
||||
skip_file_size_checks);
|
||||
}
|
||||
|
||||
FileId FileManager::register_remote(const FullRemoteFileLocation &location, FileLocationSource file_location_source,
|
||||
@ -1101,7 +1105,7 @@ Result<FileId> FileManager::register_generate(FileType file_type, FileLocationSo
|
||||
}
|
||||
|
||||
Result<FileId> FileManager::register_file(FileData &&data, FileLocationSource file_location_source, const char *source,
|
||||
bool force) {
|
||||
bool force, bool skip_file_size_checks) {
|
||||
bool has_remote = data.remote_.type() == RemoteFileLocation::Type::Full;
|
||||
bool has_generate = data.generate_ != nullptr;
|
||||
if (data.local_.type() == LocalFileLocation::Type::Full && !force) {
|
||||
@ -1114,7 +1118,7 @@ Result<FileId> FileManager::register_file(FileData &&data, FileLocationSource fi
|
||||
}
|
||||
}
|
||||
|
||||
auto status = check_local_location(data.local_.full(), data.size_);
|
||||
auto status = check_local_location(data.local_.full(), data.size_, skip_file_size_checks);
|
||||
if (status.is_error()) {
|
||||
LOG(WARNING) << "Invalid " << data.local_.full() << ": " << status << " from " << source;
|
||||
data.local_ = LocalFileLocation();
|
||||
@ -2527,6 +2531,10 @@ void FileManager::run_generate(FileNodePtr node) {
|
||||
return;
|
||||
}
|
||||
FileView file_view(node);
|
||||
if (!file_view.can_generate()) {
|
||||
LOG(INFO) << "Skip run_generate, because file " << node->main_file_id_ << " can't be generated";
|
||||
return;
|
||||
}
|
||||
if (file_view.has_local_location()) {
|
||||
LOG(INFO) << "Skip run_generate, because file " << node->main_file_id_ << " has local location";
|
||||
return;
|
||||
@ -2535,10 +2543,6 @@ void FileManager::run_generate(FileNodePtr node) {
|
||||
LOG(INFO) << "Skip run_generate, because file " << node->main_file_id_ << " can be downloaded from server";
|
||||
return;
|
||||
}
|
||||
if (!file_view.can_generate()) {
|
||||
LOG(INFO) << "Skip run_generate, because file " << node->main_file_id_ << " can't be generated";
|
||||
return;
|
||||
}
|
||||
|
||||
int8 download_priority = 0;
|
||||
int8 upload_priority = 0;
|
||||
@ -2644,7 +2648,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
|
||||
return;
|
||||
}
|
||||
if (file_view.has_generate_location() && file_view.generate_location().file_type_ == FileType::Secure) {
|
||||
// Can't upload secure file before its size is known.
|
||||
// Can't upload secure file before its size is known
|
||||
LOG(INFO) << "Can't upload secure file " << node->main_file_id_ << " before it's size is known";
|
||||
return;
|
||||
}
|
||||
@ -3314,17 +3318,27 @@ void FileManager::on_download_ok(QueryId query_id, const FullLocalFileLocation &
|
||||
return;
|
||||
}
|
||||
|
||||
auto query = finish_query(query_id).first;
|
||||
Query query;
|
||||
bool was_active;
|
||||
std::tie(query, was_active) = finish_query(query_id);
|
||||
auto file_id = query.file_id_;
|
||||
LOG(INFO) << "ON DOWNLOAD OK of " << (is_new ? "new" : "checked") << " file " << file_id << " of size " << size;
|
||||
auto r_new_file_id = register_local(local, DialogId(), size);
|
||||
auto r_new_file_id = register_local(local, DialogId(), size, false, false, true);
|
||||
Status status = Status::OK();
|
||||
if (r_new_file_id.is_error()) {
|
||||
LOG(ERROR) << "Can't register local file after download: " << r_new_file_id.error();
|
||||
status = Status::Error(PSLICE() << "Can't register local file after download: " << r_new_file_id.error().message());
|
||||
} else {
|
||||
if (is_new) {
|
||||
context_->on_new_file(size, get_file_view(r_new_file_id.ok()).get_allocated_local_size(), 1);
|
||||
}
|
||||
LOG_STATUS(merge(r_new_file_id.ok(), file_id));
|
||||
auto r_file_id = merge(r_new_file_id.ok(), file_id);
|
||||
if (r_file_id.is_error()) {
|
||||
status = r_file_id.move_as_error();
|
||||
}
|
||||
}
|
||||
if (status.is_error()) {
|
||||
LOG(ERROR) << status.message();
|
||||
return on_error_impl(get_file_node(file_id), query.type_, was_active, std::move(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,7 +410,8 @@ class FileManager : public FileLoadManager::Callback {
|
||||
|
||||
FileId register_empty(FileType type);
|
||||
Result<FileId> register_local(FullLocalFileLocation location, DialogId owner_dialog_id, int64 size,
|
||||
bool get_by_hash = false, bool force = false) TD_WARN_UNUSED_RESULT;
|
||||
bool get_by_hash = false, bool force = false,
|
||||
bool skip_file_size_checks = false) TD_WARN_UNUSED_RESULT;
|
||||
FileId register_remote(const FullRemoteFileLocation &location, FileLocationSource file_location_source,
|
||||
DialogId owner_dialog_id, int64 size, int64 expected_size, string name) TD_WARN_UNUSED_RESULT;
|
||||
Result<FileId> register_generate(FileType file_type, FileLocationSource file_location_source, string original_path,
|
||||
@ -494,8 +495,8 @@ class FileManager : public FileLoadManager::Callback {
|
||||
|
||||
FileId register_url(string url, FileType file_type, FileLocationSource file_location_source,
|
||||
DialogId owner_dialog_id);
|
||||
Result<FileId> register_file(FileData &&data, FileLocationSource file_location_source, const char *source,
|
||||
bool force);
|
||||
Result<FileId> register_file(FileData &&data, FileLocationSource file_location_source, const char *source, bool force,
|
||||
bool skip_file_size_checks = false);
|
||||
|
||||
static constexpr int8 FROM_BYTES_PRIORITY = 10;
|
||||
|
||||
@ -592,7 +593,7 @@ class FileManager : public FileLoadManager::Callback {
|
||||
|
||||
Status check_local_location(FileNodePtr node);
|
||||
bool try_fix_partial_local_location(FileNodePtr node);
|
||||
Status check_local_location(FullLocalFileLocation &location, int64 &size);
|
||||
Status check_local_location(FullLocalFileLocation &location, int64 &size, bool skip_file_size_checks);
|
||||
void try_flush_node_full(FileNodePtr node, bool new_remote, bool new_local, bool new_generate, FileDbId other_pmc_id);
|
||||
void try_flush_node(FileNodePtr node, const char *source);
|
||||
void try_flush_node_info(FileNodePtr node, const char *source);
|
||||
|
@ -321,8 +321,11 @@ class OutboundSecretMessage : public SecretChatLogEventBase<OutboundSecretMessag
|
||||
}
|
||||
|
||||
bool is_sent = false;
|
||||
bool is_service = false;
|
||||
// need send push notification to the receiver
|
||||
// should send such messages with messages_sendEncryptedService
|
||||
bool need_notify_user = false;
|
||||
bool is_rewritable = false;
|
||||
// should notify our parent about state of this message (using context and random_id)
|
||||
bool is_external = false;
|
||||
|
||||
tl_object_ptr<secret_api::DecryptedMessageAction> action;
|
||||
@ -331,7 +334,6 @@ class OutboundSecretMessage : public SecretChatLogEventBase<OutboundSecretMessag
|
||||
// Flags:
|
||||
// 2. can_fail = !file.empty() // send of other messages can't fail if chat is ok. It is usless to rewrite them with
|
||||
// empty
|
||||
// 1. is_service // use messages_sendEncryptedsService
|
||||
// 3. can_rewrite_with_empty // false for almost all service messages
|
||||
|
||||
// TODO: combine these two functions into one macros hell. Or a lambda hell.
|
||||
@ -351,7 +353,7 @@ class OutboundSecretMessage : public SecretChatLogEventBase<OutboundSecretMessag
|
||||
bool has_action = static_cast<bool>(action);
|
||||
BEGIN_STORE_FLAGS();
|
||||
STORE_FLAG(is_sent);
|
||||
STORE_FLAG(is_service);
|
||||
STORE_FLAG(need_notify_user);
|
||||
STORE_FLAG(has_action);
|
||||
STORE_FLAG(is_rewritable);
|
||||
STORE_FLAG(is_external);
|
||||
@ -381,7 +383,7 @@ class OutboundSecretMessage : public SecretChatLogEventBase<OutboundSecretMessag
|
||||
bool has_action;
|
||||
BEGIN_PARSE_FLAGS();
|
||||
PARSE_FLAG(is_sent);
|
||||
PARSE_FLAG(is_service);
|
||||
PARSE_FLAG(need_notify_user);
|
||||
PARSE_FLAG(has_action);
|
||||
PARSE_FLAG(is_rewritable);
|
||||
PARSE_FLAG(is_external);
|
||||
@ -395,9 +397,9 @@ class OutboundSecretMessage : public SecretChatLogEventBase<OutboundSecretMessag
|
||||
|
||||
StringBuilder &print(StringBuilder &sb) const override {
|
||||
return sb << "[Logevent OutboundSecretMessage " << tag("id", logevent_id()) << tag("chat_id", chat_id)
|
||||
<< tag("is_sent", is_sent) << tag("is_service", is_service) << tag("is_rewritable", is_rewritable)
|
||||
<< tag("is_external", is_external) << tag("message_id", message_id) << tag("random_id", random_id)
|
||||
<< tag("my_in_seq_no", my_in_seq_no) << tag("my_out_seq_no", my_out_seq_no)
|
||||
<< tag("is_sent", is_sent) << tag("need_notify_user", need_notify_user)
|
||||
<< tag("is_rewritable", is_rewritable) << tag("is_external", is_external) << tag("message_id", message_id)
|
||||
<< tag("random_id", random_id) << tag("my_in_seq_no", my_in_seq_no) << tag("my_out_seq_no", my_out_seq_no)
|
||||
<< tag("his_in_seq_no", his_in_seq_no) << tag("file", file) << tag("action", to_string(action)) << "]";
|
||||
}
|
||||
};
|
||||
|
@ -351,4 +351,27 @@ Result<string> check_url(Slice url) {
|
||||
return http_url.get_url();
|
||||
}
|
||||
|
||||
string remove_emoji_modifiers(string emoji) {
|
||||
static const Slice modifiers[] = {u8"\uFE0E" /* variation selector-15 */,
|
||||
u8"\uFE0F" /* variation selector-16 */,
|
||||
u8"\u200D\u2640" /* zero width joiner + female sign */,
|
||||
u8"\u200D\u2642" /* zero width joiner + male sign */,
|
||||
u8"\U0001F3FB" /* emoji modifier fitzpatrick type-1-2 */,
|
||||
u8"\U0001F3FC" /* emoji modifier fitzpatrick type-3 */,
|
||||
u8"\U0001F3FD" /* emoji modifier fitzpatrick type-4 */,
|
||||
u8"\U0001F3FE" /* emoji modifier fitzpatrick type-5 */,
|
||||
u8"\U0001F3FF" /* emoji modifier fitzpatrick type-6 */};
|
||||
bool found = true;
|
||||
while (found) {
|
||||
found = false;
|
||||
for (auto &modifier : modifiers) {
|
||||
if (ends_with(emoji, modifier) && emoji.size() > modifier.size()) {
|
||||
emoji.resize(emoji.size() - modifier.size());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return emoji;
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -39,4 +39,7 @@ string get_emoji_fingerprint(uint64 num);
|
||||
// checks whether url is a valid tg, ton or HTTP(S) URL and returns its in a canonical form
|
||||
Result<string> check_url(Slice url);
|
||||
|
||||
// removes all emoji modifiers
|
||||
string remove_emoji_modifiers(string emoji);
|
||||
|
||||
} // namespace td
|
||||
|
@ -1160,8 +1160,8 @@ DcOptions ConnectionCreator::get_default_dc_options(bool is_test) {
|
||||
auto add_ip_ports = [&res](int32 dc_id, const vector<string> &ips, const vector<int> &ports,
|
||||
HostType type = HostType::IPv4) {
|
||||
IPAddress ip_address;
|
||||
for (auto &ip : ips) {
|
||||
for (auto port : ports) {
|
||||
for (auto &ip : ips) {
|
||||
switch (type) {
|
||||
case HostType::IPv4:
|
||||
ip_address.init_ipv4_port(ip, port).ensure();
|
||||
@ -1201,7 +1201,7 @@ DcOptions ConnectionCreator::get_default_dc_options(bool is_test) {
|
||||
add_ip_ports(3, {"2001:b28:f23d:f003::e"}, ports, HostType::IPv6);
|
||||
} else {
|
||||
add_ip_ports(1, {"149.154.175.50"}, ports);
|
||||
add_ip_ports(2, {"149.154.167.51"}, ports);
|
||||
add_ip_ports(2, {"149.154.167.51", "95.161.76.100"}, ports);
|
||||
add_ip_ports(3, {"149.154.175.100"}, ports);
|
||||
add_ip_ports(4, {"149.154.167.91"}, ports);
|
||||
add_ip_ports(5, {"149.154.171.5"}, ports);
|
||||
|
@ -173,7 +173,7 @@ void DcAuthManager::dc_loop(DcInfo &dc) {
|
||||
auto id = UniqueId::next();
|
||||
auto query = G()->net_query_creator().create(id, telegram_api::auth_exportAuthorization(dc.dc_id.get_raw_id()),
|
||||
DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On);
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, dc.dc_id.get_raw_id()));
|
||||
dc.wait_id = id;
|
||||
dc.export_id = -1;
|
||||
@ -190,7 +190,7 @@ void DcAuthManager::dc_loop(DcInfo &dc) {
|
||||
auto query = G()->net_query_creator().create(
|
||||
id, telegram_api::auth_importAuthorization(dc.export_id, std::move(dc.export_bytes)), dc.dc_id,
|
||||
NetQuery::Type::Common, NetQuery::AuthFlag::Off);
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, dc.dc_id.get_raw_id()));
|
||||
dc.wait_id = id;
|
||||
dc.state = DcInfo::State::BeforeOk;
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
ListNode net_query_list_;
|
||||
|
||||
int32 NetQuery::get_my_id() {
|
||||
return G()->get_my_id();
|
||||
}
|
||||
@ -64,25 +62,34 @@ void NetQuery::set_error(Status status, string source) {
|
||||
set_error_impl(std::move(status), std::move(source));
|
||||
}
|
||||
|
||||
TsList<NetQueryDebug> &NetQuery::get_net_query_list() {
|
||||
static TsList<NetQueryDebug> net_query_list;
|
||||
return net_query_list;
|
||||
}
|
||||
|
||||
void dump_pending_network_queries() {
|
||||
auto n = NetQueryCounter::get_count();
|
||||
LOG(WARNING) << tag("pending net queries", n);
|
||||
|
||||
decltype(n) i = 0;
|
||||
bool was_gap = false;
|
||||
for (auto end = &net_query_list_, cur = end->prev; cur != end; cur = cur->prev, i++) {
|
||||
auto &net_query_list = NetQuery::get_net_query_list();
|
||||
auto guard = net_query_list.lock();
|
||||
for (auto end = net_query_list.end(), cur = net_query_list.begin(); cur != end; cur = cur->get_next(), i++) {
|
||||
if (i < 20 || i + 20 > n || i % (n / 20 + 1) == 0) {
|
||||
if (was_gap) {
|
||||
LOG(WARNING) << "...";
|
||||
was_gap = false;
|
||||
}
|
||||
auto nq = &static_cast<NetQuery &>(*cur);
|
||||
LOG(WARNING) << tag("id", nq->my_id_) << *nq << tag("total_flood", format::as_time(nq->total_timeout)) << " "
|
||||
<< tag("since start", format::as_time(Time::now_cached() - nq->start_timestamp_))
|
||||
<< tag("state", nq->debug_str_)
|
||||
<< tag("since state", format::as_time(Time::now_cached() - nq->debug_timestamp_))
|
||||
<< tag("resend_cnt", nq->debug_resend_cnt_) << tag("fail_cnt", nq->debug_send_failed_cnt_)
|
||||
<< tag("ack", nq->debug_ack) << tag("unknown", nq->debug_unknown);
|
||||
const NetQueryDebug &debug = cur->get_data_unsafe();
|
||||
const NetQuery &nq = *static_cast<const NetQuery *>(cur);
|
||||
LOG(WARNING) << tag("user", debug.my_id_) << nq << tag("total flood", format::as_time(nq.total_timeout_))
|
||||
<< tag("since start", format::as_time(Time::now_cached() - debug.start_timestamp_))
|
||||
<< tag("state", debug.state_)
|
||||
<< tag("in this state", format::as_time(Time::now_cached() - debug.state_timestamp_))
|
||||
<< tag("state changed", debug.state_change_count_) << tag("resend count", debug.resend_count_)
|
||||
<< tag("fail count", debug.send_failed_count_) << tag("ack state", debug.ack_state_)
|
||||
<< tag("unknown", debug.unknown_state_);
|
||||
} else {
|
||||
was_gap = true;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/List.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/ObjectPool.h"
|
||||
#include "td/utils/Slice.h"
|
||||
@ -24,6 +23,7 @@
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
#include "td/utils/TsList.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
@ -40,9 +40,19 @@ class NetQueryCallback : public Actor {
|
||||
virtual void on_result_resendable(NetQueryPtr query, Promise<NetQueryPtr> promise);
|
||||
};
|
||||
|
||||
extern ListNode net_query_list_;
|
||||
struct NetQueryDebug {
|
||||
double start_timestamp_ = 0;
|
||||
int32 my_id_ = 0;
|
||||
int32 resend_count_ = 0;
|
||||
string state_ = "empty";
|
||||
double state_timestamp_ = 0;
|
||||
int32 state_change_count_ = 0;
|
||||
int32 send_failed_count_ = 0;
|
||||
int ack_state_ = 0;
|
||||
bool unknown_state_ = false;
|
||||
};
|
||||
|
||||
class NetQuery : public ListNode {
|
||||
class NetQuery : public TsListNode<NetQueryDebug> {
|
||||
public:
|
||||
NetQuery() = default;
|
||||
|
||||
@ -78,7 +88,10 @@ class NetQuery : public ListNode {
|
||||
|
||||
void resend(DcId new_dc_id) {
|
||||
VLOG(net_query) << "Resend" << *this;
|
||||
debug_resend_cnt_++;
|
||||
{
|
||||
auto guard = lock();
|
||||
get_data_unsafe().resend_count_++;
|
||||
}
|
||||
dc_id_ = new_dc_id;
|
||||
status_ = Status::OK();
|
||||
state_ = State::Query;
|
||||
@ -133,7 +146,7 @@ class NetQuery : public ListNode {
|
||||
void on_net_write(size_t size);
|
||||
void on_net_read(size_t size);
|
||||
|
||||
void set_error(Status status, string source = "");
|
||||
void set_error(Status status, string source = string());
|
||||
|
||||
void set_error_resend() {
|
||||
set_error_impl(Status::Error<Error::Resend>());
|
||||
@ -213,7 +226,10 @@ class NetQuery : public ListNode {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
LOG_IF(ERROR, !is_ready()) << "Destroy not ready query " << *this << " " << tag("debug", debug_str_);
|
||||
if (!is_ready()) {
|
||||
auto guard = lock();
|
||||
LOG(ERROR) << "Destroy not ready query " << *this << " " << tag("state", get_data_unsafe().state_);
|
||||
}
|
||||
// TODO: CHECK if net_query is lost here
|
||||
cancel_slot_.close();
|
||||
*this = NetQuery();
|
||||
@ -228,15 +244,20 @@ class NetQuery : public ListNode {
|
||||
}
|
||||
|
||||
void debug_send_failed() {
|
||||
debug_send_failed_cnt_++;
|
||||
auto guard = lock();
|
||||
get_data_unsafe().send_failed_count_++;
|
||||
}
|
||||
|
||||
void debug(string str, bool may_be_lost = false) {
|
||||
void debug(string state, bool may_be_lost = false) {
|
||||
may_be_lost_ = may_be_lost;
|
||||
debug_str_ = std::move(str);
|
||||
debug_timestamp_ = Time::now();
|
||||
debug_cnt_++;
|
||||
VLOG(net_query) << *this << " " << tag("debug", debug_str_);
|
||||
{
|
||||
auto guard = lock();
|
||||
auto &data = get_data_unsafe();
|
||||
data.state_ = state;
|
||||
data.state_timestamp_ = Time::now();
|
||||
data.state_change_count_++;
|
||||
}
|
||||
VLOG(net_query) << *this << " " << tag("state", state);
|
||||
}
|
||||
|
||||
void set_callback(ActorShared<NetQueryCallback> callback) {
|
||||
@ -258,6 +279,8 @@ class NetQuery : public ListNode {
|
||||
|
||||
static int32 tl_magic(const BufferSlice &buffer_slice);
|
||||
|
||||
static TsList<NetQueryDebug> &get_net_query_list();
|
||||
|
||||
private:
|
||||
State state_ = State::Empty;
|
||||
Type type_ = Type::Common;
|
||||
@ -265,6 +288,7 @@ class NetQuery : public ListNode {
|
||||
GzipFlag gzip_flag_ = GzipFlag::Off;
|
||||
DcId dc_id_;
|
||||
|
||||
NetQueryCounter nq_counter_;
|
||||
Status status_;
|
||||
uint64 id_ = 0;
|
||||
BufferSlice query_;
|
||||
@ -273,6 +297,9 @@ class NetQuery : public ListNode {
|
||||
|
||||
NetQueryRef invoke_after_;
|
||||
uint32 session_rand_ = 0;
|
||||
|
||||
bool may_be_lost_ = false;
|
||||
|
||||
template <class T>
|
||||
struct movable_atomic : public std::atomic<T> {
|
||||
movable_atomic() = default;
|
||||
@ -290,44 +317,32 @@ class NetQuery : public ListNode {
|
||||
~movable_atomic() = default;
|
||||
};
|
||||
|
||||
static int32 get_my_id();
|
||||
|
||||
movable_atomic<uint64> session_id_{0};
|
||||
uint64 message_id_{0};
|
||||
|
||||
movable_atomic<int32> cancellation_token_{-1}; // == 0 if query is canceled
|
||||
ActorShared<NetQueryCallback> callback_;
|
||||
|
||||
void set_error_impl(Status status, string source = "") {
|
||||
void set_error_impl(Status status, string source = string()) {
|
||||
VLOG(net_query) << "Got error " << *this << " " << status;
|
||||
status_ = std::move(status);
|
||||
state_ = State::Error;
|
||||
source_ = std::move(source);
|
||||
}
|
||||
|
||||
public:
|
||||
double next_timeout = 1;
|
||||
double total_timeout = 0;
|
||||
double total_timeout_limit = 60;
|
||||
double last_timeout = 0;
|
||||
bool need_resend_on_503 = true;
|
||||
bool may_be_lost_ = false;
|
||||
string debug_str_ = "empty";
|
||||
string source_;
|
||||
double debug_timestamp_ = 0;
|
||||
int32 debug_cnt_ = 0;
|
||||
int32 debug_send_failed_cnt_ = 0;
|
||||
int32 debug_resend_cnt_ = 0;
|
||||
int debug_ack = 0;
|
||||
bool debug_unknown = false;
|
||||
int32 dispatch_ttl = -1;
|
||||
Slot cancel_slot_;
|
||||
Promise<> quick_ack_promise_;
|
||||
int32 file_type_ = -1;
|
||||
static int32 get_my_id();
|
||||
|
||||
double start_timestamp_ = 0;
|
||||
int32 my_id_ = 0;
|
||||
NetQueryCounter nq_counter_;
|
||||
public:
|
||||
double next_timeout_ = 1; // for NetQueryDelayer
|
||||
double total_timeout_ = 0; // for NetQueryDelayer/SequenceDispatcher
|
||||
double total_timeout_limit_ = 60; // for NetQueryDelayer/SequenceDispatcher and to be set by caller
|
||||
double last_timeout_ = 0; // for NetQueryDelayer/SequenceDispatcher
|
||||
string source_; // for NetQueryDelayer/SequenceDispatcher
|
||||
bool need_resend_on_503_ = true; // for NetQueryDispatcher and to be set by caller
|
||||
int32 dispatch_ttl_ = -1; // for NetQueryDispatcher and to be set by caller
|
||||
Slot cancel_slot_; // for Session and to be set by caller
|
||||
Promise<> quick_ack_promise_; // for Session and to be set by caller
|
||||
int32 file_type_ = -1; // to be set by caller
|
||||
|
||||
NetQuery(State state, uint64 id, BufferSlice &&query, BufferSlice &&answer, DcId dc_id, Type type, AuthFlag auth_flag,
|
||||
GzipFlag gzip_flag, int32 tl_constructor, double total_timeout_limit)
|
||||
@ -336,17 +351,17 @@ class NetQuery : public ListNode {
|
||||
, auth_flag_(auth_flag)
|
||||
, gzip_flag_(gzip_flag)
|
||||
, dc_id_(dc_id)
|
||||
, nq_counter_(true)
|
||||
, status_()
|
||||
, id_(id)
|
||||
, query_(std::move(query))
|
||||
, answer_(std::move(answer))
|
||||
, tl_constructor_(tl_constructor)
|
||||
, total_timeout_limit(total_timeout_limit)
|
||||
, nq_counter_(true) {
|
||||
my_id_ = get_my_id();
|
||||
start_timestamp_ = Time::now();
|
||||
, total_timeout_limit_(total_timeout_limit) {
|
||||
get_data_unsafe().my_id_ = get_my_id();
|
||||
get_data_unsafe().start_timestamp_ = Time::now();
|
||||
LOG(INFO) << *this;
|
||||
// net_query_list_.put(this);
|
||||
get_net_query_list().put(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -59,9 +59,12 @@ NetQueryPtr NetQueryCreator::create(uint64 id, const telegram_api::Function &fun
|
||||
auto td = G()->td();
|
||||
if (!td.empty()) {
|
||||
auto auth_manager = td.get_actor_unsafe()->auth_manager_.get();
|
||||
if (auth_manager && auth_manager->is_bot()) {
|
||||
if (auth_manager != nullptr && auth_manager->is_bot()) {
|
||||
total_timeout_limit = 8;
|
||||
}
|
||||
if ((auth_manager == nullptr || !auth_manager->is_authorized()) && auth_flag == NetQuery::AuthFlag::On) {
|
||||
LOG(ERROR) << "Send query before authorization: " << to_string(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto query = object_pool_.create(NetQuery::State::Query, id, std::move(slice), BufferSlice(), dc_id, type, auth_flag,
|
||||
|
@ -46,30 +46,30 @@ void NetQueryDelayer::delay(NetQueryPtr query) {
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
timeout = query->next_timeout;
|
||||
timeout = query->next_timeout_;
|
||||
if (timeout < 60) {
|
||||
query->next_timeout *= 2;
|
||||
query->next_timeout_ *= 2;
|
||||
}
|
||||
} else {
|
||||
query->next_timeout = 1;
|
||||
query->next_timeout_ = 1;
|
||||
}
|
||||
query->total_timeout += timeout;
|
||||
query->last_timeout = timeout;
|
||||
query->total_timeout_ += timeout;
|
||||
query->last_timeout_ = timeout;
|
||||
|
||||
auto error = query->error().move_as_error();
|
||||
query->resend();
|
||||
|
||||
// Fix for infinity flood control
|
||||
if (!query->need_resend_on_503 && code == -503) {
|
||||
if (!query->need_resend_on_503_ && code == -503) {
|
||||
query->set_error(Status::Error(502, "Bad Gateway"));
|
||||
query->debug("DcManager: send to DcManager");
|
||||
G()->net_query_dispatcher().dispatch(std::move(query));
|
||||
return;
|
||||
}
|
||||
|
||||
if (query->total_timeout > query->total_timeout_limit) {
|
||||
if (query->total_timeout_ > query->total_timeout_limit_) {
|
||||
// TODO: support timeouts in DcAuth and GetConfig
|
||||
LOG(WARNING) << "Failed: " << query << " " << tag("timeout", timeout) << tag("total_timeout", query->total_timeout)
|
||||
LOG(WARNING) << "Failed: " << query << " " << tag("timeout", timeout) << tag("total_timeout", query->total_timeout_)
|
||||
<< " because of " << error << " from " << query->source_;
|
||||
// NB: code must differ from tdapi FLOOD_WAIT code
|
||||
query->set_error(
|
||||
@ -79,7 +79,7 @@ void NetQueryDelayer::delay(NetQueryPtr query) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(WARNING) << "Delay: " << query << " " << tag("timeout", timeout) << tag("total_timeout", query->total_timeout)
|
||||
LOG(WARNING) << "Delay: " << query << " " << tag("timeout", timeout) << tag("total_timeout", query->total_timeout_)
|
||||
<< " because of " << error << " from " << query->source_;
|
||||
query->debug(PSTRING() << "delay for " << format::as_time(timeout));
|
||||
auto id = container_.create(QuerySlot());
|
||||
|
@ -72,7 +72,7 @@ void NetQueryDispatcher::dispatch(NetQueryPtr net_query) {
|
||||
}
|
||||
|
||||
if (!net_query->is_ready()) {
|
||||
if (net_query->dispatch_ttl == 0) {
|
||||
if (net_query->dispatch_ttl_ == 0) {
|
||||
net_query->set_error(Status::Error("DispatchTtlError"));
|
||||
}
|
||||
}
|
||||
@ -89,8 +89,8 @@ void NetQueryDispatcher::dispatch(NetQueryPtr net_query) {
|
||||
return complete_net_query(std::move(net_query));
|
||||
}
|
||||
|
||||
if (net_query->dispatch_ttl > 0) {
|
||||
net_query->dispatch_ttl--;
|
||||
if (net_query->dispatch_ttl_ > 0) {
|
||||
net_query->dispatch_ttl_--;
|
||||
}
|
||||
|
||||
size_t dc_pos = static_cast<size_t>(dest_dc_id.get_raw_id() - 1);
|
||||
|
@ -69,7 +69,7 @@ void PublicRsaKeyWatchdog::loop() {
|
||||
flood_control_.add_event(static_cast<int32>(Time::now_cached()));
|
||||
has_query_ = true;
|
||||
auto query = G()->net_query_creator().create(telegram_api::help_getCdnConfig());
|
||||
query->total_timeout_limit = 60 * 60 * 24;
|
||||
query->total_timeout_limit_ = 60 * 60 * 24;
|
||||
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this));
|
||||
}
|
||||
|
||||
|
@ -617,7 +617,10 @@ void Session::on_message_ack_impl_inner(uint64 id, int32 type, bool in_container
|
||||
}
|
||||
VLOG(net_query) << "Ack " << tag("msg_id", id) << it->second.query;
|
||||
it->second.ack = true;
|
||||
it->second.query->debug_ack |= type;
|
||||
{
|
||||
auto lock = it->second.query->lock();
|
||||
it->second.query->get_data_unsafe().ack_state_ |= type;
|
||||
}
|
||||
it->second.query->quick_ack_promise_.set_value(Unit());
|
||||
if (!in_container) {
|
||||
cleanup_container(id, &it->second);
|
||||
@ -652,7 +655,10 @@ void Session::cleanup_container(uint64 message_id, Query *query) {
|
||||
}
|
||||
|
||||
void Session::mark_as_known(uint64 id, Query *query) {
|
||||
query->query->debug_unknown = false;
|
||||
{
|
||||
auto lock = query->query->lock();
|
||||
query->query->get_data_unsafe().unknown_state_ = false;
|
||||
}
|
||||
if (!query->unknown) {
|
||||
return;
|
||||
}
|
||||
@ -665,7 +671,10 @@ void Session::mark_as_known(uint64 id, Query *query) {
|
||||
}
|
||||
|
||||
void Session::mark_as_unknown(uint64 id, Query *query) {
|
||||
query->query->debug_unknown = true;
|
||||
{
|
||||
auto lock = query->query->lock();
|
||||
query->query->get_data_unsafe().unknown_state_ = true;
|
||||
}
|
||||
if (query->unknown) {
|
||||
return;
|
||||
}
|
||||
@ -930,8 +939,11 @@ void Session::connection_send_query(ConnectionInfo *info, NetQueryPtr &&net_quer
|
||||
net_query->set_message_id(message_id);
|
||||
net_query->cancel_slot_.clear_event();
|
||||
LOG_CHECK(sent_queries_.find(message_id) == sent_queries_.end()) << message_id;
|
||||
net_query->debug_unknown = false;
|
||||
net_query->debug_ack = 0;
|
||||
{
|
||||
auto lock = net_query->lock();
|
||||
net_query->get_data_unsafe().unknown_state_ = false;
|
||||
net_query->get_data_unsafe().ack_state_ = 0;
|
||||
}
|
||||
if (!net_query->cancel_slot_.empty()) {
|
||||
LOG(DEBUG) << "Set event for net_query cancellation " << tag("message_id", format::as_hex(message_id));
|
||||
net_query->cancel_slot_.set_event(EventCreator::raw(actor_id(), message_id));
|
||||
@ -1106,7 +1118,7 @@ bool Session::connection_send_check_main_key(ConnectionInfo *info) {
|
||||
last_check_query_id_ = UniqueId::next(UniqueId::BindKey);
|
||||
NetQueryPtr query = G()->net_query_creator().create(last_check_query_id_, telegram_api::help_getNearestDc(),
|
||||
DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On);
|
||||
query->dispatch_ttl = 0;
|
||||
query->dispatch_ttl_ = 0;
|
||||
query->set_callback(actor_shared(this));
|
||||
connection_send_query(info, std::move(query));
|
||||
|
||||
@ -1143,7 +1155,7 @@ bool Session::connection_send_bind_key(ConnectionInfo *info) {
|
||||
last_bind_query_id_,
|
||||
telegram_api::auth_bindTempAuthKey(perm_auth_key_id, nonce, expires_at, std::move(encrypted)), DcId::main(),
|
||||
NetQuery::Type::Common, NetQuery::AuthFlag::On);
|
||||
query->dispatch_ttl = 0;
|
||||
query->dispatch_ttl_ = 0;
|
||||
query->set_callback(actor_shared(this));
|
||||
connection_send_query(info, std::move(query), message_id);
|
||||
|
||||
|
@ -39,7 +39,7 @@ SessionMultiProxy::SessionMultiProxy(int32 session_count, std::shared_ptr<AuthDa
|
||||
void SessionMultiProxy::send(NetQueryPtr query) {
|
||||
size_t pos = 0;
|
||||
// TODO temporary hack with total_timeout_limit
|
||||
if (query->auth_flag() == NetQuery::AuthFlag::On && query->total_timeout_limit > 7) {
|
||||
if (query->auth_flag() == NetQuery::AuthFlag::On && query->total_timeout_limit_ > 7) {
|
||||
if (query->session_rand()) {
|
||||
pos = query->session_rand() % sessions_.size();
|
||||
} else {
|
||||
|
@ -8,8 +8,10 @@
|
||||
|
||||
#include "td/utils/port/CxCli.h"
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include "td/telegram/td_api.h"
|
||||
#include "td/telegram/td_api.hpp"
|
||||
#pragma managed(pop)
|
||||
|
||||
namespace Telegram {
|
||||
namespace Td {
|
||||
|
@ -6,10 +6,13 @@
|
||||
//
|
||||
#include "td/actor/MultiPromise.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
void MultiPromiseActor::add_promise(Promise<Unit> &&promise) {
|
||||
promises_.emplace_back(std::move(promise));
|
||||
LOG(DEBUG) << "Add promise #" << promises_.size() << " to " << name_;
|
||||
}
|
||||
|
||||
Promise<Unit> MultiPromiseActor::get_promise() {
|
||||
@ -24,11 +27,13 @@ Promise<Unit> MultiPromiseActor::get_promise() {
|
||||
|
||||
future.set_event(EventCreator::raw(actor_id(), nullptr));
|
||||
futures_.emplace_back(std::move(future));
|
||||
LOG(DEBUG) << "Get promise #" << futures_.size() << " for " << name_;
|
||||
return PromiseCreator::from_promise_actor(std::move(promise));
|
||||
}
|
||||
|
||||
void MultiPromiseActor::raw_event(const Event::Raw &event) {
|
||||
received_results_++;
|
||||
LOG(DEBUG) << "Receive result #" << received_results_ << " out of " << futures_.size() << " for " << name_;
|
||||
if (received_results_ == futures_.size()) {
|
||||
if (!ignore_errors_) {
|
||||
for (auto &future : futures_) {
|
||||
@ -47,13 +52,21 @@ void MultiPromiseActor::set_ignore_errors(bool ignore_errors) {
|
||||
}
|
||||
|
||||
void MultiPromiseActor::set_result(Result<Unit> &&result) {
|
||||
// MultiPromiseActor should be cleared before he begins to send out result
|
||||
result_ = std::move(result);
|
||||
stop();
|
||||
}
|
||||
|
||||
void MultiPromiseActor::tear_down() {
|
||||
LOG(DEBUG) << "Set result for " << promises_.size() << " promises in " << name_;
|
||||
|
||||
// MultiPromiseActor should be cleared before it begins to send out result
|
||||
auto promises_copy = std::move(promises_);
|
||||
promises_.clear();
|
||||
auto futures_copy = std::move(futures_);
|
||||
futures_.clear();
|
||||
received_results_ = 0;
|
||||
stop();
|
||||
auto result = std::move(result_);
|
||||
result_ = Unit();
|
||||
|
||||
if (!promises_copy.empty()) {
|
||||
for (size_t i = 0; i + 1 < promises_copy.size(); i++) {
|
||||
|
@ -77,9 +77,12 @@ class MultiPromiseActor final
|
||||
vector<FutureActor<Unit>> futures_; // futures waiting for result of the queries
|
||||
size_t received_results_ = 0;
|
||||
bool ignore_errors_ = false;
|
||||
Result<Unit> result_;
|
||||
|
||||
void raw_event(const Event::Raw &event) override;
|
||||
|
||||
void tear_down() override;
|
||||
|
||||
void on_start_migrate(int32) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -35,8 +35,7 @@ class HttpHeaderCreator {
|
||||
sb_ << "HTTP/1.1 " << code << " " << reason << "\r\n";
|
||||
}
|
||||
void init_status_line(int http_status_code) {
|
||||
sb_ = StringBuilder(MutableSlice{header_, MAX_HEADER});
|
||||
sb_ << "HTTP/1.1 " << http_status_code << " " << get_status_line(http_status_code) << "\r\n";
|
||||
init_error(http_status_code, get_status_line(http_status_code));
|
||||
}
|
||||
void add_header(Slice key, Slice value) {
|
||||
sb_ << key << ": " << value << "\r\n";
|
||||
|
@ -8,13 +8,12 @@
|
||||
|
||||
#if !TD_EMSCRIPTEN
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/wstring_convert.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
@ -142,42 +141,14 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
|
||||
return preverify_ok;
|
||||
}
|
||||
|
||||
Status create_openssl_error(int code, Slice message) {
|
||||
const int max_result_size = 1 << 12;
|
||||
auto result = StackAllocator::alloc(max_result_size);
|
||||
StringBuilder sb(result.as_slice());
|
||||
|
||||
sb << message;
|
||||
while (unsigned long error_code = ERR_get_error()) {
|
||||
char error_buf[1024];
|
||||
ERR_error_string_n(error_code, error_buf, sizeof(error_buf));
|
||||
Slice error(error_buf, std::strlen(error_buf));
|
||||
sb << "{" << error << "}";
|
||||
}
|
||||
LOG_IF(ERROR, sb.is_error()) << "OpenSSL error buffer overflow";
|
||||
LOG(DEBUG) << sb.as_cslice();
|
||||
return Status::Error(code, sb.as_cslice());
|
||||
}
|
||||
|
||||
void openssl_clear_errors(Slice from) {
|
||||
if (ERR_peek_error() != 0) {
|
||||
LOG(ERROR) << from << ": " << create_openssl_error(0, "Unprocessed OPENSSL_ERROR");
|
||||
}
|
||||
#if TD_PORT_WINDOWS // TODO move to utils
|
||||
WSASetLastError(0);
|
||||
#else
|
||||
errno = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void do_ssl_shutdown(SSL *ssl_handle) {
|
||||
if (!SSL_is_init_finished(ssl_handle)) {
|
||||
return;
|
||||
}
|
||||
openssl_clear_errors("Before SSL_shutdown");
|
||||
clear_openssl_errors("Before SSL_shutdown");
|
||||
SSL_set_quiet_shutdown(ssl_handle, 1);
|
||||
SSL_shutdown(ssl_handle);
|
||||
openssl_clear_errors("After SSL_shutdown");
|
||||
clear_openssl_errors("After SSL_shutdown");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -209,7 +180,7 @@ class SslStreamImpl {
|
||||
}();
|
||||
CHECK(init_openssl);
|
||||
|
||||
openssl_clear_errors("Before SslFd::init");
|
||||
clear_openssl_errors("Before SslFd::init");
|
||||
|
||||
auto ssl_method =
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
@ -385,7 +356,7 @@ class SslStreamImpl {
|
||||
friend class SslWriteByteFlow;
|
||||
|
||||
Result<size_t> write(Slice slice) {
|
||||
openssl_clear_errors("Before SslFd::write");
|
||||
clear_openssl_errors("Before SslFd::write");
|
||||
auto size = SSL_write(ssl_handle_, slice.data(), static_cast<int>(slice.size()));
|
||||
if (size <= 0) {
|
||||
return process_ssl_error(size);
|
||||
@ -394,7 +365,7 @@ class SslStreamImpl {
|
||||
}
|
||||
|
||||
Result<size_t> read(MutableSlice slice) {
|
||||
openssl_clear_errors("Before SslFd::read");
|
||||
clear_openssl_errors("Before SslFd::read");
|
||||
auto size = SSL_read(ssl_handle_, slice.data(), static_cast<int>(slice.size()));
|
||||
if (size <= 0) {
|
||||
return process_ssl_error(size);
|
||||
|
@ -247,6 +247,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/tl_storers.h
|
||||
td/utils/translit.h
|
||||
td/utils/TsFileLog.h
|
||||
td/utils/TsList.h
|
||||
td/utils/type_traits.h
|
||||
td/utils/UInt.h
|
||||
td/utils/uint128.h
|
||||
@ -268,6 +269,7 @@ set(TDUTILS_TEST_SOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/HazardPointers.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/heap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/json.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/List.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/log.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/misc.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcQueue.cpp
|
||||
|
@ -28,19 +28,19 @@ struct ListNode {
|
||||
if (other.empty()) {
|
||||
clear();
|
||||
} else {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put(this);
|
||||
init_from(std::move(other));
|
||||
}
|
||||
}
|
||||
|
||||
ListNode &operator=(ListNode &&other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
this->remove();
|
||||
|
||||
if (!other.empty()) {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put(this);
|
||||
init_from(std::move(other));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -58,11 +58,12 @@ struct ListNode {
|
||||
}
|
||||
|
||||
void put(ListNode *other) {
|
||||
other->connect(next);
|
||||
this->connect(other);
|
||||
DCHECK(other->empty());
|
||||
put_unsafe(other);
|
||||
}
|
||||
|
||||
void put_back(ListNode *other) {
|
||||
DCHECK(other->empty());
|
||||
prev->connect(other);
|
||||
other->connect(this);
|
||||
}
|
||||
@ -82,11 +83,35 @@ struct ListNode {
|
||||
return next == this;
|
||||
}
|
||||
|
||||
private:
|
||||
ListNode *begin() {
|
||||
return next;
|
||||
}
|
||||
ListNode *end() {
|
||||
return this;
|
||||
}
|
||||
ListNode *get_next() {
|
||||
return next;
|
||||
}
|
||||
ListNode *get_prev() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
protected:
|
||||
void clear() {
|
||||
next = this;
|
||||
prev = this;
|
||||
}
|
||||
|
||||
void init_from(ListNode &&other) {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put_unsafe(this);
|
||||
}
|
||||
|
||||
void put_unsafe(ListNode *other) {
|
||||
other->connect(next);
|
||||
this->connect(other);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -18,8 +18,10 @@ class MovableValue {
|
||||
other.clear();
|
||||
}
|
||||
MovableValue &operator=(MovableValue &&other) {
|
||||
if (this != &other) {
|
||||
val_ = other.val_;
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
MovableValue(const MovableValue &) = delete;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "td/utils/common.h"
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -74,15 +75,34 @@ class SpanImpl {
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
InnerT &back() {
|
||||
DCHECK(!empty());
|
||||
return data_[size() - 1];
|
||||
}
|
||||
|
||||
const InnerT &back() const {
|
||||
DCHECK(!empty());
|
||||
return data_[size() - 1];
|
||||
}
|
||||
|
||||
InnerT *data() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
InnerT *begin() const {
|
||||
return data_;
|
||||
}
|
||||
InnerT *end() const {
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
std::reverse_iterator<InnerT *> rbegin() const {
|
||||
return std::reverse_iterator<InnerT *>(end());
|
||||
}
|
||||
std::reverse_iterator<InnerT *> rend() const {
|
||||
return std::reverse_iterator<InnerT *>(begin());
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return size_;
|
||||
}
|
||||
|
200
tdutils/td/utils/TsList.h
Normal file
200
tdutils/td/utils/TsList.h
Normal file
@ -0,0 +1,200 @@
|
||||
//
|
||||
// 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/utils/common.h"
|
||||
#include "td/utils/List.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class DataT>
|
||||
class TsList;
|
||||
|
||||
template <class DataT>
|
||||
class TsListNode : protected ListNode {
|
||||
public:
|
||||
TsListNode() {
|
||||
clear();
|
||||
}
|
||||
explicit TsListNode(DataT &&data) : data_(std::move(data)) {
|
||||
clear();
|
||||
}
|
||||
|
||||
~TsListNode() {
|
||||
remove();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT;
|
||||
|
||||
TsListNode(const TsListNode &) = delete;
|
||||
TsListNode &operator=(const TsListNode &) = delete;
|
||||
|
||||
TsListNode(TsListNode &&other) {
|
||||
other.validate();
|
||||
if (other.empty()) {
|
||||
data_ = std::move(other.data_);
|
||||
clear();
|
||||
} else {
|
||||
auto guard = other.lock();
|
||||
init_from(std::move(other));
|
||||
}
|
||||
validate();
|
||||
other.validate();
|
||||
}
|
||||
|
||||
TsListNode &operator=(TsListNode &&other) {
|
||||
validate();
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
other.validate();
|
||||
remove();
|
||||
|
||||
if (other.empty()) {
|
||||
data_ = std::move(other.data_);
|
||||
} else {
|
||||
auto guard = other.lock();
|
||||
init_from(std::move(other));
|
||||
}
|
||||
|
||||
validate();
|
||||
other.validate();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void validate() {
|
||||
CHECK(empty() || !ListNode::empty() || is_root);
|
||||
CHECK(!empty() || ListNode::empty());
|
||||
}
|
||||
|
||||
void remove() {
|
||||
validate();
|
||||
if (is_root) {
|
||||
CHECK(ListNode::empty());
|
||||
return;
|
||||
}
|
||||
if (empty()) {
|
||||
CHECK(ListNode::empty());
|
||||
return;
|
||||
}
|
||||
{
|
||||
auto guard = lock();
|
||||
ListNode::remove();
|
||||
if (!is_root) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
void put(TsListNode *other) {
|
||||
validate();
|
||||
other->validate();
|
||||
DCHECK(other->empty());
|
||||
DCHECK(!empty());
|
||||
DCHECK(!other->is_root);
|
||||
{
|
||||
auto guard = lock();
|
||||
ListNode::put(other);
|
||||
other->parent = parent;
|
||||
}
|
||||
validate();
|
||||
other->validate();
|
||||
}
|
||||
|
||||
void put_back(TsListNode *other) {
|
||||
DCHECK(other->empty());
|
||||
DCHECK(!empty());
|
||||
DCHECK(!other->is_root);
|
||||
auto guard = lock();
|
||||
ListNode::put_back(other);
|
||||
other->parent = parent;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return parent == nullptr;
|
||||
}
|
||||
|
||||
TsListNode *get_next() {
|
||||
return static_cast<TsListNode *>(next);
|
||||
}
|
||||
TsListNode *get_prev() {
|
||||
return static_cast<TsListNode *>(prev);
|
||||
}
|
||||
|
||||
DataT &get_data_unsafe() {
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
TsList<DataT> *parent;
|
||||
bool is_root{false};
|
||||
DataT data_;
|
||||
|
||||
friend class TsList<DataT>;
|
||||
|
||||
void clear() {
|
||||
ListNode::clear();
|
||||
if (!is_root) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void init_from(TsListNode &&other) {
|
||||
ListNode::init_from(std::move(other));
|
||||
parent = other.parent;
|
||||
other.parent = nullptr;
|
||||
data_ = std::move(other.data_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class DataT>
|
||||
class TsList : public TsListNode<DataT> {
|
||||
public:
|
||||
TsList() {
|
||||
this->parent = this;
|
||||
this->is_root = true;
|
||||
}
|
||||
TsList(const TsList &) = delete;
|
||||
TsList &operator=(const TsList &) = delete;
|
||||
TsList(TsList &&) = delete;
|
||||
TsList &operator=(TsList &&) = delete;
|
||||
~TsList() {
|
||||
CHECK(ListNode::empty());
|
||||
this->parent = nullptr;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT {
|
||||
return std::unique_lock<std::mutex>(mutex_);
|
||||
}
|
||||
TsListNode<DataT> *begin() {
|
||||
return this->get_next();
|
||||
}
|
||||
TsListNode<DataT> *end() {
|
||||
return this;
|
||||
}
|
||||
TsListNode<DataT> *get() {
|
||||
auto guard = lock();
|
||||
auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
|
||||
if (res) {
|
||||
res->parent = nullptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
template <class DataT>
|
||||
std::unique_lock<std::mutex> TsListNode<DataT>::lock() {
|
||||
CHECK(parent != nullptr);
|
||||
return parent->lock();
|
||||
}
|
||||
|
||||
} // namespace td
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user