Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2021-09-15 16:03:11 +02:00
commit 6e1836a024
32 changed files with 713 additions and 471 deletions

View File

@ -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

View File

@ -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 ..');

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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,52 +466,66 @@ 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);
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);
}
if (packet.size() >= 4 && as<int32>(packet.begin()) == mtproto_api::rpc_result::ID) {
return on_packet_rpc_result(info, packet);
}
// It is an update... I hope.
auto 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_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size << ": " << status << ' '
<< recheck_status;
}
if (status.is_error()) {
if (status.code() == 2) {
LOG(WARNING) << "Receive too old update from " << get_name() << " created in " << (Time::now() - created_at_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size << ": " << status;
callback_->on_session_failed(Status::Error("Receive too old update"));
return status;
}
VLOG(mtproto) << "Skip update " << info.message_id << " of size " << info.size << " with seq_no " << info.seq_no
<< " from " << get_name() << " created in " << (Time::now() - created_at_) << ": " << status;
return Status::OK();
} else {
VLOG(mtproto) << "Got update from " << get_name() << " created in " << (Time::now() - created_at_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size;
return callback_->on_update(as_buffer_slice(packet));
}
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;
downcast_call(*object, OnPacket(info, this, &status));
return 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()) {
status = parser.get_status();
} else {
status = this->on_packet(info, static_cast<const Type &>(*object));
}
});
if (is_mtproto_api) {
return status;
}
// It is an update... I hope.
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_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size << ": " << status << ' '
<< recheck_status;
}
if (status.is_error()) {
if (status.code() == 2) {
LOG(WARNING) << "Receive too old update from " << get_name() << " created in " << (Time::now() - created_at_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size << ": " << status;
callback_->on_session_failed(Status::Error("Receive too old update"));
return status;
}
VLOG(mtproto) << "Skip update " << info.message_id << " of size " << info.size << " with seq_no " << info.seq_no
<< " from " << get_name() << " created in " << (Time::now() - created_at_) << ": " << status;
return Status::OK();
} else {
VLOG(mtproto) << "Got update from " << get_name() << " created in " << (Time::now() - created_at_)
<< " in container " << container_id_ << " from session " << auth_data_->get_session_id()
<< " with message_id " << info.message_id << ", main_message_id = " << main_message_id_
<< ", seq_no = " << info.seq_no << " and original size " << info.size;
return callback_->on_update(as_buffer_slice(packet));
}
}
Status SessionConnection::parse_packet(TlParser &parser) {
@ -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()) {

View File

@ -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_;

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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);
}

View File

@ -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: {

View File

@ -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) {

View File

@ -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;

View 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

View 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

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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_);

View File

@ -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);

View File

@ -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,33 +46,73 @@ 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) {
void ThemeManager::loop() {
if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) {
return;
}
if (Time::now() < chat_themes_.next_reload_time) {
return promise.set_value(get_chat_themes_object());
return set_timeout_at(chat_themes_.next_reload_time);
}
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 (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));
});
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));
});
td_->create_handler<GetChatThemesQuery>(std::move(request_promise))->send(chat_themes_.hash);
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,54 +139,52 @@ 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::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) {
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());
}
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) {
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_;
chat_themes_.themes.clear();
for (auto &chat_theme : chat_themes->themes_) {
if (chat_theme->emoticon_.empty()) {
LOG(ERROR) << "Receive " << to_string(chat_theme);
continue;
}
ChatTheme theme;
theme.emoji = std::move(chat_theme->emoticon_);
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_));
chat_themes_.themes.push_back(std::move(theme));
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_;
chat_themes_.themes.clear();
for (auto &chat_theme : chat_themes->themes_) {
if (chat_theme->emoticon_.empty()) {
LOG(ERROR) << "Receive " << to_string(chat_theme);
continue;
}
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

View File

@ -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_;

View File

@ -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

View File

@ -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

View File

@ -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") {

View File

@ -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);

View File

@ -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)>>();

View File

@ -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

View 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

View File

@ -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);