Merge remote-tracking branch 'td/master'
This commit is contained in:
commit
6e1836a024
@ -392,6 +392,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/PhotoSizeSource.cpp
|
||||
td/telegram/PollManager.cpp
|
||||
td/telegram/QueryCombiner.cpp
|
||||
td/telegram/RecentDialogList.cpp
|
||||
td/telegram/ReplyMarkup.cpp
|
||||
td/telegram/ReportReason.cpp
|
||||
td/telegram/RestrictionReason.cpp
|
||||
@ -601,6 +602,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/PtsManager.h
|
||||
td/telegram/PublicDialogType.h
|
||||
td/telegram/QueryCombiner.h
|
||||
td/telegram/RecentDialogList.h
|
||||
td/telegram/ReplyMarkup.h
|
||||
td/telegram/ReportReason.h
|
||||
td/telegram/RequestActor.h
|
||||
|
12
build.html
12
build.html
@ -61,7 +61,7 @@ select.large { font-size: large; }
|
||||
<option>CentOS 8</option>
|
||||
<option>Debian 8</option>
|
||||
<option>Debian 9</option>
|
||||
<option>Debian 10</option>
|
||||
<option>Debian 10+</option>
|
||||
<option>Ubuntu 14</option>
|
||||
<option>Ubuntu 16</option>
|
||||
<option>Ubuntu 18</option>
|
||||
@ -639,7 +639,7 @@ function onOptionsChanged() {
|
||||
break;
|
||||
case 'Debian 8':
|
||||
case 'Debian 9':
|
||||
case 'Debian 10':
|
||||
case 'Debian 10+':
|
||||
case 'Ubuntu 14':
|
||||
case 'Ubuntu 16':
|
||||
case 'Ubuntu 18':
|
||||
@ -668,7 +668,7 @@ function onOptionsChanged() {
|
||||
}
|
||||
if (use_clang) {
|
||||
packages += ' clang' + getClangVersionSuffix() + ' libc++-dev';
|
||||
if (linux_distro === 'Debian 10' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
|
||||
if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
|
||||
packages += ' libc++abi-dev';
|
||||
}
|
||||
} else {
|
||||
@ -725,12 +725,12 @@ function onOptionsChanged() {
|
||||
commands.push('cd vcpkg');
|
||||
commands.push(local + 'bootstrap-vcpkg.bat');
|
||||
if (target === 'C++/CX') {
|
||||
commands.push(local + 'vcpkg.exe install gperf openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp');
|
||||
commands.push(local + 'vcpkg.exe install gperf:x86-windows openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp');
|
||||
} else {
|
||||
if (build_64bit) {
|
||||
commands.push(local + 'vcpkg.exe install gperf openssl:x64-windows zlib:x64-windows');
|
||||
commands.push(local + 'vcpkg.exe install gperf:x64-windows openssl:x64-windows zlib:x64-windows');
|
||||
} else {
|
||||
commands.push(local + 'vcpkg.exe install gperf openssl:x86-windows zlib:x86-windows');
|
||||
commands.push(local + 'vcpkg.exe install gperf:x86-windows openssl:x86-windows zlib:x86-windows');
|
||||
}
|
||||
}
|
||||
commands.push('cd ..');
|
||||
|
@ -7,13 +7,12 @@ This is an example of building TDLib with `C++/CLI` support and an example of TD
|
||||
* Download and install Microsoft Visual Studio 2015 or later.
|
||||
* Download and install [CMake](https://cmake.org/download/); choose "Add CMake to the system PATH" option while installing.
|
||||
* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start) or update it to the latest version using `vcpkg update` and following received instructions.
|
||||
* Install `zlib` and `openssl` using `vcpkg`:
|
||||
* Install `gperf`, `zlib`, and `openssl` using `vcpkg`:
|
||||
```
|
||||
cd <path to vcpkg>
|
||||
.\vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows
|
||||
.\vcpkg.exe install gperf:x64-windows gperf:x86-windows openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows
|
||||
```
|
||||
* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download#php-7.2). Add the path to php.exe to the PATH environment variable.
|
||||
* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable.
|
||||
* Build `TDLib` with CMake enabling `.NET` support and specifying correct path to `vcpkg` toolchain file:
|
||||
```
|
||||
cd <path to TDLib sources>/example/csharp
|
||||
|
@ -7,13 +7,12 @@ This is an example of building TDLib SDK for Universal Windows Platform and an e
|
||||
* Download and install Microsoft Visual Studio 2015+ with Windows 10 SDK. We recommend to use the latest available versions of Microsoft Visual Studio and Windows 10 SDK.
|
||||
* Download and install [CMake](https://cmake.org/download/).
|
||||
* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start) or update it to the latest version using `vcpkg update` and following received instructions.
|
||||
* Install `zlib` and `openssl` for all UWP architectures using `vcpkg`:
|
||||
* Install `zlib` and `openssl` for all UWP architectures and `gperf` for x86 using `vcpkg`:
|
||||
```
|
||||
cd <path to vcpkg>
|
||||
.\vcpkg.exe install openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp
|
||||
.\vcpkg.exe install gperf:x86-windows openssl-uwp:arm-uwp openssl-uwp:x64-uwp openssl-uwp:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp
|
||||
```
|
||||
* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download#php-7.2). Add the path to php.exe to the PATH environment variable.
|
||||
* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable.
|
||||
* Download and install [7-Zip](http://www.7-zip.org/download.html) archiver, which is used by the `build.ps1` script to create a Telegram.Td.UWP Visual Studio Extension. Add the path to 7z.exe to the PATH environment variable.
|
||||
Alternatively `build.ps1` supports compressing using [WinRAR](https://en.wikipedia.org/wiki/WinRAR) with option `-compress winrar` and compressing using [zip](http://gnuwin32.sourceforge.net/packages/zip.htm) with `-compress zip`.
|
||||
* Build `TDLib` using provided `build.ps1` script (TDLib should be built 6 times for multiple platforms in Debug and Release configurations, so it make take few hours). Pass path to vcpkg.exe as `-vcpkg-root` argument, for example:
|
||||
@ -23,7 +22,7 @@ powershell -ExecutionPolicy ByPass .\build.ps1 -vcpkg_root C:\vcpkg
|
||||
If you need to restart the build from scratch, call `.\build.ps1 -mode clean` first.
|
||||
* Install Visual Studio Extension "TDLib for Universal Windows Platform" located at `build-uwp\vsix\tdlib.vsix`, which was created on the previous step by `build.ps1` script.
|
||||
|
||||
Now `TDLib` can be freely used from any UWP project, built in Visual Studio.
|
||||
Now `TDLib` can be used from any UWP project, built in Visual Studio.
|
||||
|
||||
## Example of usage
|
||||
The `app/` directory contains a simple example of a C# application for Universal Windows Platform. Just open it with Visual Studio 2015 or later and run.
|
||||
|
@ -950,7 +950,7 @@ voiceChat group_call_id:int32 has_participants:Bool default_participant_id:Messa
|
||||
//@unread_mention_count Number of unread messages with a mention/reply in the chat
|
||||
//@notification_settings Notification settings for this chat
|
||||
//@message_ttl_setting Current message Time To Live setting (self-destruct timer) for the chat; 0 if not defined. TTL is counted from the time message or its content is viewed in secret chats and from the send date in other chats
|
||||
//@theme_name If non-empty, name of the theme set for the chat
|
||||
//@theme_name If non-empty, name of a theme set for the chat
|
||||
//@action_bar Describes actions which should be possible to do through a chat action bar; may be null
|
||||
//@voice_chat Contains information about voice chat of the chat
|
||||
//@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
|
||||
@ -1790,7 +1790,7 @@ messagePinMessage message_id:int53 = MessageContent;
|
||||
//@description A screenshot of a message in the chat has been taken
|
||||
messageScreenshotTaken = MessageContent;
|
||||
|
||||
//@description A theme in the chat has been changed @theme_name If non-empty, name of the new theme set for the chat. Otherwise chat theme was reset to the default one
|
||||
//@description A theme in the chat has been changed @theme_name If non-empty, name of a new theme set for the chat. Otherwise chat theme was reset to the default one
|
||||
messageChatSetTheme theme_name:string = MessageContent;
|
||||
|
||||
//@description The TTL (Time To Live) setting for messages in the chat has been changed @ttl New message TTL setting
|
||||
@ -2532,7 +2532,7 @@ chatEventUsernameChanged old_username:string new_username:string = ChatEventActi
|
||||
//@description The chat photo was changed @old_photo Previous chat photo value; may be null @new_photo New chat photo value; may be null
|
||||
chatEventPhotoChanged old_photo:chatPhoto new_photo:chatPhoto = ChatEventAction;
|
||||
|
||||
//@description The chat theme was changed @old_theme_name Previous chat theme name; empty if default one @new_theme_name New chat theme name; empty if default one
|
||||
//@description The chat theme was changed. This event shouldn't be received until chat themes would be supported in supergroups @old_theme_name Previous chat theme name; empty if the previous theme was default one @new_theme_name New chat theme name; empty if the new theme is default one
|
||||
chatEventThemeChanged old_theme_name:string new_theme_name:string = ChatEventAction;
|
||||
|
||||
//@description The can_invite_users permission of a supergroup chat was toggled @can_invite_users New value of can_invite_users permission
|
||||
@ -2702,9 +2702,10 @@ backgroundTypeWallpaper is_blurred:Bool is_moving:Bool = BackgroundType;
|
||||
|
||||
//@description A PNG or TGV (gzipped subset of SVG with MIME type "application/x-tgwallpattern") pattern to be combined with the background fill chosen by the user
|
||||
//@fill Description of the background fill
|
||||
//@intensity Intensity of the pattern when it is shown above the filled background; -100-100. If negative, the pattern color and the filled background colors needs to be inverted
|
||||
//@intensity Intensity of the pattern when it is shown above the filled background; 0-100.
|
||||
//@is_inverted True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only
|
||||
//@is_moving True, if the background needs to be slightly moved when device is tilted
|
||||
backgroundTypePattern fill:BackgroundFill intensity:int32 is_moving:Bool = BackgroundType;
|
||||
backgroundTypePattern fill:BackgroundFill intensity:int32 is_inverted:Bool is_moving:Bool = BackgroundType;
|
||||
|
||||
//@description A filled background @fill Description of the background fill
|
||||
backgroundTypeFill fill:BackgroundFill = BackgroundType;
|
||||
@ -2747,9 +2748,6 @@ themeSettings accent_color:int32 background:background message_fill:BackgroundFi
|
||||
//@dark_settings Theme settings for a dark chat theme
|
||||
chatTheme name:string light_settings:themeSettings dark_settings:themeSettings = ChatTheme;
|
||||
|
||||
//@description Contains a list of chat themes @chat_themes A list of chat themes
|
||||
chatThemes chat_themes:vector<chatTheme> = ChatThemes;
|
||||
|
||||
|
||||
//@description Contains a list of hashtags @hashtags A list of hashtags
|
||||
hashtags hashtags:vector<string> = Hashtags;
|
||||
@ -2893,7 +2891,7 @@ pushMessageContentChatChangePhoto = PushMessageContent;
|
||||
//@description A chat title was edited @title New chat title
|
||||
pushMessageContentChatChangeTitle title:string = PushMessageContent;
|
||||
|
||||
//@description A chat theme was edited @theme_name If non-empty, name of the new theme set for the chat. Otherwise chat theme was reset to the default one
|
||||
//@description A chat theme was edited @theme_name If non-empty, name of a new theme set for the chat. Otherwise chat theme was reset to the default one
|
||||
pushMessageContentChatChangeTheme theme_name:string = PushMessageContent;
|
||||
|
||||
//@description A chat member was deleted @member_name Name of the deleted member @is_current_user True, if the current user was deleted from the group
|
||||
@ -3700,7 +3698,7 @@ updateChatMessageTtlSetting chat_id:int53 message_ttl_setting:int32 = Update;
|
||||
//@description The chat action bar was changed @chat_id Chat identifier @action_bar The new value of the action bar; may be null
|
||||
updateChatActionBar chat_id:int53 action_bar:ChatActionBar = Update;
|
||||
|
||||
//@description The chat theme was changed @chat_id Chat identifier @theme_name The new name of the chat theme; may be empty if none
|
||||
//@description The chat theme was changed @chat_id Chat identifier @theme_name The new name of the chat theme; may be empty if theme was reset to default
|
||||
updateChatTheme chat_id:int53 theme_name:string = Update;
|
||||
|
||||
//@description The default chat reply markup was changed. Can occur because new messages with reply markup were received or because an old reply markup was hidden by the user
|
||||
@ -3841,6 +3839,9 @@ updateSavedAnimations animation_ids:vector<int32> = Update;
|
||||
//@description The selected background has changed @for_dark_theme True, if background for dark theme has changed @background The new selected background; may be null
|
||||
updateSelectedBackground for_dark_theme:Bool background:background = Update;
|
||||
|
||||
//@description The list of available chat themes has changed @chat_themes The new list of chat themes
|
||||
updateChatThemes chat_themes:vector<chatTheme> = Update;
|
||||
|
||||
//@description Some language pack strings have been updated @localization_target Localization target to which the language pack belongs @language_pack_id Identifier of the updated language pack @strings List of changed language pack strings
|
||||
updateLanguagePackStrings localization_target:string language_pack_id:string strings:vector<languagePackString> = Update;
|
||||
|
||||
@ -4156,6 +4157,9 @@ removeRecentlyFoundChat chat_id:int53 = Ok;
|
||||
//@description Clears the list of recently found chats
|
||||
clearRecentlyFoundChats = Ok;
|
||||
|
||||
//@description Returns recently opened chats, this is an offline request. Returns chats in the order of last opening @limit The maximum number of chats to be returned
|
||||
getRecentlyOpenedChats limit:int32 = Chats;
|
||||
|
||||
//@description Checks whether a username can be set for a chat @chat_id Chat identifier; should be identifier of a supergroup chat, or a channel chat, or a private chat with self, or zero if the chat is being created @username Username to be checked
|
||||
checkChatUsername chat_id:int53 username:string = CheckChatUsernameResult;
|
||||
|
||||
@ -4624,8 +4628,7 @@ setChatMessageTtlSetting chat_id:int53 ttl:int32 = Ok;
|
||||
//@chat_id Chat identifier @permissions New non-administrator members permissions in the chat
|
||||
setChatPermissions chat_id:int53 permissions:chatPermissions = Ok;
|
||||
|
||||
//@description Changes the chat theme. Supported only in private and secret chats @chat_id Chat identifier
|
||||
//@theme_name Name of the new chat theme; may be empty to return the default theme
|
||||
//@description Changes the chat theme. Supported only in private and secret chats @chat_id Chat identifier @theme_name Name of the new chat theme; may be empty to return the default theme
|
||||
setChatTheme chat_id:int53 theme_name:string = Ok;
|
||||
|
||||
//@description Changes the draft message in a chat @chat_id Chat identifier @message_thread_id If not 0, a message thread identifier in which the draft was changed @draft_message New draft message; may be null
|
||||
@ -5290,10 +5293,6 @@ removeBackground background_id:int64 = Ok;
|
||||
resetBackgrounds = Ok;
|
||||
|
||||
|
||||
//@description Returns the list of available chat themes
|
||||
getChatThemes = ChatThemes;
|
||||
|
||||
|
||||
//@description Returns information about the current localization target. This is an offline request if only_local is true. Can be called before authorization @only_local If true, returns only locally available information without sending network requests
|
||||
getLocalizationTargetInfo only_local:Bool = LocalizationTargetInfo;
|
||||
|
||||
|
@ -25,12 +25,14 @@
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Time.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
#include "td/utils/TlDowncastHelper.h"
|
||||
|
||||
#include "td/mtproto/mtproto_api.h"
|
||||
#include "td/mtproto/mtproto_api.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -171,22 +173,6 @@ namespace mtproto {
|
||||
*
|
||||
*/
|
||||
|
||||
class OnPacket {
|
||||
const MsgInfo &info_;
|
||||
SessionConnection *connection_;
|
||||
Status *status_;
|
||||
|
||||
public:
|
||||
OnPacket(const MsgInfo &info, SessionConnection *connection, Status *status)
|
||||
: info_(info), connection_(connection), status_(status) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void operator()(const T &func) const {
|
||||
*status_ = connection_->on_packet(info_, func);
|
||||
}
|
||||
};
|
||||
|
||||
unique_ptr<RawConnection> SessionConnection::move_as_raw_connection() {
|
||||
return std::move(raw_connection_);
|
||||
}
|
||||
@ -230,7 +216,6 @@ Status SessionConnection::on_packet_container(const MsgInfo &info, Slice packet)
|
||||
};
|
||||
|
||||
TlParser parser(packet);
|
||||
parser.fetch_int();
|
||||
int32 size = parser.fetch_int();
|
||||
if (parser.get_error()) {
|
||||
return Status::Error(PSLICE() << "Failed to parse mtproto_api::rpc_container: " << parser.get_error());
|
||||
@ -243,7 +228,6 @@ Status SessionConnection::on_packet_container(const MsgInfo &info, Slice packet)
|
||||
|
||||
Status SessionConnection::on_packet_rpc_result(const MsgInfo &info, Slice packet) {
|
||||
TlParser parser(packet);
|
||||
parser.fetch_int();
|
||||
uint64 req_msg_id = parser.fetch_long();
|
||||
if (parser.get_error()) {
|
||||
return Status::Error(PSLICE() << "Failed to parse mtproto_api::rpc_result: " << parser.get_error());
|
||||
@ -275,7 +259,7 @@ Status SessionConnection::on_packet_rpc_result(const MsgInfo &info, Slice packet
|
||||
return callback_->on_message_result_ok(req_msg_id, std::move(object), info.size);
|
||||
}
|
||||
default:
|
||||
packet.remove_prefix(4 + sizeof(req_msg_id));
|
||||
packet.remove_prefix(sizeof(req_msg_id));
|
||||
return callback_->on_message_result_ok(req_msg_id, as_buffer_slice(packet), info.size);
|
||||
}
|
||||
}
|
||||
@ -285,12 +269,15 @@ Status SessionConnection::on_packet(const MsgInfo &info, const T &packet) {
|
||||
LOG(ERROR) << "Unsupported: " << to_string(packet);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status SessionConnection::on_packet(const MsgInfo &info, const mtproto_api::destroy_auth_key_ok &destroy_auth_key) {
|
||||
return on_destroy_auth_key(destroy_auth_key);
|
||||
}
|
||||
|
||||
Status SessionConnection::on_packet(const MsgInfo &info, const mtproto_api::destroy_auth_key_none &destroy_auth_key) {
|
||||
return on_destroy_auth_key(destroy_auth_key);
|
||||
}
|
||||
|
||||
Status SessionConnection::on_packet(const MsgInfo &info, const mtproto_api::destroy_auth_key_fail &destroy_auth_key) {
|
||||
return on_destroy_auth_key(destroy_auth_key);
|
||||
}
|
||||
@ -479,20 +466,39 @@ Status SessionConnection::on_slice_packet(const MsgInfo &info, Slice packet) {
|
||||
if (info.seq_no & 1) {
|
||||
send_ack(info.message_id);
|
||||
}
|
||||
TlParser parser(packet);
|
||||
tl_object_ptr<mtproto_api::Object> object = mtproto_api::Object::fetch(parser);
|
||||
if (packet.size() < 4) {
|
||||
callback_->on_session_failed(Status::Error("Receive too small packet"));
|
||||
return Status::Error(PSLICE() << "Receive packet of size " << packet.size());
|
||||
}
|
||||
|
||||
int32 constructor_id = as<int32>(packet.begin());
|
||||
if (constructor_id == mtproto_api::msg_container::ID) {
|
||||
return on_packet_container(info, packet.substr(4));
|
||||
}
|
||||
if (constructor_id == mtproto_api::rpc_result::ID) {
|
||||
return on_packet_rpc_result(info, packet.substr(4));
|
||||
}
|
||||
|
||||
TlDowncastHelper<mtproto_api::Object> helper(constructor_id);
|
||||
Status status;
|
||||
bool is_mtproto_api = downcast_call(static_cast<mtproto_api::Object &>(helper), [&](auto &dummy) {
|
||||
// a constructor from mtproto_api
|
||||
using Type = std::decay_t<decltype(dummy)>;
|
||||
TlParser parser(packet.substr(4));
|
||||
auto object = Type::fetch(parser);
|
||||
parser.fetch_end();
|
||||
if (parser.get_error()) {
|
||||
// msg_container is not real tl object
|
||||
if (packet.size() >= 4 && as<int32>(packet.begin()) == mtproto_api::msg_container::ID) {
|
||||
return on_packet_container(info, packet);
|
||||
status = parser.get_status();
|
||||
} else {
|
||||
status = this->on_packet(info, static_cast<const Type &>(*object));
|
||||
}
|
||||
if (packet.size() >= 4 && as<int32>(packet.begin()) == mtproto_api::rpc_result::ID) {
|
||||
return on_packet_rpc_result(info, packet);
|
||||
});
|
||||
if (is_mtproto_api) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// It is an update... I hope.
|
||||
auto status = auth_data_->check_update(info.message_id);
|
||||
status = auth_data_->check_update(info.message_id);
|
||||
auto recheck_status = auth_data_->recheck_update(info.message_id);
|
||||
if (recheck_status.is_error() && recheck_status.code() == 2) {
|
||||
LOG(WARNING) << "Receive very old update from " << get_name() << " created in " << (Time::now() - created_at_)
|
||||
@ -522,11 +528,6 @@ Status SessionConnection::on_slice_packet(const MsgInfo &info, Slice packet) {
|
||||
}
|
||||
}
|
||||
|
||||
Status status;
|
||||
downcast_call(*object, OnPacket(info, this, &status));
|
||||
return status;
|
||||
}
|
||||
|
||||
Status SessionConnection::parse_packet(TlParser &parser) {
|
||||
MsgInfo info;
|
||||
Slice packet;
|
||||
@ -579,6 +580,7 @@ void SessionConnection::on_message_failed(uint64 id, Status status) {
|
||||
on_message_failed_inner(id);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionConnection::on_message_failed_inner(uint64 id) {
|
||||
auto it = service_queries_.find(id);
|
||||
if (it == service_queries_.end()) {
|
||||
|
@ -203,8 +203,6 @@ class SessionConnection final
|
||||
SessionConnection::Callback *callback_ = nullptr;
|
||||
BufferSlice *current_buffer_slice_;
|
||||
|
||||
friend class OnPacket;
|
||||
|
||||
BufferSlice as_buffer_slice(Slice packet);
|
||||
auto set_buffer_slice(BufferSlice *buffer_slice) TD_WARN_UNUSED_RESULT {
|
||||
auto old_buffer_slice = current_buffer_slice_;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "td/telegram/StickersManager.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
#include "td/telegram/ThemeManager.h"
|
||||
#include "td/telegram/TopDialogManager.h"
|
||||
#include "td/telegram/UpdatesManager.h"
|
||||
|
||||
@ -782,6 +783,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
|
||||
td->messages_manager_->on_authorization_success();
|
||||
td->notification_manager_->init();
|
||||
td->stickers_manager_->init();
|
||||
td->theme_manager_->init();
|
||||
send_closure(td->top_dialog_manager_, &TopDialogManager::do_start_up);
|
||||
td->updates_manager_->get_difference("on_get_authorization");
|
||||
td->on_online_updated(false, true);
|
||||
|
@ -1060,7 +1060,9 @@ string BackgroundManager::get_background_name_database_key(const string &name) {
|
||||
std::pair<BackgroundId, BackgroundType> BackgroundManager::on_get_background(
|
||||
BackgroundId expected_background_id, const string &expected_background_name,
|
||||
telegram_api::object_ptr<telegram_api::WallPaper> wallpaper_ptr, bool replace_type) {
|
||||
if (!(wallpaper_ptr != nullptr)) return {};
|
||||
if (wallpaper_ptr == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (wallpaper_ptr->get_id() == telegram_api::wallPaperNoFile::ID) {
|
||||
auto wallpaper = move_tl_object_as<telegram_api::wallPaperNoFile>(wallpaper_ptr);
|
||||
@ -1071,10 +1073,13 @@ std::pair<BackgroundId, BackgroundType> BackgroundManager::on_get_background(
|
||||
}
|
||||
|
||||
auto background_id = BackgroundId(wallpaper->id_);
|
||||
if (!background_id.is_valid() || background_id.is_local()) {
|
||||
if (background_id.is_local()) {
|
||||
LOG(ERROR) << "Receive " << to_string(wallpaper);
|
||||
return {};
|
||||
}
|
||||
if (!background_id.is_valid()) {
|
||||
background_id = get_next_local_background_id();
|
||||
}
|
||||
|
||||
Background background;
|
||||
background.id = background_id;
|
||||
|
@ -26,10 +26,23 @@ static bool is_valid_color(int32 color) {
|
||||
return 0 <= color && color <= 0xFFFFFF;
|
||||
}
|
||||
|
||||
static bool validate_alpha_color(int32 &color) {
|
||||
if (-0x1000000 <= color && color <= 0xFFFFFF) {
|
||||
color &= 0xFFFFFF;
|
||||
return true;
|
||||
}
|
||||
color = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_valid_rotation_angle(int32 rotation_angle) {
|
||||
return 0 <= rotation_angle && rotation_angle < 360 && rotation_angle % 45 == 0;
|
||||
}
|
||||
|
||||
static bool is_valid_intensity(int32 intensity, bool allow_negative) {
|
||||
return (allow_negative ? -100 : 0) <= intensity && intensity <= 100;
|
||||
}
|
||||
|
||||
BackgroundFill::BackgroundFill(const telegram_api::wallPaperSettings *settings) {
|
||||
if (settings == nullptr) {
|
||||
return;
|
||||
@ -38,35 +51,30 @@ BackgroundFill::BackgroundFill(const telegram_api::wallPaperSettings *settings)
|
||||
auto flags = settings->flags_;
|
||||
if ((flags & telegram_api::wallPaperSettings::BACKGROUND_COLOR_MASK) != 0) {
|
||||
top_color_ = settings->background_color_;
|
||||
if (!is_valid_color(top_color_)) {
|
||||
if (!validate_alpha_color(top_color_)) {
|
||||
LOG(ERROR) << "Receive " << to_string(*settings);
|
||||
top_color_ = 0;
|
||||
}
|
||||
}
|
||||
if ((flags & telegram_api::wallPaperSettings::FOURTH_BACKGROUND_COLOR_MASK) != 0 ||
|
||||
(flags & telegram_api::wallPaperSettings::THIRD_BACKGROUND_COLOR_MASK) != 0) {
|
||||
bottom_color_ = settings->second_background_color_;
|
||||
if (!is_valid_color(bottom_color_)) {
|
||||
if (!validate_alpha_color(bottom_color_)) {
|
||||
LOG(ERROR) << "Receive " << to_string(*settings);
|
||||
bottom_color_ = 0;
|
||||
}
|
||||
third_color_ = settings->third_background_color_;
|
||||
if (!is_valid_color(third_color_)) {
|
||||
if (!validate_alpha_color(third_color_)) {
|
||||
LOG(ERROR) << "Receive " << to_string(*settings);
|
||||
third_color_ = 0;
|
||||
}
|
||||
if ((flags & telegram_api::wallPaperSettings::FOURTH_BACKGROUND_COLOR_MASK) != 0) {
|
||||
fourth_color_ = settings->fourth_background_color_;
|
||||
if (!is_valid_color(fourth_color_)) {
|
||||
if (!validate_alpha_color(fourth_color_)) {
|
||||
LOG(ERROR) << "Receive " << to_string(*settings);
|
||||
fourth_color_ = 0;
|
||||
}
|
||||
}
|
||||
} else if ((flags & telegram_api::wallPaperSettings::SECOND_BACKGROUND_COLOR_MASK) != 0) {
|
||||
bottom_color_ = settings->second_background_color_;
|
||||
if (!is_valid_color(bottom_color_)) {
|
||||
if (!validate_alpha_color(bottom_color_)) {
|
||||
LOG(ERROR) << "Receive " << to_string(*settings);
|
||||
bottom_color_ = 0;
|
||||
}
|
||||
|
||||
rotation_angle_ = settings->rotation_;
|
||||
@ -203,10 +211,6 @@ string BackgroundFill::get_link(bool is_first) const {
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_valid_intensity(int32 intensity) {
|
||||
return -100 <= intensity && intensity <= 100;
|
||||
}
|
||||
|
||||
bool BackgroundFill::is_dark() const {
|
||||
switch (get_type()) {
|
||||
case Type::Solid:
|
||||
@ -254,7 +258,7 @@ void BackgroundType::apply_parameters_from_link(Slice name) {
|
||||
if (!intensity_arg.empty()) {
|
||||
intensity_ = to_integer<int32>(intensity_arg);
|
||||
}
|
||||
if (!is_valid_intensity(intensity_)) {
|
||||
if (!is_valid_intensity(intensity_, true)) {
|
||||
intensity_ = 50;
|
||||
}
|
||||
|
||||
@ -341,10 +345,11 @@ Result<BackgroundType> BackgroundType::get_background_type(const td_api::Backgro
|
||||
case td_api::backgroundTypePattern::ID: {
|
||||
auto pattern_type = static_cast<const td_api::backgroundTypePattern *>(background_type);
|
||||
TRY_RESULT(background_fill, BackgroundFill::get_background_fill(pattern_type->fill_.get()));
|
||||
if (!is_valid_intensity(pattern_type->intensity_)) {
|
||||
if (!is_valid_intensity(pattern_type->intensity_, false)) {
|
||||
return Status::Error(400, "Wrong intensity value");
|
||||
}
|
||||
return BackgroundType(pattern_type->is_moving_, std::move(background_fill), pattern_type->intensity_);
|
||||
auto intensity = pattern_type->is_inverted_ ? -max(pattern_type->intensity_, 1) : pattern_type->intensity_;
|
||||
return BackgroundType(pattern_type->is_moving_, std::move(background_fill), intensity);
|
||||
}
|
||||
case td_api::backgroundTypeFill::ID: {
|
||||
auto fill_type = static_cast<const td_api::backgroundTypeFill *>(background_type);
|
||||
@ -375,7 +380,7 @@ BackgroundType::BackgroundType(bool is_fill, bool is_pattern,
|
||||
is_moving_ = (settings->flags_ & telegram_api::wallPaperSettings::MOTION_MASK) != 0;
|
||||
if ((settings->flags_ & telegram_api::wallPaperSettings::INTENSITY_MASK) != 0) {
|
||||
intensity_ = settings->intensity_;
|
||||
if (!is_valid_intensity(intensity_)) {
|
||||
if (!is_valid_intensity(intensity_, true)) {
|
||||
LOG(ERROR) << "Receive " << to_string(settings);
|
||||
intensity_ = 50;
|
||||
}
|
||||
@ -414,8 +419,8 @@ td_api::object_ptr<td_api::BackgroundType> BackgroundType::get_background_type_o
|
||||
case Type::Wallpaper:
|
||||
return td_api::make_object<td_api::backgroundTypeWallpaper>(is_blurred_, is_moving_);
|
||||
case Type::Pattern:
|
||||
return td_api::make_object<td_api::backgroundTypePattern>(fill_.get_background_fill_object(), intensity_,
|
||||
is_moving_);
|
||||
return td_api::make_object<td_api::backgroundTypePattern>(
|
||||
fill_.get_background_fill_object(), intensity_ < 0 ? -intensity_ : intensity_, intensity_ < 0, is_moving_);
|
||||
case Type::Fill:
|
||||
return td_api::make_object<td_api::backgroundTypeFill>(fill_.get_background_fill_object());
|
||||
default:
|
||||
|
@ -49,7 +49,7 @@ void store(const Document &document, StorerT &storer) {
|
||||
td->documents_manager_->store_document(document.file_id, storer);
|
||||
break;
|
||||
case Document::Type::Sticker:
|
||||
td->stickers_manager_->store_sticker(document.file_id, false, storer);
|
||||
td->stickers_manager_->store_sticker(document.file_id, false, storer, "Document");
|
||||
break;
|
||||
case Document::Type::Video:
|
||||
td->videos_manager_->store_video(document.file_id, storer);
|
||||
|
@ -496,8 +496,7 @@ class Global final : public ActorContext {
|
||||
|
||||
inline Global *G_impl(const char *file, int line) {
|
||||
ActorContext *context = Scheduler::context();
|
||||
CHECK(context);
|
||||
LOG_CHECK(context->get_id() == Global::ID) << "In " << file << " at " << line;
|
||||
LOG_CHECK(context != nullptr && context->get_id() == Global::ID) << "In " << file << " at " << line;
|
||||
return static_cast<Global *>(context);
|
||||
}
|
||||
|
||||
|
@ -803,7 +803,7 @@ static void store(const MessageContent *content, StorerT &storer) {
|
||||
}
|
||||
case MessageContentType::Sticker: {
|
||||
auto m = static_cast<const MessageSticker *>(content);
|
||||
td->stickers_manager_->store_sticker(m->file_id, false, storer);
|
||||
td->stickers_manager_->store_sticker(m->file_id, false, storer, "MessageSticker");
|
||||
break;
|
||||
}
|
||||
case MessageContentType::Text: {
|
||||
|
@ -5708,7 +5708,11 @@ MessagesManager::Dialog::~Dialog() {
|
||||
}
|
||||
}
|
||||
|
||||
MessagesManager::MessagesManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
|
||||
MessagesManager::MessagesManager(Td *td, ActorShared<> parent)
|
||||
: recently_found_dialogs_{td, "recently_found", MAX_RECENT_DIALOGS}
|
||||
, recently_opened_dialogs_{td, "recently_opened", MAX_RECENT_DIALOGS}
|
||||
, td_(td)
|
||||
, parent_(std::move(parent)) {
|
||||
upload_media_callback_ = std::make_shared<UploadMediaCallback>();
|
||||
upload_thumbnail_callback_ = std::make_shared<UploadThumbnailCallback>();
|
||||
upload_dialog_photo_callback_ = std::make_shared<UploadDialogPhotoCallback>();
|
||||
@ -11172,9 +11176,8 @@ void MessagesManager::on_dialog_deleted(DialogId dialog_id, Promise<Unit> &&prom
|
||||
d->is_empty = false;
|
||||
d->need_restore_reply_markup = true;
|
||||
}
|
||||
if (remove_recently_found_dialog_internal(dialog_id)) {
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
recently_found_dialogs_.remove_dialog(dialog_id);
|
||||
recently_opened_dialogs_.remove_dialog(dialog_id);
|
||||
if (dialog_id.get_type() == DialogType::Channel) {
|
||||
G()->td_db()->get_binlog_pmc()->erase(get_channel_pts_key(dialog_id));
|
||||
}
|
||||
@ -13865,6 +13868,14 @@ FullMessageId MessagesManager::on_get_message(MessageInfo &&message_info, bool f
|
||||
if (need_update_dialog_pos && d != nullptr) {
|
||||
send_update_chat_last_message(d, "on_get_message");
|
||||
}
|
||||
if (old_message_id.is_valid() || old_message_id.is_valid_scheduled()) {
|
||||
CHECK(d != nullptr);
|
||||
if (!old_message_id.is_valid() || !message_id.is_valid() || old_message_id <= message_id) {
|
||||
LOG(ERROR) << "Failed to add just sent " << old_message_id << " to " << dialog_id << " as " << message_id
|
||||
<< " from " << source << ": " << debug_add_message_to_dialog_fail_reason_;
|
||||
}
|
||||
send_update_delete_messages(dialog_id, {message_id.get()}, true, false);
|
||||
}
|
||||
|
||||
return FullMessageId();
|
||||
}
|
||||
@ -16559,17 +16570,7 @@ std::pair<int32, vector<DialogId>> MessagesManager::search_dialogs(const string
|
||||
return {};
|
||||
}
|
||||
if (query.empty()) {
|
||||
if (!load_recently_found_dialogs(promise)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
promise.set_value(Unit());
|
||||
|
||||
update_recently_found_dialogs();
|
||||
|
||||
size_t result_size = min(static_cast<size_t>(limit), recently_found_dialog_ids_.size());
|
||||
return {narrow_cast<int32>(recently_found_dialog_ids_.size()),
|
||||
vector<DialogId>(recently_found_dialog_ids_.begin(), recently_found_dialog_ids_.begin() + result_size)};
|
||||
return recently_found_dialogs_.get_dialogs(limit, std::move(promise));
|
||||
}
|
||||
|
||||
auto result = dialogs_hints_.search(query, limit);
|
||||
@ -16583,6 +16584,10 @@ std::pair<int32, vector<DialogId>> MessagesManager::search_dialogs(const string
|
||||
return {narrow_cast<int32>(result.first), std::move(dialog_ids)};
|
||||
}
|
||||
|
||||
std::pair<int32, vector<DialogId>> MessagesManager::get_recently_opened_dialogs(int32 limit, Promise<Unit> &&promise) {
|
||||
return recently_opened_dialogs_.get_dialogs(limit, std::move(promise));
|
||||
}
|
||||
|
||||
vector<DialogId> MessagesManager::sort_dialogs_by_order(const vector<DialogId> &dialog_ids, int32 limit) const {
|
||||
CHECK(!td_->auth_manager_->is_bot());
|
||||
int64 fake_order = static_cast<int64>(dialog_ids.size()) + 1;
|
||||
@ -19246,10 +19251,13 @@ Status MessagesManager::toggle_message_sender_is_blocked(const td_api::object_pt
|
||||
dialog_id = DialogId(sender_user_id);
|
||||
}
|
||||
if (dialog_id == get_my_dialog_id()) {
|
||||
return Status::Error(5, is_blocked ? Slice("Can't block self") : Slice("Can't unblock self"));
|
||||
return Status::Error(400, is_blocked ? Slice("Can't block self") : Slice("Can't unblock self"));
|
||||
}
|
||||
|
||||
Dialog *d = get_dialog_force(dialog_id, "toggle_message_sender_is_blocked");
|
||||
if (!have_input_peer(dialog_id, AccessRights::Know)) {
|
||||
return Status::Error(400, "Message sender isn't accessible");
|
||||
}
|
||||
if (d != nullptr) {
|
||||
if (is_blocked == d->is_blocked) {
|
||||
return Status::OK();
|
||||
@ -19295,11 +19303,6 @@ uint64 MessagesManager::save_toggle_dialog_is_blocked_on_server_log_event(Dialog
|
||||
}
|
||||
|
||||
void MessagesManager::toggle_dialog_is_blocked_on_server(DialogId dialog_id, bool is_blocked, uint64 log_event_id) {
|
||||
if (log_event_id == 0 && dialog_id.get_type() == DialogType::SecretChat) {
|
||||
// don't even create new binlog events
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_event_id == 0 && G()->parameters().use_message_db) {
|
||||
log_event_id = save_toggle_dialog_is_blocked_on_server_log_event(dialog_id, is_blocked);
|
||||
}
|
||||
@ -19875,7 +19878,11 @@ void MessagesManager::read_message_contents_on_server(DialogId dialog_id, vector
|
||||
|
||||
void MessagesManager::open_dialog(Dialog *d) {
|
||||
DialogId dialog_id = d->dialog_id;
|
||||
if (d->is_opened || !have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
if (!have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
return;
|
||||
}
|
||||
recently_opened_dialogs_.add_dialog(dialog_id);
|
||||
if (d->is_opened) {
|
||||
return;
|
||||
}
|
||||
d->is_opened = true;
|
||||
@ -21767,8 +21774,7 @@ void MessagesManager::delete_bot_command_message_id(DialogId dialog_id, MessageI
|
||||
if (it == dialog_bot_command_message_ids_.end()) {
|
||||
return;
|
||||
}
|
||||
it->second.message_ids.erase(message_id);
|
||||
if (it->second.message_ids.empty()) {
|
||||
if (it->second.message_ids.erase(message_id) && it->second.message_ids.empty()) {
|
||||
dialog_bot_command_message_ids_.erase(it);
|
||||
}
|
||||
}
|
||||
@ -25125,6 +25131,10 @@ bool MessagesManager::is_broadcast_channel(DialogId dialog_id) const {
|
||||
ContactsManager::ChannelType::Broadcast;
|
||||
}
|
||||
|
||||
bool MessagesManager::is_deleted_secret_chat(DialogId dialog_id) const {
|
||||
return is_deleted_secret_chat(get_dialog(dialog_id));
|
||||
}
|
||||
|
||||
bool MessagesManager::is_deleted_secret_chat(const Dialog *d) const {
|
||||
if (d == nullptr) {
|
||||
return true;
|
||||
@ -29217,25 +29227,27 @@ FullMessageId MessagesManager::on_send_message_success(int64 random_id, MessageI
|
||||
sent_message->have_previous = true;
|
||||
sent_message->have_next = true;
|
||||
|
||||
send_update_message_send_succeeded(d, old_message_id, sent_message.get());
|
||||
|
||||
bool need_update = true;
|
||||
Message *m = add_message_to_dialog(d, std::move(sent_message), true, &need_update, &need_update_dialog_pos, source);
|
||||
if (m == nullptr) {
|
||||
if (old_message_id.is_valid() && new_message_id < old_message_id) {
|
||||
// the message ID has decreased. This could happen if some messages were lost.
|
||||
// In this case the failure is possible
|
||||
return {};
|
||||
}
|
||||
LOG(FATAL) << td_->contacts_manager_->get_my_id() << " " << dialog_id << " " << old_message_id << " "
|
||||
<< new_message_id << " " << d->last_clear_history_message_id << " " << d->max_unavailable_message_id
|
||||
<< " " << d->last_message_id << " " << d->last_new_message_id << " " << d->last_assigned_message_id
|
||||
<< " " << have_input_peer(dialog_id, AccessRights::Read) << " "
|
||||
<< debug_add_message_to_dialog_fail_reason_ << " " << source;
|
||||
}
|
||||
|
||||
send_update_message_send_succeeded(d, old_message_id, m);
|
||||
if (need_update_dialog_pos) {
|
||||
send_update_chat_last_message(d, "on_send_message_success");
|
||||
}
|
||||
|
||||
if (m == nullptr) {
|
||||
if (!(old_message_id.is_valid() && new_message_id < old_message_id) &&
|
||||
!(ttl_period > 0 && date + ttl_period <= G()->server_time())) {
|
||||
// if message ID has decreased, which could happen if some messages were lost,
|
||||
// or the message has already been deleted after TTL period, then the error is expected
|
||||
LOG(ERROR) << "Failed to add just sent " << old_message_id << " to " << dialog_id << " as " << new_message_id
|
||||
<< " from " << source << ": " << debug_add_message_to_dialog_fail_reason_;
|
||||
}
|
||||
send_update_delete_messages(dialog_id, {new_message_id.get()}, true, false);
|
||||
being_readded_message_id_ = FullMessageId();
|
||||
return {};
|
||||
}
|
||||
|
||||
try_add_active_live_location(dialog_id, m);
|
||||
update_reply_count_by_message(d, +1, m);
|
||||
update_forward_count(dialog_id, m);
|
||||
@ -30445,7 +30457,9 @@ void MessagesManager::set_dialog_has_bots(Dialog *d, bool has_bots) {
|
||||
auto it = dialog_bot_command_message_ids_.find(d->dialog_id);
|
||||
if (it != dialog_bot_command_message_ids_.end()) {
|
||||
for (auto message_id : it->second.message_ids) {
|
||||
send_update_message_content_impl(d->dialog_id, get_message(d, message_id), "set_dialog_has_bots");
|
||||
auto m = get_message(d, message_id);
|
||||
LOG_CHECK(m != nullptr) << d->dialog_id << ' ' << message_id;
|
||||
send_update_message_content_impl(d->dialog_id, m, "set_dialog_has_bots");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37630,8 +37644,8 @@ void MessagesManager::on_binlog_events(vector<BinlogEvent> &&events) {
|
||||
log_event_parse(log_event, event.data_).ensure();
|
||||
|
||||
auto dialog_id = log_event.dialog_id_;
|
||||
Dialog *d = get_dialog_force(dialog_id, "ToggleDialogIsBlockedOnServerLogEvent");
|
||||
if (d == nullptr || !have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
if (dialog_id.get_type() == DialogType::SecretChat || !have_dialog_info_force(dialog_id) ||
|
||||
!have_input_peer(dialog_id, AccessRights::Know)) {
|
||||
binlog_erase(G()->td_db()->get_binlog(), event.id_);
|
||||
break;
|
||||
}
|
||||
@ -37794,213 +37808,24 @@ void MessagesManager::on_binlog_events(vector<BinlogEvent> &&events) {
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesManager::save_recently_found_dialogs() {
|
||||
if (recently_found_dialogs_loaded_ < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
string value;
|
||||
for (auto &dialog_id : recently_found_dialog_ids_) {
|
||||
if (!value.empty()) {
|
||||
value += ',';
|
||||
}
|
||||
if (!G()->parameters().use_message_db) {
|
||||
// if there is no dialog database, prefer to save dialogs by username
|
||||
auto username = get_dialog_username(dialog_id);
|
||||
if (dialog_id.get_type() != DialogType::SecretChat && !username.empty()) {
|
||||
value += '@';
|
||||
value += username;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
value += to_string(dialog_id.get());
|
||||
}
|
||||
LOG(DEBUG) << "Save recently found chats " << value;
|
||||
G()->td_db()->get_binlog_pmc()->set("recently_found_dialog_usernames_and_ids", value);
|
||||
}
|
||||
|
||||
bool MessagesManager::load_recently_found_dialogs(Promise<Unit> &promise) {
|
||||
CHECK(!td_->auth_manager_->is_bot());
|
||||
if (recently_found_dialogs_loaded_ >= 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
string found_dialogs_str = G()->td_db()->get_binlog_pmc()->get("recently_found_dialog_usernames_and_ids");
|
||||
if (found_dialogs_str.empty()) {
|
||||
recently_found_dialogs_loaded_ = 2;
|
||||
if (!recently_found_dialog_ids_.empty()) {
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Loaded recently found chats " << found_dialogs_str;
|
||||
auto found_dialogs = full_split(found_dialogs_str, ',');
|
||||
if (recently_found_dialogs_loaded_ == 1 && resolve_recently_found_dialogs_multipromise_.promise_count() == 0) {
|
||||
// queries was sent and have already been finished
|
||||
auto newly_found_dialogs = std::move(recently_found_dialog_ids_);
|
||||
recently_found_dialog_ids_.clear();
|
||||
|
||||
for (auto it = found_dialogs.rbegin(); it != found_dialogs.rend(); ++it) {
|
||||
if ((*it)[0] == '@') {
|
||||
auto dialog_id = resolve_dialog_username(it->substr(1));
|
||||
if (dialog_id.is_valid() && have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
force_create_dialog(dialog_id, "recently found resolved dialog");
|
||||
add_recently_found_dialog_internal(dialog_id);
|
||||
}
|
||||
} else {
|
||||
auto dialog_id = DialogId(to_integer<int64>(*it));
|
||||
CHECK(dialog_id.is_valid());
|
||||
if (have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
force_create_dialog(dialog_id, "recently found dialog");
|
||||
add_recently_found_dialog_internal(dialog_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = newly_found_dialogs.rbegin(); it != newly_found_dialogs.rend(); ++it) {
|
||||
add_recently_found_dialog_internal(*it);
|
||||
}
|
||||
recently_found_dialogs_loaded_ = 2;
|
||||
if (!newly_found_dialogs.empty()) {
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
resolve_recently_found_dialogs_multipromise_.add_promise(std::move(promise));
|
||||
if (recently_found_dialogs_loaded_ == 0) {
|
||||
recently_found_dialogs_loaded_ = 1;
|
||||
|
||||
resolve_recently_found_dialogs_multipromise_.set_ignore_errors(true);
|
||||
auto lock = resolve_recently_found_dialogs_multipromise_.get_promise();
|
||||
|
||||
for (auto &found_dialog : found_dialogs) {
|
||||
if (found_dialog[0] == '@') {
|
||||
search_public_dialog(found_dialog, false, resolve_recently_found_dialogs_multipromise_.get_promise());
|
||||
}
|
||||
}
|
||||
if (G()->parameters().use_message_db) {
|
||||
for (auto &found_dialog : found_dialogs) {
|
||||
if (found_dialog[0] != '@') {
|
||||
auto dialog_id = DialogId(to_integer<int64>(found_dialog));
|
||||
CHECK(dialog_id.is_valid());
|
||||
// TODO use asynchronous load
|
||||
// get_dialog(dialog_id, resolve_recently_found_dialogs_multipromise_.get_promise());
|
||||
get_dialog_force(dialog_id, "load_recently_found_dialogs");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
get_dialogs_from_list(DialogListId(FolderId::main()), MAX_GET_DIALOGS + 2,
|
||||
PromiseCreator::lambda(
|
||||
[promise = resolve_recently_found_dialogs_multipromise_.get_promise()](
|
||||
td_api::object_ptr<td_api::chats> &&chats) mutable { promise.set_value(Unit()); }));
|
||||
td_->contacts_manager_->search_contacts("", 1, resolve_recently_found_dialogs_multipromise_.get_promise());
|
||||
}
|
||||
|
||||
lock.set_value(Unit());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Status MessagesManager::add_recently_found_dialog(DialogId dialog_id) {
|
||||
if (!have_dialog_force(dialog_id, "add_recently_found_dialog")) {
|
||||
return Status::Error(5, "Chat not found");
|
||||
return Status::Error(400, "Chat not found");
|
||||
}
|
||||
if (add_recently_found_dialog_internal(dialog_id)) {
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
|
||||
recently_found_dialogs_.add_dialog(dialog_id);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status MessagesManager::remove_recently_found_dialog(DialogId dialog_id) {
|
||||
if (!have_dialog_force(dialog_id, "remove_recently_found_dialog")) {
|
||||
return Status::Error(5, "Chat not found");
|
||||
return Status::Error(400, "Chat not found");
|
||||
}
|
||||
if (remove_recently_found_dialog_internal(dialog_id)) {
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
|
||||
recently_found_dialogs_.remove_dialog(dialog_id);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void MessagesManager::clear_recently_found_dialogs() {
|
||||
recently_found_dialogs_loaded_ = 2;
|
||||
if (recently_found_dialog_ids_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
recently_found_dialog_ids_.clear();
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
|
||||
bool MessagesManager::add_recently_found_dialog_internal(DialogId dialog_id) {
|
||||
CHECK(have_dialog(dialog_id));
|
||||
|
||||
if (!recently_found_dialog_ids_.empty() && recently_found_dialog_ids_[0] == dialog_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO create function
|
||||
auto it = std::find(recently_found_dialog_ids_.begin(), recently_found_dialog_ids_.end(), dialog_id);
|
||||
if (it == recently_found_dialog_ids_.end()) {
|
||||
if (narrow_cast<int32>(recently_found_dialog_ids_.size()) == MAX_RECENTLY_FOUND_DIALOGS) {
|
||||
CHECK(!recently_found_dialog_ids_.empty());
|
||||
recently_found_dialog_ids_.back() = dialog_id;
|
||||
} else {
|
||||
recently_found_dialog_ids_.push_back(dialog_id);
|
||||
}
|
||||
it = recently_found_dialog_ids_.end() - 1;
|
||||
}
|
||||
std::rotate(recently_found_dialog_ids_.begin(), it, it + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagesManager::remove_recently_found_dialog_internal(DialogId dialog_id) {
|
||||
CHECK(have_dialog(dialog_id));
|
||||
return td::remove(recently_found_dialog_ids_, dialog_id);
|
||||
}
|
||||
|
||||
void MessagesManager::update_recently_found_dialogs() {
|
||||
vector<DialogId> dialog_ids;
|
||||
for (auto dialog_id : recently_found_dialog_ids_) {
|
||||
const Dialog *d = get_dialog(dialog_id);
|
||||
if (d == nullptr) {
|
||||
continue;
|
||||
}
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
// always keep
|
||||
break;
|
||||
case DialogType::Chat: {
|
||||
auto channel_id = td_->contacts_manager_->get_chat_migrated_to_channel_id(dialog_id.get_chat_id());
|
||||
if (channel_id.is_valid() && get_dialog(DialogId(channel_id)) != nullptr) {
|
||||
dialog_id = DialogId(channel_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DialogType::Channel:
|
||||
// always keep
|
||||
break;
|
||||
case DialogType::SecretChat:
|
||||
if (is_deleted_secret_chat(d)) {
|
||||
dialog_id = DialogId();
|
||||
}
|
||||
break;
|
||||
case DialogType::None:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (dialog_id.is_valid()) {
|
||||
dialog_ids.push_back(dialog_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (dialog_ids != recently_found_dialog_ids_) {
|
||||
recently_found_dialog_ids_ = std::move(dialog_ids);
|
||||
save_recently_found_dialogs();
|
||||
}
|
||||
recently_found_dialogs_.clear_dialogs();
|
||||
}
|
||||
|
||||
void MessagesManager::suffix_load_loop(Dialog *d) {
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "td/telegram/NotificationGroupType.h"
|
||||
#include "td/telegram/NotificationId.h"
|
||||
#include "td/telegram/NotificationSettings.h"
|
||||
#include "td/telegram/RecentDialogList.h"
|
||||
#include "td/telegram/ReplyMarkup.h"
|
||||
#include "td/telegram/ReportReason.h"
|
||||
#include "td/telegram/RestrictionReason.h"
|
||||
@ -374,6 +375,8 @@ class MessagesManager final : public Actor {
|
||||
|
||||
void clear_recently_found_dialogs();
|
||||
|
||||
std::pair<int32, vector<DialogId>> get_recently_opened_dialogs(int32 limit, Promise<Unit> &&promise);
|
||||
|
||||
DialogId resolve_dialog_username(const string &username) const;
|
||||
|
||||
DialogId search_public_dialog(const string &username_to_search, bool force, Promise<Unit> &&promise);
|
||||
@ -581,6 +584,8 @@ class MessagesManager final : public Actor {
|
||||
|
||||
bool is_message_edited_recently(FullMessageId full_message_id, int32 seconds);
|
||||
|
||||
bool is_deleted_secret_chat(DialogId dialog_id) const;
|
||||
|
||||
Result<std::pair<string, bool>> get_message_link(FullMessageId full_message_id, int32 media_timestamp, bool for_group,
|
||||
bool for_comment);
|
||||
|
||||
@ -1670,7 +1675,7 @@ class MessagesManager final : public Actor {
|
||||
static constexpr int32 MIN_CHANNEL_DIFFERENCE = 1;
|
||||
static constexpr int32 MAX_CHANNEL_DIFFERENCE = 100;
|
||||
static constexpr int32 MAX_BOT_CHANNEL_DIFFERENCE = 100000; // server side limit
|
||||
static constexpr int32 MAX_RECENTLY_FOUND_DIALOGS = 50; // some reasonable value
|
||||
static constexpr int32 MAX_RECENT_DIALOGS = 50; // 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
|
||||
@ -2987,16 +2992,6 @@ class MessagesManager final : public Actor {
|
||||
|
||||
static MessageId get_next_yet_unsent_scheduled_message_id(Dialog *d, int32 date);
|
||||
|
||||
bool add_recently_found_dialog_internal(DialogId dialog_id);
|
||||
|
||||
bool remove_recently_found_dialog_internal(DialogId dialog_id);
|
||||
|
||||
void update_recently_found_dialogs();
|
||||
|
||||
void save_recently_found_dialogs();
|
||||
|
||||
bool load_recently_found_dialogs(Promise<Unit> &promise);
|
||||
|
||||
void reget_message_from_server_if_needed(DialogId dialog_id, const Message *m);
|
||||
|
||||
void speculatively_update_active_group_call_id(Dialog *d, const Message *m);
|
||||
@ -3091,10 +3086,8 @@ class MessagesManager final : public Actor {
|
||||
|
||||
static DialogId get_message_original_sender(const Message *m);
|
||||
|
||||
int32 recently_found_dialogs_loaded_ = 0; // 0 - not loaded, 1 - load request was sent, 2 - loaded
|
||||
MultiPromiseActor resolve_recently_found_dialogs_multipromise_{"ResolveRecentlyFoundDialogsMultiPromiseActor"};
|
||||
|
||||
vector<DialogId> recently_found_dialog_ids_;
|
||||
RecentDialogList recently_found_dialogs_;
|
||||
RecentDialogList recently_opened_dialogs_;
|
||||
|
||||
class UploadMediaCallback;
|
||||
class UploadThumbnailCallback;
|
||||
|
255
td/telegram/RecentDialogList.cpp
Normal file
255
td/telegram/RecentDialogList.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
||||
//
|
||||
// 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/RecentDialogList.h"
|
||||
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/MessagesManager.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
#include "td/telegram/TdParameters.h"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
RecentDialogList::RecentDialogList(Td *td, const char *name, size_t max_size)
|
||||
: td_(td), name_(name), max_size_(max_size) {
|
||||
register_actor(PSLICE() << name << "_chats", this).release();
|
||||
}
|
||||
|
||||
string RecentDialogList::get_binlog_key() const {
|
||||
return PSTRING() << name_ << "_dialog_usernames_and_ids";
|
||||
}
|
||||
|
||||
void RecentDialogList::save_dialogs() const {
|
||||
if (!is_loaded_) {
|
||||
return;
|
||||
}
|
||||
CHECK(removed_dialog_ids_.empty());
|
||||
|
||||
SliceBuilder sb;
|
||||
for (auto &dialog_id : dialog_ids_) {
|
||||
sb << ',';
|
||||
if (!G()->parameters().use_message_db) {
|
||||
// if there is no dialog database, prefer to save dialogs by username
|
||||
string username;
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
username = td_->contacts_manager_->get_user_username(dialog_id.get_user_id());
|
||||
break;
|
||||
case DialogType::Chat:
|
||||
break;
|
||||
case DialogType::Channel:
|
||||
username = td_->contacts_manager_->get_channel_username(dialog_id.get_channel_id());
|
||||
break;
|
||||
case DialogType::SecretChat:
|
||||
break;
|
||||
case DialogType::None:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (!username.empty() && username.find(',') == string::npos) {
|
||||
sb << '@' << username;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sb << dialog_id.get();
|
||||
}
|
||||
auto result = sb.as_cslice();
|
||||
if (!result.empty()) {
|
||||
result.remove_prefix(1);
|
||||
}
|
||||
G()->td_db()->get_binlog_pmc()->set(get_binlog_key(), result.str());
|
||||
}
|
||||
|
||||
void RecentDialogList::load_dialogs(Promise<Unit> &&promise) {
|
||||
if (is_loaded_) {
|
||||
return promise.set_value(Unit());
|
||||
}
|
||||
|
||||
load_list_queries_.push_back(std::move(promise));
|
||||
if (load_list_queries_.size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto found_dialogs = full_split(G()->td_db()->get_binlog_pmc()->get(get_binlog_key()), ',');
|
||||
MultiPromiseActorSafe mpas{"LoadRecentDialogListMultiPromiseActor"};
|
||||
mpas.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), found_dialogs](Unit) mutable {
|
||||
send_closure(actor_id, &RecentDialogList::on_load_dialogs, std::move(found_dialogs));
|
||||
}));
|
||||
mpas.set_ignore_errors(true);
|
||||
auto lock = mpas.get_promise();
|
||||
|
||||
vector<DialogId> dialog_ids;
|
||||
for (auto &found_dialog : found_dialogs) {
|
||||
if (found_dialog[0] == '@') {
|
||||
td_->messages_manager_->search_public_dialog(found_dialog, false, mpas.get_promise());
|
||||
} else {
|
||||
dialog_ids.push_back(DialogId(to_integer<int64>(found_dialog)));
|
||||
}
|
||||
}
|
||||
if (!dialog_ids.empty()) {
|
||||
if (G()->parameters().use_message_db) {
|
||||
td_->messages_manager_->load_dialogs(std::move(dialog_ids), mpas.get_promise());
|
||||
} else {
|
||||
td_->messages_manager_->get_dialogs_from_list(
|
||||
DialogListId(FolderId::main()), 102,
|
||||
PromiseCreator::lambda([promise = mpas.get_promise()](td_api::object_ptr<td_api::chats> &&chats) mutable {
|
||||
promise.set_value(Unit());
|
||||
}));
|
||||
td_->contacts_manager_->search_contacts("", 1, mpas.get_promise());
|
||||
}
|
||||
}
|
||||
|
||||
lock.set_value(Unit());
|
||||
}
|
||||
|
||||
void RecentDialogList::on_load_dialogs(vector<string> &&found_dialogs) {
|
||||
auto promises = std::move(load_list_queries_);
|
||||
CHECK(!promises.empty());
|
||||
|
||||
auto newly_found_dialogs = std::move(dialog_ids_);
|
||||
reset_to_empty(dialog_ids_);
|
||||
|
||||
for (auto it = found_dialogs.rbegin(); it != found_dialogs.rend(); ++it) {
|
||||
DialogId dialog_id;
|
||||
if ((*it)[0] == '@') {
|
||||
dialog_id = td_->messages_manager_->resolve_dialog_username(it->substr(1));
|
||||
} else {
|
||||
dialog_id = DialogId(to_integer<int64>(*it));
|
||||
}
|
||||
if (dialog_id.is_valid() && removed_dialog_ids_.count(dialog_id) == 0 &&
|
||||
td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
|
||||
td_->messages_manager_->force_create_dialog(dialog_id, "recent dialog");
|
||||
do_add_dialog(dialog_id);
|
||||
}
|
||||
}
|
||||
for (auto it = newly_found_dialogs.rbegin(); it != newly_found_dialogs.rend(); ++it) {
|
||||
do_add_dialog(*it);
|
||||
}
|
||||
is_loaded_ = true;
|
||||
removed_dialog_ids_.clear();
|
||||
if (!newly_found_dialogs.empty()) {
|
||||
save_dialogs();
|
||||
}
|
||||
|
||||
for (auto &promise : promises) {
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDialogList::add_dialog(DialogId dialog_id) {
|
||||
if (!is_loaded_) {
|
||||
load_dialogs(Promise<Unit>());
|
||||
}
|
||||
if (do_add_dialog(dialog_id)) {
|
||||
save_dialogs();
|
||||
}
|
||||
}
|
||||
|
||||
bool RecentDialogList::do_add_dialog(DialogId dialog_id) {
|
||||
if (!dialog_ids_.empty() && dialog_ids_[0] == dialog_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO create function
|
||||
auto it = std::find(dialog_ids_.begin(), dialog_ids_.end(), dialog_id);
|
||||
if (it == dialog_ids_.end()) {
|
||||
if (dialog_ids_.size() == max_size_) {
|
||||
CHECK(!dialog_ids_.empty());
|
||||
dialog_ids_.back() = dialog_id;
|
||||
} else {
|
||||
dialog_ids_.push_back(dialog_id);
|
||||
}
|
||||
it = dialog_ids_.end() - 1;
|
||||
}
|
||||
std::rotate(dialog_ids_.begin(), it, it + 1);
|
||||
removed_dialog_ids_.erase(dialog_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecentDialogList::remove_dialog(DialogId dialog_id) {
|
||||
if (!is_loaded_) {
|
||||
load_dialogs(Promise<Unit>());
|
||||
}
|
||||
if (td::remove(dialog_ids_, dialog_id)) {
|
||||
save_dialogs();
|
||||
} else if (!is_loaded_) {
|
||||
removed_dialog_ids_.insert(dialog_id);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDialogList::update_dialogs() {
|
||||
CHECK(is_loaded_);
|
||||
vector<DialogId> dialog_ids;
|
||||
for (auto dialog_id : dialog_ids_) {
|
||||
if (!td_->messages_manager_->have_dialog(dialog_id)) {
|
||||
continue;
|
||||
}
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
// always keep
|
||||
break;
|
||||
case DialogType::Chat: {
|
||||
auto channel_id = td_->contacts_manager_->get_chat_migrated_to_channel_id(dialog_id.get_chat_id());
|
||||
if (channel_id.is_valid() && td_->messages_manager_->have_dialog(DialogId(channel_id))) {
|
||||
dialog_id = DialogId(channel_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DialogType::Channel:
|
||||
// always keep
|
||||
break;
|
||||
case DialogType::SecretChat:
|
||||
if (td_->messages_manager_->is_deleted_secret_chat(dialog_id)) {
|
||||
dialog_id = DialogId();
|
||||
}
|
||||
break;
|
||||
case DialogType::None:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (dialog_id.is_valid()) {
|
||||
dialog_ids.push_back(dialog_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (dialog_ids != dialog_ids_) {
|
||||
dialog_ids_ = std::move(dialog_ids);
|
||||
save_dialogs();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int32, vector<DialogId>> RecentDialogList::get_dialogs(int32 limit, Promise<Unit> &&promise) {
|
||||
load_dialogs(std::move(promise));
|
||||
if (!is_loaded_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
update_dialogs();
|
||||
|
||||
CHECK(limit >= 0);
|
||||
int32 total_count = narrow_cast<int32>(dialog_ids_.size());
|
||||
return {total_count, vector<DialogId>(dialog_ids_.begin(), dialog_ids_.begin() + min(limit, total_count))};
|
||||
}
|
||||
|
||||
void RecentDialogList::clear_dialogs() {
|
||||
if (dialog_ids_.empty() && is_loaded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_loaded_ = true;
|
||||
dialog_ids_.clear();
|
||||
removed_dialog_ids_.clear();
|
||||
save_dialogs();
|
||||
}
|
||||
|
||||
} // namespace td
|
59
td/telegram/RecentDialogList.h
Normal file
59
td/telegram/RecentDialogList.h
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
||||
//
|
||||
// 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/actor/actor.h"
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
namespace td {
|
||||
|
||||
class Td;
|
||||
|
||||
// stores list of Dialog identifiers of a limited size
|
||||
class RecentDialogList : public Actor {
|
||||
public:
|
||||
RecentDialogList(Td *td, const char *name, size_t max_size);
|
||||
|
||||
void add_dialog(DialogId dialog_id);
|
||||
|
||||
void remove_dialog(DialogId dialog_id);
|
||||
|
||||
std::pair<int32, vector<DialogId>> get_dialogs(int32 limit, Promise<Unit> &&promise);
|
||||
|
||||
void clear_dialogs();
|
||||
|
||||
private:
|
||||
Td *td_;
|
||||
const char *name_;
|
||||
size_t max_size_;
|
||||
vector<DialogId> dialog_ids_;
|
||||
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids_;
|
||||
|
||||
bool is_loaded_ = false;
|
||||
vector<Promise<Unit>> load_list_queries_;
|
||||
|
||||
void load_dialogs(Promise<Unit> &&promise);
|
||||
|
||||
void on_load_dialogs(vector<string> &&found_dialogs);
|
||||
|
||||
bool do_add_dialog(DialogId dialog_id);
|
||||
|
||||
string get_binlog_key() const;
|
||||
|
||||
void update_dialogs();
|
||||
|
||||
void save_dialogs() const;
|
||||
};
|
||||
|
||||
} // namespace td
|
@ -1132,7 +1132,7 @@ class StickersManager::StickerListLogEvent {
|
||||
StickersManager *stickers_manager = storer.context()->td().get_actor_unsafe()->stickers_manager_.get();
|
||||
td::store(narrow_cast<int32>(sticker_ids.size()), storer);
|
||||
for (auto sticker_id : sticker_ids) {
|
||||
stickers_manager->store_sticker(sticker_id, false, storer);
|
||||
stickers_manager->store_sticker(sticker_id, false, storer, "StickerListLogEvent");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ class StickersManager final : public Actor {
|
||||
void merge_stickers(FileId new_id, FileId old_id, bool can_delete_old);
|
||||
|
||||
template <class StorerT>
|
||||
void store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer) const;
|
||||
void store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer, const char *source) const;
|
||||
|
||||
template <class ParserT>
|
||||
FileId parse_sticker(bool in_sticker_set, ParserT &parser);
|
||||
|
@ -21,7 +21,7 @@
|
||||
namespace td {
|
||||
|
||||
template <class StorerT>
|
||||
void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer) const {
|
||||
void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer, const char *source) const {
|
||||
auto it = stickers_.find(file_id);
|
||||
if (it == stickers_.end() || it->second == nullptr) {
|
||||
return;
|
||||
@ -171,7 +171,7 @@ void StickersManager::store_sticker_set(const StickerSet *sticker_set, bool with
|
||||
store(stored_sticker_count, storer);
|
||||
for (uint32 i = 0; i < stored_sticker_count; i++) {
|
||||
auto sticker_id = sticker_set->sticker_ids[i];
|
||||
store_sticker(sticker_id, true, storer);
|
||||
store_sticker(sticker_id, true, storer, "store_sticker_set");
|
||||
|
||||
if (was_loaded) {
|
||||
auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
|
||||
|
@ -964,6 +964,25 @@ class GetInactiveSupergroupChatsRequest final : public RequestActor<> {
|
||||
}
|
||||
};
|
||||
|
||||
class GetRecentlyOpenedChatsRequest final : public RequestActor<> {
|
||||
int32 limit_;
|
||||
|
||||
std::pair<int32, vector<DialogId>> dialog_ids_;
|
||||
|
||||
void do_run(Promise<Unit> &&promise) final {
|
||||
dialog_ids_ = td->messages_manager_->get_recently_opened_dialogs(limit_, std::move(promise));
|
||||
}
|
||||
|
||||
void do_send_result() final {
|
||||
send_result(MessagesManager::get_chats_object(dialog_ids_));
|
||||
}
|
||||
|
||||
public:
|
||||
GetRecentlyOpenedChatsRequest(ActorShared<Td> td, uint64 request_id, int32 limit)
|
||||
: RequestActor(std::move(td), request_id), limit_(limit) {
|
||||
}
|
||||
};
|
||||
|
||||
class GetMessageRequest final : public RequestOnceActor {
|
||||
FullMessageId full_message_id_;
|
||||
|
||||
@ -4369,6 +4388,7 @@ void Td::send_update(tl_object_ptr<td_api::Update> &&object) {
|
||||
}
|
||||
|
||||
switch (object_id) {
|
||||
case td_api::updateChatThemes::ID:
|
||||
case td_api::updateFavoriteStickers::ID:
|
||||
case td_api::updateInstalledStickerSets::ID:
|
||||
case td_api::updateRecentStickers::ID:
|
||||
@ -5435,6 +5455,11 @@ void Td::on_request(uint64 id, const td_api::clearRecentlyFoundChats &request) {
|
||||
send_closure(actor_id(this), &Td::send_result, id, make_tl_object<td_api::ok>());
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getRecentlyOpenedChats &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST(GetRecentlyOpenedChatsRequest, request.limit_);
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::openChat &request) {
|
||||
CHECK_IS_USER();
|
||||
answer_ok_query(id, messages_manager_->open_dialog(DialogId(request.chat_id_)));
|
||||
@ -8135,12 +8160,6 @@ void Td::on_request(uint64 id, const td_api::resetBackgrounds &request) {
|
||||
background_manager_->reset_backgrounds(std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, const td_api::getChatThemes &request) {
|
||||
CHECK_IS_USER();
|
||||
CREATE_REQUEST_PROMISE();
|
||||
theme_manager_->get_chat_themes(std::move(promise));
|
||||
}
|
||||
|
||||
void Td::on_request(uint64 id, td_api::getRecentlyVisitedTMeUrls &request) {
|
||||
CHECK_IS_USER();
|
||||
CLEAN_INPUT_STRING(request.referrer_);
|
||||
|
@ -595,6 +595,8 @@ class Td final : public Actor {
|
||||
|
||||
void on_request(uint64 id, const td_api::clearRecentlyFoundChats &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getRecentlyOpenedChats &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getGroupsInCommon &request);
|
||||
|
||||
void on_request(uint64 id, td_api::checkChatUsername &request);
|
||||
@ -1195,8 +1197,6 @@ class Td final : public Actor {
|
||||
|
||||
void on_request(uint64 id, const td_api::resetBackgrounds &request);
|
||||
|
||||
void on_request(uint64 id, const td_api::getChatThemes &request);
|
||||
|
||||
void on_request(uint64 id, td_api::getRecentlyVisitedTMeUrls &request);
|
||||
|
||||
void on_request(uint64 id, td_api::setBotUpdatesStatus &request);
|
||||
|
@ -6,6 +6,7 @@
|
||||
//
|
||||
#include "td/telegram/ThemeManager.h"
|
||||
|
||||
#include "td/telegram/AuthManager.h"
|
||||
#include "td/telegram/BackgroundManager.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/net/NetQueryCreator.h"
|
||||
@ -14,6 +15,7 @@
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
namespace td {
|
||||
@ -44,26 +46,45 @@ class GetChatThemesQuery final : public Td::ResultHandler {
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(const ThemeManager::ThemeSettings &lhs, const ThemeManager::ThemeSettings &rhs) {
|
||||
return lhs.accent_color == rhs.accent_color && lhs.background_id == rhs.background_id &&
|
||||
lhs.background_type == rhs.background_type && lhs.base_theme == rhs.base_theme &&
|
||||
lhs.message_colors == rhs.message_colors && lhs.animate_message_colors == rhs.animate_message_colors;
|
||||
}
|
||||
|
||||
bool operator!=(const ThemeManager::ThemeSettings &lhs, const ThemeManager::ThemeSettings &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
ThemeManager::ThemeManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
|
||||
chat_themes_.next_reload_time = Time::now();
|
||||
}
|
||||
|
||||
void ThemeManager::start_up() {
|
||||
init();
|
||||
}
|
||||
|
||||
void ThemeManager::init() {
|
||||
if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
chat_themes_.next_reload_time = Time::now(); // TODO load chat themes from binlog
|
||||
loop();
|
||||
}
|
||||
|
||||
void ThemeManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
||||
void ThemeManager::get_chat_themes(Promise<td_api::object_ptr<td_api::chatThemes>> &&promise) {
|
||||
if (Time::now() < chat_themes_.next_reload_time) {
|
||||
return promise.set_value(get_chat_themes_object());
|
||||
void ThemeManager::loop() {
|
||||
if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chat_themes_.themes.empty()) {
|
||||
promise.set_value(get_chat_themes_object());
|
||||
pending_get_chat_themes_queries_.push_back(Promise<td_api::object_ptr<td_api::chatThemes>>());
|
||||
} else {
|
||||
pending_get_chat_themes_queries_.push_back(std::move(promise));
|
||||
if (Time::now() < chat_themes_.next_reload_time) {
|
||||
return set_timeout_at(chat_themes_.next_reload_time);
|
||||
}
|
||||
if (pending_get_chat_themes_queries_.size() == 1) {
|
||||
|
||||
auto request_promise = PromiseCreator::lambda(
|
||||
[actor_id = actor_id(this)](Result<telegram_api::object_ptr<telegram_api::account_ChatThemes>> result) {
|
||||
send_closure(actor_id, &ThemeManager::on_get_chat_themes, std::move(result));
|
||||
@ -71,6 +92,27 @@ void ThemeManager::get_chat_themes(Promise<td_api::object_ptr<td_api::chatThemes
|
||||
|
||||
td_->create_handler<GetChatThemesQuery>(std::move(request_promise))->send(chat_themes_.hash);
|
||||
}
|
||||
|
||||
void ThemeManager::on_update_theme(telegram_api::object_ptr<telegram_api::theme> &&theme, Promise<Unit> &&promise) {
|
||||
CHECK(theme != nullptr);
|
||||
bool is_changed = false;
|
||||
for (auto &chat_theme : chat_themes_.themes) {
|
||||
if (chat_theme.light_id == theme->id_ || chat_theme.dark_id == theme->id_) {
|
||||
auto theme_settings = get_chat_theme_settings(std::move(theme->settings_));
|
||||
if (chat_theme.light_id == theme->id_ && chat_theme.light_theme != theme_settings) {
|
||||
chat_theme.light_theme = theme_settings;
|
||||
is_changed = true;
|
||||
}
|
||||
if (chat_theme.dark_id == theme->id_ && chat_theme.dark_theme != theme_settings) {
|
||||
chat_theme.dark_theme = theme_settings;
|
||||
is_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_changed) {
|
||||
send_update_chat_themes();
|
||||
}
|
||||
promise.set_value(Unit());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::themeSettings> ThemeManager::get_theme_settings_object(const ThemeSettings &settings) const {
|
||||
@ -97,31 +139,29 @@ td_api::object_ptr<td_api::chatTheme> ThemeManager::get_chat_theme_object(const
|
||||
get_theme_settings_object(theme.dark_theme));
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::chatThemes> ThemeManager::get_chat_themes_object() const {
|
||||
return td_api::make_object<td_api::chatThemes>(
|
||||
td_api::object_ptr<td_api::updateChatThemes> ThemeManager::get_update_chat_themes_object() const {
|
||||
return td_api::make_object<td_api::updateChatThemes>(
|
||||
transform(chat_themes_.themes, [this](const ChatTheme &theme) { return get_chat_theme_object(theme); }));
|
||||
}
|
||||
|
||||
void ThemeManager::on_get_chat_themes(Result<telegram_api::object_ptr<telegram_api::account_ChatThemes>> result) {
|
||||
auto promises = std::move(pending_get_chat_themes_queries_);
|
||||
CHECK(!promises.empty());
|
||||
reset_to_empty(pending_get_chat_themes_queries_);
|
||||
|
||||
if (result.is_error()) {
|
||||
// do not clear chat_themes_
|
||||
|
||||
auto error = result.move_as_error();
|
||||
for (auto &promise : promises) {
|
||||
promise.set_error(error.clone());
|
||||
void ThemeManager::send_update_chat_themes() const {
|
||||
send_closure(G()->td(), &Td::send_update, get_update_chat_themes_object());
|
||||
}
|
||||
|
||||
void ThemeManager::on_get_chat_themes(Result<telegram_api::object_ptr<telegram_api::account_ChatThemes>> result) {
|
||||
if (result.is_error()) {
|
||||
set_timeout_in(Random::fast(40, 60));
|
||||
return;
|
||||
}
|
||||
|
||||
chat_themes_.next_reload_time = Time::now() + THEME_CACHE_TIME;
|
||||
set_timeout_at(chat_themes_.next_reload_time);
|
||||
|
||||
auto chat_themes_ptr = result.move_as_ok();
|
||||
LOG(DEBUG) << "Receive " << to_string(chat_themes_ptr);
|
||||
if (chat_themes_ptr->get_id() != telegram_api::account_chatThemesNotModified::ID) {
|
||||
if (chat_themes_ptr->get_id() == telegram_api::account_chatThemesNotModified::ID) {
|
||||
return;
|
||||
}
|
||||
CHECK(chat_themes_ptr->get_id() == telegram_api::account_chatThemes::ID);
|
||||
auto chat_themes = telegram_api::move_object_as<telegram_api::account_chatThemes>(chat_themes_ptr);
|
||||
chat_themes_.hash = chat_themes->hash_;
|
||||
@ -134,17 +174,17 @@ void ThemeManager::on_get_chat_themes(Result<telegram_api::object_ptr<telegram_a
|
||||
|
||||
ChatTheme theme;
|
||||
theme.emoji = std::move(chat_theme->emoticon_);
|
||||
theme.light_id = chat_theme->theme_->id_;
|
||||
theme.dark_id = chat_theme->dark_theme_->id_;
|
||||
theme.light_theme = get_chat_theme_settings(std::move(chat_theme->theme_->settings_));
|
||||
theme.dark_theme = get_chat_theme_settings(std::move(chat_theme->dark_theme_->settings_));
|
||||
if (theme.light_theme.message_colors.empty() || theme.dark_theme.message_colors.empty()) {
|
||||
continue;
|
||||
}
|
||||
chat_themes_.themes.push_back(std::move(theme));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &promise : promises) {
|
||||
if (promise) {
|
||||
promise.set_value(get_chat_themes_object());
|
||||
}
|
||||
}
|
||||
send_update_chat_themes();
|
||||
}
|
||||
|
||||
ThemeManager::BaseTheme ThemeManager::get_base_theme(
|
||||
@ -184,4 +224,12 @@ ThemeManager::ThemeSettings ThemeManager::get_chat_theme_settings(
|
||||
return result;
|
||||
}
|
||||
|
||||
void ThemeManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
|
||||
if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || chat_themes_.themes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updates.push_back(get_update_chat_themes_object());
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -25,7 +25,11 @@ class ThemeManager final : public Actor {
|
||||
public:
|
||||
ThemeManager(Td *td, ActorShared<> parent);
|
||||
|
||||
void get_chat_themes(Promise<td_api::object_ptr<td_api::chatThemes>> &&promise);
|
||||
void init();
|
||||
|
||||
void on_update_theme(telegram_api::object_ptr<telegram_api::theme> &&theme, Promise<Unit> &&promise);
|
||||
|
||||
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
|
||||
|
||||
private:
|
||||
enum class BaseTheme : int32 { Classic, Day, Night, Tinted, Arctic };
|
||||
@ -41,8 +45,14 @@ class ThemeManager final : public Actor {
|
||||
bool animate_message_colors = false;
|
||||
};
|
||||
|
||||
friend bool operator==(const ThemeSettings &lhs, const ThemeSettings &rhs);
|
||||
|
||||
friend bool operator!=(const ThemeSettings &lhs, const ThemeSettings &rhs);
|
||||
|
||||
struct ChatTheme {
|
||||
string emoji;
|
||||
int64 light_id = 0;
|
||||
int64 dark_id = 0;
|
||||
ThemeSettings light_theme;
|
||||
ThemeSettings dark_theme;
|
||||
};
|
||||
@ -53,6 +63,10 @@ class ThemeManager final : public Actor {
|
||||
vector<ChatTheme> themes;
|
||||
};
|
||||
|
||||
void start_up() final;
|
||||
|
||||
void loop() final;
|
||||
|
||||
void tear_down() final;
|
||||
|
||||
void on_get_chat_themes(Result<telegram_api::object_ptr<telegram_api::account_ChatThemes>> result);
|
||||
@ -61,14 +75,14 @@ class ThemeManager final : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::chatTheme> get_chat_theme_object(const ChatTheme &theme) const;
|
||||
|
||||
td_api::object_ptr<td_api::chatThemes> get_chat_themes_object() const;
|
||||
td_api::object_ptr<td_api::updateChatThemes> get_update_chat_themes_object() const;
|
||||
|
||||
void send_update_chat_themes() const;
|
||||
|
||||
static BaseTheme get_base_theme(const telegram_api::object_ptr<telegram_api::BaseTheme> &base_theme);
|
||||
|
||||
ThemeSettings get_chat_theme_settings(telegram_api::object_ptr<telegram_api::themeSettings> settings);
|
||||
|
||||
vector<Promise<td_api::object_ptr<td_api::chatThemes>>> pending_get_chat_themes_queries_;
|
||||
|
||||
ChatThemes chat_themes_;
|
||||
|
||||
Td *td_;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "td/telegram/StickersManager.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
#include "td/telegram/ThemeManager.h"
|
||||
#include "td/telegram/WebPagesManager.h"
|
||||
|
||||
#include "td/actor/MultiPromise.h"
|
||||
@ -3206,10 +3207,10 @@ void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateChannelParticip
|
||||
add_pending_qts_update(std::move(update), qts, std::move(promise));
|
||||
}
|
||||
|
||||
// unsupported updates
|
||||
|
||||
void UpdatesManager::on_update(tl_object_ptr<telegram_api::updateTheme> update, Promise<Unit> &&promise) {
|
||||
promise.set_value(Unit());
|
||||
td_->theme_manager_->on_update_theme(std::move(update->theme_), std::move(promise));
|
||||
}
|
||||
|
||||
// unsupported updates
|
||||
|
||||
} // namespace td
|
||||
|
@ -486,9 +486,9 @@ class UpdatesManager final : public Actor {
|
||||
void on_update(tl_object_ptr<telegram_api::updateChatParticipant> update, Promise<Unit> &&promise);
|
||||
void on_update(tl_object_ptr<telegram_api::updateChannelParticipant> update, Promise<Unit> &&promise);
|
||||
|
||||
// unsupported updates
|
||||
|
||||
void on_update(tl_object_ptr<telegram_api::updateTheme> update, Promise<Unit> &&promise);
|
||||
|
||||
// unsupported updates
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -1535,20 +1535,22 @@ class CliClient final : public Actor {
|
||||
|
||||
static td_api::object_ptr<td_api::BackgroundType> get_solid_pattern_background(int32 color, int32 intensity,
|
||||
bool is_moving) {
|
||||
return get_gradient_pattern_background(color, color, intensity, is_moving);
|
||||
return get_gradient_pattern_background(color, color, intensity, false, is_moving);
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::BackgroundType> get_gradient_pattern_background(int32 top_color, int32 bottom_color,
|
||||
int32 intensity, bool is_moving) {
|
||||
int32 intensity, bool is_inverted,
|
||||
bool is_moving) {
|
||||
return td_api::make_object<td_api::backgroundTypePattern>(get_background_fill(top_color, bottom_color), intensity,
|
||||
is_moving);
|
||||
is_inverted, is_moving);
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::BackgroundType> get_freeform_gradient_pattern_background(vector<int32> colors,
|
||||
int32 intensity,
|
||||
bool is_inverted,
|
||||
bool is_moving) {
|
||||
return td_api::make_object<td_api::backgroundTypePattern>(get_background_fill(std::move(colors)), intensity,
|
||||
is_moving);
|
||||
is_inverted, is_moving);
|
||||
}
|
||||
|
||||
static td_api::object_ptr<td_api::BackgroundType> get_solid_background(int32 color) {
|
||||
@ -2229,14 +2231,16 @@ class CliClient final : public Actor {
|
||||
send_get_background_url(get_solid_pattern_background(0, 0, false));
|
||||
send_get_background_url(get_solid_pattern_background(0xFFFFFF, 100, true));
|
||||
send_get_background_url(get_solid_pattern_background(0xABCDEF, 49, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0, 0, 0, false));
|
||||
send_get_background_url(get_gradient_pattern_background(0xFFFFFF, 0, 100, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0xABCDEF, 0xFEDCBA, 49, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0, 0x1000000, 49, true));
|
||||
send_get_background_url(get_freeform_gradient_pattern_background({0xABCDEF, 0xFEDCBA}, 49, true));
|
||||
send_get_background_url(get_freeform_gradient_pattern_background({0xABCDEF, 0x111111, 0x222222}, 49, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0, 0, 0, false, false));
|
||||
send_get_background_url(get_gradient_pattern_background(0, 0, 0, true, false));
|
||||
send_get_background_url(get_gradient_pattern_background(0xFFFFFF, 0, 100, false, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0xFFFFFF, 0, 100, true, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0xABCDEF, 0xFEDCBA, 49, false, true));
|
||||
send_get_background_url(get_gradient_pattern_background(0, 0x1000000, 49, false, true));
|
||||
send_get_background_url(get_freeform_gradient_pattern_background({0xABCDEF, 0xFEDCBA}, 49, false, true));
|
||||
send_get_background_url(get_freeform_gradient_pattern_background({0xABCDEF, 0x111111, 0x222222}, 49, true, true));
|
||||
send_get_background_url(
|
||||
get_freeform_gradient_pattern_background({0xABCDEF, 0xFEDCBA, 0x111111, 0x222222}, 49, true));
|
||||
get_freeform_gradient_pattern_background({0xABCDEF, 0xFEDCBA, 0x111111, 0x222222}, 49, false, true));
|
||||
send_get_background_url(get_solid_background(-1));
|
||||
send_get_background_url(get_solid_background(0xABCDEF));
|
||||
send_get_background_url(get_solid_background(0x1000000));
|
||||
@ -2261,7 +2265,7 @@ class CliClient final : public Actor {
|
||||
} else if (op == "sbggp" || op == "sbggpd") {
|
||||
send_request(td_api::make_object<td_api::setBackground>(
|
||||
td_api::make_object<td_api::inputBackgroundLocal>(as_input_file(args)),
|
||||
get_gradient_pattern_background(0xABCDEF, 0xFE, 51, false), op == "sbggpd"));
|
||||
get_gradient_pattern_background(0xABCDEF, 0xFE, 51, op == "sbggpd", false), op == "sbggpd"));
|
||||
} else if (op == "sbgs" || op == "sbgsd") {
|
||||
int32 color;
|
||||
get_args(args, color);
|
||||
@ -2296,8 +2300,6 @@ class CliClient final : public Actor {
|
||||
send_request(td_api::make_object<td_api::removeBackground>(to_integer<int64>(args)));
|
||||
} else if (op == "rbgs") {
|
||||
send_request(td_api::make_object<td_api::resetBackgrounds>());
|
||||
} else if (op == "gcts") {
|
||||
send_request(td_api::make_object<td_api::getChatThemes>());
|
||||
} else if (op == "gcos") {
|
||||
send_request(td_api::make_object<td_api::getCountries>());
|
||||
} else if (op == "gcoc") {
|
||||
@ -4050,6 +4052,8 @@ class CliClient final : public Actor {
|
||||
send_request(td_api::make_object<td_api::removeRecentlyFoundChat>(as_chat_id(args)));
|
||||
} else if (op == "crfcs") {
|
||||
send_request(td_api::make_object<td_api::clearRecentlyFoundChats>());
|
||||
} else if (op == "groc") {
|
||||
send_request(td_api::make_object<td_api::getRecentlyOpenedChats>(as_limit(args)));
|
||||
} else if (op == "gwpp") {
|
||||
send_request(td_api::make_object<td_api::getWebPagePreview>(as_caption(args)));
|
||||
} else if (op == "gwpiv") {
|
||||
|
@ -172,7 +172,7 @@ class DcOption {
|
||||
|
||||
friend bool operator==(const DcOption &lhs, const DcOption &rhs);
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const DcOption::PrintFlags &flags);
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const PrintFlags &flags);
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const DcOption &dc_option);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/tl_storers.h"
|
||||
#include "td/utils/TlDowncastHelper.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@ -149,21 +150,6 @@ Status from_json(std::vector<T> &to, JsonValue from) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class DowncastHelper final : public T {
|
||||
public:
|
||||
explicit DowncastHelper(int32 constructor) : constructor_(constructor) {
|
||||
}
|
||||
int32 get_id() const final {
|
||||
return constructor_;
|
||||
}
|
||||
void store(TlStorerToString &s, const char *field_name) const final {
|
||||
}
|
||||
|
||||
private:
|
||||
int32 constructor_{0};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::enable_if_t<!std::is_constructible<T>::value, Status> from_json(tl_object_ptr<T> &to, JsonValue from) {
|
||||
if (from.type() != JsonValue::Type::Object) {
|
||||
@ -185,7 +171,7 @@ std::enable_if_t<!std::is_constructible<T>::value, Status> from_json(tl_object_p
|
||||
return Status::Error(PSLICE() << "Expected String or Integer, got " << constructor_value.type());
|
||||
}
|
||||
|
||||
DowncastHelper<T> helper(constructor);
|
||||
TlDowncastHelper<T> helper(constructor);
|
||||
Status status;
|
||||
bool ok = downcast_call(static_cast<T &>(helper), [&](auto &dummy) {
|
||||
auto result = make_tl_object<std::decay_t<decltype(dummy)>>();
|
||||
|
@ -256,10 +256,10 @@ set(TDUTILS_SOURCE
|
||||
td/utils/Time.h
|
||||
td/utils/TimedStat.h
|
||||
td/utils/Timer.h
|
||||
td/utils/TsFileLog.h
|
||||
td/utils/tl_helpers.h
|
||||
td/utils/tl_parsers.h
|
||||
td/utils/tl_storers.h
|
||||
td/utils/TlDowncastHelper.h
|
||||
td/utils/translit.h
|
||||
td/utils/TsCerr.h
|
||||
td/utils/TsFileLog.h
|
||||
|
26
tdutils/td/utils/TlDowncastHelper.h
Normal file
26
tdutils/td/utils/TlDowncastHelper.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
||||
//
|
||||
// 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
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class T>
|
||||
class TlDowncastHelper final : public T {
|
||||
public:
|
||||
explicit TlDowncastHelper(int32 constructor) : constructor_(constructor) {
|
||||
}
|
||||
int32 get_id() const final {
|
||||
return constructor_;
|
||||
}
|
||||
void store(TlStorerToString &s, const char *field_name) const final {
|
||||
}
|
||||
|
||||
private:
|
||||
int32 constructor_{0};
|
||||
};
|
||||
|
||||
} // namespace td
|
@ -10,6 +10,8 @@ char disable_linker_warning_about_empty_file_wstring_convert_cpp TD_UNUSED;
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/utf8.h"
|
||||
|
||||
#include <cwchar>
|
||||
@ -18,7 +20,7 @@ namespace td {
|
||||
|
||||
Result<std::wstring> to_wstring(CSlice slice) {
|
||||
if (!check_utf8(slice)) {
|
||||
return Status::Error("Wrong string encoding");
|
||||
return Status::Error(PSLICE() << "String was expected to be encoded in UTF-8: " << base64_encode(slice));
|
||||
}
|
||||
|
||||
size_t wstring_len = utf8_utf16_length(slice);
|
||||
|
Loading…
x
Reference in New Issue
Block a user