Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-04-22 15:35:30 +02:00
commit d84305048e
148 changed files with 7346 additions and 2631 deletions

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TDLib VERSION 1.8.2 LANGUAGES CXX C)
project(TDLib VERSION 1.8.3 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
@ -282,6 +282,7 @@ set(TDLIB_SOURCE
td/telegram/Account.cpp
td/telegram/AnimationsManager.cpp
td/telegram/AttachMenuManager.cpp
td/telegram/AudiosManager.cpp
td/telegram/AuthManager.cpp
td/telegram/AutoDownloadSettings.cpp
@ -289,6 +290,8 @@ set(TDLIB_SOURCE
td/telegram/BackgroundType.cpp
td/telegram/BotCommand.cpp
td/telegram/BotCommandScope.cpp
td/telegram/BotMenuButton.cpp
td/telegram/BotMenuButton.h
td/telegram/CallActor.cpp
td/telegram/CallDiscardReason.cpp
td/telegram/CallManager.cpp
@ -393,6 +396,8 @@ set(TDLIB_SOURCE
td/telegram/NewPasswordState.cpp
td/telegram/NotificationManager.cpp
td/telegram/NotificationSettings.cpp
td/telegram/NotificationSettingsManager.cpp
td/telegram/NotificationSound.cpp
td/telegram/NotificationType.cpp
td/telegram/OptionManager.cpp
td/telegram/Payments.cpp
@ -400,6 +405,7 @@ set(TDLIB_SOURCE
td/telegram/PhoneNumberManager.cpp
td/telegram/PrivacyManager.cpp
td/telegram/Photo.cpp
td/telegram/PhotoSize.cpp
td/telegram/PhotoSizeSource.cpp
td/telegram/PollManager.cpp
td/telegram/QueryCombiner.cpp
@ -470,6 +476,7 @@ set(TDLIB_SOURCE
td/telegram/Account.h
td/telegram/AffectedHistory.h
td/telegram/AnimationsManager.h
td/telegram/AttachMenuManager.h
td/telegram/AudiosManager.h
td/telegram/AuthManager.h
td/telegram/AutoDownloadSettings.h
@ -486,6 +493,7 @@ set(TDLIB_SOURCE
td/telegram/ChainId.h
td/telegram/ChannelId.h
td/telegram/ChannelParticipantFilter.h
td/telegram/ChannelType.h
td/telegram/ChatId.h
td/telegram/ClientActor.h
td/telegram/ConfigManager.h
@ -617,12 +625,17 @@ set(TDLIB_SOURCE
td/telegram/NotificationId.h
td/telegram/NotificationManager.h
td/telegram/NotificationSettings.h
td/telegram/NotificationSettingsManager.h
td/telegram/NotificationSound.h
td/telegram/NotificationSoundType.h
td/telegram/NotificationType.h
td/telegram/OptionManager.h
td/telegram/PasswordManager.h
td/telegram/Payments.h
td/telegram/PhoneNumberManager.h
td/telegram/Photo.h
td/telegram/PhotoFormat.h
td/telegram/PhotoSize.h
td/telegram/PhotoSizeSource.h
td/telegram/PollId.h
td/telegram/PollManager.h
@ -701,6 +714,7 @@ set(TDLIB_SOURCE
td/telegram/NotificationSettings.hpp
td/telegram/Payments.hpp
td/telegram/Photo.hpp
td/telegram/PhotoSize.hpp
td/telegram/PhotoSizeSource.hpp
td/telegram/PollId.hpp
td/telegram/PollManager.hpp

View File

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

View File

@ -277,6 +277,7 @@ function split_file($file, $chunks, $undo) {
if (!preg_match('/Td::~?Td/', $new_content)) { // destructor Td::~Td needs to see definitions of all forward-declared classes
$td_methods = array(
'animations_manager[_(-][^.]|AnimationsManager[^;>]' => "AnimationsManager",
'attach_menu_manager[_(-][^.]|AttachMenuManager[^;>]' => "AttachMenuManager",
'audios_manager[_(-][^.]|AudiosManager' => "AudiosManager",
'auth_manager[_(-][^.]|AuthManager' => 'AuthManager',
'background_manager[_(-][^.]|BackgroundManager' => "BackgroundManager",
@ -297,6 +298,7 @@ function split_file($file, $chunks, $undo) {
'MessageCopyOptions' => 'MessageCopyOptions',
'messages_manager[_(-][^.]|MessagesManager' => 'MessagesManager',
'notification_manager[_(-][^.]|NotificationManager|notifications[)]' => 'NotificationManager',
'notification_settings_manager[_(-][^.]|NotificationSettingsManager' => 'NotificationSettingsManager',
'option_manager[_(-][^.]|OptionManager' => "OptionManager",
'phone_number_manager[_(-][^.]|PhoneNumberManager' => "PhoneNumberManager",
'poll_manager[_(-][^.]|PollManager' => "PollManager",

View File

@ -26,7 +26,7 @@ static void usage() {
td::TsCerr() << "Options:\n";
td::TsCerr() << " -v<N>\tSet verbosity level to N\n";
td::TsCerr() << " -h/--help\tDisplay this information\n";
td::TsCerr() << " -d/--dc-id\tIdentifier of a datacenter, to which try to connect (default is 2)\n";
td::TsCerr() << " -d/--dc-id\tIdentifier of a datacenter to which try to connect (default is 2)\n";
td::TsCerr() << " -l/--proxy-list\tName of a file with proxies to check; one proxy per line\n";
td::TsCerr() << " -t/--timeout\tMaximum overall timeout for the request (default is 10 seconds)\n";
std::exit(2);

View File

@ -3,11 +3,13 @@
<head>
<title>TDLight build instructions</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--background: #fafafa;
--color: black;
--color-primary: #0088ff;
--color-primary-light: #42a7ff;
--color-code-block: #ebf9ff;
--color-select-border: rgb(211, 211, 211);
--color-checkbox-background: rgb(211, 211, 211);
@ -38,6 +40,14 @@
body {
font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
}
:focus:not(:focus-visible) {
outline: none;
}
input:focus-visible, select:focus-visible, button:focus-visible {
box-shadow: 0 0 0 3px var(--color-primary);
outline: none;
}
.hide {
display: none;
}
@ -62,6 +72,7 @@
padding: 5px;
margin-bottom: 0;
display: block;
overflow-x: auto;
}
#buildCommands ul {
list-style: '$ ';
@ -70,30 +81,29 @@
a {
color: var(--color-primary);
text-decoration-color: transparent;
transition: text-decoration-color 200ms;
transition: text-decoration 200ms, color 200ms;
}
a:hover {
text-decoration: underline;
}
a:focus-visible {
text-decoration: underline 2px;
color: var(--color-primary-light);
outline: none;
}
select, button {
border: 1px solid var(--color-select-border);
background-color: var(--background);
color: var(--color);
padding: 5px;
margin-top: 5px;
transition: border 200ms, padding 200ms;
box-shadow: 0 0 0 0 var(--color-primary);
transition: border 200ms, padding 200ms, box-shadow 200ms;
border-radius: 999em;
font-size: 16px;
cursor: pointer;
}
select:focus, button:focus {
outline: none;
border-color: var(--color-primary);
border-width: 2px;
padding: 4px;
}
label * {
vertical-align: middle;
}
@ -108,7 +118,8 @@
width: 20px;
border-radius: 3px;
position: relative;
transition: background-color 200ms;
top: -1px; // Fix alignment
transition: background-color 200ms, box-shadow 200ms;
}
input[type=checkbox]::after {
content: "";
@ -141,7 +152,8 @@
width: 20px;
border-radius: 100%;
position: relative;
transition: background-color 200ms;
top: -2px; // Fix alignment
transition: background-color 200ms, box-shadow 200ms;
}
input[type=radio]::after {
content: "";
@ -181,7 +193,7 @@
<div class="main">
<div id="languageSelectDiv">
<p>Choose a programming language, from which you want to use TDLight:</p>
<p>Choose a programming language from which you want to use TDLight:</p>
<select id="languageSelect" onchange="onLanguageChanged(false)" autofocus class="large">
<option>Choose a programming language:</option>
<option>Python</option>
@ -213,7 +225,7 @@
</div>
<div id="osSelectDiv" class="hide">
<p>Choose an operating system, on which you want to use TDLight:</p>
<p>Choose an operating system on which you want to use TDLight:</p>
<select id="osSelect" onchange="onOsChanged()" class="large">
<option>Choose an operating system:</option>
</select>
@ -221,7 +233,7 @@
</div>
<div id="linuxSelectDiv" class="hide">
<p>Choose a Linux distro, on which you want to use TDLight:</p>
<p>Choose a Linux distro on which you want to use TDLight:</p>
<select id="linuxSelect" onchange="onOsChanged()" class="large">
<option>Choose a Linux distro:</option>
<option>Alpine</option>

View File

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

View File

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

View File

@ -366,10 +366,14 @@ userTypeRegular = UserType;
//@description A deleted user or deleted bot. No information on the user besides the user identifier is available. It is not possible to perform any active actions on this type of user
userTypeDeleted = UserType;
//@description A bot (see https://core.telegram.org/bots) @can_join_groups True, if the bot can be invited to basic group and supergroup chats
//@description A bot (see https://core.telegram.org/bots)
//@can_join_groups True, if the bot can be invited to basic group and supergroup chats
//@can_read_all_group_messages True, if the bot can read all messages in basic group or supergroup chats and not just those addressed to the bot. In private and channel chats a bot can always read all messages
//@is_inline True, if the bot supports inline queries @inline_query_placeholder Placeholder for inline queries (displayed on the application input field) @need_location True, if the location of the user is expected to be sent with every inline query to this bot
userTypeBot can_join_groups:Bool can_read_all_group_messages:Bool is_inline:Bool inline_query_placeholder:string need_location:Bool = UserType;
//@is_inline True, if the bot supports inline queries
//@inline_query_placeholder Placeholder for inline queries (displayed on the application input field)
//@need_location True, if the location of the user is expected to be sent with every inline query to this bot
//@can_be_added_to_attachment_menu True, if the bot can be added to attachment menu
userTypeBot can_join_groups:Bool can_read_all_group_messages:Bool is_inline:Bool inline_query_placeholder:string need_location:Bool can_be_added_to_attachment_menu:Bool = UserType;
//@description No information on the user besides the user identifier is available, yet this user has not been deleted. This object is extremely rare and must be handled like a deleted user. It is not possible to perform any actions on users of this type
userTypeUnknown = UserType;
@ -395,6 +399,9 @@ botCommand command:string description:string = BotCommand;
//@description Contains a list of bot commands @bot_user_id Bot's user identifier @commands List of bot commands
botCommands bot_user_id:int53 commands:vector<botCommand> = BotCommands;
//@description Describes a button to be shown instead of bot commands menu button @text Text of the button @url URL to be passed to openWebApp
botMenuButton text:string url:string = BotMenuButton;
//@description Represents a location to which a chat is connected @location The location @address Location address; 1-64 characters, as defined by the chat owner
chatLocation location:location address:string = ChatLocation;
@ -433,6 +440,32 @@ inputChatPhotoStatic photo:InputFile = InputChatPhoto;
inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputChatPhoto;
//@description Describes actions that a user is allowed to take in a chat
//@can_send_messages True, if the user can send text messages, contacts, locations, and venues
//@can_send_media_messages True, if the user can send audio files, documents, photos, videos, video notes, and voice notes. Implies can_send_messages permissions
//@can_send_polls True, if the user can send polls. Implies can_send_messages permissions
//@can_send_other_messages True, if the user can send animations, games, stickers, and dice and use inline bots. Implies can_send_messages permissions
//@can_add_web_page_previews True, if the user may add a web page preview to their messages. Implies can_send_messages permissions
//@can_change_info True, if the user can change the chat title, photo, and other settings
//@can_invite_users True, if the user can invite new users to the chat
//@can_pin_messages True, if the user can pin messages
chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_polls:Bool can_send_other_messages:Bool can_add_web_page_previews:Bool can_change_info:Bool can_invite_users:Bool can_pin_messages:Bool = ChatPermissions;
//@description Describes rights of the administrator
//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only
//@can_change_info True, if the administrator can change the chat title, photo, and other settings
//@can_post_messages True, if the administrator can create channel posts; applicable to channels only
//@can_edit_messages True, if the administrator can edit messages of other users and pin messages; applicable to channels only
//@can_delete_messages True, if the administrator can delete messages of other users
//@can_invite_users True, if the administrator can invite new users to the chat
//@can_restrict_members True, if the administrator can restrict, ban, or unban chat members; always true for channels
//@can_pin_messages True, if the administrator can pin messages; applicable to basic groups and supergroups only
//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them
//@can_manage_video_chats True, if the administrator can manage video chats
//@is_anonymous True, if the administrator isn't shown in the chat member list and sends messages anonymously; applicable to supergroups only
chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatAdministratorRights;
//@description Represents a user
//@id User identifier
//@first_name First name of the user
@ -453,6 +486,15 @@ inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputC
//@language_code IETF language tag of the user's language; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string = User;
//@description Contains information about a bot
//@share_text The text that is shown on the bot's profile page and is sent together with the link when users share the bot
//@param_description The text shown in the chat with the bot if the chat is empty
//@menu_button Information about a button to show instead of the bot commands menu button; may be null if ordinary bot commands menu must be shown
//@commands List of the bot commands
//@default_group_administrator_rights Default administrator rights for adding the bot to basic group and supergroup chats; may be null
//@default_channel_administrator_rights Default administrator rights for adding the bot to channels; may be null
botInfo share_text:string description:string menu_button:botMenuButton commands:vector<botCommand> default_group_administrator_rights:chatAdministratorRights default_channel_administrator_rights:chatAdministratorRights = BotInfo;
//@description Contains full information about a user
//@photo User profile photo; may be null
//@is_blocked True, if the user is blocked by the current user
@ -462,11 +504,9 @@ user id:int53 first_name:string last_name:string username:string phone_number:st
//@has_private_forwards True, if the user can't be linked in forwarded messages due to their privacy settings
//@need_phone_number_privacy_exception True, if the current user needs to explicitly allow to share their phone number with the user when the method addContact is used
//@bio A short user bio
//@share_text For bots, the text that is shown on the bot's profile page and is sent together with the link when users share the bot
//@param_description For bots, the text shown in the chat with the bot if the chat is empty
//@group_in_common_count Number of group chats where both the other user and the current user are a member; 0 for the current user
//@commands For bots, list of the bot commands
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool need_phone_number_privacy_exception:Bool bio:string share_text:string description:string group_in_common_count:int32 commands:vector<botCommand> = UserFullInfo;
//@bot_info For bots, information about the bot; may be null
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool need_phone_number_privacy_exception:Bool bio:string group_in_common_count:int32 bot_info:botInfo = UserFullInfo;
//@description Represents a list of users @total_count Approximate total number of users found @user_ids A list of user identifiers
users total_count:int32 user_ids:vector<int53> = Users;
@ -479,18 +519,6 @@ chatAdministrator user_id:int53 custom_title:string is_owner:Bool = ChatAdminist
chatAdministrators administrators:vector<chatAdministrator> = ChatAdministrators;
//@description Describes actions that a user is allowed to take in a chat
//@can_send_messages True, if the user can send text messages, contacts, locations, and venues
//@can_send_media_messages True, if the user can send audio files, documents, photos, videos, video notes, and voice notes. Implies can_send_messages permissions
//@can_send_polls True, if the user can send polls. Implies can_send_messages permissions
//@can_send_other_messages True, if the user can send animations, games, stickers, and dice and use inline bots. Implies can_send_messages permissions
//@can_add_web_page_previews True, if the user may add a web page preview to their messages. Implies can_send_messages permissions
//@can_change_info True, if the user can change the chat title, photo, and other settings
//@can_invite_users True, if the user can invite new users to the chat
//@can_pin_messages True, if the user can pin messages
chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_polls:Bool can_send_other_messages:Bool can_add_web_page_previews:Bool can_change_info:Bool can_invite_users:Bool can_pin_messages:Bool = ChatPermissions;
//@class ChatMemberStatus @description Provides information about the status of a member in a chat
//@description The user is the owner of the chat and has all the administrator privileges
@ -502,18 +530,8 @@ chatMemberStatusCreator custom_title:string is_anonymous:Bool is_member:Bool = C
//@description The user is a member of the chat and has some additional privileges. In basic groups, administrators can edit and delete messages sent by others, add new members, ban unprivileged members, and manage video chats. In supergroups and channels, there are more detailed options for administrator privileges
//@custom_title A custom title of the administrator; 0-16 characters without emojis; applicable to supergroups only
//@can_be_edited True, if the current user can edit the administrator privileges for the called user
//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only
//@can_change_info True, if the administrator can change the chat title, photo, and other settings
//@can_post_messages True, if the administrator can create channel posts; applicable to channels only
//@can_edit_messages True, if the administrator can edit messages of other users and pin messages; applicable to channels only
//@can_delete_messages True, if the administrator can delete messages of other users
//@can_invite_users True, if the administrator can invite new users to the chat
//@can_restrict_members True, if the administrator can restrict, ban, or unban chat members; always true for channels
//@can_pin_messages True, if the administrator can pin messages; applicable to basic groups and supergroups only
//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them
//@can_manage_video_chats True, if the administrator can manage video chats
//@is_anonymous True, if the administrator isn't shown in the chat member list and sends messages anonymously; applicable to supergroups only
chatMemberStatusAdministrator custom_title:string can_be_edited:Bool can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatMemberStatus;
//@rights Rights of the administrator
chatMemberStatusAdministrator custom_title:string can_be_edited:Bool rights:chatAdministratorRights = ChatMemberStatus;
//@description The user is a member of the chat, without any additional privileges or restrictions
chatMemberStatusMember = ChatMemberStatus;
@ -790,7 +808,7 @@ messageReplyInfo reply_count:int32 recent_replier_ids:vector<MessageSender> last
//@reaction Text representation of the reaction
//@total_count Number of times the reaction was added
//@is_chosen True, if the reaction is chosen by the current user
//@recent_sender_ids Identifiers of at most 3 recent message senders, added the reaction; available in private chats, basic groups and supergroups
//@recent_sender_ids Identifiers of at most 3 recent message senders, added the reaction; available in private, basic group and supergroup chats
messageReaction reaction:string total_count:int32 is_chosen:Bool recent_sender_ids:vector<MessageSender> = MessageReaction;
//@description Contains information about interactions with a message
@ -881,7 +899,7 @@ messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalen
//@message_id Message identifier; unique for the chat to which the sponsored message belongs among both ordinary and sponsored messages
//@sponsor_chat_id Sponsor chat identifier; 0 if the sponsor chat is accessible through an invite link
//@sponsor_chat_info Information about the sponsor chat; may be null unless sponsor_chat_id == 0
//@link An internal link to be opened when the sponsored message is clicked; may be null. If null, the sponsor chat needs to be opened instead
//@link An internal link to be opened when the sponsored message is clicked; may be null if the sponsor chat needs to be opened instead
//@content Content of the message. Currently, can be only of the type messageText
sponsoredMessage message_id:int53 sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
@ -912,32 +930,32 @@ foundFileDownloads total_counts:downloadedFileCounts files:vector<fileDownload>
//@description Notification settings applied to all private and secret chats when the corresponding chat setting has a default value
notificationSettingsScopePrivateChats = NotificationSettingsScope;
//@description Notification settings applied to all basic groups and supergroups when the corresponding chat setting has a default value
//@description Notification settings applied to all basic group and supergroup chats when the corresponding chat setting has a default value
notificationSettingsScopeGroupChats = NotificationSettingsScope;
//@description Notification settings applied to all channels when the corresponding chat setting has a default value
//@description Notification settings applied to all channel chats when the corresponding chat setting has a default value
notificationSettingsScopeChannelChats = NotificationSettingsScope;
//@description Contains information about notification settings for a chat
//@use_default_mute_for If true, mute_for is ignored and the value for the relevant type of chat is used instead @mute_for Time left before notifications will be unmuted, in seconds
//@use_default_sound If true, sound is ignored and the value for the relevant type of chat is used instead @sound The name of an audio file to be used for notification sounds; only applies to iOS applications
//@use_default_sound If true, the value for the relevant type of chat is used instead of sound_id @sound_id Identifier of the notification sound to be played; 0 if sound is disabled
//@use_default_show_preview If true, show_preview is ignored and the value for the relevant type of chat is used instead @show_preview True, if message content must be displayed in notifications
//@use_default_disable_pinned_message_notifications If true, disable_pinned_message_notifications is ignored and the value for the relevant type of chat is used instead @disable_pinned_message_notifications If true, notifications for incoming pinned messages will be created as for an ordinary unread message
//@use_default_disable_mention_notifications If true, disable_mention_notifications is ignored and the value for the relevant type of chat is used instead @disable_mention_notifications If true, notifications for messages with mentions will be created as for an ordinary unread message
chatNotificationSettings use_default_mute_for:Bool mute_for:int32 use_default_sound:Bool sound:string use_default_show_preview:Bool show_preview:Bool use_default_disable_pinned_message_notifications:Bool disable_pinned_message_notifications:Bool use_default_disable_mention_notifications:Bool disable_mention_notifications:Bool = ChatNotificationSettings;
chatNotificationSettings use_default_mute_for:Bool mute_for:int32 use_default_sound:Bool sound_id:int64 use_default_show_preview:Bool show_preview:Bool use_default_disable_pinned_message_notifications:Bool disable_pinned_message_notifications:Bool use_default_disable_mention_notifications:Bool disable_mention_notifications:Bool = ChatNotificationSettings;
//@description Contains information about notification settings for several chats
//@mute_for Time left before notifications will be unmuted, in seconds
//@sound The name of an audio file to be used for notification sounds; only applies to iOS applications
//@sound_id Identifier of the notification sound to be played; 0 if sound is disabled
//@show_preview True, if message content must be displayed in notifications
//@disable_pinned_message_notifications True, if notifications for incoming pinned messages will be created as for an ordinary unread message
//@disable_mention_notifications True, if notifications for messages with mentions will be created as for an ordinary unread message
scopeNotificationSettings mute_for:int32 sound:string show_preview:Bool disable_pinned_message_notifications:Bool disable_mention_notifications:Bool = ScopeNotificationSettings;
scopeNotificationSettings mute_for:int32 sound_id:int64 show_preview:Bool disable_pinned_message_notifications:Bool disable_mention_notifications:Bool = ScopeNotificationSettings;
//@description Contains information about a message draft
//@reply_to_message_id Identifier of the message to reply to; 0 if none
//@reply_to_message_id Identifier of the replied message; 0 if none
//@date Point in time (Unix timestamp) when the draft was created
//@input_message_text Content of the message draft; must be of the type inputMessageText
draftMessage reply_to_message_id:int53 date:int32 input_message_text:InputMessageContent = DraftMessage;
@ -1125,6 +1143,9 @@ keyboardButtonTypeRequestLocation = KeyboardButtonType;
//@description A button that allows the user to create and send a poll when pressed; available only in private chats @force_regular If true, only regular polls must be allowed to create @force_quiz If true, only polls in quiz mode must be allowed to create
keyboardButtonTypeRequestPoll force_regular:Bool force_quiz:Bool = KeyboardButtonType;
//@description A button that opens a web app by calling getWebAppUrl @url An HTTP URL to pass to getWebAppUrl
keyboardButtonTypeWebApp url:string = KeyboardButtonType;
//@description Represents a single button in a bot keyboard @text Text of the button @type Type of the button
keyboardButton text:string type:KeyboardButtonType = KeyboardButton;
@ -1135,9 +1156,12 @@ keyboardButton text:string type:KeyboardButtonType = KeyboardButton;
//@description A button that opens a specified URL @url HTTP or tg:// URL to open
inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType;
//@description A button that opens a specified URL and automatically authorize the current user if allowed to do so @url An HTTP URL to open @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages
//@description A button that opens a specified URL and automatically authorize the current user by calling getLoginUrlInfo @url An HTTP URL to pass to getLoginUrlInfo @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages
inlineKeyboardButtonTypeLoginUrl url:string id:int53 forward_text:string = InlineKeyboardButtonType;
//@description A button that opens a web app by calling openWebApp @url An HTTP URL to pass to openWebApp
inlineKeyboardButtonTypeWebApp url:string = InlineKeyboardButtonType;
//@description A button that sends a callback query to a bot @data Data to be sent to the bot via a callback query
inlineKeyboardButtonTypeCallback data:bytes = InlineKeyboardButtonType;
@ -1195,6 +1219,10 @@ loginUrlInfoOpen url:string skip_confirm:Bool = LoginUrlInfo;
loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int53 request_write_access:Bool = LoginUrlInfo;
//@description Contains information about a web app @launch_id Unique identifier for the web app launch @url A web app URL to open in a web view
webAppInfo launch_id:int64 url:string = WebAppInfo;
//@description Contains information about a message thread
//@chat_id Identifier of the chat to which the message thread belongs
//@message_thread_id Message thread identifier, unique within the chat
@ -1373,7 +1401,7 @@ pageBlockCollage page_blocks:vector<PageBlock> caption:pageBlockCaption = PageBl
//@description A slideshow @page_blocks Slideshow item contents @caption Block caption
pageBlockSlideshow page_blocks:vector<PageBlock> caption:pageBlockCaption = PageBlock;
//@description A link to a chat @title Chat title @photo Chat photo; may be null @username Chat username, by which all other information about the chat can be resolved
//@description A link to a chat @title Chat title @photo Chat photo; may be null @username Chat username by which all other information about the chat can be resolved
pageBlockChatLink title:string photo:chatPhotoInfo username:string = PageBlock;
//@description A table @caption Table caption @cells Table cells @is_bordered True, if the table is bordered @is_striped True, if the table is striped
@ -1453,6 +1481,12 @@ bankCardInfo title:string actions:vector<bankCardActionOpenUrl> = BankCardInfo;
address country_code:string state:string city:string street_line1:string street_line2:string postal_code:string = Address;
//@description Contains parameters of the app theme @background_color A color of the background in the RGB24 format @text_color A color of text in the RGB24 format
//@hint_color A color of hints in the RGB24 format @link_color A color of links in the RGB24 format @button_color A color of the buttons in the RGB24 format
//@button_text_color A color of text on the buttons in the RGB24 format
themeParameters background_color:int32 text_color:int32 hint_color:int32 link_color:int32 button_color:int32 button_text_color:int32 = ThemeParameters;
//@description Portion of the price of a product (e.g., "delivery cost", "tax amount") @label Label for this portion of the product price @amount Currency amount in the smallest units of the currency
labeledPricePart label:string amount:int53 = LabeledPricePart;
@ -1496,11 +1530,6 @@ inputCredentialsGooglePay data:string = InputCredentials;
//@description Stripe payment provider @publishable_key Stripe API publishable key @need_country True, if the user country must be provided @need_postal_code True, if the user ZIP/postal code must be provided @need_cardholder_name True, if the cardholder name must be provided
paymentsProviderStripe publishable_key:string need_country:Bool need_postal_code:Bool need_cardholder_name:Bool = PaymentsProviderStripe;
//@description Theme colors for a payment form @background_color A color of the payment form background in the RGB24 format @text_color A color of text in the RGB24 format
//@hint_color A color of hints in the RGB24 format @link_color A color of links in the RGB24 format @button_color A color of the buttons in the RGB24 format
//@button_text_color A color of text on the buttons in the RGB24 format
paymentFormTheme background_color:int32 text_color:int32 hint_color:int32 link_color:int32 button_color:int32 button_text_color:int32 = PaymentFormTheme;
//@description Contains information about an invoice payment form
//@id The payment form identifier
//@invoice Full information of the invoice
@ -1934,7 +1963,13 @@ messageContactRegistered = MessageContent;
//@description The current user has connected a website by logging in using Telegram Login Widget on it @domain_name Domain name of the connected website
messageWebsiteConnected domain_name:string = MessageContent;
//@description Telegram Passport data has been sent @types List of Telegram Passport element types sent
//@description Data from a web app has been sent to a bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app
messageWebAppDataSent button_text:string = MessageContent;
//@description Data from a web app has been received; for bots only @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app @data Received data
messageWebAppDataReceived button_text:string data:string = MessageContent;
//@description Telegram Passport data has been sent to a bot @types List of Telegram Passport element types sent
messagePassportDataSent types:vector<PassportElementType> = MessageContent;
//@description Telegram Passport data has been received; for bots only @elements List of received Telegram Passport elements @credentials Encrypted data credentials
@ -2496,6 +2531,25 @@ diceStickersSlotMachine background:sticker lever:sticker left_reel:sticker cente
importedContacts user_ids:vector<int53> importer_count:vector<int32> = ImportedContacts;
//@description Describes a color to highlight a bot added to attachment menu @light_color Color in the RGB24 format for light themes @dark_color Color in the RGB24 format for dark themes
attachmentMenuBotColor light_color:int32 dark_color:int32 = AttachmentMenuBotColor;
//@description Represents a bot added to attachment menu
//@bot_user_id User identifier of the bot added to attachment menu
//@name Name for the bot in attachment menu
//@name_color Color to highlight selected name of the bot if appropriate; may be null
//@default_icon Default attachment menu icon for the bot in SVG format; may be null
//@ios_static_icon Attachment menu icon for the bot in SVG format for the official iOS app; may be null
//@ios_animated_icon Attachment menu icon for the bot in TGS format for the official iOS app; may be null
//@android_icon Attachment menu icon for the bot in TGS format for the official Android app; may be null
//@macos_icon Attachment menu icon for the bot in TGS format for the official native macOS app; may be null
//@icon_color Color to highlight selected icon of the bot if appropriate; may be null
attachmentMenuBot bot_user_id:int53 name:string name_color:attachmentMenuBotColor default_icon:file ios_static_icon:file ios_animated_icon:file android_icon:file macos_icon:file icon_color:attachmentMenuBotColor = AttachmentMenuBot;
//@description Information about the message sent by answerWebAppQuery @inline_message_id Identifier of the sent inline message, if known
sentWebAppMessage inline_message_id:string = SentWebAppMessage;
//@description Contains an HTTP URL @url The URL
httpUrl url:string = HttpUrl;
@ -3119,9 +3173,22 @@ notificationGroupTypeSecretChat = NotificationGroupType;
notificationGroupTypeCalls = NotificationGroupType;
//@description Describes a notification sound in MP3 format
//@id Unique identifier of the notification sound
//@duration Duration of the sound, in seconds
//@date Point in time (Unix timestamp) when the sound was created
//@title Title of the notification sound
//@data Arbitrary data, defined while the sound was uploaded
//@sound File containing the sound
notificationSound id:int64 duration:int32 date:int32 title:string data:string sound:file = NotificationSound;
//@description Contains a list of notification sounds @notification_sounds A list of notification sounds
notificationSounds notification_sounds:vector<notificationSound> = NotificationSounds;
//@description Contains information about a notification @id Unique persistent identifier of this notification @date Notification date
//@is_silent True, if the notification was initially silent @type Notification type
notification id:int32 date:int32 is_silent:Bool type:NotificationType = Notification;
//@sound_id Identifier of the notification sound to be played; 0 if sound is disabled @type Notification type
notification id:int32 date:int32 sound_id:int64 type:NotificationType = Notification;
//@description Describes a group of notifications @id Unique persistent auto-incremented from 1 identifier of the notification group @type Type of the group
//@chat_id Identifier of a chat to which all notifications in the group belong
@ -3254,7 +3321,7 @@ sessions sessions:vector<session> inactive_session_ttl_days:int32 = Sessions;
//@log_in_date Point in time (Unix timestamp) when the user was logged in
//@last_active_date Point in time (Unix timestamp) when obtained authorization was last used
//@ip IP address from which the user was logged in, in human-readable format
//@location Human-readable description of a country and a region, from which the user was logged in, based on the IP address
//@location Human-readable description of a country and a region from which the user was logged in, based on the IP address
connectedWebsite id:int64 domain_name:string bot_user_id:int53 browser:string platform:string log_in_date:int32 last_active_date:int32 ip:string location:string = ConnectedWebsite;
//@description Contains a list of websites the current user is logged in with Telegram @websites List of connected websites
@ -3299,6 +3366,13 @@ chatReportReasonCustom = ChatReportReason;
//@description The link is a link to the active sessions section of the app. Use getActiveSessions to handle the link
internalLinkTypeActiveSessions = InternalLinkType;
//@description The link is a link to an attachment menu bot to be opened in the specified chat. Process given chat_link to open corresponding chat.
//-Then call searchPublicChat with the given bot username, check that the user is a bot and can be added to attachment menu. Then use getAttachmentMenuBot to receive information about the bot.
//-If the bot isn't added to attachment menu, then user needs to confirm adding the bot to attachment menu. If user confirms adding, then use toggleBotIsAddedToAttachmentMenu to add it.
//-If attachment menu bots can't be used in the current chat, show an error to the user. If the bot is added to attachment menu, then use openWebApp with the given URL
//@chat_link An internal link pointing to a chat; may be null if the current chat needs to be kept @bot_username Username of the bot @url URL to be passed to openWebApp
internalLinkTypeAttachmentMenuBot chat_link:InternalLinkType bot_username:string url:string = InternalLinkType;
//@description The link contains an authentication code. Call checkAuthenticationCode with the code if the current authorization state is authorizationStateWaitCode @code The authentication code
internalLinkTypeAuthenticationCode code:string = InternalLinkType;
@ -3311,9 +3385,19 @@ internalLinkTypeBackground background_name:string = InternalLinkType;
internalLinkTypeBotStart bot_username:string start_parameter:string = InternalLinkType;
//@description The link is a link to a Telegram bot, which is supposed to be added to a group chat. Call searchPublicChat with the given bot username, check that the user is a bot and can be added to groups,
//-ask the current user to select a group to add the bot to, and then call sendBotStartMessage with the given start parameter and the chosen group chat. Bots can be added to a public group only by administrators of the group
//@bot_username Username of the bot @start_parameter The parameter to be passed to sendBotStartMessage
internalLinkTypeBotStartInGroup bot_username:string start_parameter:string = InternalLinkType;
//-ask the current user to select a basic group or a supergroup chat to add the bot to, taking into account that bots can be added to a public supergroup only by administrators of the supergroup.
//-If administrator rights are provided by the link, call getChatMember to receive the current bot rights in the chat and if the bot already is an administrator,
//-check that the current user can edit its administrator rights, combine received rights with the requested administrator rights, show confirmation box to the user,
//-and call setChatMemberStatus with the chosen chat and confirmed administrator rights. Before call to setChatMemberStatus it may be required to upgrade the chosen basic group chat to a supergroup chat.
//-Then if start_parameter isn't empty, call sendBotStartMessage with the given start parameter and the chosen chat, otherwise just send /start message with bot's username added to the chat.
//@bot_username Username of the bot @start_parameter The parameter to be passed to sendBotStartMessage @administrator_rights Expected administrator rights for the bot; may be null
internalLinkTypeBotStartInGroup bot_username:string start_parameter:string administrator_rights:chatAdministratorRights = InternalLinkType;
//@description The link is a link to a Telegram bot, which is supposed to be added to a channel chat as an administrator. Call searchPublicChat with the given bot username and check that the user is a bot,
//-ask the current user to select a channel chat to add the bot to as an administrator. Then call getChatMember to receive the current bot rights in the chat and if the bot already is an administrator,
//-check that the current user can edit its administrator rights and combine received rights with the requested administrator rights. Then show confirmation box to the user, and call setChatMemberStatus with the chosen chat and confirmed rights
//@bot_username Username of the bot @administrator_rights Expected administrator rights for the bot
internalLinkTypeBotAddToChannel bot_username:string administrator_rights:chatAdministratorRights = InternalLinkType;
//@description The link is a link to the change phone number section of the app
internalLinkTypeChangePhoneNumber = InternalLinkType;
@ -3422,6 +3506,9 @@ fileTypeAudio = FileType;
//@description The file is a document
fileTypeDocument = FileType;
//@description The file is a notification sound
fileTypeNotificationSound = FileType;
//@description The file is a photo
fileTypePhoto = FileType;
@ -3933,10 +4020,10 @@ updateNotification notification_group_id:int32 notification:notification = Updat
//@type New type of the notification group
//@chat_id Identifier of a chat to which all notifications in the group belong
//@notification_settings_chat_id Chat identifier, which notification settings must be applied to the added notifications
//@is_silent True, if the notifications must be shown without sound
//@notification_sound_id Identifier of the notification sound to be played; 0 if sound is disabled
//@total_count Total number of unread notifications in the group, can be bigger than number of active notifications
//@added_notifications List of added group notifications, sorted by notification ID @removed_notification_ids Identifiers of removed group notifications, sorted by notification ID
updateNotificationGroup notification_group_id:int32 type:NotificationGroupType chat_id:int53 notification_settings_chat_id:int53 is_silent:Bool total_count:int32 added_notifications:vector<notification> removed_notification_ids:vector<int32> = Update;
updateNotificationGroup notification_group_id:int32 type:NotificationGroupType chat_id:int53 notification_settings_chat_id:int53 notification_sound_id:int64 total_count:int32 added_notifications:vector<notification> removed_notification_ids:vector<int32> = Update;
//@description Contains active notifications that was shown on previous application launches. This update is sent only if the message database is used. In that case it comes once before any updateNotification and updateNotificationGroup update @groups Lists of active notification groups
updateActiveNotifications groups:vector<notificationGroup> = Update;
@ -4065,6 +4152,9 @@ updateFavoriteStickers sticker_ids:vector<int32> = Update;
//@description The list of saved animations was updated @animation_ids The new list of file identifiers of saved animations
updateSavedAnimations animation_ids:vector<int32> = Update;
//@description The list of saved notifications sounds was updated. This update may not be sent until information about a notification sound was requested for the first time @notification_sound_ids The new list of identifiers of saved notification sounds
updateSavedNotificationSounds notification_sound_ids:vector<int64> = 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;
@ -4083,6 +4173,12 @@ updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService
//@description The list of users nearby has changed. The update is guaranteed to be sent only 60 seconds after a successful searchChatsNearby request @users_nearby The new list of users nearby
updateUsersNearby users_nearby:vector<chatNearby> = Update;
//@description The list of bots added to attachment menu has changed @bots The new list of bots added to attachment menu. The bots must be shown in attachment menu only in private chats. The bots must not be shown on scheduled messages screen
updateAttachmentMenuBots bots:vector<attachmentMenuBot> = Update;
//@description A message was sent by an opened web app, so the web app needs to be closed @web_app_launch_id Identifier of web app launch
updateWebAppMessageSent web_app_launch_id:int64 = Update;
//@description The list of supported reactions has changed @reactions The new list of supported reactions
updateReactions reactions:vector<reaction> = Update;
@ -4100,7 +4196,7 @@ updateAnimationSearchParameters provider:string emojis:vector<string> = Update;
updateSuggestedActions added_actions:vector<SuggestedAction> removed_actions:vector<SuggestedAction> = Update;
//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location; may be null
//@chat_type The type of the chat, from which the query originated; may be null if unknown @query Text of the query @offset Offset of the first entry to return
//@chat_type The type of the chat from which the query originated; may be null if unknown @query Text of the query @offset Offset of the first entry to return
updateNewInlineQuery id:int64 sender_user_id:int53 user_location:location chat_type:ChatType query:string offset:string = Update;
//@description The user has chosen a result of an inline query; for bots only @sender_user_id Identifier of the user who sent the query @user_location User location; may be null
@ -4108,11 +4204,11 @@ updateNewInlineQuery id:int64 sender_user_id:int53 user_location:location chat_t
updateNewChosenInlineResult sender_user_id:int53 user_location:location query:string result_id:string inline_message_id:string = Update;
//@description A new incoming callback query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query
//@chat_id Identifier of the chat where the query was sent @message_id Identifier of the message, from which the query originated
//@chat_id Identifier of the chat where the query was sent @message_id Identifier of the message from which the query originated
//@chat_instance Identifier that uniquely corresponds to the chat to which the message was sent @payload Query payload
updateNewCallbackQuery id:int64 sender_user_id:int53 chat_id:int53 message_id:int53 chat_instance:int64 payload:CallbackQueryPayload = Update;
//@description A new incoming callback query from a message sent via a bot; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @inline_message_id Identifier of the inline message, from which the query originated
//@description A new incoming callback query from a message sent via a bot; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @inline_message_id Identifier of the inline message from which the query originated
//@chat_instance An identifier uniquely corresponding to the chat a message was sent to @payload Query payload
updateNewInlineCallbackQuery id:int64 sender_user_id:int53 inline_message_id:string chat_instance:int64 payload:CallbackQueryPayload = Update;
@ -4443,7 +4539,7 @@ getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 off
//@chat_id Chat identifier @remove_from_chat_list Pass true to remove the chat from all chat lists @revoke Pass true to delete chat history for all users
deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
//@description Deletes a chat along with all messages in the corresponding chat for all chat members; requires owner privileges. For group chats this will release the username and remove all members. Chats with more than 1000 members can't be deleted using this method @chat_id Chat identifier
//@description Deletes a chat along with all messages in the corresponding chat for all chat members. For group chats this will release the username and remove all members. Use the field chat.can_be_deleted_for_all_users to find whether the method can be applied to the chat @chat_id Chat identifier
deleteChat chat_id:int53 = Ok;
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
@ -4574,7 +4670,7 @@ setChatMessageSender chat_id:int53 message_sender_id:MessageSender = Ok;
//@description Sends a message. Returns the sent message
//@chat_id Target chat
//@message_thread_id If not 0, a message thread identifier in which the message will be sent
//@reply_to_message_id Identifier of the message to reply to or 0
//@reply_to_message_id Identifier of the replied message; 0 if none
//@options Options to be used to send the message; pass null to use default options
//@reply_markup Markup for replying to the message; pass null if none; for bots only
//@input_message_content The content of the message to be sent
@ -4583,7 +4679,7 @@ sendMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 opti
//@description Sends 2-10 messages grouped together into an album. Currently, only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only grouped in an album with messages of the same type. Returns sent messages
//@chat_id Target chat
//@message_thread_id If not 0, a message thread identifier in which the messages will be sent
//@reply_to_message_id Identifier of a message to reply to or 0
//@reply_to_message_id Identifier of a replied message; 0 if none
//@options Options to be used to send the messages; pass null to use default options
//@input_message_contents Contents of messages to be sent. At most 10 messages can be added to an album
//@only_preview Pass true to get fake messages instead of actually sending them
@ -4596,7 +4692,7 @@ sendBotStartMessage bot_user_id:int53 chat_id:int53 parameter:string = Message;
//@description Sends the result of an inline query as a message. Returns the sent message. Always clears a chat draft message
//@chat_id Target chat
//@message_thread_id If not 0, a message thread identifier in which the message will be sent
//@reply_to_message_id Identifier of a message to reply to or 0
//@reply_to_message_id Identifier of a replied message; 0 if none
//@options Options to be used to send the message; pass null to use default options
//@query_id Identifier of the inline query
//@result_id Identifier of the inline result
@ -4624,7 +4720,7 @@ sendChatScreenshotTakenNotification chat_id:int53 = Ok;
//@description Adds a local message to a chat. The message is persistent across application restarts only if the message database is used. Returns the added message
//@chat_id Target chat
//@sender_id Identifier of the sender of the message
//@reply_to_message_id Identifier of the message to reply to or 0
//@reply_to_message_id Identifier of the replied message; 0 if none
//@disable_notification Pass true to disable notification for the message
//@input_message_content The content of the message to be added
addLocalMessage chat_id:int53 sender_id:MessageSender reply_to_message_id:int53 disable_notification:Bool input_message_content:InputMessageContent = Message;
@ -4768,6 +4864,9 @@ getJsonValue json:string = JsonValue;
//@description Converts a JsonValue object to corresponding JSON-serialized string. Can be called synchronously @json_value The JsonValue object
getJsonString json_value:JsonValue = Text;
//@description Converts a themeParameters object to corresponding JSON-serialized string. Can be called synchronously @theme Theme parameters to convert to JSON
getThemeParametersJsonString theme:themeParameters = Text;
//@description Changes the user answer to a poll. A poll in quiz mode can be answered only once
//@chat_id Identifier of the chat to which the poll belongs
@ -4824,6 +4923,34 @@ getInlineQueryResults bot_user_id:int53 chat_id:int53 user_location:location que
answerInlineQuery inline_query_id:int64 is_personal:Bool results:vector<InputInlineQueryResult> cache_time:int32 next_offset:string switch_pm_text:string switch_pm_parameter:string = Ok;
//@description Returns an HTTPS URL of a web app to open after keyboardButtonTypeWebApp button is pressed
//@bot_user_id Identifier of the target bot
//@url The URL from the keyboardButtonTypeWebApp button
//@theme Preferred web app theme; pass null to use the default theme
getWebAppUrl bot_user_id:int53 url:string theme:themeParameters = HttpUrl;
//@description Sends data received from a keyboardButtonTypeWebApp web app to a bot
//@bot_user_id Identifier of the target bot @button_text Text of the keyboardButtonTypeWebApp button, which opened the web app @data Received data
sendWebAppData bot_user_id:int53 button_text:string data:string = Ok;
//@description Informs TDLib that a web app is being opened from attachment menu, a botMenuButton button, an internalLinkTypeAttachmentMenuBot link, or an inlineKeyboardButtonTypeWebApp button.
//-For each bot, a confirmation alert about data sent to the bot must be shown once
//@chat_id Identifier of the chat in which the web app is opened. Web apps can be opened only in private chats for now
//@bot_user_id Identifier of the bot, providing the web app
//@url The URL from an inlineKeyboardButtonTypeWebApp button, a botMenuButton button, or an internalLinkTypeAttachmentMenuBot link, or an empty string otherwise
//@theme Preferred web app theme; pass null to use the default theme
//@reply_to_message_id Identifier of the replied message for the message sent by the web app; 0 if none
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters reply_to_message_id:int53 = WebAppInfo;
//@description Informs TDLib that a previously opened web app was closed @web_app_launch_id Identifier of web app launch, received from openWebApp
closeWebApp web_app_launch_id:int64 = Ok;
//@description Sets the result of interaction with a web app and sends corresponding message on behalf of the user to the chat from which the query originated; for bots only
//@web_app_query_id Identifier of the web app query
//@result The result of the query
answerWebAppQuery web_app_query_id:string result:InputInlineQueryResult = SentWebAppMessage;
//@description Sends a callback query to a bot and returns an answer. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @chat_id Identifier of the chat with the message @message_id Identifier of the message from which the query originated @payload Query payload
getCallbackQueryAnswer chat_id:int53 message_id:int53 payload:CallbackQueryPayload = CallbackQueryAnswer;
@ -4969,8 +5096,8 @@ setChatTitle chat_id:int53 title:string = Ok;
setChatPhoto chat_id:int53 photo:InputChatPhoto = Ok;
//@description Changes the message TTL in a chat. Requires can_delete_messages administrator right in basic groups, supergroups and channels
//-Message TTL can't be changed in a chat with the current user (Saved Messages) and the chat 777000 (Telegram)
//@chat_id Chat identifier @ttl New TTL value, in seconds; must be one of 0, 86400, 7 * 86400, or 31 * 86400 unless the chat is secret
//-Message TTL can't be changed in a chat with the current user (Saved Messages) and the chat 777000 (Telegram).
//@chat_id Chat identifier @ttl New TTL value, in seconds; unless the chat is secret, it must be from 0 up to 365 * 86400 and be divisible by 86400
setChatMessageTtl chat_id:int53 ttl:int32 = Ok;
//@description Changes the chat members permissions. Supported only for basic groups and supergroups. Requires can_restrict_members administrator right
@ -5080,6 +5207,19 @@ getChatAdministrators chat_id:int53 = ChatAdministrators;
clearAllDraftMessages exclude_secret_chats:Bool = Ok;
//@description Returns saved notification sound by its identifier. Returns a 404 error if there is no saved notification sound with the specified identifier @notification_sound_id Identifier of the notification sound
getSavedNotificationSound notification_sound_id:int64 = NotificationSounds;
//@description Returns list of saved notification sounds. If a sound isn't in the list, then default sound needs to be used
getSavedNotificationSounds = NotificationSounds;
//@description Adds a new notification sound to the list of saved notification sounds. The new notification sound is added to the top of the list. If it is already in the list, its position isn't changed @sound Notification sound file to add
addSavedNotificationSound sound:InputFile = NotificationSound;
//@description Removes a notification sound from the list of saved notification sounds @notification_sound_id Identifier of the notification sound
removeSavedNotificationSound notification_sound_id:int64 = Ok;
//@description Returns list of chats with non-default notification settings
//@scope If specified, only chats from the scope will be returned; pass null to return chats from all scopes
//@compare_sound Pass true to include in the response chats with only non-default sound
@ -5091,7 +5231,7 @@ getScopeNotificationSettings scope:NotificationSettingsScope = ScopeNotification
//@description Changes notification settings for chats of a given type @scope Types of chats for which to change the notification settings @notification_settings The new notification settings for the given scope
setScopeNotificationSettings scope:NotificationSettingsScope notification_settings:scopeNotificationSettings = Ok;
//@description Resets all notification settings to their default values. By default, all chats are unmuted, the sound is set to "default" and message previews are shown
//@description Resets all notification settings to their default values. By default, all chats are unmuted and message previews are shown
resetAllNotificationSettings = Ok;
@ -5103,6 +5243,13 @@ toggleChatIsPinned chat_list:ChatList chat_id:int53 is_pinned:Bool = Ok;
setPinnedChats chat_list:ChatList chat_ids:vector<int53> = Ok;
//@description Returns information about a bot that can be added to attachment menu @bot_user_id Bot's user identifier
getAttachmentMenuBot bot_user_id:int53 = AttachmentMenuBot;
//@description Adds or removes a bot to attachment menu. Bot can be added to attachment menu, only if userTypeBot.can_be_added_to_attachment_menu == true @bot_user_id Bot's user identifier @is_added Pass true to add the bot to attachment menu; pass false to remove the bot from attachment menu
toggleBotIsAddedToAttachmentMenu bot_user_id:int53 is_added:Bool = Ok;
//@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates
//@file_id Identifier of the file to download
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile/addFileToDownloads was called will be downloaded first
@ -5306,7 +5453,7 @@ getVideoChatAvailableParticipants chat_id:int53 = MessageSenders;
setVideoChatDefaultParticipant chat_id:int53 default_participant_id:MessageSender = Ok;
//@description Creates a video chat (a group call bound to a chat). Available only for basic groups, supergroups and channels; requires can_manage_video_chats rights
//@chat_id Chat identifier, in which the video chat will be created
//@chat_id Identifier of a chat in which the video chat will be created
//@title Group call title; if empty, chat title will be used
//@start_date Point in time (Unix timestamp) when the group call is supposed to be started by an administrator; 0 to start the video chat immediately. The date must be at least 10 seconds and at most 8 days in the future
//@is_rtmp_stream Pass true to create an RTMP stream instead of an ordinary video chat; requires creator privileges
@ -5625,6 +5772,20 @@ deleteCommands scope:BotCommandScope language_code:string = Ok;
//@language_code A two-letter ISO 639-1 language code or an empty string
getCommands scope:BotCommandScope language_code:string = BotCommands;
//@description Sets menu button for the given user or for all users; for bots only
//@user_id Identifier of the user or 0 to set menu button for all users
//@menu_button New menu button
setMenuButton user_id:int53 menu_button:botMenuButton = Ok;
//@description Returns menu button set by the bot for the given user; for bots only @user_id Identifier of the user or 0 to get the default menu button
getMenuButton user_id:int53 = BotMenuButton;
//@description Sets default administrator rights for adding the bot to basic group and supergroup chats; for bots only @default_group_administrator_rights Default administrator rights for adding the bot to basic group and supergroup chats; may be null
setDefaultGroupAdministratorRights default_group_administrator_rights:chatAdministratorRights = Ok;
//@description Sets default administrator rights for adding the bot to channel chats; for bots only @default_channel_administrator_rights Default administrator rights for adding the bot to channels; may be null
setDefaultChannelAdministratorRights default_channel_administrator_rights:chatAdministratorRights = Ok;
//@description Returns all active sessions of the current user
getActiveSessions = Sessions;
@ -5692,7 +5853,7 @@ getChatEventLog chat_id:int53 query:string from_event_id:int64 limit:int32 filte
//@chat_id Chat identifier of the Invoice message
//@message_id Message identifier
//@theme Preferred payment form theme; pass null to use the default theme
getPaymentForm chat_id:int53 message_id:int53 theme:paymentFormTheme = PaymentForm;
getPaymentForm chat_id:int53 message_id:int53 theme:themeParameters = PaymentForm;
//@description Validates the order information provided by a user and returns the available shipping options for a flexible invoice
//@chat_id Chat identifier of the Invoice message
@ -5985,7 +6146,7 @@ setStickerPositionInSet sticker:InputFile position:int32 = Ok;
removeStickerFromSet sticker:InputFile = Ok;
//@description Returns information about a file with a map thumbnail in PNG format. Only map thumbnail files with size less than 1MB can be downloaded @location Location of the map center @zoom Map zoom level; 13-20 @width Map width in pixels before applying scale; 16-1024 @height Map height in pixels before applying scale; 16-1024 @scale Map scale; 1-3 @chat_id Identifier of a chat, in which the thumbnail will be shown. Use 0 if unknown
//@description Returns information about a file with a map thumbnail in PNG format. Only map thumbnail files with size less than 1MB can be downloaded @location Location of the map center @zoom Map zoom level; 13-20 @width Map width in pixels before applying scale; 16-1024 @height Map height in pixels before applying scale; 16-1024 @scale Map scale; 1-3 @chat_id Identifier of a chat in which the thumbnail will be shown. Use 0 if unknown
getMapThumbnailFile location:location zoom:int32 width:int32 height:int32 scale:int32 chat_id:int53 = File;
@ -6103,7 +6264,7 @@ testSquareInt x:int32 = TestInt;
//@description Sends a simple network request to the Telegram servers; for testing only. Can be called before authorization
testNetwork = Ok;
//@description Sends a simple network request to the Telegram servers via proxy; for testing only. Can be called before authorization @server Proxy server IP address @port Proxy server port @type Proxy type
//@dc_id Identifier of a datacenter, with which to test connection @timeout The maximum overall timeout for the request
//@dc_id Identifier of a datacenter with which to test connection @timeout The maximum overall timeout for the request
testProxy server:string port:int32 type:ProxyType dc_id:int32 timeout:double = Ok;
//@description Forces an updates.getDifference call to the Telegram servers; for testing only
testGetDifference = Ok;

View File

@ -100,7 +100,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -113,13 +113,13 @@ userStatusLastWeek#7bf09fc = UserStatus;
userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
channelFull#e13c3d20 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
channelFull#ea68a619 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -179,6 +179,8 @@ messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction;
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -208,9 +210,9 @@ inputNotifyUsers#193b4417 = InputNotifyPeer;
inputNotifyChats#4a95e84e = InputNotifyPeer;
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings;
inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings;
peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings;
peerNotifySettings#a83b0426 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound = PeerNotifySettings;
peerSettings#a518110d flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int = PeerSettings;
@ -228,7 +230,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#cf366521 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string = UserFull;
userFull#8c72ea81 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@ -374,6 +376,10 @@ updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = U
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -565,7 +571,7 @@ messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
botCommand#c27ac8c7 command:string description:string = BotCommand;
botInfo#1b74b335 user_id:long description:string commands:Vector<BotCommand> = BotInfo;
botInfo#e4169b5d user_id:long description:string commands:Vector<BotCommand> menu_button:BotMenuButton = BotInfo;
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
@ -580,6 +586,8 @@ inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true te
keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton;
inputKeyboardButtonUserProfile#e988037b text:string user_id:InputUser = KeyboardButton;
keyboardButtonUserProfile#308660c1 text:string user_id:long = KeyboardButton;
keyboardButtonWebView#13767230 text:string url:string = KeyboardButton;
keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
@ -1319,6 +1327,38 @@ phone.groupCallStreamChannels#d0e482b2 channels:Vector<GroupCallStreamChannel> =
phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl;
attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;
attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;
attachMenuBot#e93cb772 flags:# inactive:flags.0?true bot_id:long short_name:string icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;
attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector<User> = AttachMenuBotsBot;
webViewResultUrl#c14557c query_id:long url:string = WebViewResult;
simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult;
webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent;
botMenuButtonDefault#7533a588 = BotMenuButton;
botMenuButtonCommands#4258c205 = BotMenuButton;
botMenuButton#c7b57ce6 text:string url:string = BotMenuButton;
account.savedRingtonesNotModified#fbf6e8b1 = account.SavedRingtones;
account.savedRingtones#c1e92cc5 hash:long ringtones:Vector<Document> = account.SavedRingtones;
notificationSoundDefault#97e8bebe = NotificationSound;
notificationSoundNone#6f0c34df = NotificationSound;
notificationSoundLocal#830b9ae4 title:string data:string = NotificationSound;
notificationSoundRingtone#ff6c8049 id:long = NotificationSound;
account.savedRingtone#b7263f6d = account.SavedRingtone;
account.savedRingtoneConverted#1f307eb7 document:Document = account.SavedRingtone;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1422,6 +1462,9 @@ account.declinePasswordReset#4c9409f6 = Bool;
account.getChatThemes#d638de89 hash:long = account.Themes;
account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool;
account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_requests_disabled:flags.0?Bool call_requests_disabled:flags.1?Bool = Bool;
account.getSavedRingtones#e1902288 hash:long = account.SavedRingtones;
account.saveRingtone#3dea5b03 id:InputDocument unsave:Bool = account.SavedRingtone;
account.uploadRingtone#831a83a2 file:InputFile file_name:string mime_type:string = Document;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@ -1608,6 +1651,14 @@ messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?in
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#fa04dff flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int = WebViewResult;
messages.prolongWebView#d22ad148 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int = Bool;
messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1675,7 +1726,7 @@ channels.editBanned#96e6cd81 channel:InputChannel participant:InputPeer banned_r
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
channels.deleteHistory#9baa9647 flags:# for_everyone:flags.0?true channel:InputChannel max_id:int = Updates;
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
@ -1695,6 +1746,10 @@ bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
bots.setBotCommands#517165a scope:BotCommandScope lang_code:string commands:Vector<BotCommand> = Bool;
bots.resetBotCommands#3d8de0f9 scope:BotCommandScope lang_code:string = Bool;
bots.getBotCommands#e34c0dd6 scope:BotCommandScope lang_code:string = Vector<BotCommand>;
bots.setBotMenuButton#4504d54f user_id:InputUser button:BotMenuButton = Bool;
bots.getBotMenuButton#9c60eb28 user_id:InputUser = BotMenuButton;
bots.setBotBroadcastDefaultAdminRights#788464e1 admin_rights:ChatAdminRights = Bool;
bots.setBotGroupDefaultAdminRights#925ec9ea admin_rights:ChatAdminRights = Bool;
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;

View File

@ -302,8 +302,8 @@ class TlWriterCCommon final : public tl::TL_writer {
class_name + ", struct TdVector" + class_name + " *);\n";
}
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final {
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final {
if (is_header_ != 1 || class_name == "") {
return "";
}

View File

@ -46,8 +46,8 @@ class TD_TL_writer_cpp : public TD_TL_writer {
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;

View File

@ -200,8 +200,8 @@ class TlWriterDotNet final : public TL_writer {
return ss.str();
}
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final {
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final {
if (!is_header_) {
return "";
}

View File

@ -33,8 +33,8 @@ class TD_TL_writer_h : public TD_TL_writer {
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;

View File

@ -33,8 +33,8 @@ class TD_TL_writer_hpp final : public TD_TL_writer {
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final;
std::string gen_class_end() const final;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const final;

View File

@ -58,8 +58,8 @@ class TD_TL_writer_java final : public tl::TL_writer {
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final;
std::string gen_class_end() const final;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const final;

View File

@ -54,8 +54,8 @@ class TD_TL_writer_jni_cpp final : public TD_TL_writer_cpp {
std::string gen_base_type_class_name(int arity) const final;
std::string gen_base_tl_class_name() const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const final;

View File

@ -34,8 +34,8 @@ class TD_TL_writer_jni_h final : public TD_TL_writer_h {
std::string gen_output_begin() const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy, const tl::tl_tree *result) const final;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy,
const tl::tl_tree *result) const final;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const final;

View File

@ -393,6 +393,72 @@ class ResetWebAuthorizationsQuery final : public Td::ResultHandler {
}
};
class SetBotGroupDefaultAdminRightsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetBotGroupDefaultAdminRightsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(AdministratorRights administrator_rights) {
send_query(G()->net_query_creator().create(
telegram_api::bots_setBotGroupDefaultAdminRights(administrator_rights.get_chat_admin_rights())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::bots_setBotGroupDefaultAdminRights>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to set group default administrator rights";
td_->contacts_manager_->invalidate_user_full(td_->contacts_manager_->get_my_id());
promise_.set_value(Unit());
}
void on_error(Status status) final {
if (status.message() == "RIGHTS_NOT_MODIFIED") {
return promise_.set_value(Unit());
}
td_->contacts_manager_->invalidate_user_full(td_->contacts_manager_->get_my_id());
promise_.set_error(std::move(status));
}
};
class SetBotBroadcastDefaultAdminRightsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetBotBroadcastDefaultAdminRightsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(AdministratorRights administrator_rights) {
send_query(G()->net_query_creator().create(
telegram_api::bots_setBotBroadcastDefaultAdminRights(administrator_rights.get_chat_admin_rights())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::bots_setBotBroadcastDefaultAdminRights>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to set channel default administrator rights";
td_->contacts_manager_->invalidate_user_full(td_->contacts_manager_->get_my_id());
promise_.set_value(Unit());
}
void on_error(Status status) final {
if (status.message() == "RIGHTS_NOT_MODIFIED") {
return promise_.set_value(Unit());
}
td_->contacts_manager_->invalidate_user_full(td_->contacts_manager_->get_my_id());
promise_.set_error(std::move(status));
}
};
void set_account_ttl(Td *td, int32 account_ttl, Promise<Unit> &&promise) {
td->create_handler<SetAccountTtlQuery>(std::move(promise))->send(account_ttl);
}
@ -453,4 +519,15 @@ void disconnect_all_websites(Td *td, Promise<Unit> &&promise) {
td->create_handler<ResetWebAuthorizationsQuery>(std::move(promise))->send();
}
void set_default_group_administrator_rights(Td *td, AdministratorRights administrator_rights, Promise<Unit> &&promise) {
td->contacts_manager_->invalidate_user_full(td->contacts_manager_->get_my_id());
td->create_handler<SetBotGroupDefaultAdminRightsQuery>(std::move(promise))->send(administrator_rights);
}
void set_default_channel_administrator_rights(Td *td, AdministratorRights administrator_rights,
Promise<Unit> &&promise) {
td->contacts_manager_->invalidate_user_full(td->contacts_manager_->get_my_id());
td->create_handler<SetBotBroadcastDefaultAdminRightsQuery>(std::move(promise))->send(administrator_rights);
}
} // namespace td

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/td_api.h"
#include "td/actor/PromiseFuture.h"
@ -41,4 +42,9 @@ void disconnect_website(Td *td, int64 website_id, Promise<Unit> &&promise);
void disconnect_all_websites(Td *td, Promise<Unit> &&promise);
void set_default_group_administrator_rights(Td *td, AdministratorRights administrator_rights, Promise<Unit> &&promise);
void set_default_channel_administrator_rights(Td *td, AdministratorRights administrator_rights,
Promise<Unit> &&promise);
} // namespace td

View File

@ -18,6 +18,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/misc.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
@ -592,11 +593,7 @@ void AnimationsManager::on_load_saved_animations_finished(vector<FileId> &&saved
saved_animation_ids_ = std::move(saved_animation_ids);
are_saved_animations_loaded_ = true;
send_update_saved_animations(from_database);
auto promises = std::move(load_saved_animations_queries_);
load_saved_animations_queries_.clear();
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(load_saved_animations_queries_);
}
void AnimationsManager::on_get_saved_animations(
@ -641,11 +638,7 @@ void AnimationsManager::on_get_saved_animations(
}
if (is_repair) {
auto promises = std::move(repair_saved_animations_queries_);
repair_saved_animations_queries_.clear();
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(repair_saved_animations_queries_);
} else {
on_load_saved_animations_finished(std::move(saved_animation_ids));
@ -661,12 +654,7 @@ void AnimationsManager::on_get_saved_animations_failed(bool is_repair, Status er
are_saved_animations_being_loaded_ = false;
next_saved_animations_load_time_ = Time::now_cached() + Random::fast(5, 10);
}
auto &queries = is_repair ? repair_saved_animations_queries_ : load_saved_animations_queries_;
auto promises = std::move(queries);
queries.clear();
for (auto &promise : promises) {
promise.set_error(error.clone());
}
fail_promises(is_repair ? repair_saved_animations_queries_ : load_saved_animations_queries_, std::move(error));
}
int64 AnimationsManager::get_saved_animations_hash(const char *source) const {

View File

@ -8,7 +8,7 @@
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"

View File

@ -9,7 +9,7 @@
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PhotoSize.hpp"
#include "td/telegram/Version.h"
#include "td/utils/common.h"

View File

@ -0,0 +1,918 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/AttachMenuManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TdParameters.h"
#include "td/telegram/ThemeManager.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/tl_helpers.h"
namespace td {
class RequestWebViewQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::webAppInfo>> promise_;
DialogId dialog_id_;
UserId bot_user_id_;
MessageId reply_to_message_id_;
bool from_attach_menu_ = false;
public:
explicit RequestWebViewQuery(Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogId dialog_id, UserId bot_user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, MessageId reply_to_message_id, bool silent) {
dialog_id_ = dialog_id;
bot_user_id_ = bot_user_id;
reply_to_message_id_ = reply_to_message_id;
int32 flags = 0;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
CHECK(input_peer != nullptr);
string start_parameter;
if (begins_with(url, "start://")) {
start_parameter = url.substr(8);
url = string();
flags |= telegram_api::messages_requestWebView::START_PARAM_MASK;
} else if (begins_with(url, "menu://")) {
url = url.substr(7);
flags |= telegram_api::messages_requestWebView::FROM_BOT_MENU_MASK;
flags |= telegram_api::messages_requestWebView::URL_MASK;
} else if (!url.empty()) {
flags |= telegram_api::messages_requestWebView::URL_MASK;
} else {
from_attach_menu_ = true;
}
tl_object_ptr<telegram_api::dataJSON> theme_parameters;
if (theme != nullptr) {
theme_parameters = make_tl_object<telegram_api::dataJSON>(string());
theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false);
flags |= telegram_api::messages_requestWebView::THEME_PARAMS_MASK;
}
if (reply_to_message_id.is_valid()) {
flags |= telegram_api::messages_requestWebView::REPLY_TO_MSG_ID_MASK;
}
if (silent) {
flags |= telegram_api::messages_requestWebView::SILENT_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::messages_requestWebView(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(input_user), url, start_parameter,
std::move(theme_parameters), reply_to_message_id.get_server_message_id().get())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_requestWebView>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, reply_to_message_id_);
promise_.set_value(td_api::make_object<td_api::webAppInfo>(ptr->query_id_, ptr->url_));
}
void on_error(Status status) final {
if (!td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "RequestWebViewQuery")) {
if (from_attach_menu_) {
td_->attach_menu_manager_->reload_attach_menu_bots(Promise<Unit>());
}
}
promise_.set_error(std::move(status));
}
};
class ProlongWebViewQuery final : public Td::ResultHandler {
DialogId dialog_id_;
public:
void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId reply_to_message_id, bool silent) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
auto r_input_user = td_->contacts_manager_->get_input_user(bot_user_id);
if (input_peer == nullptr || r_input_user.is_error()) {
return;
}
int32 flags = 0;
if (reply_to_message_id.is_valid()) {
flags |= telegram_api::messages_prolongWebView::REPLY_TO_MSG_ID_MASK;
}
if (silent) {
flags |= telegram_api::messages_prolongWebView::SILENT_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::messages_prolongWebView(
flags, false /*ignored*/, std::move(input_peer), r_input_user.move_as_ok(), query_id,
reply_to_message_id.get_server_message_id().get())));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_prolongWebView>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool ptr = result_ptr.ok();
if (!ptr) {
LOG(ERROR) << "Failed to prolong a web view";
}
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "ProlongWebViewQuery");
}
};
class GetAttachMenuBotsQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::AttachMenuBots>> promise_;
public:
explicit GetAttachMenuBotsQuery(Promise<telegram_api::object_ptr<telegram_api::AttachMenuBots>> &&promise)
: promise_(std::move(promise)) {
}
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::messages_getAttachMenuBots(hash)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getAttachMenuBots>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAttachMenuBotsQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetAttachMenuBotQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> promise_;
public:
explicit GetAttachMenuBotQuery(Promise<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&promise)
: promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::InputUser> &&input_user) {
send_query(G()->net_query_creator().create(telegram_api::messages_getAttachMenuBot(std::move(input_user))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_getAttachMenuBot>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAttachMenuBotQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ToggleBotInAttachMenuQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ToggleBotInAttachMenuQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::InputUser> &&input_user, bool is_added) {
send_query(
G()->net_query_creator().create(telegram_api::messages_toggleBotInAttachMenu(std::move(input_user), is_added)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_toggleBotInAttachMenu>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
if (!result) {
LOG(ERROR) << "Failed to add a bot to attachment menu";
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
td_->attach_menu_manager_->reload_attach_menu_bots(Promise<Unit>());
promise_.set_error(std::move(status));
}
};
bool operator==(const AttachMenuManager::AttachMenuBotColor &lhs, const AttachMenuManager::AttachMenuBotColor &rhs) {
return lhs.light_color_ == rhs.light_color_ && lhs.dark_color_ == rhs.dark_color_;
}
bool operator!=(const AttachMenuManager::AttachMenuBotColor &lhs, const AttachMenuManager::AttachMenuBotColor &rhs) {
return !(lhs == rhs);
}
template <class StorerT>
void AttachMenuManager::AttachMenuBotColor::store(StorerT &storer) const {
td::store(light_color_, storer);
td::store(dark_color_, storer);
}
template <class ParserT>
void AttachMenuManager::AttachMenuBotColor::parse(ParserT &parser) {
td::parse(light_color_, parser);
td::parse(dark_color_, parser);
}
bool operator==(const AttachMenuManager::AttachMenuBot &lhs, const AttachMenuManager::AttachMenuBot &rhs) {
return lhs.user_id_ == rhs.user_id_ && lhs.name_ == rhs.name_ &&
lhs.default_icon_file_id_ == rhs.default_icon_file_id_ &&
lhs.ios_static_icon_file_id_ == rhs.ios_static_icon_file_id_ &&
lhs.ios_animated_icon_file_id_ == rhs.ios_animated_icon_file_id_ &&
lhs.android_icon_file_id_ == rhs.android_icon_file_id_ && lhs.macos_icon_file_id_ == rhs.macos_icon_file_id_ &&
lhs.is_added_ == rhs.is_added_ && lhs.name_color_ == rhs.name_color_ && lhs.icon_color_ == rhs.icon_color_;
}
bool operator!=(const AttachMenuManager::AttachMenuBot &lhs, const AttachMenuManager::AttachMenuBot &rhs) {
return !(lhs == rhs);
}
template <class StorerT>
void AttachMenuManager::AttachMenuBot::store(StorerT &storer) const {
bool has_ios_static_icon_file_id = ios_static_icon_file_id_.is_valid();
bool has_ios_animated_icon_file_id = ios_animated_icon_file_id_.is_valid();
bool has_android_icon_file_id = android_icon_file_id_.is_valid();
bool has_macos_icon_file_id = macos_icon_file_id_.is_valid();
bool has_name_color = name_color_ != AttachMenuBotColor();
bool has_icon_color = icon_color_ != AttachMenuBotColor();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_ios_static_icon_file_id);
STORE_FLAG(has_ios_animated_icon_file_id);
STORE_FLAG(has_android_icon_file_id);
STORE_FLAG(has_macos_icon_file_id);
STORE_FLAG(is_added_);
STORE_FLAG(has_name_color);
STORE_FLAG(has_icon_color);
END_STORE_FLAGS();
td::store(user_id_, storer);
td::store(name_, storer);
td::store(default_icon_file_id_, storer);
if (has_ios_static_icon_file_id) {
td::store(ios_static_icon_file_id_, storer);
}
if (has_ios_animated_icon_file_id) {
td::store(ios_animated_icon_file_id_, storer);
}
if (has_android_icon_file_id) {
td::store(android_icon_file_id_, storer);
}
if (has_macos_icon_file_id) {
td::store(macos_icon_file_id_, storer);
}
if (has_name_color) {
td::store(name_color_, storer);
}
if (has_icon_color) {
td::store(icon_color_, storer);
}
}
template <class ParserT>
void AttachMenuManager::AttachMenuBot::parse(ParserT &parser) {
bool has_ios_static_icon_file_id;
bool has_ios_animated_icon_file_id;
bool has_android_icon_file_id;
bool has_macos_icon_file_id;
bool has_name_color;
bool has_icon_color;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_ios_static_icon_file_id);
PARSE_FLAG(has_ios_animated_icon_file_id);
PARSE_FLAG(has_android_icon_file_id);
PARSE_FLAG(has_macos_icon_file_id);
PARSE_FLAG(is_added_);
PARSE_FLAG(has_name_color);
PARSE_FLAG(has_icon_color);
END_PARSE_FLAGS();
td::parse(user_id_, parser);
td::parse(name_, parser);
td::parse(default_icon_file_id_, parser);
if (has_ios_static_icon_file_id) {
td::parse(ios_static_icon_file_id_, parser);
}
if (has_ios_animated_icon_file_id) {
td::parse(ios_animated_icon_file_id_, parser);
}
if (has_android_icon_file_id) {
td::parse(android_icon_file_id_, parser);
}
if (has_macos_icon_file_id) {
td::parse(macos_icon_file_id_, parser);
}
if (has_name_color) {
td::parse(name_color_, parser);
}
if (has_icon_color) {
td::parse(icon_color_, parser);
}
}
class AttachMenuManager::AttachMenuBotsLogEvent {
public:
int64 hash_ = 0;
vector<AttachMenuBot> attach_menu_bots_;
AttachMenuBotsLogEvent() = default;
AttachMenuBotsLogEvent(int64 hash, vector<AttachMenuBot> attach_menu_bots)
: hash_(hash), attach_menu_bots_(std::move(attach_menu_bots)) {
}
template <class StorerT>
void store(StorerT &storer) const {
td::store(hash_, storer);
td::store(attach_menu_bots_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
td::parse(hash_, parser);
td::parse(attach_menu_bots_, parser);
}
};
AttachMenuManager::AttachMenuManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
void AttachMenuManager::tear_down() {
parent_.reset();
}
void AttachMenuManager::start_up() {
init();
}
void AttachMenuManager::init() {
if (!is_active()) {
return;
}
if (is_inited_) {
return;
}
is_inited_ = true;
if (!G()->parameters().use_chat_info_db) {
G()->td_db()->get_binlog_pmc()->erase(get_attach_menu_bots_database_key());
} else {
auto attach_menu_bots_string = G()->td_db()->get_binlog_pmc()->get(get_attach_menu_bots_database_key());
if (!attach_menu_bots_string.empty()) {
AttachMenuBotsLogEvent attach_menu_bots_log_event;
bool is_valid = true;
is_valid &= log_event_parse(attach_menu_bots_log_event, attach_menu_bots_string).is_ok();
Dependencies dependencies;
for (auto &attach_menu_bot : attach_menu_bots_log_event.attach_menu_bots_) {
if (!attach_menu_bot.user_id_.is_valid() || !attach_menu_bot.default_icon_file_id_.is_valid()) {
is_valid = false;
}
if (!is_valid) {
break;
}
dependencies.add(attach_menu_bot.user_id_);
}
if (is_valid && dependencies.resolve_force(td_, "AttachMenuBotsLogEvent")) {
hash_ = attach_menu_bots_log_event.hash_;
attach_menu_bots_ = std::move(attach_menu_bots_log_event.attach_menu_bots_);
} else {
LOG(ERROR) << "Ignore invalid attachment menu bots log event";
}
}
}
class StateCallback final : public StateManager::Callback {
public:
explicit StateCallback(ActorId<AttachMenuManager> parent) : parent_(std::move(parent)) {
}
bool on_online(bool is_online) final {
if (is_online) {
send_closure(parent_, &AttachMenuManager::on_online, is_online);
}
return parent_.is_alive();
}
private:
ActorId<AttachMenuManager> parent_;
};
send_closure(G()->state_manager(), &StateManager::add_callback, make_unique<StateCallback>(actor_id(this)));
send_update_attach_menu_bots();
reload_attach_menu_bots(Promise<Unit>());
}
void AttachMenuManager::timeout_expired() {
if (!is_active()) {
return;
}
reload_attach_menu_bots(Promise<Unit>());
}
bool AttachMenuManager::is_active() const {
return !G()->close_flag() && td_->auth_manager_->is_authorized() && !td_->auth_manager_->is_bot();
}
void AttachMenuManager::on_online(bool is_online) {
if (is_online) {
ping_web_view();
} else {
ping_web_view_timeout_.cancel_timeout();
}
}
void AttachMenuManager::ping_web_view_static(void *td_void) {
if (G()->close_flag()) {
return;
}
CHECK(td_void != nullptr);
auto td = static_cast<Td *>(td_void);
td->attach_menu_manager_->ping_web_view();
}
void AttachMenuManager::ping_web_view() {
if (G()->close_flag() || opened_web_views_.empty()) {
return;
}
for (const auto &it : opened_web_views_) {
const auto &opened_web_view = it.second;
bool silent = td_->messages_manager_->get_dialog_silent_send_message(opened_web_view.dialog_id_);
td_->create_handler<ProlongWebViewQuery>()->send(opened_web_view.dialog_id_, opened_web_view.bot_user_id_, it.first,
opened_web_view.reply_to_message_id_, silent);
}
schedule_ping_web_view();
}
void AttachMenuManager::schedule_ping_web_view() {
ping_web_view_timeout_.set_callback(ping_web_view_static);
ping_web_view_timeout_.set_callback_data(static_cast<void *>(td_));
ping_web_view_timeout_.set_timeout_in(PING_WEB_VIEW_TIMEOUT);
}
void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,
string &&url, td_api::object_ptr<td_api::themeParameters> &&theme,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise) {
TRY_STATUS_PROMISE(promise, td_->contacts_manager_->get_bot_data(bot_user_id));
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
if (!td_->messages_manager_->have_dialog_force(dialog_id, "request_web_view")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
switch (dialog_id.get_type()) {
case DialogType::User:
// ok
break;
case DialogType::Chat:
case DialogType::Channel:
case DialogType::SecretChat:
return promise.set_error(Status::Error(400, "Web apps can be opened only in private chats"));
case DialogType::None:
default:
UNREACHABLE();
}
if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Write)) {
return promise.set_error(Status::Error(400, "Have no write access to the chat"));
}
if (!reply_to_message_id.is_valid() || !reply_to_message_id.is_server() ||
!td_->messages_manager_->have_message_force({dialog_id, reply_to_message_id}, "request_web_view")) {
reply_to_message_id = MessageId();
}
bool silent = td_->messages_manager_->get_dialog_silent_send_message(dialog_id);
td_->create_handler<RequestWebViewQuery>(std::move(promise))
->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), reply_to_message_id,
silent);
}
void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id,
MessageId reply_to_message_id) {
if (query_id == 0) {
LOG(ERROR) << "Receive web app query identifier == 0";
return;
}
if (opened_web_views_.empty()) {
schedule_ping_web_view();
}
OpenedWebView opened_web_view;
opened_web_view.dialog_id_ = dialog_id;
opened_web_view.bot_user_id_ = bot_user_id;
opened_web_view.reply_to_message_id_ = reply_to_message_id;
opened_web_views_.emplace(query_id, std::move(opened_web_view));
}
void AttachMenuManager::close_web_view(int64 query_id, Promise<Unit> &&promise) {
opened_web_views_.erase(query_id);
if (opened_web_views_.empty()) {
ping_web_view_timeout_.cancel_timeout();
}
promise.set_value(Unit());
}
Result<AttachMenuManager::AttachMenuBot> AttachMenuManager::get_attach_menu_bot(
tl_object_ptr<telegram_api::attachMenuBot> &&bot) const {
UserId user_id(bot->bot_id_);
if (!td_->contacts_manager_->have_user(user_id)) {
return Status::Error(PSLICE() << "Have no information about " << user_id);
}
AttachMenuBot attach_menu_bot;
attach_menu_bot.is_added_ = !bot->inactive_;
attach_menu_bot.user_id_ = user_id;
attach_menu_bot.name_ = std::move(bot->short_name_);
for (auto &icon : bot->icons_) {
Slice name = icon->name_;
int32 document_id = icon->icon_->get_id();
if (document_id == telegram_api::documentEmpty::ID) {
return Status::Error(PSLICE() << "Have no icon for " << user_id << " with name " << name);
}
CHECK(document_id == telegram_api::document::ID);
if (name != "default_static" && name != "ios_static" && name != "ios_animated" && name != "android_animated" &&
name != "macos_animated") {
LOG(ERROR) << "Have icon for " << user_id << " with name " << name;
continue;
}
auto expected_document_type = ends_with(name, "_static") ? Document::Type::General : Document::Type::Sticker;
auto parsed_document =
td_->documents_manager_->on_get_document(move_tl_object_as<telegram_api::document>(icon->icon_), DialogId());
if (parsed_document.type != expected_document_type) {
LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id;
continue;
}
bool expect_colors = false;
switch (name[5]) {
case 'l':
attach_menu_bot.default_icon_file_id_ = parsed_document.file_id;
break;
case 't':
attach_menu_bot.ios_static_icon_file_id_ = parsed_document.file_id;
break;
case 'n':
attach_menu_bot.ios_animated_icon_file_id_ = parsed_document.file_id;
break;
case 'i':
attach_menu_bot.android_icon_file_id_ = parsed_document.file_id;
expect_colors = true;
break;
case '_':
attach_menu_bot.macos_icon_file_id_ = parsed_document.file_id;
break;
default:
UNREACHABLE();
}
if (expect_colors) {
if (icon->colors_.empty()) {
LOG(ERROR) << "Have no colors for attachment menu bot icon for " << user_id;
} else {
for (auto &color : icon->colors_) {
if (color->name_ != "light_icon" && color->name_ != "light_text" && color->name_ != "dark_icon" &&
color->name_ != "dark_text") {
LOG(ERROR) << "Receive unexpected attachment menu color " << color->name_ << " for " << user_id;
continue;
}
auto alpha = (color->color_ >> 24) & 0xFF;
if (alpha != 0 && alpha != 0xFF) {
LOG(ERROR) << "Receive alpha in attachment menu color " << color->name_ << " for " << user_id;
}
auto c = color->color_ & 0xFFFFFF;
switch (color->name_[6]) {
case 'i':
attach_menu_bot.icon_color_.light_color_ = c;
break;
case 't':
attach_menu_bot.name_color_.light_color_ = c;
break;
case 'c':
attach_menu_bot.icon_color_.dark_color_ = c;
break;
case 'e':
attach_menu_bot.name_color_.dark_color_ = c;
break;
default:
UNREACHABLE();
}
}
if (attach_menu_bot.icon_color_.light_color_ == -1 || attach_menu_bot.icon_color_.dark_color_ == -1) {
LOG(ERROR) << "Receive wrong icon_color for " << user_id;
attach_menu_bot.icon_color_ = AttachMenuBotColor();
}
if (attach_menu_bot.name_color_.light_color_ == -1 || attach_menu_bot.name_color_.dark_color_ == -1) {
LOG(ERROR) << "Receive wrong name_color for " << user_id;
attach_menu_bot.name_color_ = AttachMenuBotColor();
}
}
} else {
if (!icon->colors_.empty()) {
LOG(ERROR) << "Have unexpected colors for attachment menu bot icon for " << user_id << " with name " << name;
}
}
}
if (!attach_menu_bot.default_icon_file_id_.is_valid()) {
return Status::Error(PSLICE() << "Have no default icon for " << user_id);
}
return std::move(attach_menu_bot);
}
void AttachMenuManager::reload_attach_menu_bots(Promise<Unit> &&promise) {
if (!is_active()) {
return;
}
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
Result<telegram_api::object_ptr<telegram_api::AttachMenuBots>> &&result) mutable {
send_closure(actor_id, &AttachMenuManager::on_reload_attach_menu_bots, std::move(result), std::move(promise));
});
td_->create_handler<GetAttachMenuBotsQuery>(std::move(query_promise))->send(hash_);
}
void AttachMenuManager::on_reload_attach_menu_bots(
Result<telegram_api::object_ptr<telegram_api::AttachMenuBots>> &&result, Promise<Unit> &&promise) {
if (!is_active()) {
return promise.set_value(Unit());
}
if (result.is_error()) {
set_timeout_in(Random::fast(60, 120));
return promise.set_value(Unit());
}
is_inited_ = true;
set_timeout_in(Random::fast(3600, 4800));
auto attach_menu_bots_ptr = result.move_as_ok();
auto constructor_id = attach_menu_bots_ptr->get_id();
if (constructor_id == telegram_api::attachMenuBotsNotModified::ID) {
return promise.set_value(Unit());
}
CHECK(constructor_id == telegram_api::attachMenuBots::ID);
auto attach_menu_bots = move_tl_object_as<telegram_api::attachMenuBots>(attach_menu_bots_ptr);
td_->contacts_manager_->on_get_users(std::move(attach_menu_bots->users_), "on_reload_attach_menu_bots");
auto new_hash = attach_menu_bots->hash_;
vector<AttachMenuBot> new_attach_menu_bots;
for (auto &bot : attach_menu_bots->bots_) {
auto r_attach_menu_bot = get_attach_menu_bot(std::move(bot));
if (r_attach_menu_bot.is_error()) {
LOG(ERROR) << r_attach_menu_bot.error().message();
new_hash = 0;
continue;
}
if (!r_attach_menu_bot.ok().is_added_) {
LOG(ERROR) << "Receive non-added attachment menu bot " << r_attach_menu_bot.ok().user_id_;
new_hash = 0;
continue;
}
new_attach_menu_bots.push_back(r_attach_menu_bot.move_as_ok());
}
bool need_update = new_attach_menu_bots != attach_menu_bots_;
if (need_update || hash_ != new_hash) {
hash_ = new_hash;
attach_menu_bots_ = std::move(new_attach_menu_bots);
if (need_update) {
send_update_attach_menu_bots();
}
save_attach_menu_bots();
}
promise.set_value(Unit());
}
void AttachMenuManager::remove_bot_from_attach_menu(UserId user_id) {
for (auto it = attach_menu_bots_.begin(); it != attach_menu_bots_.end(); ++it) {
if (it->user_id_ == user_id) {
hash_ = 0;
attach_menu_bots_.erase(it);
send_update_attach_menu_bots();
save_attach_menu_bots();
return;
}
}
}
void AttachMenuManager::get_attach_menu_bot(UserId user_id,
Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise) {
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
TRY_RESULT_PROMISE(promise, bot_data, td_->contacts_manager_->get_bot_data(user_id));
if (!bot_data.can_be_added_to_attach_menu) {
return promise.set_error(Status::Error(400, "The bot can't be added to attachment menu"));
}
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), user_id, promise = std::move(promise)](
Result<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&result) mutable {
send_closure(actor_id, &AttachMenuManager::on_get_attach_menu_bot, user_id, std::move(result),
std::move(promise));
});
td_->create_handler<GetAttachMenuBotQuery>(std::move(query_promise))->send(std::move(input_user));
}
void AttachMenuManager::on_get_attach_menu_bot(
UserId user_id, Result<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&result,
Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
TRY_RESULT_PROMISE(promise, bot, std::move(result));
td_->contacts_manager_->on_get_users(std::move(bot->users_), "on_get_attach_menu_bot");
auto r_attach_menu_bot = get_attach_menu_bot(std::move(bot->bot_));
if (r_attach_menu_bot.is_error()) {
LOG(ERROR) << r_attach_menu_bot.error().message();
return promise.set_error(Status::Error(500, "Receive invalid response"));
}
auto attach_menu_bot = r_attach_menu_bot.move_as_ok();
if (attach_menu_bot.user_id_ != user_id) {
return promise.set_error(Status::Error(500, "Receive wrong bot"));
}
if (attach_menu_bot.is_added_) {
bool is_found = false;
for (auto &old_bot : attach_menu_bots_) {
if (old_bot.user_id_ == user_id) {
is_found = true;
break;
}
}
if (!is_found) {
LOG(INFO) << "Add missing attachment menu bot " << user_id;
}
hash_ = 0;
attach_menu_bots_.insert(attach_menu_bots_.begin(), attach_menu_bot);
send_update_attach_menu_bots();
save_attach_menu_bots();
} else {
remove_bot_from_attach_menu(user_id);
}
promise.set_value(get_attachment_menu_bot_object(attach_menu_bot));
}
void AttachMenuManager::toggle_bot_is_added_to_attach_menu(UserId user_id, bool is_added, Promise<Unit> &&promise) {
CHECK(is_active());
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(user_id));
bool is_found = false;
for (auto &bot : attach_menu_bots_) {
if (bot.user_id_ == user_id) {
is_found = true;
break;
}
}
if (is_added == is_found) {
return promise.set_value(Unit());
}
if (is_added) {
TRY_RESULT_PROMISE(promise, bot_data, td_->contacts_manager_->get_bot_data(user_id));
if (!bot_data.can_be_added_to_attach_menu) {
return promise.set_error(Status::Error(400, "The bot can't be added to attachment menu"));
}
} else {
remove_bot_from_attach_menu(user_id);
}
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &AttachMenuManager::reload_attach_menu_bots, std::move(promise));
}
});
td_->create_handler<ToggleBotInAttachMenuQuery>(std::move(query_promise))->send(std::move(input_user), is_added);
}
td_api::object_ptr<td_api::attachmentMenuBot> AttachMenuManager::get_attachment_menu_bot_object(
const AttachMenuBot &bot) const {
auto get_file = [td = td_](FileId file_id) -> td_api::object_ptr<td_api::file> {
if (!file_id.is_valid()) {
return nullptr;
}
return td->file_manager_->get_file_object(file_id);
};
auto get_attach_menu_bot_color_object =
[](const AttachMenuBotColor &color) -> td_api::object_ptr<td_api::attachmentMenuBotColor> {
if (color == AttachMenuBotColor()) {
return nullptr;
}
return td_api::make_object<td_api::attachmentMenuBotColor>(color.light_color_, color.dark_color_);
};
return td_api::make_object<td_api::attachmentMenuBot>(
td_->contacts_manager_->get_user_id_object(bot.user_id_, "get_attachment_menu_bot_object"), bot.name_,
get_attach_menu_bot_color_object(bot.name_color_), get_file(bot.default_icon_file_id_),
get_file(bot.ios_static_icon_file_id_), get_file(bot.ios_animated_icon_file_id_),
get_file(bot.android_icon_file_id_), get_file(bot.macos_icon_file_id_),
get_attach_menu_bot_color_object(bot.icon_color_));
}
td_api::object_ptr<td_api::updateAttachmentMenuBots> AttachMenuManager::get_update_attachment_menu_bots_object() const {
CHECK(is_active());
CHECK(is_inited_);
auto bots =
transform(attach_menu_bots_, [this](const AttachMenuBot &bot) { return get_attachment_menu_bot_object(bot); });
return td_api::make_object<td_api::updateAttachmentMenuBots>(std::move(bots));
}
void AttachMenuManager::send_update_attach_menu_bots() const {
send_closure(G()->td(), &Td::send_update, get_update_attachment_menu_bots_object());
}
string AttachMenuManager::get_attach_menu_bots_database_key() {
return "attach_bots";
}
void AttachMenuManager::save_attach_menu_bots() {
if (!G()->parameters().use_chat_info_db) {
return;
}
if (attach_menu_bots_.empty()) {
G()->td_db()->get_binlog_pmc()->erase(get_attach_menu_bots_database_key());
} else {
AttachMenuBotsLogEvent attach_menu_bots_log_event{hash_, attach_menu_bots_};
G()->td_db()->get_binlog_pmc()->set(get_attach_menu_bots_database_key(),
log_event_store(attach_menu_bots_log_event).as_slice().str());
}
}
void AttachMenuManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
if (!is_active()) {
return;
}
updates.push_back(get_update_attachment_menu_bots_object());
}
} // namespace td

View File

@ -0,0 +1,146 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
namespace td {
class Td;
class AttachMenuManager final : public Actor {
public:
AttachMenuManager(Td *td, ActorShared<> parent);
void init();
void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id);
void close_web_view(int64 query_id, Promise<Unit> &&promise);
void reload_attach_menu_bots(Promise<Unit> &&promise);
void get_attach_menu_bot(UserId user_id, Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise);
void toggle_bot_is_added_to_attach_menu(UserId user_id, bool is_added, Promise<Unit> &&promise);
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
private:
static const int32 PING_WEB_VIEW_TIMEOUT = 60;
void start_up() final;
void timeout_expired() final;
void tear_down() final;
struct AttachMenuBotColor {
int32 light_color_ = -1;
int32 dark_color_ = -1;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
friend bool operator==(const AttachMenuBotColor &lhs, const AttachMenuBotColor &rhs);
friend bool operator!=(const AttachMenuBotColor &lhs, const AttachMenuBotColor &rhs);
struct AttachMenuBot {
bool is_added_ = false;
UserId user_id_;
string name_;
AttachMenuBotColor name_color_;
FileId default_icon_file_id_;
FileId ios_static_icon_file_id_;
FileId ios_animated_icon_file_id_;
FileId android_icon_file_id_;
FileId macos_icon_file_id_;
AttachMenuBotColor icon_color_;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
class AttachMenuBotsLogEvent;
friend bool operator==(const AttachMenuBot &lhs, const AttachMenuBot &rhs);
friend bool operator!=(const AttachMenuBot &lhs, const AttachMenuBot &rhs);
bool is_active() const;
void on_online(bool is_online);
static void ping_web_view_static(void *td_void);
void ping_web_view();
void schedule_ping_web_view();
Result<AttachMenuBot> get_attach_menu_bot(tl_object_ptr<telegram_api::attachMenuBot> &&bot) const;
td_api::object_ptr<td_api::attachmentMenuBot> get_attachment_menu_bot_object(const AttachMenuBot &bot) const;
td_api::object_ptr<td_api::updateAttachmentMenuBots> get_update_attachment_menu_bots_object() const;
void remove_bot_from_attach_menu(UserId user_id);
void send_update_attach_menu_bots() const;
static string get_attach_menu_bots_database_key();
void save_attach_menu_bots();
void on_reload_attach_menu_bots(Result<telegram_api::object_ptr<telegram_api::AttachMenuBots>> &&result,
Promise<Unit> &&promise);
void on_get_attach_menu_bot(UserId user_id,
Result<telegram_api::object_ptr<telegram_api::attachMenuBotsBot>> &&result,
Promise<td_api::object_ptr<td_api::attachmentMenuBot>> &&promise);
Td *td_;
ActorShared<> parent_;
bool is_inited_ = false;
int64 hash_ = 0;
vector<AttachMenuBot> attach_menu_bots_;
struct OpenedWebView {
DialogId dialog_id_;
UserId bot_user_id_;
MessageId reply_to_message_id_;
};
FlatHashMap<int64, OpenedWebView> opened_web_views_;
Timeout ping_web_view_timeout_;
};
} // namespace td

View File

@ -8,11 +8,13 @@
#include "td/telegram/AuthManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/Td.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
@ -23,7 +25,9 @@ AudiosManager::AudiosManager(Td *td) : td_(td) {
int32 AudiosManager::get_audio_duration(FileId file_id) const {
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
if (it == audios_.end()) {
return 0;
}
return it->second->duration;
}
@ -43,6 +47,28 @@ tl_object_ptr<td_api::audio> AudiosManager::get_audio_object(FileId file_id) con
td_->file_manager_->get_file_object(file_id));
}
td_api::object_ptr<td_api::notificationSound> AudiosManager::get_notification_sound_object(FileId file_id) const {
if (!file_id.is_valid()) {
return nullptr;
}
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
auto audio = it->second.get();
CHECK(audio != nullptr);
auto file_view = td_->file_manager_->get_file_view(file_id);
CHECK(!file_view.empty());
CHECK(file_view.get_type() == FileType::Ringtone);
CHECK(file_view.has_remote_location());
auto document_id = file_view.remote_location().get_id();
auto title = audio->title;
if (title.empty() && !audio->file_name.empty()) {
title = PathView(audio->file_name).file_name_without_extension().str();
}
return td_api::make_object<td_api::notificationSound>(document_id, audio->duration, audio->date, title,
audio->performer, td_->file_manager_->get_file_object(file_id));
}
FileId AudiosManager::on_get_audio(unique_ptr<Audio> new_audio, bool replace) {
auto file_id = new_audio->file_id;
CHECK(file_id.is_valid());
@ -66,6 +92,9 @@ FileId AudiosManager::on_get_audio(unique_ptr<Audio> new_audio, bool replace) {
LOG(DEBUG) << "Audio " << file_id << " file name has changed";
a->file_name = std::move(new_audio->file_name);
}
if (a->date != new_audio->date) {
a->date = new_audio->date;
}
if (a->minithumbnail != new_audio->minithumbnail) {
a->minithumbnail = std::move(new_audio->minithumbnail);
}
@ -158,7 +187,8 @@ void AudiosManager::delete_audio_thumbnail(FileId file_id) {
}
void AudiosManager::create_audio(FileId file_id, string minithumbnail, PhotoSize thumbnail, string file_name,
string mime_type, int32 duration, string title, string performer, bool replace) {
string mime_type, int32 duration, string title, string performer, int32 date,
bool replace) {
auto a = make_unique<Audio>();
a->file_id = file_id;
a->file_name = std::move(file_name);
@ -166,6 +196,7 @@ void AudiosManager::create_audio(FileId file_id, string minithumbnail, PhotoSize
a->duration = max(duration, 0);
a->title = std::move(title);
a->performer = std::move(performer);
a->date = date;
if (!td_->auth_manager_->is_bot()) {
a->minithumbnail = std::move(minithumbnail);
}

View File

@ -7,7 +7,7 @@
#pragma once
#include "td/telegram/files/FileId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -30,8 +30,10 @@ class AudiosManager {
tl_object_ptr<td_api::audio> get_audio_object(FileId file_id) const;
td_api::object_ptr<td_api::notificationSound> get_notification_sound_object(FileId file_id) const;
void create_audio(FileId file_id, string minithumbnail, PhotoSize thumbnail, string file_name, string mime_type,
int32 duration, string title, string performer, bool replace);
int32 duration, string title, string performer, int32 date, bool replace);
tl_object_ptr<telegram_api::InputMedia> get_input_media(FileId file_id,
tl_object_ptr<telegram_api::InputFile> input_file,
@ -63,6 +65,7 @@ class AudiosManager {
string file_name;
string mime_type;
int32 duration = 0;
int32 date = 0;
string title;
string performer;
string minithumbnail;

View File

@ -9,7 +9,7 @@
#include "td/telegram/AudiosManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PhotoSize.hpp"
#include "td/telegram/Version.h"
#include "td/utils/common.h"
@ -24,45 +24,117 @@ void AudiosManager::store_audio(FileId file_id, StorerT &storer) const {
auto it = audios_.find(file_id);
CHECK(it != audios_.end());
const Audio *audio = it->second.get();
store(audio->file_name, storer);
store(audio->mime_type, storer);
store(audio->duration, storer);
store(audio->title, storer);
store(audio->performer, storer);
store(audio->minithumbnail, storer);
store(audio->thumbnail, storer);
bool has_file_name = !audio->file_name.empty();
bool has_mime_type = !audio->mime_type.empty();
bool has_duration = audio->duration != 0;
bool has_title = !audio->title.empty();
bool has_performer = !audio->performer.empty();
bool has_minithumbnail = !audio->minithumbnail.empty();
bool has_thumbnail = audio->thumbnail.file_id.is_valid();
bool has_date = audio->date != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_file_name);
STORE_FLAG(has_mime_type);
STORE_FLAG(has_duration);
STORE_FLAG(has_title);
STORE_FLAG(has_performer);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(has_thumbnail);
STORE_FLAG(has_date);
END_STORE_FLAGS();
if (has_file_name) {
store(audio->file_name, storer);
}
if (has_mime_type) {
store(audio->mime_type, storer);
}
if (has_duration) {
store(audio->duration, storer);
}
if (has_title) {
store(audio->title, storer);
}
if (has_performer) {
store(audio->performer, storer);
}
if (has_minithumbnail) {
store(audio->minithumbnail, storer);
}
if (has_thumbnail) {
store(audio->thumbnail, storer);
}
if (has_date) {
store(audio->date, storer);
}
store(file_id, storer);
}
template <class ParserT>
FileId AudiosManager::parse_audio(ParserT &parser) {
auto audio = make_unique<Audio>();
string tmp_filename;
parse(tmp_filename, parser);
parse(audio->mime_type, parser);
if ( G()->shared_config().get_option_boolean("disable_document_filenames") && (
audio->mime_type.rfind("image/") == 0 ||
audio->mime_type.rfind("video/") == 0 ||
audio->mime_type.rfind("audio/") == 0)) {
audio->file_name = "0";
bool has_file_name;
bool has_mime_type;
bool has_duration;
bool has_title;
bool has_performer;
bool has_minithumbnail;
bool has_thumbnail;
bool has_date;
if (parser.version() >= static_cast<int32>(Version::AddAudioFlags)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_file_name);
PARSE_FLAG(has_mime_type);
PARSE_FLAG(has_duration);
PARSE_FLAG(has_title);
PARSE_FLAG(has_performer);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(has_thumbnail);
PARSE_FLAG(has_date);
END_PARSE_FLAGS();
} else {
audio->file_name = tmp_filename;
has_file_name = true;
has_mime_type = true;
has_duration = true;
has_title = true;
has_performer = true;
has_minithumbnail = parser.version() >= static_cast<int32>(Version::SupportMinithumbnails);
has_thumbnail = true;
has_date = false;
}
parse(audio->duration, parser);
parse(audio->title, parser);
parse(audio->performer, parser);
if (parser.version() >= static_cast<int32>(Version::SupportMinithumbnails)) {
if (has_file_name) {
string tmp_filename;
parse(tmp_filename, parser);
if (G()->shared_config().get_option_boolean("disable_document_filenames")) {
audio->file_name = "0";
} else {
audio->file_name = tmp_filename;
}
}
if (has_mime_type) {
parse(audio->mime_type, parser);
}
if (has_duration) {
parse(audio->duration, parser);
}
if (has_title) {
parse(audio->title, parser);
}
if (has_performer) {
parse(audio->performer, parser);
}
if (has_minithumbnail) {
string tmp_minithumbnail;
parse(tmp_minithumbnail, parser);
if (!G()->shared_config().get_option_boolean("disable_minithumbnails")) {
audio->minithumbnail = tmp_minithumbnail;
}
}
parse(audio->thumbnail, parser);
if (has_thumbnail) {
parse(audio->thumbnail, parser);
}
if (has_date) {
parse(audio->date, parser);
}
parse(audio->file_id, parser);
if (parser.get_error() != nullptr || !audio->file_id.is_valid()) {
return FileId();

View File

@ -6,6 +6,7 @@
//
#include "td/telegram/AuthManager.h"
#include "td/telegram/AttachMenuManager.h"
#include "td/telegram/AuthManager.hpp"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConfigShared.h"
@ -786,6 +787,7 @@ void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authoriz
if (auth->setup_password_required_ && auth->otherwise_relogin_days_ > 0) {
G()->shared_config().set_option_integer("otherwise_relogin_days", auth->otherwise_relogin_days_);
}
td_->attach_menu_manager_->init();
td_->messages_manager_->on_authorization_success();
td_->notification_manager_->init();
td_->stickers_manager_->init();

View File

@ -17,7 +17,7 @@
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/Global.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TdParameters.h"
@ -565,9 +565,7 @@ void BackgroundManager::on_load_background_from_database(string name, string val
}
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
}
td_api::object_ptr<td_api::updateSelectedBackground> BackgroundManager::get_update_selected_background_object(

View File

@ -11,7 +11,6 @@
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/Photo.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"

View File

@ -54,12 +54,7 @@ Result<BotCommandScope> BotCommandScope::get_bot_command_scope(Td *td,
type = Type::DialogParticipant;
dialog_id = DialogId(scope->chat_id_);
user_id = UserId(scope->user_id_);
if (!user_id.is_valid()) {
return Status::Error(400, "User not found");
}
if (!td->contacts_manager_->have_input_user(user_id)) {
return Status::Error(400, "Can't access the user");
}
TRY_STATUS(td->contacts_manager_->get_input_user(user_id));
break;
}
default:
@ -83,8 +78,7 @@ Result<BotCommandScope> BotCommandScope::get_bot_command_scope(Td *td,
// ok
break;
case DialogType::Channel:
if (td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) !=
ContactsManager::ChannelType::Megagroup) {
if (td->contacts_manager_->is_broadcast_channel(dialog_id.get_channel_id())) {
return Status::Error(400, "Can't change commands in channel chats");
}
break;
@ -100,9 +94,8 @@ telegram_api::object_ptr<telegram_api::BotCommandScope> BotCommandScope::get_inp
const Td *td) const {
auto input_peer =
dialog_id_.is_valid() ? td->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read) : nullptr;
auto input_user = td->contacts_manager_->have_input_user(user_id_)
? td->contacts_manager_->get_input_user(user_id_).move_as_ok()
: nullptr;
auto r_input_user = td->contacts_manager_->get_input_user(user_id_);
auto input_user = r_input_user.is_ok() ? r_input_user.move_as_ok() : nullptr;
switch (type_) {
case Type::Default:
return telegram_api::make_object<telegram_api::botCommandScopeDefault>();

View File

@ -0,0 +1,163 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/BotMenuButton.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/LinkManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/Td.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
namespace td {
class SetBotMenuButtonQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetBotMenuButtonQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(UserId user_id, telegram_api::object_ptr<telegram_api::BotMenuButton> input_bot_menu_button) {
auto input_user = user_id.is_valid() ? td_->contacts_manager_->get_input_user(user_id).move_as_ok()
: make_tl_object<telegram_api::inputUserEmpty>();
send_query(G()->net_query_creator().create(
telegram_api::bots_setBotMenuButton(std::move(input_user), std::move(input_bot_menu_button))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::bots_setBotMenuButton>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
if (!result_ptr.ok()) {
LOG(ERROR) << "Receive false as result of SetBotMenuButtonQuery";
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetBotMenuButtonQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::botMenuButton>> promise_;
public:
explicit GetBotMenuButtonQuery(Promise<td_api::object_ptr<td_api::botMenuButton>> &&promise)
: promise_(std::move(promise)) {
}
void send(UserId user_id) {
auto input_user = user_id.is_valid() ? td_->contacts_manager_->get_input_user(user_id).move_as_ok()
: make_tl_object<telegram_api::inputUserEmpty>();
send_query(G()->net_query_creator().create(telegram_api::bots_getBotMenuButton(std::move(input_user))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::bots_getBotMenuButton>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetBotMenuButtonQuery: " << to_string(ptr);
auto bot_menu_button = get_bot_menu_button(std::move(ptr));
promise_.set_value(bot_menu_button == nullptr ? td_api::make_object<td_api::botMenuButton>()
: bot_menu_button->get_bot_menu_button_object(td_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
unique_ptr<BotMenuButton> get_bot_menu_button(telegram_api::object_ptr<telegram_api::BotMenuButton> &&bot_menu_button) {
CHECK(bot_menu_button != nullptr);
switch (bot_menu_button->get_id()) {
case telegram_api::botMenuButtonCommands::ID:
return nullptr;
case telegram_api::botMenuButtonDefault::ID:
return td::make_unique<BotMenuButton>(string(), "default");
case telegram_api::botMenuButton::ID: {
auto button = telegram_api::move_object_as<telegram_api::botMenuButton>(bot_menu_button);
if (button->text_.empty()) {
LOG(ERROR) << "Receive bot menu button with empty text: " << to_string(button);
return nullptr;
}
return td::make_unique<BotMenuButton>(std::move(button->text_), std::move(button->url_));
}
default:
UNREACHABLE();
return nullptr;
}
}
td_api::object_ptr<td_api::botMenuButton> BotMenuButton::get_bot_menu_button_object(Td *td) const {
bool is_bot = td->auth_manager_->is_bot();
return td_api::make_object<td_api::botMenuButton>(text_, is_bot ? url_ : "menu://" + url_);
}
bool operator==(const BotMenuButton &lhs, const BotMenuButton &rhs) {
return lhs.text_ == rhs.text_ && lhs.url_ == rhs.url_;
}
td_api::object_ptr<td_api::botMenuButton> get_bot_menu_button_object(Td *td, const BotMenuButton *bot_menu_button) {
if (bot_menu_button == nullptr) {
return nullptr;
}
return bot_menu_button->get_bot_menu_button_object(td);
}
void set_menu_button(Td *td, UserId user_id, td_api::object_ptr<td_api::botMenuButton> &&menu_button,
Promise<Unit> &&promise) {
if (!user_id.is_valid() && user_id != UserId()) {
return promise.set_error(Status::Error(400, "User not found"));
}
telegram_api::object_ptr<telegram_api::BotMenuButton> input_bot_menu_button;
if (menu_button == nullptr) {
input_bot_menu_button = telegram_api::make_object<telegram_api::botMenuButtonCommands>();
} else if (menu_button->text_.empty()) {
if (menu_button->url_ != "default") {
return promise.set_error(Status::Error(400, "Menu button text can't be empty"));
}
input_bot_menu_button = telegram_api::make_object<telegram_api::botMenuButtonDefault>();
} else {
if (!clean_input_string(menu_button->text_)) {
return promise.set_error(Status::Error(400, "Menu button text must be encoded in UTF-8"));
}
if (!clean_input_string(menu_button->url_)) {
return promise.set_error(Status::Error(400, "Menu button URL must be encoded in UTF-8"));
}
auto r_url = LinkManager::check_link(menu_button->url_, true, !G()->is_test_dc());
if (r_url.is_error()) {
return promise.set_error(Status::Error(400, PSLICE() << "Menu button web app URL '" << menu_button->url_
<< "' is invalid: " << r_url.error().message()));
}
input_bot_menu_button = telegram_api::make_object<telegram_api::botMenuButton>(menu_button->text_, r_url.ok());
}
td->create_handler<SetBotMenuButtonQuery>(std::move(promise))->send(user_id, std::move(input_bot_menu_button));
}
void get_menu_button(Td *td, UserId user_id, Promise<td_api::object_ptr<td_api::botMenuButton>> &&promise) {
if (!user_id.is_valid() && user_id != UserId()) {
return promise.set_error(Status::Error(400, "User not found"));
}
td->create_handler<GetBotMenuButtonQuery>(std::move(promise))->send(user_id);
}
} // namespace td

View File

@ -0,0 +1,84 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
class Td;
class BotMenuButton {
string text_;
string url_;
friend bool operator==(const BotMenuButton &lhs, const BotMenuButton &rhs);
public:
BotMenuButton() = default;
BotMenuButton(string &&text, string &&url) : text_(std::move(text)), url_(std::move(url)) {
}
td_api::object_ptr<td_api::botMenuButton> get_bot_menu_button_object(Td *td) const;
template <class StorerT>
void store(StorerT &storer) const {
bool has_text = !text_.empty();
bool has_url = !url_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_text);
STORE_FLAG(has_url);
END_STORE_FLAGS();
if (has_text) {
td::store(text_, storer);
}
if (has_url) {
td::store(url_, storer);
}
}
template <class ParserT>
void parse(ParserT &parser) {
bool has_text;
bool has_url;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_text);
PARSE_FLAG(has_url);
END_PARSE_FLAGS();
if (has_text) {
td::parse(text_, parser);
}
if (has_url) {
td::parse(url_, parser);
}
}
};
bool operator==(const BotMenuButton &lhs, const BotMenuButton &rhs);
inline bool operator!=(const BotMenuButton &lhs, const BotMenuButton &rhs) {
return !(lhs == rhs);
}
unique_ptr<BotMenuButton> get_bot_menu_button(telegram_api::object_ptr<telegram_api::BotMenuButton> &&bot_menu_button);
td_api::object_ptr<td_api::botMenuButton> get_bot_menu_button_object(Td *td, const BotMenuButton *bot_menu_button);
void set_menu_button(Td *td, UserId user_id, td_api::object_ptr<td_api::botMenuButton> &&menu_button,
Promise<Unit> &&promise);
void get_menu_button(Td *td, UserId user_id, Promise<td_api::object_ptr<td_api::botMenuButton>> &&promise);
} // namespace td

15
td/telegram/ChannelType.h Normal file
View File

@ -0,0 +1,15 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
namespace td {
enum class ChannelType : uint8 { Broadcast, Megagroup, Unknown };
} // namespace td

View File

@ -96,7 +96,7 @@ class ClientManager final {
ClientId client_id;
/**
* Request identifier, to which the response corresponds, or 0 for incoming updates from TDLib.
* Request identifier to which the response corresponds, or 0 for incoming updates from TDLib.
*/
RequestId request_id;

View File

@ -77,7 +77,7 @@ public:
/// <summary>
/// Launches a cycle which will fetch all results of queries to TDLib and incoming updates from TDLib.
/// Must be called once on a separate dedicated thread, on which all updates and query results from all Clients will be handled.
/// Must be called once on a separate dedicated thread on which all updates and query results from all Clients will be handled.
/// Never returns.
/// </summary>
static void Run() {

View File

@ -1147,47 +1147,32 @@ void ConfigManager::on_result(NetQueryPtr res) {
auto result_ptr = fetch_result<telegram_api::help_dismissSuggestion>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(promises, result_ptr.move_as_error());
return;
}
remove_suggested_action(suggested_actions_, suggested_action);
reget_app_config(Auto());
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
return;
}
if (token == 6 || token == 7) {
is_set_archive_and_mute_request_sent_ = false;
bool archive_and_mute = (token == 7);
auto promises = std::move(set_archive_and_mute_queries_[archive_and_mute]);
set_archive_and_mute_queries_[archive_and_mute].clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_setGlobalPrivacySettings>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(set_archive_and_mute_queries_[archive_and_mute], result_ptr.move_as_error());
} else {
if (last_set_archive_and_mute_ == archive_and_mute) {
do_set_archive_and_mute(archive_and_mute);
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(set_archive_and_mute_queries_[archive_and_mute]);
}
if (!set_archive_and_mute_queries_[!archive_and_mute].empty()) {
if (archive_and_mute == last_set_archive_and_mute_) {
promises = std::move(set_archive_and_mute_queries_[!archive_and_mute]);
set_archive_and_mute_queries_[!archive_and_mute].clear();
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(set_archive_and_mute_queries_[!archive_and_mute]);
} else {
set_archive_and_mute(!archive_and_mute, Auto());
}
@ -1195,14 +1180,9 @@ void ConfigManager::on_result(NetQueryPtr res) {
return;
}
if (token == 5) {
auto promises = std::move(get_global_privacy_settings_queries_);
get_global_privacy_settings_queries_.clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_getGlobalPrivacySettings>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(get_global_privacy_settings_queries_, result_ptr.move_as_error());
return;
}
@ -1213,40 +1193,27 @@ void ConfigManager::on_result(NetQueryPtr res) {
LOG(ERROR) << "Receive wrong response: " << to_string(result);
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(get_global_privacy_settings_queries_);
return;
}
if (token == 3 || token == 4) {
is_set_content_settings_request_sent_ = false;
bool ignore_sensitive_content_restrictions = (token == 4);
auto promises = std::move(set_content_settings_queries_[ignore_sensitive_content_restrictions]);
set_content_settings_queries_[ignore_sensitive_content_restrictions].clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_setContentSettings>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(set_content_settings_queries_[ignore_sensitive_content_restrictions], result_ptr.move_as_error());
} else {
if (G()->shared_config().get_option_boolean("can_ignore_sensitive_content_restrictions") &&
last_set_content_settings_ == ignore_sensitive_content_restrictions) {
do_set_ignore_sensitive_content_restrictions(ignore_sensitive_content_restrictions);
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(set_content_settings_queries_[ignore_sensitive_content_restrictions]);
}
if (!set_content_settings_queries_[!ignore_sensitive_content_restrictions].empty()) {
if (ignore_sensitive_content_restrictions == last_set_content_settings_) {
promises = std::move(set_content_settings_queries_[!ignore_sensitive_content_restrictions]);
set_content_settings_queries_[!ignore_sensitive_content_restrictions].clear();
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(set_content_settings_queries_[!ignore_sensitive_content_restrictions]);
} else {
set_content_settings(!ignore_sensitive_content_restrictions, Auto());
}
@ -1254,14 +1221,9 @@ void ConfigManager::on_result(NetQueryPtr res) {
return;
}
if (token == 2) {
auto promises = std::move(get_content_settings_queries_);
get_content_settings_queries_.clear();
CHECK(!promises.empty());
auto result_ptr = fetch_result<telegram_api::account_getContentSettings>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(get_content_settings_queries_, result_ptr.move_as_error());
return;
}
@ -1269,9 +1231,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
do_set_ignore_sensitive_content_restrictions(result->sensitive_enabled_);
G()->shared_config().set_option_boolean("can_ignore_sensitive_content_restrictions", result->sensitive_can_change_);
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(get_content_settings_queries_);
return;
}
if (token == 1) {
@ -1282,12 +1242,8 @@ void ConfigManager::on_result(NetQueryPtr res) {
CHECK(!promises.empty() || !unit_promises.empty());
auto result_ptr = fetch_result<telegram_api::help_getAppConfig>(std::move(res));
if (result_ptr.is_error()) {
for (auto &promise : promises) {
promise.set_error(result_ptr.error().clone());
}
for (auto &promise : unit_promises) {
promise.set_error(result_ptr.error().clone());
}
fail_promises(promises, result_ptr.error().clone());
fail_promises(unit_promises, result_ptr.move_as_error());
return;
}
@ -1296,9 +1252,7 @@ void ConfigManager::on_result(NetQueryPtr res) {
for (auto &promise : promises) {
promise.set_value(convert_json_value_object(result));
}
for (auto &promise : unit_promises) {
promise.set_value(Unit());
}
set_promises(unit_promises);
return;
}
@ -1530,7 +1484,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (value->get_id() == telegram_api::jsonArray::ID) {
auto reasons = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &reason : reasons) {
auto reason_name = get_json_value_string(std::move(reason), "ignore_restriction_reasons");
auto reason_name = get_json_value_string(std::move(reason), key);
if (!reason_name.empty() && reason_name.find(',') == string::npos) {
if (!ignored_restriction_reasons.empty()) {
ignored_restriction_reasons += ',';
@ -1546,14 +1500,14 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
continue;
}
if (key == "emojies_animated_zoom") {
animated_emoji_zoom = get_json_value_double(std::move(key_value->value_), "emojies_animated_zoom");
animated_emoji_zoom = get_json_value_double(std::move(key_value->value_), key);
continue;
}
if (key == "emojies_send_dice") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto emojis = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &emoji : emojis) {
auto emoji_text = get_json_value_string(std::move(emoji), "emojies_send_dice");
auto emoji_text = get_json_value_string(std::move(emoji), key);
if (!emoji_text.empty()) {
dice_emoji_index[emoji_text] = dice_emojis.size();
dice_emojis.push_back(emoji_text);
@ -1640,14 +1594,14 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
continue;
}
if (key == "gif_search_branding") {
animation_search_provider = get_json_value_string(std::move(key_value->value_), "gif_search_branding");
animation_search_provider = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "gif_search_emojies") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto emojis = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &emoji : emojis) {
auto emoji_str = get_json_value_string(std::move(emoji), "gif_search_emojies");
auto emoji_str = get_json_value_string(std::move(emoji), key);
if (!emoji_str.empty() && emoji_str.find(',') == string::npos) {
if (!animation_search_emojis.empty()) {
animation_search_emojis += ',';
@ -1666,7 +1620,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (value->get_id() == telegram_api::jsonArray::ID) {
auto actions = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &action : actions) {
auto action_str = get_json_value_string(std::move(action), "pending_suggestions");
auto action_str = get_json_value_string(std::move(action), key);
SuggestedAction suggested_action(action_str);
if (!suggested_action.is_empty()) {
if (archive_and_mute &&
@ -1685,19 +1639,18 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
continue;
}
if (key == "autoarchive_setting_available") {
can_archive_and_mute_new_chats_from_unknown_users =
get_json_value_bool(std::move(key_value->value_), "autoarchive_setting_available");
can_archive_and_mute_new_chats_from_unknown_users = get_json_value_bool(std::move(key_value->value_), key);
continue;
}
if (key == "autologin_token") {
autologin_token = get_json_value_string(std::move(key_value->value_), "autologin_token");
autologin_token = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "autologin_domains") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto domains = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &domain : domains) {
autologin_domains.push_back(get_json_value_string(std::move(domain), "autologin_domains"));
autologin_domains.push_back(get_json_value_string(std::move(domain), key));
}
} else {
LOG(ERROR) << "Receive unexpected autologin_domains " << to_string(*value);
@ -1708,7 +1661,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (value->get_id() == telegram_api::jsonArray::ID) {
auto domains = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &domain : domains) {
autologin_domains.push_back(get_json_value_string(std::move(domain), "url_auth_domains"));
autologin_domains.push_back(get_json_value_string(std::move(domain), key));
}
} else {
LOG(ERROR) << "Receive unexpected url_auth_domains " << to_string(*value);
@ -1750,20 +1703,34 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
continue;
}
if (key == "chat_read_mark_expire_period") {
chat_read_mark_expire_period = get_json_value_int(std::move(key_value->value_), "chat_read_mark_expire_period");
chat_read_mark_expire_period = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "chat_read_mark_size_threshold") {
chat_read_mark_size_threshold =
get_json_value_int(std::move(key_value->value_), "chat_read_mark_size_threshold");
chat_read_mark_size_threshold = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "reactions_default") {
default_reaction = get_json_value_string(std::move(key_value->value_), "reactions_default");
default_reaction = get_json_value_string(std::move(key_value->value_), key);
continue;
}
if (key == "reactions_uniq_max") {
reactions_uniq_max = get_json_value_int(std::move(key_value->value_), "reactions_uniq_max");
reactions_uniq_max = get_json_value_int(std::move(key_value->value_), key);
continue;
}
if (key == "ringtone_duration_max") {
auto setting_value = get_json_value_int(std::move(key_value->value_), key);
G()->shared_config().set_option_integer("notification_sound_duration_max", setting_value);
continue;
}
if (key == "ringtone_size_max") {
auto setting_value = get_json_value_int(std::move(key_value->value_), key);
G()->shared_config().set_option_integer("notification_sound_size_max", setting_value);
continue;
}
if (key == "ringtone_saved_count_max") {
auto setting_value = get_json_value_int(std::move(key_value->value_), key);
G()->shared_config().set_option_integer("notification_sound_count_max", setting_value);
continue;
}

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,9 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/BotCommand.h"
#include "td/telegram/BotMenuButton.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChannelType.h"
#include "td/telegram/ChatId.h"
#include "td/telegram/Contact.h"
#include "td/telegram/DialogAdministrator.h"
@ -80,7 +82,6 @@ class ContactsManager final : public Actor {
static ChannelId get_channel_id(const tl_object_ptr<telegram_api::Chat> &chat);
Result<tl_object_ptr<telegram_api::InputUser>> get_input_user(UserId user_id) const;
bool have_input_user(UserId user_id) const;
// TODO get_input_chat ???
@ -221,6 +222,8 @@ class ContactsManager final : public Actor {
void on_update_bot_commands(DialogId dialog_id, UserId bot_user_id,
vector<tl_object_ptr<telegram_api::botCommand>> &&bot_commands);
void on_update_bot_menu_button(UserId bot_user_id, tl_object_ptr<telegram_api::BotMenuButton> &&bot_menu_button);
void on_update_dialog_administrators(DialogId dialog_id, vector<DialogAdministrator> &&administrators,
bool have_access, bool from_database);
@ -457,6 +460,7 @@ class ContactsManager final : public Actor {
bool can_read_all_group_messages;
bool is_inline;
bool need_location;
bool can_be_added_to_attach_menu;
};
Result<BotData> get_bot_data(UserId user_id) const TD_WARN_UNUSED_RESULT;
@ -494,6 +498,7 @@ class ContactsManager final : public Actor {
FileSourceId get_chat_full_file_source_id(ChatId chat_id);
void reload_chat_full(ChatId chat_id, Promise<Unit> &&promise);
int32 get_chat_date(ChatId chat_id) const;
int32 get_chat_participant_count(ChatId chat_id) const;
bool get_chat_is_active(ChatId chat_id) const;
ChannelId get_chat_migrated_to_channel_id(ChatId chat_id) const;
@ -520,9 +525,9 @@ class ContactsManager final : public Actor {
bool get_secret_chat(SecretChatId secret_chat_id, bool force, Promise<Unit> &&promise);
bool get_secret_chat_full(SecretChatId secret_chat_id, Promise<Unit> &&promise);
enum class ChannelType : uint8 { Broadcast, Megagroup, Unknown };
ChannelType get_channel_type(ChannelId channel_id) const;
bool is_broadcast_channel(ChannelId channel_id) const;
bool is_megagroup_channel(ChannelId channel_id) const;
int32 get_channel_date(ChannelId channel_id) const;
DialogParticipantStatus get_channel_status(ChannelId channel_id) const;
DialogParticipantStatus get_channel_permissions(ChannelId channel_id) const;
@ -531,17 +536,22 @@ class ContactsManager final : public Actor {
bool get_channel_has_linked_channel(ChannelId channel_id) const;
ChannelId get_channel_linked_channel_id(ChannelId channel_id);
int32 get_channel_slow_mode_delay(ChannelId channel_id);
bool get_channel_can_be_deleted(ChannelId channel_id);
void add_dialog_participant(DialogId dialog_id, UserId user_id, int32 forward_limit, Promise<Unit> &&promise);
void add_dialog_participants(DialogId dialog_id, const vector<UserId> &user_ids, Promise<Unit> &&promise);
void set_dialog_participant_status(DialogId dialog_id, DialogId participant_dialog_id,
DialogParticipantStatus &&status, Promise<Unit> &&promise);
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status,
Promise<Unit> &&promise);
void ban_dialog_participant(DialogId dialog_id, DialogId participant_dialog_id, int32 banned_until_date,
bool revoke_messages, Promise<Unit> &&promise);
void on_set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus status);
void get_dialog_participant(DialogId dialog_id, DialogId participant_dialog_id,
Promise<td_api::object_ptr<td_api::chatMember>> &&promise);
@ -656,6 +666,7 @@ class ContactsManager final : public Actor {
bool is_contact = false;
bool is_mutual_contact = false;
bool need_apply_min_photo = false;
bool can_be_added_to_attach_menu = false;
bool is_photo_inited = false;
@ -696,7 +707,10 @@ class ContactsManager final : public Actor {
string description;
string private_forward_name;
unique_ptr<BotMenuButton> menu_button;
vector<BotCommand> commands;
AdministratorRights group_administrator_rights;
AdministratorRights broadcast_administrator_rights;
int32 common_chat_count = 0;
@ -899,6 +913,7 @@ class ContactsManager final : public Actor {
bool can_view_statistics = false;
bool is_can_view_statistics_inited = false;
bool is_all_history_available = true;
bool can_be_deleted = false;
bool is_slow_mode_next_send_date_changed = true;
bool is_changed = true; // have new changes that need to be sent to the client and database
@ -1031,6 +1046,7 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FLAG_IS_SCAM = 1 << 24;
static constexpr int32 USER_FLAG_NEED_APPLY_MIN_PHOTO = 1 << 25;
static constexpr int32 USER_FLAG_IS_FAKE = 1 << 26;
static constexpr int32 USER_FLAG_IS_ATTACH_MENU_BOT = 1 << 27;
static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0;
static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1;
@ -1042,9 +1058,10 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FULL_FLAG_HAS_SCHEDULED_MESSAGES = 1 << 12;
static constexpr int32 USER_FULL_FLAG_HAS_MESSAGE_TTL = 1 << 14;
static constexpr int32 USER_FULL_FLAG_HAS_PRIVATE_FORWARD_NAME = 1 << 16;
static constexpr int32 USER_FULL_FLAG_HAS_GROUP_ADMINISTRATOR_RIGHTS = 1 << 17;
static constexpr int32 USER_FULL_FLAG_HAS_BROADCAST_ADMINISTRATOR_RIGHTS = 1 << 18;
static constexpr int32 CHAT_FLAG_USER_IS_CREATOR = 1 << 0;
static constexpr int32 CHAT_FLAG_USER_WAS_KICKED = 1 << 1;
static constexpr int32 CHAT_FLAG_USER_HAS_LEFT = 1 << 2;
// static constexpr int32 CHAT_FLAG_ADMINISTRATORS_ENABLED = 1 << 3;
// static constexpr int32 CHAT_FLAG_IS_ADMINISTRATOR = 1 << 4;
@ -1225,6 +1242,8 @@ class ContactsManager final : public Actor {
static void on_update_user_full_common_chat_count(UserFull *user_full, UserId user_id, int32 common_chat_count);
static void on_update_user_full_commands(UserFull *user_full, UserId user_id,
vector<tl_object_ptr<telegram_api::botCommand>> &&bot_commands);
static void on_update_user_full_menu_button(UserFull *user_full, UserId user_id,
tl_object_ptr<telegram_api::BotMenuButton> &&bot_menu_button);
void on_update_user_full_need_phone_number_privacy_exception(UserFull *user_full, UserId user_id,
bool need_phone_number_privacy_exception) const;
@ -1558,12 +1577,6 @@ class ContactsManager final : public Actor {
void update_dialogs_for_discussion(DialogId dialog_id, bool is_suitable);
void set_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status,
Promise<Unit> &&promise);
void set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus status, Promise<Unit> &&promise);
void send_edit_chat_admin_query(ChatId chat_id, UserId user_id, bool is_administrator, Promise<Unit> &&promise);
void delete_chat_participant(ChatId chat_id, UserId user_id, bool revoke_messages, Promise<Unit> &&promise);
@ -1584,8 +1597,18 @@ class ContactsManager final : public Actor {
void add_channel_participant_to_cache(ChannelId channel_id, const DialogParticipant &dialog_participant,
bool allow_replace);
void update_channel_participant_status_cache(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus &&dialog_participant_status);
const DialogParticipant *get_channel_participant_from_cache(ChannelId channel_id, DialogId participant_dialog_id);
void set_chat_participant_status(ChatId chat_id, UserId user_id, DialogParticipantStatus status,
Promise<Unit> &&promise);
void set_channel_participant_status(ChannelId channel_id, DialogId participant_dialog_id,
td_api::object_ptr<td_api::ChatMemberStatus> &&chat_member_status,
Promise<Unit> &&promise);
void set_channel_participant_status_impl(ChannelId channel_id, DialogId participant_dialog_id,
DialogParticipantStatus status, DialogParticipantStatus old_status,
Promise<Unit> &&promise);

View File

@ -362,15 +362,11 @@ void CountryInfoManager::on_get_country_list(const string &language_code,
it->second->next_reload_time = max(Time::now() + Random::fast(60, 120), it->second->next_reload_time);
// if we have data for the language, then we don't need to fail promises
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
return;
}
}
for (auto &promise : promises) {
promise.set_error(r_country_list.error().clone());
}
fail_promises(promises, r_country_list.move_as_error());
return;
}
@ -379,9 +375,7 @@ void CountryInfoManager::on_get_country_list(const string &language_code,
on_get_country_list_impl(language_code, r_country_list.move_as_ok());
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
}
void CountryInfoManager::on_get_country_list_impl(const string &language_code,

View File

@ -22,6 +22,7 @@
#include "td/utils/format.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
@ -431,9 +432,13 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) {
}
info.state = TokenInfo::State::Sync;
} else {
int32 retry_after = 0;
if (r_flag.is_error()) {
if (!G()->is_expected_error(r_flag.error())) {
LOG(ERROR) << "Failed to " << info.state << " device: " << r_flag.error();
auto &error = r_flag.error();
if (!G()->is_expected_error(error)) {
LOG(ERROR) << "Failed to " << info.state << " device: " << error;
} else {
retry_after = Global::get_retry_after(error.code(), error.message());
}
info.promise.set_error(r_flag.move_as_error());
} else {
@ -441,7 +446,7 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) {
}
if (info.state == TokenInfo::State::Reregister) {
// keep trying to reregister the token
return loop();
return set_timeout_in(clamp(retry_after, 1, 3600));
} else if (info.state == TokenInfo::State::Register) {
info.state = TokenInfo::State::Unregister;
} else {

View File

@ -400,6 +400,8 @@ bool DialogAction::is_canceled_by_message_of_type(MessageContentType message_con
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return false;
default:
UNREACHABLE();

View File

@ -96,9 +96,8 @@ void DialogActionBar::fix(Td *td, DialogId dialog_id, bool is_dialog_blocked, Fo
}
}
if (can_invite_members_) {
if (dialog_type != DialogType::Chat &&
(dialog_type != DialogType::Channel || td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) ==
ContactsManager::ChannelType::Broadcast)) {
if (dialog_type != DialogType::Chat && (dialog_type != DialogType::Channel ||
td->contacts_manager_->is_broadcast_channel(dialog_id.get_channel_id()))) {
LOG(ERROR) << "Receive can_invite_members in " << dialog_id;
can_invite_members_ = false;
} else if (can_report_spam_ || can_add_contact_ || can_block_user_ || can_share_phone_number_ || can_unarchive_) {

View File

@ -18,6 +18,7 @@
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/MessageTtl.h"
#include "td/telegram/Photo.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
@ -65,7 +66,8 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
return td_api::make_object<td_api::chatEventMemberLeft>();
case telegram_api::channelAdminLogEventActionParticipantInvite::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantInvite>(action_ptr);
DialogParticipant dialog_participant(std::move(action->participant_));
DialogParticipant dialog_participant(std::move(action->participant_),
td->contacts_manager_->get_channel_type(channel_id));
if (!dialog_participant.is_valid() || dialog_participant.dialog_id_.get_type() != DialogType::User) {
LOG(ERROR) << "Wrong invite: " << dialog_participant;
return nullptr;
@ -77,8 +79,9 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionParticipantToggleBan::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantToggleBan>(action_ptr);
DialogParticipant old_dialog_participant(std::move(action->prev_participant_));
DialogParticipant new_dialog_participant(std::move(action->new_participant_));
auto channel_type = td->contacts_manager_->get_channel_type(channel_id);
DialogParticipant old_dialog_participant(std::move(action->prev_participant_), channel_type);
DialogParticipant new_dialog_participant(std::move(action->new_participant_), channel_type);
if (old_dialog_participant.dialog_id_ != new_dialog_participant.dialog_id_) {
LOG(ERROR) << old_dialog_participant.dialog_id_ << " VS " << new_dialog_participant.dialog_id_;
return nullptr;
@ -94,8 +97,9 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionParticipantToggleAdmin::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionParticipantToggleAdmin>(action_ptr);
DialogParticipant old_dialog_participant(std::move(action->prev_participant_));
DialogParticipant new_dialog_participant(std::move(action->new_participant_));
auto channel_type = td->contacts_manager_->get_channel_type(channel_id);
DialogParticipant old_dialog_participant(std::move(action->prev_participant_), channel_type);
DialogParticipant new_dialog_participant(std::move(action->new_participant_), channel_type);
if (old_dialog_participant.dialog_id_ != new_dialog_participant.dialog_id_) {
LOG(ERROR) << old_dialog_participant.dialog_id_ << " VS " << new_dialog_participant.dialog_id_;
return nullptr;
@ -136,8 +140,8 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
}
case telegram_api::channelAdminLogEventActionDefaultBannedRights::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionDefaultBannedRights>(action_ptr);
auto old_permissions = get_restricted_rights(std::move(action->prev_banned_rights_));
auto new_permissions = get_restricted_rights(std::move(action->new_banned_rights_));
auto old_permissions = RestrictedRights(action->prev_banned_rights_);
auto new_permissions = RestrictedRights(action->new_banned_rights_);
return td_api::make_object<td_api::chatEventPermissionsChanged>(old_permissions.get_chat_permissions_object(),
new_permissions.get_chat_permissions_object());
}

View File

@ -18,10 +18,50 @@
namespace td {
AdministratorRights::AdministratorRights(const tl_object_ptr<telegram_api::chatAdminRights> &rights,
ChannelType channel_type) {
if (rights == nullptr) {
flags_ = 0;
return;
}
if (!rights->other_) {
LOG(ERROR) << "Receive wrong other flag in " << to_string(rights);
}
*this =
AdministratorRights(rights->anonymous_, rights->other_, rights->change_info_, rights->post_messages_,
rights->edit_messages_, rights->delete_messages_, rights->invite_users_, rights->ban_users_,
rights->pin_messages_, rights->add_admins_, rights->manage_call_, channel_type);
}
AdministratorRights::AdministratorRights(const td_api::object_ptr<td_api::chatAdministratorRights> &rights,
ChannelType channel_type) {
if (rights == nullptr) {
flags_ = 0;
return;
}
*this = AdministratorRights(rights->is_anonymous_, rights->can_manage_chat_, rights->can_change_info_,
rights->can_post_messages_, rights->can_edit_messages_, rights->can_delete_messages_,
rights->can_invite_users_, rights->can_restrict_members_, rights->can_pin_messages_,
rights->can_promote_members_, rights->can_manage_video_chats_, channel_type);
}
AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dialog, bool can_change_info,
bool can_post_messages, bool can_edit_messages, bool can_delete_messages,
bool can_invite_users, bool can_restrict_members, bool can_pin_messages,
bool can_promote_members, bool can_manage_calls) {
bool can_promote_members, bool can_manage_calls, ChannelType channel_type) {
switch (channel_type) {
case ChannelType::Broadcast:
can_pin_messages = false;
is_anonymous = false;
break;
case ChannelType::Megagroup:
can_post_messages = false;
can_edit_messages = false;
break;
case ChannelType::Unknown:
break;
}
flags_ = (static_cast<uint32>(can_manage_dialog) * CAN_MANAGE_DIALOG) |
(static_cast<uint32>(can_change_info) * CAN_CHANGE_INFO_AND_SETTINGS) |
(static_cast<uint32>(can_post_messages) * CAN_POST_MESSAGES) |
@ -35,6 +75,9 @@ AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dial
(static_cast<uint32>(is_anonymous) * IS_ANONYMOUS);
if (flags_ != 0) {
flags_ |= CAN_MANAGE_DIALOG;
if (channel_type == ChannelType::Broadcast) {
flags_ |= CAN_RESTRICT_MEMBERS;
}
}
}
@ -79,6 +122,13 @@ telegram_api::object_ptr<telegram_api::chatAdminRights> AdministratorRights::get
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/);
}
td_api::object_ptr<td_api::chatAdministratorRights> AdministratorRights::get_chat_administrator_rights_object() const {
return td_api::make_object<td_api::chatAdministratorRights>(
can_manage_dialog(), can_change_info_and_settings(), can_post_messages(), can_edit_messages(),
can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), can_promote_members(),
can_manage_calls(), is_anonymous());
}
bool operator==(const AdministratorRights &lhs, const AdministratorRights &rhs) {
return lhs.flags_ == rhs.flags_;
}
@ -125,6 +175,38 @@ StringBuilder &operator<<(StringBuilder &string_builder, const AdministratorRigh
return string_builder;
}
RestrictedRights::RestrictedRights(const tl_object_ptr<telegram_api::chatBannedRights> &rights) {
if (rights == nullptr) {
flags_ = 0;
return;
}
if (rights->view_messages_) {
LOG(ERROR) << "Can't view messages in banned rights " << to_string(rights);
}
LOG_IF(ERROR, rights->until_date_ != std::numeric_limits<int32>::max())
<< "Have until date " << rights->until_date_ << " in restricted rights";
*this = RestrictedRights(!rights->send_messages_, !rights->send_media_, !rights->send_stickers_, !rights->send_gifs_,
!rights->send_games_, !rights->send_inline_, !rights->embed_links_, !rights->send_polls_,
!rights->change_info_, !rights->invite_users_, !rights->pin_messages_);
}
RestrictedRights::RestrictedRights(const td_api::object_ptr<td_api::chatPermissions> &rights) {
if (rights == nullptr) {
flags_ = 0;
return;
}
bool can_send_polls = rights->can_send_polls_;
bool can_send_media = rights->can_send_media_messages_;
bool can_send_messages = rights->can_send_messages_ || can_send_media || can_send_polls ||
rights->can_send_other_messages_ || rights->can_add_web_page_previews_;
*this = RestrictedRights(can_send_messages, can_send_media, rights->can_send_other_messages_,
rights->can_send_other_messages_, rights->can_send_other_messages_,
rights->can_send_other_messages_, rights->can_add_web_page_previews_, can_send_polls,
rights->can_change_info_, rights->can_invite_users_, rights->can_pin_messages_);
}
RestrictedRights::RestrictedRights(bool can_send_messages, bool can_send_media, bool can_send_stickers,
bool can_send_animations, bool can_send_games, bool can_use_inline_bots,
bool can_add_web_page_previews, bool can_send_polls,
@ -270,18 +352,6 @@ DialogParticipantStatus DialogParticipantStatus::Administrator(AdministratorRigh
std::move(rank));
}
DialogParticipantStatus DialogParticipantStatus::Administrator(bool is_anonymous, string &&rank, bool can_be_edited,
bool can_manage_dialog, bool can_change_info,
bool can_post_messages, bool can_edit_messages,
bool can_delete_messages, bool can_invite_users,
bool can_restrict_members, bool can_pin_messages,
bool can_promote_members, bool can_manage_calls) {
auto administrator_rights = AdministratorRights(
is_anonymous, can_manage_dialog, can_change_info, can_post_messages, can_edit_messages, can_delete_messages,
can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_manage_calls);
return Administrator(administrator_rights, std::move(rank), can_be_edited);
}
DialogParticipantStatus DialogParticipantStatus::Member() {
return DialogParticipantStatus(Type::Member, IS_MEMBER | RestrictedRights::ALL_RESTRICTED_RIGHTS, 0, string());
}
@ -305,23 +375,24 @@ DialogParticipantStatus DialogParticipantStatus::Banned(int32 banned_until_date)
}
DialogParticipantStatus DialogParticipantStatus::GroupAdministrator(bool is_creator) {
return Administrator(false, string(), is_creator, true, true, false, false, true, true, true, true, false, true);
return Administrator(
AdministratorRights(false, true, true, false, false, true, true, true, true, false, true, ChannelType::Unknown),
string(), is_creator);
}
DialogParticipantStatus DialogParticipantStatus::ChannelAdministrator(bool is_creator, bool is_megagroup) {
if (is_megagroup) {
return Administrator(false, string(), is_creator, true, true, false, false, true, true, true, true, false, false);
} else {
return Administrator(false, string(), is_creator, true, false, true, true, true, false, true, false, false, false);
}
auto rights = is_megagroup ? AdministratorRights(false, true, true, false, false, true, true, true, true, false,
false, ChannelType::Megagroup)
: AdministratorRights(false, true, false, true, true, true, false, true, false, false,
false, ChannelType::Broadcast);
return Administrator(rights, string(), is_creator);
}
DialogParticipantStatus::DialogParticipantStatus(bool can_be_edited,
tl_object_ptr<telegram_api::chatAdminRights> &&admin_rights,
string rank) {
string rank, ChannelType channel_type) {
CHECK(admin_rights != nullptr);
uint32 flags =
::td::get_administrator_rights(std::move(admin_rights)).flags_ | AdministratorRights::CAN_MANAGE_DIALOG;
uint32 flags = AdministratorRights(admin_rights, channel_type).flags_ | AdministratorRights::CAN_MANAGE_DIALOG;
if (can_be_edited) {
flags |= CAN_BE_EDITED;
}
@ -339,8 +410,7 @@ DialogParticipantStatus::DialogParticipantStatus(bool is_member,
auto until_date = fix_until_date(banned_rights->until_date_);
banned_rights->until_date_ = std::numeric_limits<int32>::max();
uint32 flags =
::td::get_restricted_rights(std::move(banned_rights)).flags_ | (static_cast<uint32>(is_member) * IS_MEMBER);
uint32 flags = RestrictedRights(banned_rights).flags_ | (static_cast<uint32>(is_member) * IS_MEMBER);
*this = DialogParticipantStatus(Type::Restricted, flags, until_date, string());
}
@ -356,9 +426,7 @@ tl_object_ptr<td_api::ChatMemberStatus> DialogParticipantStatus::get_chat_member
return td_api::make_object<td_api::chatMemberStatusCreator>(rank_, is_anonymous(), is_member());
case Type::Administrator:
return td_api::make_object<td_api::chatMemberStatusAdministrator>(
rank_, can_be_edited(), can_manage_dialog(), can_change_info_and_settings(), can_post_messages(),
can_edit_messages(), can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(),
can_promote_members(), can_manage_calls(), is_anonymous());
rank_, can_be_edited(), get_administrator_rights().get_chat_administrator_rights_object());
case Type::Member:
return td_api::make_object<td_api::chatMemberStatusMember>();
case Type::Restricted:
@ -498,7 +566,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant
}
}
DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr<td_api::ChatMemberStatus> &status) {
DialogParticipantStatus get_dialog_participant_status(const td_api::object_ptr<td_api::ChatMemberStatus> &status,
ChannelType channel_type) {
auto constructor_id = status == nullptr ? td_api::chatMemberStatusMember::ID : status->get_id();
switch (constructor_id) {
case td_api::chatMemberStatusCreator::ID: {
@ -515,18 +584,14 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr<td_api
if (!clean_input_string(custom_title)) {
custom_title.clear();
}
AdministratorRights administrator_rights(st->is_anonymous_, st->can_manage_chat_, st->can_change_info_,
st->can_post_messages_, st->can_edit_messages_, st->can_delete_messages_,
st->can_invite_users_, st->can_restrict_members_, st->can_pin_messages_,
st->can_promote_members_, st->can_manage_video_chats_);
return DialogParticipantStatus::Administrator(administrator_rights, std::move(custom_title),
true /*st->can_be_edited_*/);
return DialogParticipantStatus::Administrator(AdministratorRights(st->rights_, channel_type),
std::move(custom_title), true /*st->can_be_edited_*/);
}
case td_api::chatMemberStatusMember::ID:
return DialogParticipantStatus::Member();
case td_api::chatMemberStatusRestricted::ID: {
auto st = static_cast<const td_api::chatMemberStatusRestricted *>(status.get());
return DialogParticipantStatus::Restricted(::td::get_restricted_rights(st->permissions_), st->is_member_,
return DialogParticipantStatus::Restricted(RestrictedRights(st->permissions_), st->is_member_,
st->restricted_until_date_);
}
case td_api::chatMemberStatusLeft::ID:
@ -541,52 +606,6 @@ DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr<td_api
}
}
AdministratorRights get_administrator_rights(tl_object_ptr<telegram_api::chatAdminRights> &&admin_rights) {
if (admin_rights == nullptr) {
return AdministratorRights(false, false, false, false, false, false, false, false, false, false, false);
}
if (!admin_rights->other_) {
LOG(ERROR) << "Receive wrong other flag in " << to_string(admin_rights);
}
return AdministratorRights(admin_rights->anonymous_, admin_rights->other_, admin_rights->change_info_,
admin_rights->post_messages_, admin_rights->edit_messages_, admin_rights->delete_messages_,
admin_rights->invite_users_, admin_rights->ban_users_, admin_rights->pin_messages_,
admin_rights->add_admins_, admin_rights->manage_call_);
}
RestrictedRights get_restricted_rights(tl_object_ptr<telegram_api::chatBannedRights> &&banned_rights) {
if (banned_rights == nullptr) {
return RestrictedRights(false, false, false, false, false, false, false, false, false, false, false);
}
if (banned_rights->view_messages_) {
LOG(ERROR) << "Can't view messages in banned rights " << to_string(banned_rights);
}
LOG_IF(ERROR, banned_rights->until_date_ != std::numeric_limits<int32>::max())
<< "Have until date " << banned_rights->until_date_ << " in restricted rights";
return RestrictedRights(!banned_rights->send_messages_, !banned_rights->send_media_, !banned_rights->send_stickers_,
!banned_rights->send_gifs_, !banned_rights->send_games_, !banned_rights->send_inline_,
!banned_rights->embed_links_, !banned_rights->send_polls_, !banned_rights->change_info_,
!banned_rights->invite_users_, !banned_rights->pin_messages_);
}
RestrictedRights get_restricted_rights(const td_api::object_ptr<td_api::chatPermissions> &permissions) {
if (permissions == nullptr) {
return RestrictedRights(false, false, false, false, false, false, false, false, false, false, false);
}
bool can_send_polls = permissions->can_send_polls_;
bool can_send_media = permissions->can_send_media_messages_;
bool can_send_messages = permissions->can_send_messages_ || can_send_media || can_send_polls ||
permissions->can_send_other_messages_ || permissions->can_add_web_page_previews_;
return RestrictedRights(can_send_messages, can_send_media, permissions->can_send_other_messages_,
permissions->can_send_other_messages_, permissions->can_send_other_messages_,
permissions->can_send_other_messages_, permissions->can_add_web_page_previews_,
can_send_polls, permissions->can_change_info_, permissions->can_invite_users_,
permissions->can_pin_messages_);
}
DialogParticipant::DialogParticipant(DialogId dialog_id, UserId inviter_user_id, int32 joined_date,
DialogParticipantStatus status)
: dialog_id_(dialog_id), inviter_user_id_(inviter_user_id), joined_date_(joined_date), status_(std::move(status)) {
@ -626,7 +645,8 @@ DialogParticipant::DialogParticipant(tl_object_ptr<telegram_api::ChatParticipant
}
}
DialogParticipant::DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr) {
DialogParticipant::DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr,
ChannelType channel_type) {
CHECK(participant_ptr != nullptr);
switch (participant_ptr->get_id()) {
@ -653,7 +673,7 @@ DialogParticipant::DialogParticipant(tl_object_ptr<telegram_api::ChannelParticip
auto participant = move_tl_object_as<telegram_api::channelParticipantAdmin>(participant_ptr);
*this = {DialogId(UserId(participant->user_id_)), UserId(participant->promoted_by_), participant->date_,
DialogParticipantStatus(participant->can_edit_, std::move(participant->admin_rights_),
std::move(participant->rank_))};
std::move(participant->rank_), channel_type)};
break;
}
case telegram_api::channelParticipantLeft::ID: {

View File

@ -6,8 +6,8 @@
//
#pragma once
#include "td/telegram/ChannelType.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
@ -43,17 +43,27 @@ class AdministratorRights {
friend class DialogParticipantStatus;
explicit AdministratorRights(int32 flags) : flags_(flags & ALL_ADMINISTRATOR_RIGHTS) {
explicit AdministratorRights(int32 flags) : flags_(flags & (ALL_ADMINISTRATOR_RIGHTS | IS_ANONYMOUS)) {
}
public:
AdministratorRights() : flags_(0) {
}
AdministratorRights(const tl_object_ptr<telegram_api::chatAdminRights> &admin_rights, ChannelType channel_type);
AdministratorRights(const td_api::object_ptr<td_api::chatAdministratorRights> &administrator_rights,
ChannelType channel_type);
AdministratorRights(bool is_anonymous, bool can_manage_dialog, bool can_change_info, bool can_post_messages,
bool can_edit_messages, bool can_delete_messages, bool can_invite_users,
bool can_restrict_members, bool can_pin_messages, bool can_promote_members,
bool can_manage_calls);
bool can_restrict_members, bool can_pin_messages, bool can_promote_members, bool can_manage_calls,
ChannelType channel_type);
telegram_api::object_ptr<telegram_api::chatAdminRights> get_chat_admin_rights() const;
td_api::object_ptr<td_api::chatAdministratorRights> get_chat_administrator_rights_object() const;
bool can_manage_dialog() const {
return (flags_ & CAN_MANAGE_DIALOG) != 0;
}
@ -152,6 +162,10 @@ class RestrictedRights {
}
public:
explicit RestrictedRights(const tl_object_ptr<telegram_api::chatBannedRights> &rights);
explicit RestrictedRights(const td_api::object_ptr<td_api::chatPermissions> &rights);
RestrictedRights(bool can_send_messages, bool can_send_media, bool can_send_stickers, bool can_send_animations,
bool can_send_games, bool can_use_inline_bots, bool can_add_web_page_previews, bool can_send_polls,
bool can_change_info_and_settings, bool can_invite_users, bool can_pin_messages);
@ -261,12 +275,6 @@ class DialogParticipantStatus {
static DialogParticipantStatus Administrator(AdministratorRights administrator_rights, string &&rank,
bool can_be_edited);
static DialogParticipantStatus Administrator(bool is_anonymous, string &&rank, bool can_be_edited,
bool can_manage_dialog, bool can_change_info, bool can_post_messages,
bool can_edit_messages, bool can_delete_messages, bool can_invite_users,
bool can_restrict_members, bool can_pin_messages,
bool can_promote_members, bool can_manage_calls);
static DialogParticipantStatus Member();
static DialogParticipantStatus Restricted(RestrictedRights restricted_rights, bool is_member,
@ -282,8 +290,11 @@ class DialogParticipantStatus {
// legacy rights
static DialogParticipantStatus ChannelAdministrator(bool is_creator, bool is_megagroup);
DialogParticipantStatus(bool can_be_edited, tl_object_ptr<telegram_api::chatAdminRights> &&admin_rights, string rank);
// forcely returns an administrator
DialogParticipantStatus(bool can_be_edited, tl_object_ptr<telegram_api::chatAdminRights> &&admin_rights, string rank,
ChannelType channel_type);
// forcely returns a restricted or banned
DialogParticipantStatus(bool is_member, tl_object_ptr<telegram_api::chatBannedRights> &&banned_rights);
RestrictedRights get_effective_restricted_rights() const;
@ -317,7 +328,7 @@ class DialogParticipantStatus {
}
bool can_delete_messages() const {
return get_administrator_rights().can_edit_messages();
return get_administrator_rights().can_delete_messages();
}
bool can_invite_users() const {
@ -489,7 +500,7 @@ struct DialogParticipant {
DialogParticipant(tl_object_ptr<telegram_api::ChatParticipant> &&participant_ptr, int32 chat_creation_date,
bool is_creator);
explicit DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr);
DialogParticipant(tl_object_ptr<telegram_api::ChannelParticipant> &&participant_ptr, ChannelType channel_type);
static DialogParticipant left(DialogId dialog_id) {
return {dialog_id, UserId(), 0, DialogParticipantStatus::Left()};
@ -539,12 +550,7 @@ struct DialogParticipants {
td_api::object_ptr<td_api::chatMembers> get_chat_members_object(Td *td) const;
};
DialogParticipantStatus get_dialog_participant_status(const tl_object_ptr<td_api::ChatMemberStatus> &status);
AdministratorRights get_administrator_rights(tl_object_ptr<telegram_api::chatAdminRights> &&admin_rights);
RestrictedRights get_restricted_rights(tl_object_ptr<telegram_api::chatBannedRights> &&banned_rights);
RestrictedRights get_restricted_rights(const td_api::object_ptr<td_api::chatPermissions> &permissions);
DialogParticipantStatus get_dialog_participant_status(const td_api::object_ptr<td_api::ChatMemberStatus> &status,
ChannelType channel_type);
} // namespace td

View File

@ -7,6 +7,7 @@
#include "td/telegram/DialogParticipantFilter.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/Td.h"
@ -34,7 +35,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipant
}
}
DialogParticipantFilter::DialogParticipantFilter(const tl_object_ptr<td_api::ChatMembersFilter> &filter) {
DialogParticipantFilter::DialogParticipantFilter(const td_api::object_ptr<td_api::ChatMembersFilter> &filter) {
if (filter == nullptr) {
type_ = Type::Members;
return;

View File

@ -6,11 +6,8 @@
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
@ -28,7 +25,7 @@ class DialogParticipantFilter {
friend StringBuilder &operator<<(StringBuilder &string_builder, const DialogParticipantFilter &filter);
public:
explicit DialogParticipantFilter(const tl_object_ptr<td_api::ChatMembersFilter> &filter);
explicit DialogParticipantFilter(const td_api::object_ptr<td_api::ChatMembersFilter> &filter);
td_api::object_ptr<td_api::SupergroupMembersFilter> get_supergroup_members_filter_object(const string &query) const;

View File

@ -16,7 +16,6 @@
#include "td/telegram/files/FileType.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/StickerFormat.h"
@ -66,7 +65,8 @@ tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId fil
Document DocumentsManager::on_get_document(RemoteDocument remote_document, DialogId owner_dialog_id,
MultiPromiseActor *load_data_multipromise_ptr,
Document::Type default_document_type, bool is_background, bool is_pattern) {
Document::Type default_document_type, bool is_background, bool is_pattern,
bool is_ringtone) {
tl_object_ptr<telegram_api::documentAttributeAnimated> animated;
tl_object_ptr<telegram_api::documentAttributeVideo> video;
tl_object_ptr<telegram_api::documentAttributeAudio> audio;
@ -225,10 +225,20 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
}
}
if (is_ringtone) {
if (document_type != Document::Type::Audio) {
LOG(ERROR) << "Receive notification tone of type " << document_type;
document_type = Document::Type::Audio;
}
file_type = FileType::Ringtone;
default_extension = Slice("mp3");
}
int64 id;
int64 access_hash;
int32 dc_id;
int32 size;
int32 date = 0;
string mime_type;
string file_reference;
string minithumbnail;
@ -263,6 +273,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
access_hash = document->access_hash_;
dc_id = document->dc_id_;
size = document->size_;
if (is_ringtone) {
date = document->date_;
}
mime_type = std::move(document->mime_type_);
file_reference = document->file_reference_.as_slice().str();
@ -452,7 +465,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
performer = std::move(audio->performer_);
}
td_->audios_manager_->create_audio(file_id, std::move(minithumbnail), std::move(thumbnail), std::move(file_name),
std::move(mime_type), duration, std::move(title), std::move(performer),
std::move(mime_type), duration, std::move(title), std::move(performer), date,
!is_web);
break;
}

View File

@ -10,7 +10,8 @@
#include "td/telegram/Document.h"
#include "td/telegram/EncryptedFile.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/td_api.h"
@ -81,7 +82,7 @@ class DocumentsManager {
Document on_get_document(RemoteDocument remote_document, DialogId owner_dialog_id,
MultiPromiseActor *load_data_multipromise_ptr = nullptr,
Document::Type default_document_type = Document::Type::General, bool is_background = false,
bool is_pattern = false);
bool is_pattern = false, bool is_ringtone = false);
void create_document(FileId file_id, string minithumbnail, PhotoSize thumbnail, string file_name, string mime_type,
bool replace);

View File

@ -9,7 +9,7 @@
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PhotoSize.hpp"
#include "td/telegram/Version.h"
#include "td/utils/logging.h"

View File

@ -335,6 +335,7 @@ class DownloadManagerImpl final : public DownloadManager {
return;
}
LOG(INFO) << "File " << file_id << " was viewed from " << file_source_id;
auto r_file_info_ptr = get_file_info(file_id, file_source_id);
if (r_file_info_ptr.is_error()) {
return;
@ -760,6 +761,7 @@ class DownloadManagerImpl final : public DownloadManager {
return;
}
LOG(INFO) << "Mark download " << download_id << " as viewed";
unviewed_completed_download_ids_.erase(download_id);
if (unviewed_completed_download_ids_.empty()) {
clear_counters();

View File

@ -13,6 +13,7 @@
#include "td/telegram/files/FileManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/NotificationSettingsManager.h"
#include "td/telegram/StickerSetId.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/ConfigShared.h"
@ -61,6 +62,7 @@ fileSourceBackground background_id:int64 access_hash:int64 = FileSource; // repa
fileSourceBasicGroupFull basic_group_id:int32 = FileSource; // repaired with messages.getFullChat
fileSourceSupergroupFull supergroup_id:int32 = FileSource; // repaired with messages.getFullChannel
fileSourceAppConfig = FileSource; // repaired with help.getAppConfig, not reliable
fileSourceSavedRingtones = FileSource; // repaired with account.getSavedRingtones
*/
FileSourceId FileReferenceManager::get_current_file_source_id() const {
@ -132,6 +134,11 @@ bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_sou
return is_added;
}
FileSourceId FileReferenceManager::create_saved_ringtones_file_source() {
FileSourceSavedRingtones source;
return add_file_source_id(source, "saved notification sounds");
}
bool FileReferenceManager::remove_file_source(NodeId node_id, FileSourceId file_source_id) {
CHECK(node_id.is_valid());
bool is_removed = nodes_[node_id].file_source_ids.remove(file_source_id);
@ -306,6 +313,10 @@ void FileReferenceManager::send_query(Destination dest, FileSourceId file_source
},
[&](const FileSourceAppConfig &source) {
send_closure_later(G()->config_manager(), &ConfigManager::reget_app_config, std::move(promise));
},
[&](const FileSourceSavedRingtones &source) {
send_closure_later(G()->notification_settings_manager(), &NotificationSettingsManager::repair_saved_ringtones,
std::move(promise));
}));
}

View File

@ -52,6 +52,7 @@ class FileReferenceManager final : public Actor {
FileSourceId create_chat_full_file_source(ChatId chat_id);
FileSourceId create_channel_full_file_source(ChannelId channel_id);
FileSourceId create_app_config_file_source();
FileSourceId create_saved_ringtones_file_source();
using NodeId = FileId;
void repair_file_reference(NodeId node_id, Promise<> promise);
@ -146,12 +147,15 @@ class FileReferenceManager final : public Actor {
struct FileSourceAppConfig {
// empty
};
struct FileSourceSavedRingtones {
// empty
};
// append only
using FileSource =
Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto, FileSourceWallpapers,
FileSourceWebPage, FileSourceSavedAnimations, FileSourceRecentStickers, FileSourceFavoriteStickers,
FileSourceBackground, FileSourceChatFull, FileSourceChannelFull, FileSourceAppConfig>;
using FileSource = Variant<FileSourceMessage, FileSourceUserPhoto, FileSourceChatPhoto, FileSourceChannelPhoto,
FileSourceWallpapers, FileSourceWebPage, FileSourceSavedAnimations,
FileSourceRecentStickers, FileSourceFavoriteStickers, FileSourceBackground,
FileSourceChatFull, FileSourceChannelFull, FileSourceAppConfig, FileSourceSavedRingtones>;
vector<FileSource> file_sources_;
int64 query_generation_{0};

View File

@ -15,6 +15,7 @@
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/NotificationSettingsManager.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/UserId.h"
@ -50,7 +51,7 @@ void FileReferenceManager::store_file_source(FileSourceId file_source_id, Storer
},
[&](const FileSourceChatFull &source) { td::store(source.chat_id, storer); },
[&](const FileSourceChannelFull &source) { td::store(source.channel_id, storer); },
[&](const FileSourceAppConfig &source) {}));
[&](const FileSourceAppConfig &source) {}, [&](const FileSourceSavedRingtones &source) {}));
}
template <class ParserT>
@ -114,6 +115,8 @@ FileSourceId FileReferenceManager::parse_file_source(Td *td, ParserT &parser) {
}
case 12:
return td->stickers_manager_->get_app_config_file_source_id();
case 13:
return td->notification_settings_manager_->get_saved_ringtones_file_source_id();
default:
parser.set_error("Invalid type in FileSource");
return FileSourceId();

View File

@ -120,9 +120,7 @@ Result<Game> process_input_message_game(const ContactsManager *contacts_manager,
auto input_message_game = move_tl_object_as<td_api::inputMessageGame>(input_message_content);
UserId bot_user_id(input_message_game->bot_user_id_);
if (!contacts_manager->have_input_user(bot_user_id)) {
return Status::Error(400, "Game owner bot is not accessible");
}
TRY_STATUS(contacts_manager->get_input_user(bot_user_id));
if (!clean_input_string(input_message_game->game_short_name_)) {
return Status::Error(400, "Game short name must be encoded in UTF-8");

View File

@ -135,6 +135,23 @@ Status Global::init(const TdParameters &parameters, ActorId<Td> td, unique_ptr<T
return Status::OK();
}
int32 Global::get_retry_after(int32 error_code, Slice error_message) {
if (error_code != 429) {
return 0;
}
Slice retry_after_prefix("Too Many Requests: retry after ");
if (!begins_with(error_message, retry_after_prefix)) {
return 0;
}
auto r_retry_after = to_integer_safe<int32>(error_message.substr(retry_after_prefix.size()));
if (r_retry_after.is_ok() && r_retry_after.ok() > 0) {
return r_retry_after.ok();
}
return 0;
}
int32 Global::to_unix_time(double server_time) const {
LOG_CHECK(1.0 <= server_time && server_time <= 2140000000.0)
<< server_time << ' ' << Clocks::system() << ' ' << is_server_time_reliable() << ' '

View File

@ -48,6 +48,7 @@ class MessagesManager;
class MtprotoHeader;
class NetQueryDispatcher;
class NotificationManager;
class NotificationSettingsManager;
class OptionManager;
class PasswordManager;
class SecretChatsManager;
@ -321,6 +322,13 @@ class Global final : public ActorContext {
notification_manager_ = notification_manager;
}
ActorId<NotificationSettingsManager> notification_settings_manager() const {
return notification_settings_manager_;
}
void set_notification_settings_manager(ActorId<NotificationSettingsManager> notification_settings_manager) {
notification_settings_manager_ = notification_settings_manager;
}
ActorId<OptionManager> option_manager() const {
return option_manager_;
}
@ -478,6 +486,8 @@ class Global final : public ActorContext {
return close_flag();
}
static int32 get_retry_after(int32 error_code, Slice error_message);
const std::vector<std::shared_ptr<NetStatsCallback>> &get_net_stats_file_callbacks() {
return net_stats_file_callbacks_;
}
@ -513,6 +523,7 @@ class Global final : public ActorContext {
ActorId<LinkManager> link_manager_;
ActorId<MessagesManager> messages_manager_;
ActorId<NotificationManager> notification_manager_;
ActorId<NotificationSettingsManager> notification_settings_manager_;
ActorId<OptionManager> option_manager_;
ActorId<PasswordManager> password_manager_;
ActorId<SecretChatsManager> secret_chats_manager_;

View File

@ -1474,9 +1474,7 @@ void GroupCallManager::finish_get_group_call(InputGroupCallId input_group_call_i
}
if (result.is_error()) {
for (auto &promise : promises) {
promise.set_error(result.error().clone());
}
fail_promises(promises, result.move_as_error());
return;
}
@ -2951,16 +2949,10 @@ void GroupCallManager::process_group_call_after_join_requests(InputGroupCallId i
return;
}
auto promises = std::move(group_call->after_join);
reset_to_empty(group_call->after_join);
if (!group_call->is_active || !group_call->is_joined) {
for (auto &promise : promises) {
promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING"));
}
fail_promises(group_call->after_join, Status::Error(400, "GROUPCALL_JOIN_MISSING"));
} else {
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(group_call->after_join);
}
}
@ -3778,6 +3770,7 @@ void GroupCallManager::toggle_group_call_participant_is_muted(GroupCallId group_
promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
promise = Promise<Unit>();
}
send_closure(actor_id, &GroupCallManager::on_toggle_group_call_participant_is_muted, input_group_call_id, dialog_id,
generation, std::move(promise));
@ -3873,6 +3866,7 @@ void GroupCallManager::set_group_call_participant_volume_level(GroupCallId group
promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
promise = Promise<Unit>();
}
send_closure(actor_id, &GroupCallManager::on_set_group_call_participant_volume_level, input_group_call_id,
dialog_id, generation, std::move(promise));
@ -3972,6 +3966,7 @@ void GroupCallManager::toggle_group_call_participant_is_hand_raised(GroupCallId
promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
promise = Promise<Unit>();
}
send_closure(actor_id, &GroupCallManager::on_toggle_group_call_participant_is_hand_raised, input_group_call_id,
dialog_id, generation, std::move(promise));
@ -4344,10 +4339,7 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
// never update ended calls
} else if (!call.is_active) {
// always update to an ended call, droping also is_joined, is_speaking and other local flags
auto promises = std::move(group_call->after_join);
for (auto &promise : promises) {
promise.set_error(Status::Error(400, "Group call ended"));
}
fail_promises(group_call->after_join, Status::Error(400, "Group call ended"));
*group_call = std::move(call);
need_update = true;
} else {

View File

@ -29,6 +29,8 @@
#include "td/telegram/net/DcId.h"
#include "td/telegram/Payments.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/ReplyMarkup.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
@ -36,6 +38,8 @@
#include "td/telegram/TdDb.h"
#include "td/telegram/TdParameters.h"
#include "td/telegram/telegram_api.hpp"
#include "td/telegram/ThemeManager.h"
#include "td/telegram/UpdatesManager.h"
#include "td/telegram/Venue.h"
#include "td/telegram/VideosManager.h"
#include "td/telegram/VoiceNotesManager.h"
@ -46,6 +50,7 @@
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Time.h"
@ -152,7 +157,7 @@ class SetInlineBotResultsQuery final : public Td::ResultHandler {
bool result = result_ptr.ok();
if (!result) {
LOG(INFO) << "Sending answer to an inline query has failed";
LOG(ERROR) << "Sending answer to an inline query has failed";
}
promise_.set_value(Unit());
}
@ -162,6 +167,102 @@ class SetInlineBotResultsQuery final : public Td::ResultHandler {
}
};
class RequestSimpleWebViewQuery final : public Td::ResultHandler {
Promise<string> promise_;
public:
explicit RequestSimpleWebViewQuery(Promise<string> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::InputUser> &&input_user, const string &url,
const td_api::object_ptr<td_api::themeParameters> &theme) {
tl_object_ptr<telegram_api::dataJSON> theme_parameters;
int32 flags = 0;
if (theme != nullptr) {
flags |= telegram_api::messages_requestSimpleWebView::THEME_PARAMS_MASK;
theme_parameters = make_tl_object<telegram_api::dataJSON>(string());
theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false);
}
send_query(G()->net_query_creator().create(
telegram_api::messages_requestSimpleWebView(flags, std::move(input_user), url, std::move(theme_parameters))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_requestSimpleWebView>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for RequestSimpleWebViewQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr->url_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class SendWebViewDataQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SendWebViewDataQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::InputUser> &&input_user, int64 random_id, const string &button_text,
const string &data) {
send_query(G()->net_query_creator().create(
telegram_api::messages_sendWebViewData(std::move(input_user), random_id, button_text, data)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_sendWebViewData>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for SendWebViewDataQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class SendWebViewResultMessageQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::sentWebAppMessage>> promise_;
public:
explicit SendWebViewResultMessageQuery(Promise<td_api::object_ptr<td_api::sentWebAppMessage>> &&promise)
: promise_(std::move(promise)) {
}
void send(const string &bot_query_id, tl_object_ptr<telegram_api::InputBotInlineResult> &&result) {
send_query(G()->net_query_creator().create(
telegram_api::messages_sendWebViewResultMessage(bot_query_id, std::move(result))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::messages_sendWebViewResultMessage>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for SendWebViewResultMessageQuery: " << to_string(ptr);
promise_.set_value(td_api::make_object<td_api::sentWebAppMessage>(
InlineQueriesManager::get_inline_message_id(std::move(ptr->msg_id_))));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
InlineQueriesManager::InlineQueriesManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
drop_inline_query_result_timeout_.set_callback(on_drop_inline_query_result_timeout_callback);
drop_inline_query_result_timeout_.set_callback_data(static_cast<void *>(this));
@ -368,9 +469,7 @@ void InlineQueriesManager::answer_inline_query(
int64 inline_query_id, bool is_personal, vector<td_api::object_ptr<td_api::InputInlineQueryResult>> &&input_results,
int32 cache_time, const string &next_offset, const string &switch_pm_text, const string &switch_pm_parameter,
Promise<Unit> &&promise) const {
if (!td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Method can be used by bots only"));
}
CHECK(td_->auth_manager_->is_bot());
if (!switch_pm_text.empty()) {
if (switch_pm_parameter.empty()) {
@ -399,6 +498,40 @@ void InlineQueriesManager::answer_inline_query(
switch_pm_text, switch_pm_parameter);
}
void InlineQueriesManager::get_simple_web_view_url(UserId bot_user_id, string &&url,
const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<string> &&promise) {
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
TRY_RESULT_PROMISE(promise, bot_data, td_->contacts_manager_->get_bot_data(bot_user_id));
td_->create_handler<RequestSimpleWebViewQuery>(std::move(promise))->send(std::move(input_user), url, theme);
}
void InlineQueriesManager::send_web_view_data(UserId bot_user_id, string &&button_text, string &&data,
Promise<Unit> &&promise) const {
TRY_RESULT_PROMISE(promise, bot_data, td_->contacts_manager_->get_bot_data(bot_user_id));
int64 random_id;
do {
random_id = Random::secure_int64();
} while (random_id == 0);
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
td_->create_handler<SendWebViewDataQuery>(std::move(promise))
->send(std::move(input_user), random_id, button_text, data);
}
void InlineQueriesManager::answer_web_view_query(
const string &web_view_query_id, td_api::object_ptr<td_api::InputInlineQueryResult> &&input_result,
Promise<td_api::object_ptr<td_api::sentWebAppMessage>> &&promise) const {
CHECK(td_->auth_manager_->is_bot());
TRY_RESULT_PROMISE(promise, result, get_input_bot_inline_result(std::move(input_result), nullptr, nullptr));
td_->create_handler<SendWebViewResultMessageQuery>(std::move(promise))->send(web_view_query_id, std::move(result));
}
Result<tl_object_ptr<telegram_api::InputBotInlineResult>> InlineQueriesManager::get_input_bot_inline_result(
td_api::object_ptr<td_api::InputInlineQueryResult> &&result, bool *is_gallery, bool *force_vertical) const {
if (result == nullptr) {
@ -1332,8 +1465,7 @@ void InlineQueriesManager::on_get_inline_query_results(DialogId dialog_id, UserI
break;
}
if (dialog_type == DialogType::Channel &&
td_->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) ==
ContactsManager::ChannelType::Broadcast) {
td_->contacts_manager_->is_broadcast_channel(dialog_id.get_channel_id())) {
continue;
}
if (dialog_type == DialogType::SecretChat) {

View File

@ -47,6 +47,15 @@ class InlineQueriesManager final : public Actor {
const string &next_offset, const string &switch_pm_text, const string &switch_pm_parameter,
Promise<Unit> &&promise) const;
void get_simple_web_view_url(UserId bot_user_id, string &&url,
const td_api::object_ptr<td_api::themeParameters> &theme, Promise<string> &&promise);
void send_web_view_data(UserId bot_user_id, string &&button_text, string &&data, Promise<Unit> &&promise) const;
void answer_web_view_query(const string &web_view_query_id,
td_api::object_ptr<td_api::InputInlineQueryResult> &&input_result,
Promise<td_api::object_ptr<td_api::sentWebAppMessage>> &&promise) const;
uint64 send_inline_query(UserId bot_user_id, DialogId dialog_id, Location user_location, const string &query,
const string &offset, Promise<Unit> &&promise);

View File

@ -1227,9 +1227,7 @@ void LanguagePackManager::on_get_all_language_pack_strings(
}
if (r_strings.is_error()) {
for (auto &promise : promises) {
promise.set_error(r_strings.error().clone());
}
fail_promises(promises, r_strings.move_as_error());
return;
}
@ -1533,11 +1531,7 @@ void LanguagePackManager::on_failed_get_difference(string language_pack, string
reset_to_empty(language->get_difference_queries_);
}
}
for (auto &query : get_difference_queries) {
if (query) {
query.set_error(error.clone());
}
}
fail_promises(get_difference_queries, std::move(error));
}
void LanguagePackManager::add_custom_server_language(string language_code, Promise<Unit> &&promise) {

View File

@ -8,10 +8,12 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChannelType.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessageId.h"
@ -102,12 +104,74 @@ static string get_url_query_hash(bool is_tg, const HttpUrlQuery &url_query) {
return string();
}
static AdministratorRights get_administrator_rights(Slice rights, bool for_channel) {
bool can_manage_dialog = false;
bool can_change_info = false;
bool can_post_messages = false;
bool can_edit_messages = false;
bool can_delete_messages = false;
bool can_invite_users = false;
bool can_restrict_members = false;
bool can_pin_messages = false;
bool can_promote_members = false;
bool can_manage_calls = false;
bool is_anonymous = false;
for (auto right : full_split(rights, ' ')) {
if (right == "change_info") {
can_change_info = true;
} else if (right == "post_messages") {
can_post_messages = true;
} else if (right == "edit_messages") {
can_edit_messages = true;
} else if (right == "delete_messages") {
can_delete_messages = true;
} else if (right == "restrict_members") {
can_restrict_members = true;
} else if (right == "invite_users") {
can_invite_users = true;
} else if (right == "pin_messages") {
can_pin_messages = true;
} else if (right == "promote_members") {
can_promote_members = true;
} else if (right == "manage_video_chats") {
can_manage_calls = true;
} else if (right == "anonymous") {
is_anonymous = true;
} else if (right == "manage_chat") {
can_manage_dialog = true;
}
}
return AdministratorRights(is_anonymous, can_manage_dialog, can_change_info, can_post_messages, can_edit_messages,
can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages,
can_promote_members, can_manage_calls,
for_channel ? ChannelType::Broadcast : ChannelType::Megagroup);
}
class LinkManager::InternalLinkActiveSessions final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeActiveSessions>();
}
};
class LinkManager::InternalLinkAttachMenuBot final : public InternalLink {
unique_ptr<InternalLink> dialog_link_;
string bot_username_;
string url_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeAttachmentMenuBot>(
dialog_link_ == nullptr ? nullptr : dialog_link_->get_internal_link_type_object(), bot_username_, url_);
}
public:
InternalLinkAttachMenuBot(unique_ptr<InternalLink> dialog_link, string bot_username, Slice start_parameter)
: dialog_link_(std::move(dialog_link)), bot_username_(std::move(bot_username)) {
if (!start_parameter.empty()) {
url_ = PSTRING() << "start://" << start_parameter;
}
}
};
class LinkManager::InternalLinkAuthenticationCode final : public InternalLink {
string code_;
@ -132,6 +196,21 @@ class LinkManager::InternalLinkBackground final : public InternalLink {
}
};
class LinkManager::InternalLinkBotAddToChannel final : public InternalLink {
string bot_username_;
AdministratorRights administrator_rights_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeBotAddToChannel>(
bot_username_, administrator_rights_.get_chat_administrator_rights_object());
}
public:
InternalLinkBotAddToChannel(string bot_username, AdministratorRights &&administrator_rights)
: bot_username_(std::move(bot_username)), administrator_rights_(std::move(administrator_rights)) {
}
};
class LinkManager::InternalLinkBotStart final : public InternalLink {
string bot_username_;
string start_parameter_;
@ -149,14 +228,20 @@ class LinkManager::InternalLinkBotStart final : public InternalLink {
class LinkManager::InternalLinkBotStartInGroup final : public InternalLink {
string bot_username_;
string start_parameter_;
AdministratorRights administrator_rights_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeBotStartInGroup>(bot_username_, start_parameter_);
return td_api::make_object<td_api::internalLinkTypeBotStartInGroup>(
bot_username_, start_parameter_,
administrator_rights_ == AdministratorRights() ? nullptr
: administrator_rights_.get_chat_administrator_rights_object());
}
public:
InternalLinkBotStartInGroup(string bot_username, string start_parameter)
: bot_username_(std::move(bot_username)), start_parameter_(std::move(start_parameter)) {
InternalLinkBotStartInGroup(string bot_username, string start_parameter, AdministratorRights &&administrator_rights)
: bot_username_(std::move(bot_username))
, start_parameter_(std::move(start_parameter))
, administrator_rights_(std::move(administrator_rights)) {
}
};
@ -623,7 +708,7 @@ static bool tolower_begins_with(Slice str, Slice prefix) {
return true;
}
Result<string> LinkManager::check_link(Slice link) {
Result<string> LinkManager::check_link(Slice link, bool http_only, bool https_only) {
bool is_tg = false;
bool is_ton = false;
if (tolower_begins_with(link, "tg:")) {
@ -637,7 +722,13 @@ Result<string> LinkManager::check_link(Slice link) {
link.remove_prefix(2);
}
TRY_RESULT(http_url, parse_url(link));
if (https_only && (http_url.protocol_ != HttpUrl::Protocol::Https || is_tg || is_ton)) {
return Status::Error("Only HTTPS links are allowed");
}
if (is_tg || is_ton) {
if (http_only) {
return Status::Error("Only HTTP links are allowed");
}
if (tolower_begins_with(link, "http://") || http_url.protocol_ == HttpUrl::Protocol::Https ||
!http_url.userinfo_.empty() || http_url.specified_port_ != 0 || http_url.is_ipv6_) {
return Status::Error(is_tg ? Slice("Wrong tg URL") : Slice("Wrong ton URL"));
@ -824,18 +915,41 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
return td::make_unique<InternalLinkVoiceChat>(std::move(username), arg.second, arg.first == "livestream");
}
if (arg.first == "start" && is_valid_start_parameter(arg.second)) {
// resolve?domain=<bot_username>?start=<parameter>
// resolve?domain=<bot_username>&start=<parameter>
return td::make_unique<InternalLinkBotStart>(std::move(username), arg.second);
}
if (arg.first == "startgroup" && is_valid_start_parameter(arg.second)) {
// resolve?domain=<bot_username>?startgroup=<parameter>
return td::make_unique<InternalLinkBotStartInGroup>(std::move(username), arg.second);
// resolve?domain=<bot_username>&startgroup=<parameter>
// resolve?domain=<bot_username>&startgroup=>parameter>&admin=change_info+delete_messages+restrict_members
// resolve?domain=<bot_username>&startgroup&admin=change_info+delete_messages+restrict_members
auto administrator_rights = get_administrator_rights(url_query.get_arg("admin"), false);
return td::make_unique<InternalLinkBotStartInGroup>(std::move(username), arg.second,
std::move(administrator_rights));
}
if (arg.first == "startchannel") {
// resolve?domain=<bot_username>&startchannel&admin=change_info+post_messages+promote_members
auto administrator_rights = get_administrator_rights(url_query.get_arg("admin"), true);
if (administrator_rights != AdministratorRights()) {
return td::make_unique<InternalLinkBotAddToChannel>(std::move(username), std::move(administrator_rights));
}
}
if (arg.first == "game" && !arg.second.empty()) {
// resolve?domain=<bot_username>?game=<short_name>
// resolve?domain=<bot_username>&game=<short_name>
return td::make_unique<InternalLinkGame>(std::move(username), arg.second);
}
}
if (!url_query.get_arg("attach").empty()) {
// resolve?domain=<username>&attach=<bot_username>
// resolve?domain=<username>&attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(
td::make_unique<InternalLinkPublicDialog>(std::move(username)), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
} else if (url_query.has_arg("startattach")) {
// resolve?domain=<bot_username>&startattach
// resolve?domain=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(nullptr, std::move(username),
url_query.get_arg("startattach"));
}
if (username == "telegrampassport") {
// resolve?domain=telegrampassport&bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
return get_internal_link_passport(query, url_query.args_);
@ -843,8 +957,15 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
// resolve?domain=<username>
return td::make_unique<InternalLinkPublicDialog>(std::move(username));
} else if (is_valid_phone_number(get_arg("phone"))) {
auto user_link = td::make_unique<InternalLinkUserPhoneNumber>(get_arg("phone"));
if (!url_query.get_arg("attach").empty()) {
// resolve?phone=<phone_number>&attach=<bot_username>
// resolve?phone=<phone_number>&attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(std::move(user_link), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
}
// resolve?phone=12345
return td::make_unique<InternalLinkUserPhoneNumber>(get_arg("phone"));
return user_link;
}
} else if (path.size() == 1 && path[0] == "login") {
// login?code=123456
@ -1010,8 +1131,15 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
} else if (path[0][0] == ' ' || path[0][0] == '+') {
if (path[0].size() >= 2) {
if (is_valid_phone_number(Slice(path[0]).substr(1))) {
auto user_link = td::make_unique<InternalLinkUserPhoneNumber>(path[0].substr(1));
if (!url_query.get_arg("attach").empty()) {
// /+<phone_number>?attach=<bot_username>
// /+<phone_number>?attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(std::move(user_link), url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
}
// /+<phone_number>
return td::make_unique<InternalLinkUserPhoneNumber>(path[0].substr(1));
return user_link;
} else {
// /+<link>
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
@ -1101,13 +1229,36 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
}
if (arg.first == "startgroup" && is_valid_start_parameter(arg.second)) {
// /<bot_username>?startgroup=<parameter>
return td::make_unique<InternalLinkBotStartInGroup>(std::move(username), arg.second);
// /<bot_username>?startgroup=<parameter>&admin=change_info+delete_messages+restrict_members
// /<bot_username>?startgroup&admin=change_info+delete_messages+restrict_members
auto administrator_rights = get_administrator_rights(url_query.get_arg("admin"), false);
return td::make_unique<InternalLinkBotStartInGroup>(std::move(username), arg.second,
std::move(administrator_rights));
}
if (arg.first == "startchannel") {
// /<bot_username>?startchannel&admin=change_info+post_messages+promote_members
auto administrator_rights = get_administrator_rights(url_query.get_arg("admin"), true);
if (administrator_rights != AdministratorRights()) {
return td::make_unique<InternalLinkBotAddToChannel>(std::move(username), std::move(administrator_rights));
}
}
if (arg.first == "game" && !arg.second.empty()) {
// /<bot_username>?game=<short_name>
return td::make_unique<InternalLinkGame>(std::move(username), arg.second);
}
}
if (!url_query.get_arg("attach").empty()) {
// /<username>?attach=<bot_username>
// /<username>?attach=<bot_username>&startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(td::make_unique<InternalLinkPublicDialog>(std::move(username)),
url_query.get_arg("attach").str(),
url_query.get_arg("startattach"));
} else if (url_query.has_arg("startattach")) {
// /<bot_username>?startattach
// /<bot_username>?startattach=<start_parameter>
return td::make_unique<InternalLinkAttachMenuBot>(nullptr, std::move(username), url_query.get_arg("startattach"));
}
// /<username>
return td::make_unique<InternalLinkPublicDialog>(std::move(username));
}

View File

@ -47,7 +47,7 @@ class LinkManager final : public Actor {
};
// checks whether the link is a valid tg, ton or HTTP(S) URL and returns it in a canonical form
static Result<string> check_link(Slice link);
static Result<string> check_link(Slice link, bool http_only = false, bool https_only = false);
// checks whether the link is a supported tg or t.me link and parses it
static unique_ptr<InternalLink> parse_internal_link(Slice link);
@ -82,8 +82,10 @@ class LinkManager final : public Actor {
void tear_down() final;
class InternalLinkActiveSessions;
class InternalLinkAttachMenuBot;
class InternalLinkAuthenticationCode;
class InternalLinkBackground;
class InternalLinkBotAddToChannel;
class InternalLinkBotStart;
class InternalLinkBotStartInGroup;
class InternalLinkChangePhoneNumber;

View File

@ -46,6 +46,8 @@
#include "td/telegram/Payments.hpp"
#include "td/telegram/Photo.h"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/PollId.h"
#include "td/telegram/PollId.hpp"
@ -449,7 +451,7 @@ class MessageChatSetTtl final : public MessageContent {
class MessageUnsupported final : public MessageContent {
public:
static constexpr int32 CURRENT_VERSION = 10;
static constexpr int32 CURRENT_VERSION = 11;
int32 version = CURRENT_VERSION;
MessageUnsupported() = default;
@ -745,6 +747,34 @@ class MessageChatSetTheme final : public MessageContent {
}
};
class MessageWebViewDataSent final : public MessageContent {
public:
string button_text;
MessageWebViewDataSent() = default;
explicit MessageWebViewDataSent(string &&button_text) : button_text(std::move(button_text)) {
}
MessageContentType get_type() const final {
return MessageContentType::WebViewDataSent;
}
};
class MessageWebViewDataReceived final : public MessageContent {
public:
string button_text;
string data;
MessageWebViewDataReceived() = default;
MessageWebViewDataReceived(string &&button_text, string &&data)
: button_text(std::move(button_text)), data(std::move(data)) {
}
MessageContentType get_type() const final {
return MessageContentType::WebViewDataReceived;
}
};
template <class StorerT>
static void store(const MessageContent *content, StorerT &storer) {
CHECK(content != nullptr);
@ -1048,6 +1078,17 @@ static void store(const MessageContent *content, StorerT &storer) {
store(m->emoji, storer);
break;
}
case MessageContentType::WebViewDataSent: {
const auto *m = static_cast<const MessageWebViewDataSent *>(content);
store(m->button_text, storer);
break;
}
case MessageContentType::WebViewDataReceived: {
const auto *m = static_cast<const MessageWebViewDataReceived *>(content);
store(m->button_text, storer);
store(m->data, storer);
break;
}
default:
UNREACHABLE();
}
@ -1468,6 +1509,19 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
content = std::move(m);
break;
}
case MessageContentType::WebViewDataSent: {
auto m = make_unique<MessageWebViewDataSent>();
parse(m->button_text, parser);
content = std::move(m);
break;
}
case MessageContentType::WebViewDataReceived: {
auto m = make_unique<MessageWebViewDataReceived>();
parse(m->button_text, parser);
parse(m->data, parser);
content = std::move(m);
break;
}
default:
LOG(FATAL) << "Have unknown message content type " << static_cast<int32>(content_type);
}
@ -1686,7 +1740,7 @@ static Result<InputMessageContent> create_input_message_content(
td->audios_manager_->create_audio(file_id, string(), thumbnail, std::move(file_name), std::move(mime_type),
input_audio->duration_, std::move(input_audio->title_),
std::move(input_audio->performer_), false);
std::move(input_audio->performer_), 0, false);
content = make_unique<MessageAudio>(file_id, std::move(caption));
break;
@ -2081,6 +2135,8 @@ bool can_have_input_media(const Td *td, const MessageContent *content) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return false;
case MessageContentType::Animation:
case MessageContentType::Audio:
@ -2196,6 +2252,8 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
break;
default:
UNREACHABLE();
@ -2312,6 +2370,8 @@ static tl_object_ptr<telegram_api::InputMedia> get_input_media_impl(
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
break;
default:
UNREACHABLE();
@ -2476,6 +2536,8 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
break;
default:
UNREACHABLE();
@ -2533,8 +2595,8 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
}
break;
case MessageContentType::Game:
if (dialog_type == DialogType::Channel && td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) ==
ContactsManager::ChannelType::Broadcast) {
if (dialog_type == DialogType::Channel &&
td->contacts_manager_->is_broadcast_channel(dialog_id.get_channel_id())) {
// return Status::Error(400, "Games can't be sent to channel chats");
}
if (dialog_type == DialogType::SecretChat) {
@ -2572,8 +2634,7 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
return Status::Error(400, "Not enough rights to send polls to the chat");
}
if (dialog_type == DialogType::Channel &&
td->contacts_manager_->get_channel_type(dialog_id.get_channel_id()) ==
ContactsManager::ChannelType::Broadcast &&
td->contacts_manager_->is_broadcast_channel(dialog_id.get_channel_id()) &&
!td->poll_manager_->get_poll_is_anonymous(static_cast<const MessagePoll *>(content)->poll_id)) {
return Status::Error(400, "Non-anonymous polls can't be sent to channel chats");
}
@ -2645,6 +2706,8 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
UNREACHABLE();
}
return Status::OK();
@ -2771,6 +2834,8 @@ static int32 get_message_content_media_index_mask(const MessageContent *content,
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return 0;
default:
UNREACHABLE();
@ -3244,10 +3309,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
}
need_update = true;
}
if (old_->caption != new_->caption) {
need_update = true;
}
if (old_->is_listened != new_->is_listened) {
if (old_->caption != new_->caption || old_->is_listened != new_->is_listened) {
need_update = true;
}
break;
@ -3414,10 +3476,7 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
case MessageContentType::PassportDataReceived: {
const auto *old_ = static_cast<const MessagePassportDataReceived *>(old_content);
const auto *new_ = static_cast<const MessagePassportDataReceived *>(new_content);
if (old_->values != new_->values) {
need_update = true;
}
if (old_->credentials != new_->credentials) {
if (old_->values != new_->values || old_->credentials != new_->credentials) {
need_update = true;
}
break;
@ -3478,6 +3537,22 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
}
break;
}
case MessageContentType::WebViewDataSent: {
const auto *old_ = static_cast<const MessageWebViewDataSent *>(old_content);
const auto *new_ = static_cast<const MessageWebViewDataSent *>(new_content);
if (old_->button_text != new_->button_text) {
need_update = true;
}
break;
}
case MessageContentType::WebViewDataReceived: {
const auto *old_ = static_cast<const MessageWebViewDataReceived *>(old_content);
const auto *new_ = static_cast<const MessageWebViewDataReceived *>(new_content);
if (old_->button_text != new_->button_text || old_->data != new_->data) {
need_update = true;
}
break;
}
case MessageContentType::Unsupported: {
const auto *old_ = static_cast<const MessageUnsupported *>(old_content);
const auto *new_ = static_cast<const MessageUnsupported *>(new_content);
@ -3614,6 +3689,8 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type;
break;
default:
@ -4524,6 +4601,8 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return nullptr;
default:
UNREACHABLE();
@ -4788,6 +4867,22 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
}
case telegram_api::messageActionChatJoinedByRequest::ID:
return make_unique<MessageChatJoinedByLink>(true);
case telegram_api::messageActionWebViewDataSent::ID: {
if (td->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive messageActionWebViewDataSent in " << owner_dialog_id;
break;
}
auto data_sent = move_tl_object_as<telegram_api::messageActionWebViewDataSent>(action);
return td::make_unique<MessageWebViewDataSent>(std::move(data_sent->text_));
}
case telegram_api::messageActionWebViewDataSentMe::ID: {
if (!td->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive messageActionWebViewDataSentMe in " << owner_dialog_id;
break;
}
auto data_sent = move_tl_object_as<telegram_api::messageActionWebViewDataSentMe>(action);
return td::make_unique<MessageWebViewDataReceived>(std::move(data_sent->text_), std::move(data_sent->data_));
}
default:
UNREACHABLE();
}
@ -5036,6 +5131,14 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
const auto *m = static_cast<const MessageChatSetTheme *>(content);
return make_tl_object<td_api::messageChatSetTheme>(m->emoji);
}
case MessageContentType::WebViewDataSent: {
const auto *m = static_cast<const MessageWebViewDataSent *>(content);
return make_tl_object<td_api::messageWebAppDataSent>(m->button_text);
}
case MessageContentType::WebViewDataReceived: {
const auto *m = static_cast<const MessageWebViewDataReceived *>(content);
return make_tl_object<td_api::messageWebAppDataReceived>(m->button_text, m->data);
}
default:
UNREACHABLE();
return nullptr;
@ -5385,6 +5488,8 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return string();
default:
UNREACHABLE();
@ -5632,6 +5737,10 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
}
case MessageContentType::ChatSetTheme:
break;
case MessageContentType::WebViewDataSent:
break;
case MessageContentType::WebViewDataReceived:
break;
default:
UNREACHABLE();
break;
@ -5664,6 +5773,10 @@ bool is_active_reaction(Td *td, const string &reaction) {
return td->stickers_manager_->is_active_reaction(reaction);
}
void init_stickers_manager(Td *td) {
td->stickers_manager_->init();
}
void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date) {
send_closure(G()->top_dialog_manager(), &TopDialogManager::on_dialog_used, category, dialog_id, date);
}

View File

@ -250,6 +250,8 @@ bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogActi
bool is_active_reaction(Td *td, const string &reaction);
void init_stickers_manager(Td *td);
void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date);
void update_used_hashtags(Td *td, const MessageContent *content);

View File

@ -104,6 +104,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType cont
return string_builder << "InviteToGroupCall";
case MessageContentType::ChatSetTheme:
return string_builder << "ChatSetTheme";
case MessageContentType::WebViewDataSent:
return string_builder << "WebViewDataSent";
case MessageContentType::WebViewDataReceived:
return string_builder << "WebViewDataReceived";
default:
UNREACHABLE();
return string_builder;
@ -159,6 +163,8 @@ bool is_allowed_media_group_content(MessageContentType content_type) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return false;
default:
UNREACHABLE();
@ -222,6 +228,8 @@ bool is_secret_message_content(int32 ttl, MessageContentType content_type) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return false;
default:
UNREACHABLE();
@ -278,6 +286,8 @@ bool is_service_message_content(MessageContentType content_type) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return true;
default:
UNREACHABLE();
@ -334,6 +344,8 @@ bool can_have_message_content_caption(MessageContentType content_type) {
case MessageContentType::GroupCall:
case MessageContentType::InviteToGroupCall:
case MessageContentType::ChatSetTheme:
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
return false;
default:
UNREACHABLE();

View File

@ -61,7 +61,9 @@ enum class MessageContentType : int32 {
ProximityAlertTriggered,
GroupCall,
InviteToGroupCall,
ChatSetTheme
ChatSetTheme,
WebViewDataSent,
WebViewDataReceived
};
StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type);

View File

@ -3276,8 +3276,8 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
}
auto user_id = LinkManager::get_link_user_id(entity->url_);
if (user_id.is_valid()) {
if (contacts_manager != nullptr && !contacts_manager->have_input_user(user_id)) {
return Status::Error(400, "Have no access to the user");
if (contacts_manager != nullptr) {
TRY_STATUS(contacts_manager->get_input_user(user_id));
}
entities.emplace_back(offset, length, user_id);
break;
@ -3292,8 +3292,8 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
case td_api::textEntityTypeMentionName::ID: {
auto entity = static_cast<td_api::textEntityTypeMentionName *>(input_entity->type_.get());
UserId user_id(entity->user_id_);
if (contacts_manager != nullptr && !contacts_manager->have_input_user(user_id)) {
return Status::Error(400, "Have no access to the user");
if (contacts_manager != nullptr) {
TRY_STATUS(contacts_manager->get_input_user(user_id));
}
entities.emplace_back(offset, length, user_id);
break;
@ -3432,12 +3432,13 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
LOG(ERROR) << "Receive invalid " << user_id << " in MentionName from " << source;
continue;
}
if (contacts_manager == nullptr || !contacts_manager->have_user(user_id)) {
if (contacts_manager == nullptr) {
LOG(ERROR) << "Receive unknown " << user_id << " in MentionName from " << source;
continue;
}
if (!contacts_manager->have_input_user(user_id)) {
LOG(ERROR) << "Receive inaccessible " << user_id << " in MentionName from " << source;
auto r_input_user = contacts_manager->get_input_user(user_id);
if (r_input_user.is_error()) {
LOG(ERROR) << "Receive wrong " << user_id << ": " << r_input_user.error() << " from " << source;
continue;
}
entities.emplace_back(entity->offset_, entity->length_, user_id);

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,7 @@
#include "td/telegram/NotificationGroupType.h"
#include "td/telegram/NotificationId.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/Photo.h"
#include "td/telegram/RecentDialogList.h"
#include "td/telegram/ReplyMarkup.h"
#include "td/telegram/ReportReason.h"
@ -385,6 +386,10 @@ class MessagesManager final : public Actor {
void delete_messages(DialogId dialog_id, const vector<MessageId> &message_ids, bool revoke, Promise<Unit> &&promise);
void on_failed_message_deletion(DialogId dialog_id, const vector<int32> &server_message_ids);
void on_failed_scheduled_message_deletion(DialogId dialog_id, const vector<MessageId> &message_ids);
void delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke, Promise<Unit> &&promise);
void delete_all_call_messages(bool revoke, Promise<Unit> &&promise);
@ -423,6 +428,8 @@ class MessagesManager final : public Actor {
void set_dialog_default_send_message_as_dialog_id(DialogId dialog_id, DialogId message_sender_dialog_id,
Promise<Unit> &&promise);
bool get_dialog_silent_send_message(DialogId dialog_id) const;
Result<td_api::object_ptr<td_api::message>> send_message(
DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id,
tl_object_ptr<td_api::messageSendOptions> &&options, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
@ -703,23 +710,13 @@ class MessagesManager final : public Actor {
void click_animated_emoji_message(FullMessageId full_message_id,
Promise<td_api::object_ptr<td_api::sticker>> &&promise);
td_api::object_ptr<td_api::updateScopeNotificationSettings> get_update_scope_notification_settings_object(
NotificationSettingsScope scope) const;
vector<DialogId> get_dialog_notification_settings_exceptions(NotificationSettingsScope scope, bool filter_scope,
bool compare_sound, bool force, Promise<Unit> &&promise);
const ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope,
Promise<Unit> &&promise);
Status set_dialog_notification_settings(DialogId dialog_id,
tl_object_ptr<td_api::chatNotificationSettings> &&notification_settings)
TD_WARN_UNUSED_RESULT;
Status set_scope_notification_settings(NotificationSettingsScope scope,
tl_object_ptr<td_api::scopeNotificationSettings> &&notification_settings)
TD_WARN_UNUSED_RESULT;
void reset_all_notification_settings();
tl_object_ptr<td_api::chat> get_chat_object(DialogId dialog_id) const;
@ -855,15 +852,12 @@ class MessagesManager final : public Actor {
void on_resolved_username(const string &username, DialogId dialog_id);
void drop_username(const string &username);
tl_object_ptr<telegram_api::InputNotifyPeer> get_input_notify_peer(DialogId dialog_id) const;
void on_update_notification_scope_is_muted(NotificationSettingsScope scope, bool is_muted);
void on_update_dialog_notify_settings(DialogId dialog_id,
tl_object_ptr<telegram_api::peerNotifySettings> &&peer_notify_settings,
const char *source);
void on_update_scope_notify_settings(NotificationSettingsScope scope,
tl_object_ptr<telegram_api::peerNotifySettings> &&peer_notify_settings);
void on_update_dialog_available_reactions(DialogId dialog_id, vector<string> &&available_reactions);
void hide_dialog_action_bar(DialogId dialog_id);
@ -922,12 +916,6 @@ class MessagesManager final : public Actor {
void force_create_dialog(DialogId dialog_id, const char *source, bool expect_no_access = false,
bool force_update_dialog_pos = false);
void send_get_dialog_notification_settings_query(DialogId dialog_id, Promise<Unit> &&promise);
void send_get_scope_notification_settings_query(NotificationSettingsScope scope, Promise<Unit> &&promise);
void on_get_dialog_notification_settings_query_finished(DialogId dialog_id, Status &&status);
void on_get_dialog_query_finished(DialogId dialog_id, Status &&status);
void remove_sponsored_dialog();
@ -971,6 +959,10 @@ class MessagesManager final : public Actor {
void remove_message_notifications(DialogId dialog_id, NotificationGroupId group_id,
NotificationId max_notification_id, MessageId max_message_id);
void remove_scope_pinned_message_notifications(NotificationSettingsScope scope);
void on_update_scope_mention_notifications(NotificationSettingsScope scope, bool disable_mention_notifications);
void upload_dialog_photo(DialogId dialog_id, FileId file_id, bool is_animation, double main_frame_timestamp,
bool is_reupload, Promise<Unit> &&promise, vector<int> bad_parts = {});
@ -1747,7 +1739,6 @@ class MessagesManager final : public Actor {
class ToggleDialogReportSpamStateOnServerLogEvent;
class UnpinAllDialogMessagesOnServerLogEvent;
class UpdateDialogNotificationSettingsOnServerLogEvent;
class UpdateScopeNotificationSettingsOnServerLogEvent;
class DialogFiltersLogEvent;
@ -1833,7 +1824,7 @@ class MessagesManager final : public Actor {
void on_resolve_secret_chat_message_via_bot_username(const string &via_bot_username, MessageInfo *message_info_ptr,
Promise<Unit> &&promise);
void add_secret_message(unique_ptr<PendingSecretMessage> pending_secret_message, Promise<Unit> lock_promise = Auto());
void add_secret_message(unique_ptr<PendingSecretMessage> pending_secret_message, Promise<Unit> lock_promise = {});
void on_add_secret_message_ready(int64 token);
@ -2175,7 +2166,7 @@ class MessagesManager final : public Actor {
int32 calc_new_unread_count(Dialog *d, MessageId max_message_id, MessageType type, int32 hint_unread_count) const;
void repair_server_unread_count(DialogId dialog_id, int32 unread_count);
void repair_server_unread_count(DialogId dialog_id, int32 unread_count, const char *source);
void repair_channel_server_unread_count(Dialog *d);
@ -2618,30 +2609,16 @@ class MessagesManager final : public Actor {
bool set_dialog_last_notification(DialogId dialog_id, NotificationGroupInfo &group_info, int32 last_notification_date,
NotificationId last_notification_id, const char *source);
static string get_notification_settings_scope_database_key(NotificationSettingsScope scope);
static void save_scope_notification_settings(NotificationSettingsScope scope,
const ScopeNotificationSettings &new_settings);
bool update_dialog_notification_settings(DialogId dialog_id, DialogNotificationSettings *current_settings,
const DialogNotificationSettings &new_settings);
bool update_scope_notification_settings(NotificationSettingsScope scope, ScopeNotificationSettings *current_settings,
const ScopeNotificationSettings &new_settings);
DialogNotificationSettings &&new_settings);
void schedule_dialog_unmute(DialogId dialog_id, bool use_default, int32 mute_until);
void update_dialog_unmute_timeout(Dialog *d, bool &old_use_default, int32 &old_mute_until, bool new_use_default,
int32 new_mute_until);
void schedule_scope_unmute(NotificationSettingsScope scope, int32 mute_until);
void update_scope_unmute_timeout(NotificationSettingsScope scope, int32 &old_mute_until, int32 new_mute_until);
void on_dialog_unmute(DialogId dialog_id);
void on_scope_unmute(NotificationSettingsScope scope);
bool update_dialog_silent_send_message(Dialog *d, bool silent_send_message);
vector<string> get_message_available_reactions(const Dialog *d, const Message *m);
@ -2650,7 +2627,7 @@ class MessagesManager final : public Actor {
void set_dialog_available_reactions(Dialog *d, vector<string> &&available_reactions);
void set_dialog_available_reactions_generation(Dialog *d, uint32 new_generation);
void set_dialog_next_available_reactions_generation(Dialog *d, uint32 generation);
void hide_dialog_message_reactions(Dialog *d);
@ -2866,16 +2843,12 @@ class MessagesManager final : public Actor {
std::pair<bool, int32> get_dialog_mute_until(DialogId dialog_id, const Dialog *d) const;
int64 get_dialog_notification_ringtone_id(DialogId dialog_id, const Dialog *d) const;
NotificationSettingsScope get_dialog_notification_setting_scope(DialogId dialog_id) const;
int32 get_scope_mute_until(DialogId dialog_id) const;
DialogNotificationSettings *get_dialog_notification_settings(DialogId dialog_id, bool force);
ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope);
const ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope) const;
vector<FileId> get_message_file_ids(const Message *m) const;
void cancel_upload_message_content_files(const MessageContent *content);
@ -3041,8 +3014,6 @@ class MessagesManager final : public Actor {
void on_updated_dialog_notification_settings(DialogId dialog_id, uint64 generation);
void update_scope_notification_settings_on_server(NotificationSettingsScope scope, uint64 log_event_id);
void reset_all_notification_settings_on_server(uint64 log_event_id);
void toggle_dialog_report_spam_state_on_server(DialogId dialog_id, bool is_spam_dialog, uint64 log_event_id,
@ -3076,8 +3047,6 @@ class MessagesManager final : public Actor {
void update_list_last_dialog_date(DialogList &list);
void load_notification_settings();
static string get_channel_pts_key(DialogId dialog_id);
int32 load_channel_pts(DialogId dialog_id) const;
@ -3266,8 +3235,6 @@ class MessagesManager final : public Actor {
static uint64 save_read_message_contents_on_server_log_event(DialogId dialog_id,
const vector<MessageId> &message_ids);
static uint64 save_update_scope_notification_settings_on_server_log_event(NotificationSettingsScope scope);
static uint64 save_reset_all_notification_settings_on_server_log_event();
static uint64 save_reget_dialog_log_event(DialogId dialog_id);
@ -3498,8 +3465,6 @@ class MessagesManager final : public Actor {
};
FlatHashMap<DialogId, MessageEmbeddingCodes, DialogIdHash> message_embedding_codes_[2];
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_notification_settings_queries_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_queries_;
FlatHashMap<DialogId, uint64, DialogIdHash> get_dialog_query_log_event_id_;
@ -3526,10 +3491,6 @@ class MessagesManager final : public Actor {
FlatHashMap<DialogId, std::vector<ActiveDialogAction>, DialogIdHash> active_dialog_actions_;
ScopeNotificationSettings users_notification_settings_;
ScopeNotificationSettings chats_notification_settings_;
ScopeNotificationSettings channels_notification_settings_;
FlatHashMap<NotificationGroupId, DialogId, NotificationGroupIdHash> notification_group_id_to_dialog_id_;
uint64 current_message_edit_generation_ = 0;

View File

@ -20,11 +20,11 @@ class Notification {
public:
NotificationId notification_id;
int32 date = 0;
bool is_silent = false;
int64 ringtone_id = -1;
unique_ptr<NotificationType> type;
Notification(NotificationId notification_id, int32 date, bool is_silent, unique_ptr<NotificationType> type)
: notification_id(notification_id), date(date), is_silent(is_silent), type(std::move(type)) {
Notification(NotificationId notification_id, int32 date, int64 ringtone_id, unique_ptr<NotificationType> type)
: notification_id(notification_id), date(date), ringtone_id(ringtone_id), type(std::move(type)) {
}
};
@ -32,13 +32,13 @@ inline td_api::object_ptr<td_api::notification> get_notification_object(DialogId
const Notification &notification) {
CHECK(notification.type != nullptr);
return td_api::make_object<td_api::notification>(notification.notification_id.get(), notification.date,
notification.is_silent,
notification.ringtone_id,
notification.type->get_notification_type_object(dialog_id));
}
inline StringBuilder &operator<<(StringBuilder &sb, const Notification &notification) {
return sb << "notification[" << notification.notification_id << ", " << notification.date << ", "
<< notification.is_silent << ", " << *notification.type << ']';
<< notification.ringtone_id << ", " << *notification.type << ']';
}
} // namespace td

View File

@ -859,7 +859,7 @@ int32 NotificationManager::get_notification_delay_ms(DialogId dialog_id, const P
void NotificationManager::add_notification(NotificationGroupId group_id, NotificationGroupType group_type,
DialogId dialog_id, int32 date, DialogId notification_settings_dialog_id,
bool initial_is_silent, bool is_silent, int32 min_delay_ms,
int64 initial_ringtone_id, int64 ringtone_id, int32 min_delay_ms,
NotificationId notification_id, unique_ptr<NotificationType> type,
const char *source) {
if (is_disabled() || max_notification_group_count_ == 0) {
@ -874,7 +874,7 @@ void NotificationManager::add_notification(NotificationGroupId group_id, Notific
CHECK(type != nullptr);
VLOG(notifications) << "Add " << notification_id << " to " << group_id << " of type " << group_type << " in "
<< dialog_id << " with settings from " << notification_settings_dialog_id
<< (is_silent ? " silently" : " with sound") << ": " << *type;
<< (ringtone_id == 0 ? " silently" : " with sound") << ": " << *type;
if (!type->is_temporary()) {
remove_temporary_notifications(group_id, "add_notification");
@ -908,8 +908,8 @@ void NotificationManager::add_notification(NotificationGroupId group_id, Notific
PendingNotification notification;
notification.date = date;
notification.settings_dialog_id = notification_settings_dialog_id;
notification.initial_is_silent = initial_is_silent;
notification.is_silent = is_silent;
notification.initial_ringtone_id = initial_ringtone_id;
notification.ringtone_id = ringtone_id;
notification.notification_id = notification_id;
notification.type = std::move(type);
@ -947,8 +947,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const NotificationManag
return string_builder << "update[" << NotificationGroupId(p->notification_group_id_) << " of type "
<< get_notification_group_type(p->type_) << " from " << DialogId(p->chat_id_)
<< " with settings from " << DialogId(p->notification_settings_chat_id_)
<< (p->is_silent_ ? " silently" : " with sound") << "; total_count = " << p->total_count_
<< ", add " << added_notification_ids << ", remove " << p->removed_notification_ids_;
<< (p->notification_sound_id_ == 0 ? " silently" : " with sound")
<< "; total_count = " << p->total_count_ << ", add " << added_notification_ids
<< ", remove " << p->removed_notification_ids_;
}
default:
UNREACHABLE();
@ -1146,8 +1147,8 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
first_add_notification_pos[notification_id] = cur_pos;
}
td::remove_if(update_ptr->added_notifications_, [](auto &notification) { return notification == nullptr; });
if (update_ptr->added_notifications_.empty() && !update_ptr->is_silent_) {
update_ptr->is_silent_ = true;
if (update_ptr->added_notifications_.empty() && update_ptr->notification_sound_id_ != 0) {
update_ptr->notification_sound_id_ = 0;
is_changed = true;
}
@ -1298,15 +1299,13 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
if ((last_update_ptr->notification_settings_chat_id_ == update_ptr->notification_settings_chat_id_ ||
last_update_ptr->added_notifications_.empty()) &&
!has_common_notifications(last_update_ptr->added_notifications_, update_ptr->removed_notification_ids_) &&
!has_common_notifications(update_ptr->added_notifications_, last_update_ptr->removed_notification_ids_)) {
!has_common_notifications(update_ptr->added_notifications_, last_update_ptr->removed_notification_ids_) &&
last_update_ptr->notification_sound_id_ == update_ptr->notification_sound_id_) {
// combine updates
VLOG(notifications) << "Combine " << as_notification_update(last_update_ptr) << " and "
<< as_notification_update(update_ptr);
CHECK(last_update_ptr->notification_group_id_ == update_ptr->notification_group_id_);
CHECK(last_update_ptr->chat_id_ == update_ptr->chat_id_);
if (last_update_ptr->is_silent_ && !update_ptr->is_silent_) {
last_update_ptr->is_silent_ = false;
}
last_update_ptr->notification_settings_chat_id_ = update_ptr->notification_settings_chat_id_;
last_update_ptr->type_ = std::move(update_ptr->type_);
last_update_ptr->total_count_ = update_ptr->total_count_;
@ -1397,7 +1396,7 @@ bool NotificationManager::do_flush_pending_notifications(NotificationGroupKey &g
added_notifications.reserve(pending_notifications.size());
for (auto &pending_notification : pending_notifications) {
Notification notification(pending_notification.notification_id, pending_notification.date,
pending_notification.initial_is_silent, std::move(pending_notification.type));
pending_notification.initial_ringtone_id, std::move(pending_notification.type));
added_notifications.push_back(get_notification_object(group_key.dialog_id, notification));
CHECK(added_notifications.back()->type_ != nullptr);
@ -1425,7 +1424,7 @@ bool NotificationManager::do_flush_pending_notifications(NotificationGroupKey &g
if (!added_notifications.empty()) {
add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
group_key.group_id.get(), get_notification_group_type_object(group.type), group_key.dialog_id.get(),
pending_notifications[0].settings_dialog_id.get(), pending_notifications[0].is_silent, group.total_count,
pending_notifications[0].settings_dialog_id.get(), pending_notifications[0].ringtone_id, group.total_count,
std::move(added_notifications), std::move(removed_notification_ids)));
} else {
CHECK(removed_notification_ids.empty());
@ -1528,7 +1527,7 @@ void NotificationManager::flush_pending_notifications(NotificationGroupId group_
group.total_count += narrow_cast<int32>(group.pending_notifications.size());
for (auto &pending_notification : group.pending_notifications) {
group.notifications.emplace_back(pending_notification.notification_id, pending_notification.date,
pending_notification.initial_is_silent, std::move(pending_notification.type));
pending_notification.initial_ringtone_id, std::move(pending_notification.type));
}
} else {
if (!was_updated) {
@ -1541,18 +1540,18 @@ void NotificationManager::flush_pending_notifications(NotificationGroupId group_
}
DialogId notification_settings_dialog_id;
bool is_silent = false;
int64 ringtone_id = -1;
// split notifications by groups with common settings
vector<PendingNotification> grouped_notifications;
for (auto &pending_notification : group.pending_notifications) {
if (notification_settings_dialog_id != pending_notification.settings_dialog_id ||
is_silent != pending_notification.is_silent) {
ringtone_id != pending_notification.ringtone_id) {
if (do_flush_pending_notifications(group_key, group, grouped_notifications)) {
force_update = true;
}
notification_settings_dialog_id = pending_notification.settings_dialog_id;
is_silent = pending_notification.is_silent;
ringtone_id = pending_notification.ringtone_id;
}
grouped_notifications.push_back(std::move(pending_notification));
}
@ -1659,9 +1658,7 @@ void NotificationManager::on_notification_processed(NotificationId notification_
auto promises = std::move(promise_it->second);
push_notification_promises_.erase(promise_it);
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
}
}
@ -3304,8 +3301,8 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(), std::move(sender_photo),
nullptr, 0, Auto(), string(), string());
false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name, string(), string(), string(),
std::move(sender_photo), nullptr, 0, Auto(), string(), string());
td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload");
}
@ -3444,10 +3441,15 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
std::move(promise));
} else {
bool is_from_scheduled = has_json_object_field(custom, "schedule");
bool is_silent = has_json_object_field(custom, "silent");
int64 ringtone_id = -1;
if (has_json_object_field(custom, "silent")) {
ringtone_id = 0;
} else if (has_json_object_field(custom, "ringtone")) {
TRY_RESULT_ASSIGN(ringtone_id, get_json_object_long_field(custom, "ringtone"));
}
add_message_push_notification(dialog_id, MessageId(server_message_id), random_id, sender_user_id, sender_dialog_id,
std::move(sender_name), sent_date, is_from_scheduled, contains_mention, is_silent,
is_silent, std::move(loc_key), std::move(arg), std::move(attached_photo),
std::move(sender_name), sent_date, is_from_scheduled, contains_mention, ringtone_id,
ringtone_id, std::move(loc_key), std::move(arg), std::move(attached_photo),
std::move(attached_document), NotificationId(), 0, std::move(promise));
}
return Status::OK();
@ -3464,7 +3466,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
int32 date_;
bool is_from_scheduled_;
bool contains_mention_;
bool is_silent_;
int64 ringtone_id_;
string loc_key_;
string arg_;
Photo photo_;
@ -3481,9 +3483,11 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
bool has_photo = !photo_.is_empty();
bool has_document = !document_.empty();
bool has_sender_dialog_id = sender_dialog_id_.is_valid();
bool is_silent = ringtone_id_ == 0;
bool has_ringtone_id = !is_silent && ringtone_id_ != -1;
BEGIN_STORE_FLAGS();
STORE_FLAG(contains_mention_);
STORE_FLAG(is_silent_);
STORE_FLAG(is_silent);
STORE_FLAG(has_message_id);
STORE_FLAG(has_random_id);
STORE_FLAG(has_sender);
@ -3493,6 +3497,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
STORE_FLAG(has_document);
STORE_FLAG(is_from_scheduled_);
STORE_FLAG(has_sender_dialog_id);
STORE_FLAG(has_ringtone_id);
END_STORE_FLAGS();
td::store(dialog_id_, storer);
if (has_message_id) {
@ -3522,6 +3527,9 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
if (has_sender_dialog_id) {
td::store(sender_dialog_id_, storer);
}
if (has_ringtone_id) {
td::store(ringtone_id_, storer);
}
}
template <class ParserT>
@ -3534,9 +3542,11 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
bool has_photo;
bool has_document;
bool has_sender_dialog_id;
bool is_silent;
bool has_ringtone_id;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(contains_mention_);
PARSE_FLAG(is_silent_);
PARSE_FLAG(is_silent);
PARSE_FLAG(has_message_id);
PARSE_FLAG(has_random_id);
PARSE_FLAG(has_sender);
@ -3546,6 +3556,7 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
PARSE_FLAG(has_document);
PARSE_FLAG(is_from_scheduled_);
PARSE_FLAG(has_sender_dialog_id);
PARSE_FLAG(has_ringtone_id);
END_PARSE_FLAGS();
td::parse(dialog_id_, parser);
if (has_message_id) {
@ -3577,16 +3588,21 @@ class NotificationManager::AddMessagePushNotificationLogEvent {
if (has_sender_dialog_id) {
td::parse(sender_dialog_id_, parser);
}
if (has_ringtone_id) {
td::parse(ringtone_id_, parser);
} else {
ringtone_id_ = is_silent ? 0 : -1;
}
}
};
void NotificationManager::add_message_push_notification(DialogId dialog_id, MessageId message_id, int64 random_id,
UserId sender_user_id, DialogId sender_dialog_id,
string sender_name, int32 date, bool is_from_scheduled,
bool contains_mention, bool initial_is_silent, bool is_silent,
string loc_key, string arg, Photo photo, Document document,
NotificationId notification_id, uint64 log_event_id,
Promise<Unit> promise) {
bool contains_mention, int64 initial_ringtone_id,
int64 ringtone_id, string loc_key, string arg, Photo photo,
Document document, NotificationId notification_id,
uint64 log_event_id, Promise<Unit> promise) {
auto is_pinned = begins_with(loc_key, "PINNED_");
auto r_info = td_->messages_manager_->get_message_push_notification_info(
dialog_id, message_id, random_id, sender_user_id, sender_dialog_id, date, is_from_scheduled, contains_mention,
@ -3641,15 +3657,15 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
sender_user_id.get(), 0, user_name, string(), string(), string(), nullptr, nullptr, 0, Auto(), string(),
string());
false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(), string(), nullptr, nullptr, 0,
Auto(), string(), string());
td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification");
}
if (log_event_id == 0 && G()->parameters().use_message_db) {
AddMessagePushNotificationLogEvent log_event{
dialog_id, message_id, random_id, sender_user_id, sender_dialog_id, sender_name,
date, is_from_scheduled, contains_mention, initial_is_silent, loc_key, arg,
dialog_id, message_id, random_id, sender_user_id, sender_dialog_id, sender_name,
date, is_from_scheduled, contains_mention, initial_ringtone_id, loc_key, arg,
photo, document, notification_id};
log_event_id = binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::AddMessagePushNotification,
get_log_event_storer(log_event));
@ -3678,7 +3694,7 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess
<< group_type << " with settings from " << settings_dialog_id;
add_notification(
group_id, group_type, dialog_id, date, settings_dialog_id, initial_is_silent, is_silent, 0, notification_id,
group_id, group_type, dialog_id, date, settings_dialog_id, initial_ringtone_id, ringtone_id, 0, notification_id,
create_new_push_message_notification(sender_user_id, sender_dialog_id, sender_name, is_outgoing, message_id,
std::move(loc_key), std::move(arg), std::move(photo), std::move(document)),
"add_message_push_notification");
@ -4131,7 +4147,7 @@ void NotificationManager::on_binlog_events(vector<BinlogEvent> &&events) {
add_message_push_notification(
log_event.dialog_id_, log_event.message_id_, log_event.random_id_, log_event.sender_user_id_,
log_event.sender_dialog_id_, log_event.sender_name_, log_event.date_, log_event.is_from_scheduled_,
log_event.contains_mention_, log_event.is_silent_, true, log_event.loc_key_, log_event.arg_,
log_event.contains_mention_, log_event.ringtone_id_, 0, log_event.loc_key_, log_event.arg_,
log_event.photo_, log_event.document_, log_event.notification_id_, event.id_,
PromiseCreator::lambda([](Result<Unit> result) {
if (result.is_error() && result.error().code() != 200 && result.error().code() != 406) {

View File

@ -17,6 +17,7 @@
#include "td/telegram/NotificationGroupType.h"
#include "td/telegram/NotificationId.h"
#include "td/telegram/NotificationType.h"
#include "td/telegram/Photo.h"
#include "td/telegram/td_api.h"
#include "td/actor/actor.h"
@ -66,7 +67,7 @@ class NotificationManager final : public Actor {
void load_group_force(NotificationGroupId group_id);
void add_notification(NotificationGroupId group_id, NotificationGroupType group_type, DialogId dialog_id, int32 date,
DialogId notification_settings_dialog_id, bool initial_is_silent, bool is_silent,
DialogId notification_settings_dialog_id, int64 initial_ringtone_id, int64 ringtone_id,
int32 min_delay_ms, NotificationId notification_id, unique_ptr<NotificationType> type,
const char *source);
@ -160,8 +161,8 @@ class NotificationManager final : public Actor {
struct PendingNotification {
int32 date = 0;
DialogId settings_dialog_id;
bool initial_is_silent = false;
bool is_silent = false;
int64 initial_ringtone_id = -1;
int64 ringtone_id = -1;
NotificationId notification_id;
unique_ptr<NotificationType> type;
@ -169,7 +170,7 @@ class NotificationManager final : public Actor {
return string_builder << "PendingNotification[" << pending_notification.notification_id << " of type "
<< pending_notification.type << " sent at " << pending_notification.date
<< " with settings from " << pending_notification.settings_dialog_id
<< ", is_silent = " << pending_notification.is_silent << "]";
<< ", ringtone_id = " << pending_notification.ringtone_id << "]";
}
};
@ -312,9 +313,9 @@ class NotificationManager final : public Actor {
void add_message_push_notification(DialogId dialog_id, MessageId message_id, int64 random_id, UserId sender_user_id,
DialogId sender_dialog_id, string sender_name, int32 date, bool is_from_scheduled,
bool contains_mention, bool initial_is_silent, bool is_silent, string loc_key,
string arg, Photo photo, Document document, NotificationId notification_id,
uint64 log_event_id, Promise<Unit> promise);
bool contains_mention, int64 initial_ringtone_id, int64 ringtone_id,
string loc_key, string arg, Photo photo, Document document,
NotificationId notification_id, uint64 log_event_id, Promise<Unit> promise);
void edit_message_push_notification(DialogId dialog_id, MessageId message_id, int32 edit_date, string loc_key,
string arg, Photo photo, Document document, uint64 log_event_id,

View File

@ -7,7 +7,6 @@
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/Global.h"
#include "td/telegram/misc.h"
#include "td/utils/common.h"
@ -21,7 +20,6 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogNotificatio
<< ", " << notification_settings.disable_pinned_message_notifications << ", "
<< notification_settings.disable_mention_notifications << ", "
<< notification_settings.use_default_mute_until << ", "
<< notification_settings.use_default_sound << ", "
<< notification_settings.use_default_show_preview << ", "
<< notification_settings.use_default_disable_pinned_message_notifications << ", "
<< notification_settings.use_default_disable_mention_notifications << ", "
@ -69,9 +67,9 @@ td_api::object_ptr<td_api::chatNotificationSettings> get_chat_notification_setti
CHECK(notification_settings != nullptr);
return td_api::make_object<td_api::chatNotificationSettings>(
notification_settings->use_default_mute_until, max(0, notification_settings->mute_until - G()->unix_time()),
notification_settings->use_default_sound, notification_settings->sound,
notification_settings->use_default_show_preview, notification_settings->show_preview,
notification_settings->use_default_disable_pinned_message_notifications,
is_notification_sound_default(notification_settings->sound),
get_notification_sound_ringtone_id(notification_settings->sound), notification_settings->use_default_show_preview,
notification_settings->show_preview, notification_settings->use_default_disable_pinned_message_notifications,
notification_settings->disable_pinned_message_notifications,
notification_settings->use_default_disable_mention_notifications,
notification_settings->disable_mention_notifications);
@ -81,8 +79,9 @@ td_api::object_ptr<td_api::scopeNotificationSettings> get_scope_notification_set
const ScopeNotificationSettings *notification_settings) {
CHECK(notification_settings != nullptr);
return td_api::make_object<td_api::scopeNotificationSettings>(
max(0, notification_settings->mute_until - G()->unix_time()), notification_settings->sound,
notification_settings->show_preview, notification_settings->disable_pinned_message_notifications,
max(0, notification_settings->mute_until - G()->unix_time()),
get_notification_sound_ringtone_id(notification_settings->sound), notification_settings->show_preview,
notification_settings->disable_pinned_message_notifications,
notification_settings->disable_mention_notifications);
}
@ -133,23 +132,17 @@ Result<DialogNotificationSettings> get_dialog_notification_settings(
if (notification_settings == nullptr) {
return Status::Error(400, "New notification settings must be non-empty");
}
if (!clean_input_string(notification_settings->sound_)) {
return Status::Error(400, "Notification settings sound must be encoded in UTF-8");
}
if (notification_settings->sound_.empty()) {
notification_settings->sound_ = "default";
}
int32 mute_until =
notification_settings->use_default_mute_for_ ? 0 : get_mute_until(notification_settings->mute_for_);
return DialogNotificationSettings(notification_settings->use_default_mute_for_, mute_until,
notification_settings->use_default_sound_, std::move(notification_settings->sound_),
notification_settings->use_default_show_preview_,
notification_settings->show_preview_, old_silent_send_message,
notification_settings->use_default_disable_pinned_message_notifications_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->use_default_disable_mention_notifications_,
notification_settings->disable_mention_notifications_);
return DialogNotificationSettings(
notification_settings->use_default_mute_for_, mute_until,
get_notification_sound(notification_settings->use_default_sound_, notification_settings->sound_id_),
notification_settings->use_default_show_preview_, notification_settings->show_preview_, old_silent_send_message,
notification_settings->use_default_disable_pinned_message_notifications_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->use_default_disable_mention_notifications_,
notification_settings->disable_mention_notifications_);
}
Result<ScopeNotificationSettings> get_scope_notification_settings(
@ -157,39 +150,43 @@ Result<ScopeNotificationSettings> get_scope_notification_settings(
if (notification_settings == nullptr) {
return Status::Error(400, "New notification settings must be non-empty");
}
if (!clean_input_string(notification_settings->sound_)) {
return Status::Error(400, "Notification settings sound must be encoded in UTF-8");
}
if (notification_settings->sound_.empty()) {
notification_settings->sound_ = "default";
}
auto mute_until = get_mute_until(notification_settings->mute_for_);
return ScopeNotificationSettings(mute_until, std::move(notification_settings->sound_),
return ScopeNotificationSettings(mute_until, get_notification_sound(false, notification_settings->sound_id_),
notification_settings->show_preview_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->disable_mention_notifications_);
}
static unique_ptr<NotificationSound> get_peer_notification_sound(
tl_object_ptr<telegram_api::peerNotifySettings> &settings) {
telegram_api::NotificationSound *sound =
#if TD_ANDROID
settings->android_sound_.get();
#elif TD_DARWIN_IOS || TD_DARWIN_TV_OS || TD_DARWIN_WATCH_OS
settings->ios_sound_.get();
#else
settings->other_sound_.get();
#endif
return get_notification_sound(sound);
}
DialogNotificationSettings get_dialog_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_use_default_disable_pinned_message_notifications,
bool old_disable_pinned_message_notifications,
bool old_use_default_disable_mention_notifications,
bool old_disable_mention_notifications) {
if (settings == nullptr) {
return DialogNotificationSettings();
}
bool use_default_mute_until = (settings->flags_ & telegram_api::peerNotifySettings::MUTE_UNTIL_MASK) == 0;
bool use_default_sound = (settings->flags_ & telegram_api::peerNotifySettings::SOUND_MASK) == 0;
bool use_default_show_preview = (settings->flags_ & telegram_api::peerNotifySettings::SHOW_PREVIEWS_MASK) == 0;
auto mute_until = use_default_mute_until || settings->mute_until_ <= G()->unix_time() ? 0 : settings->mute_until_;
auto sound = std::move(settings->sound_);
if (sound.empty()) {
sound = "default";
}
bool silent_send_message =
(settings->flags_ & telegram_api::peerNotifySettings::SILENT_MASK) == 0 ? false : settings->silent_;
return {use_default_mute_until,
mute_until,
use_default_sound,
std::move(sound),
get_peer_notification_sound(settings),
use_default_show_preview,
settings->show_previews_,
silent_send_message,
@ -202,22 +199,21 @@ DialogNotificationSettings get_dialog_notification_settings(tl_object_ptr<telegr
ScopeNotificationSettings get_scope_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_disable_pinned_message_notifications,
bool old_disable_mention_notifications) {
if (settings == nullptr) {
return ScopeNotificationSettings();
}
auto mute_until = (settings->flags_ & telegram_api::peerNotifySettings::MUTE_UNTIL_MASK) == 0 ||
settings->mute_until_ <= G()->unix_time()
? 0
: settings->mute_until_;
auto sound = std::move(settings->sound_);
if (sound.empty()) {
sound = "default";
}
auto show_preview =
(settings->flags_ & telegram_api::peerNotifySettings::SHOW_PREVIEWS_MASK) == 0 ? false : settings->show_previews_;
return {mute_until, std::move(sound), show_preview, old_disable_pinned_message_notifications,
return {mute_until, get_peer_notification_sound(settings), show_preview, old_disable_pinned_message_notifications,
old_disable_mention_notifications};
}
bool are_default_dialog_notification_settings(const DialogNotificationSettings &settings, bool compare_sound) {
return settings.use_default_mute_until && (!compare_sound || settings.use_default_sound) &&
return settings.use_default_mute_until && (!compare_sound || is_notification_sound_default(settings.sound)) &&
settings.use_default_show_preview && settings.use_default_disable_pinned_message_notifications &&
settings.use_default_disable_mention_notifications;
}

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/NotificationSound.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -18,11 +19,10 @@ namespace td {
class DialogNotificationSettings {
public:
int32 mute_until = 0;
string sound = "default";
unique_ptr<NotificationSound> sound;
bool show_preview = true;
bool silent_send_message = false;
bool use_default_mute_until = true;
bool use_default_sound = true;
bool use_default_show_preview = true;
bool is_use_default_fixed = true;
bool is_secret_chat_show_preview_fixed = false;
@ -36,7 +36,7 @@ class DialogNotificationSettings {
DialogNotificationSettings() = default;
DialogNotificationSettings(bool use_default_mute_until, int32 mute_until, bool use_default_sound, string sound,
DialogNotificationSettings(bool use_default_mute_until, int32 mute_until, unique_ptr<NotificationSound> &&sound,
bool use_default_show_preview, bool show_preview, bool silent_send_message,
bool use_default_disable_pinned_message_notifications,
bool disable_pinned_message_notifications, bool use_default_disable_mention_notifications,
@ -46,7 +46,6 @@ class DialogNotificationSettings {
, show_preview(show_preview)
, silent_send_message(silent_send_message)
, use_default_mute_until(use_default_mute_until)
, use_default_sound(use_default_sound)
, use_default_show_preview(use_default_show_preview)
, is_synchronized(true)
, use_default_disable_pinned_message_notifications(use_default_disable_pinned_message_notifications)
@ -61,7 +60,7 @@ enum class NotificationSettingsScope : int32 { Private, Group, Channel };
class ScopeNotificationSettings {
public:
int32 mute_until = 0;
string sound = "default";
unique_ptr<NotificationSound> sound;
bool show_preview = true;
bool is_synchronized = false;
@ -71,7 +70,7 @@ class ScopeNotificationSettings {
ScopeNotificationSettings() = default;
ScopeNotificationSettings(int32 mute_until, string sound, bool show_preview,
ScopeNotificationSettings(int32 mute_until, unique_ptr<NotificationSound> &&sound, bool show_preview,
bool disable_pinned_message_notifications, bool disable_mention_notifications)
: mute_until(mute_until)
, sound(std::move(sound))

View File

@ -8,6 +8,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/NotificationSound.h"
#include "td/utils/tl_helpers.h"
@ -17,7 +18,8 @@ template <class StorerT>
void store(const DialogNotificationSettings &notification_settings, StorerT &storer) {
bool is_muted = !notification_settings.use_default_mute_until && notification_settings.mute_until != 0 &&
notification_settings.mute_until > G()->unix_time();
bool has_sound = !notification_settings.use_default_sound && notification_settings.sound != "default";
bool has_sound = notification_settings.sound != nullptr;
bool has_ringtone_support = true;
BEGIN_STORE_FLAGS();
STORE_FLAG(is_muted);
STORE_FLAG(has_sound);
@ -25,7 +27,7 @@ void store(const DialogNotificationSettings &notification_settings, StorerT &sto
STORE_FLAG(notification_settings.silent_send_message);
STORE_FLAG(notification_settings.is_synchronized);
STORE_FLAG(notification_settings.use_default_mute_until);
STORE_FLAG(notification_settings.use_default_sound);
STORE_FLAG(false); // use_default_sound
STORE_FLAG(notification_settings.use_default_show_preview);
STORE_FLAG(notification_settings.is_use_default_fixed);
STORE_FLAG(!notification_settings.use_default_disable_pinned_message_notifications);
@ -33,6 +35,7 @@ void store(const DialogNotificationSettings &notification_settings, StorerT &sto
STORE_FLAG(!notification_settings.use_default_disable_mention_notifications);
STORE_FLAG(notification_settings.disable_mention_notifications);
STORE_FLAG(notification_settings.is_secret_chat_show_preview_fixed);
STORE_FLAG(has_ringtone_support);
END_STORE_FLAGS();
if (is_muted) {
store(notification_settings.mute_until, storer);
@ -46,8 +49,10 @@ template <class ParserT>
void parse(DialogNotificationSettings &notification_settings, ParserT &parser) {
bool is_muted;
bool has_sound;
bool use_default_sound;
bool use_disable_pinned_message_notifications;
bool use_disable_mention_notifications;
bool has_ringtone_support;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_muted);
PARSE_FLAG(has_sound);
@ -55,7 +60,7 @@ void parse(DialogNotificationSettings &notification_settings, ParserT &parser) {
PARSE_FLAG(notification_settings.silent_send_message);
PARSE_FLAG(notification_settings.is_synchronized);
PARSE_FLAG(notification_settings.use_default_mute_until);
PARSE_FLAG(notification_settings.use_default_sound);
PARSE_FLAG(use_default_sound);
PARSE_FLAG(notification_settings.use_default_show_preview);
PARSE_FLAG(notification_settings.is_use_default_fixed);
PARSE_FLAG(use_disable_pinned_message_notifications);
@ -63,6 +68,7 @@ void parse(DialogNotificationSettings &notification_settings, ParserT &parser) {
PARSE_FLAG(use_disable_mention_notifications);
PARSE_FLAG(notification_settings.disable_mention_notifications);
PARSE_FLAG(notification_settings.is_secret_chat_show_preview_fixed);
PARSE_FLAG(has_ringtone_support);
END_PARSE_FLAGS();
notification_settings.use_default_disable_pinned_message_notifications = !use_disable_pinned_message_notifications;
notification_settings.use_default_disable_mention_notifications = !use_disable_mention_notifications;
@ -70,14 +76,21 @@ void parse(DialogNotificationSettings &notification_settings, ParserT &parser) {
parse(notification_settings.mute_until, parser);
}
if (has_sound) {
parse(notification_settings.sound, parser);
if (has_ringtone_support) {
parse_notification_sound(notification_settings.sound, parser);
} else {
string sound;
parse(sound, parser);
notification_settings.sound = use_default_sound ? nullptr : get_legacy_notification_sound(sound);
}
}
}
template <class StorerT>
void store(const ScopeNotificationSettings &notification_settings, StorerT &storer) {
bool is_muted = notification_settings.mute_until != 0 && notification_settings.mute_until > G()->unix_time();
bool has_sound = notification_settings.sound != "default";
bool has_sound = notification_settings.sound != nullptr;
bool has_ringtone_support = true;
BEGIN_STORE_FLAGS();
STORE_FLAG(is_muted);
STORE_FLAG(has_sound);
@ -86,6 +99,7 @@ void store(const ScopeNotificationSettings &notification_settings, StorerT &stor
STORE_FLAG(notification_settings.is_synchronized);
STORE_FLAG(notification_settings.disable_pinned_message_notifications);
STORE_FLAG(notification_settings.disable_mention_notifications);
STORE_FLAG(has_ringtone_support);
END_STORE_FLAGS();
if (is_muted) {
store(notification_settings.mute_until, storer);
@ -100,6 +114,7 @@ void parse(ScopeNotificationSettings &notification_settings, ParserT &parser) {
bool is_muted;
bool has_sound;
bool silent_send_message_ignored;
bool has_ringtone_support;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_muted);
PARSE_FLAG(has_sound);
@ -108,13 +123,20 @@ void parse(ScopeNotificationSettings &notification_settings, ParserT &parser) {
PARSE_FLAG(notification_settings.is_synchronized);
PARSE_FLAG(notification_settings.disable_pinned_message_notifications);
PARSE_FLAG(notification_settings.disable_mention_notifications);
PARSE_FLAG(has_ringtone_support);
END_PARSE_FLAGS();
(void)silent_send_message_ignored;
if (is_muted) {
parse(notification_settings.mute_until, parser);
}
if (has_sound) {
parse(notification_settings.sound, parser);
if (has_ringtone_support) {
parse_notification_sound(notification_settings.sound, parser);
} else {
string sound;
parse(sound, parser);
notification_settings.sound = get_legacy_notification_sound(sound);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
struct BinlogEvent;
class NotificationSound;
class Td;
class NotificationSettingsManager final : public Actor {
public:
NotificationSettingsManager(Td *td, ActorShared<> parent);
NotificationSettingsManager(const NotificationSettingsManager &) = delete;
NotificationSettingsManager &operator=(const NotificationSettingsManager &) = delete;
NotificationSettingsManager(NotificationSettingsManager &&) = delete;
NotificationSettingsManager &operator=(NotificationSettingsManager &&) = delete;
~NotificationSettingsManager() final;
int32 get_scope_mute_until(NotificationSettingsScope scope) const;
const unique_ptr<NotificationSound> &get_scope_notification_sound(NotificationSettingsScope scope) const;
bool get_scope_disable_pinned_message_notifications(NotificationSettingsScope scope) const;
bool get_scope_disable_mention_notifications(NotificationSettingsScope scope) const;
tl_object_ptr<telegram_api::InputNotifyPeer> get_input_notify_peer(DialogId dialog_id) const;
void on_update_scope_notify_settings(NotificationSettingsScope scope,
tl_object_ptr<telegram_api::peerNotifySettings> &&peer_notify_settings);
void add_saved_ringtone(td_api::object_ptr<td_api::InputFile> &&input_file,
Promise<td_api::object_ptr<td_api::notificationSound>> &&promise);
FileId get_saved_ringtone(int64 ringtone_id, Promise<Unit> &&promise);
vector<FileId> get_saved_ringtones(Promise<Unit> &&promise);
void remove_saved_ringtone(int64 ringtone_id, Promise<Unit> &&promise);
void reload_saved_ringtones(Promise<Unit> &&promise);
void repair_saved_ringtones(Promise<Unit> &&promise);
FileSourceId get_saved_ringtones_file_source_id();
void send_save_ringtone_query(FileId ringtone_file_id, bool unsave,
Promise<telegram_api::object_ptr<telegram_api::account_SavedRingtone>> &&promise);
void send_get_dialog_notification_settings_query(DialogId dialog_id, Promise<Unit> &&promise);
const ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope,
Promise<Unit> &&promise);
void send_get_scope_notification_settings_query(NotificationSettingsScope scope, Promise<Unit> &&promise);
void on_get_dialog_notification_settings_query_finished(DialogId dialog_id, Status &&status);
void update_dialog_notify_settings(DialogId dialog_id, const DialogNotificationSettings &new_settings,
Promise<Unit> &&promise);
Status set_scope_notification_settings(NotificationSettingsScope scope,
td_api::object_ptr<td_api::scopeNotificationSettings> &&notification_settings)
TD_WARN_UNUSED_RESULT;
void reset_scope_notification_settings();
void reset_notify_settings(Promise<Unit> &&promise);
void get_notify_settings_exceptions(NotificationSettingsScope scope, bool filter_scope, bool compare_sound,
Promise<Unit> &&promise);
void init();
void after_get_difference();
void on_binlog_events(vector<BinlogEvent> &&events);
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
private:
class UpdateScopeNotificationSettingsOnServerLogEvent;
class RingtoneListLogEvent;
class UploadRingtoneCallback;
void start_up() final;
void tear_down() final;
void timeout_expired() final;
bool is_active() const;
static void on_scope_unmute_timeout_callback(void *notification_settings_manager_ptr, int64 scope_int);
Result<FileId> get_ringtone(telegram_api::object_ptr<telegram_api::Document> &&ringtone) const;
void upload_ringtone(FileId file_id, bool is_reupload,
Promise<td_api::object_ptr<td_api::notificationSound>> &&promise, vector<int> bad_parts = {});
void on_upload_ringtone(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file);
void on_upload_ringtone_error(FileId file_id, Status status);
void on_upload_saved_ringtone(telegram_api::object_ptr<telegram_api::Document> &&saved_ringtone,
Promise<td_api::object_ptr<td_api::notificationSound>> &&promise);
void on_add_saved_ringtone(FileId file_id,
telegram_api::object_ptr<telegram_api::account_SavedRingtone> &&saved_ringtone,
Promise<td_api::object_ptr<td_api::notificationSound>> &&promise);
void on_remove_saved_ringtone(int64 ringtone_id, Promise<Unit> &&promise);
void on_reload_saved_ringtones(bool is_repair,
Result<telegram_api::object_ptr<telegram_api::account_SavedRingtones>> &&result);
static string get_saved_ringtones_database_key();
void load_saved_ringtones(Promise<Unit> &&promise);
void on_load_saved_ringtones(Promise<Unit> &&promise);
void save_saved_ringtones_to_database() const;
void on_saved_ringtones_updated(bool from_database);
ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope);
const ScopeNotificationSettings *get_scope_notification_settings(NotificationSettingsScope scope) const;
td_api::object_ptr<td_api::updateScopeNotificationSettings> get_update_scope_notification_settings_object(
NotificationSettingsScope scope) const;
td_api::object_ptr<td_api::updateSavedNotificationSounds> get_update_saved_notification_sounds_object() const;
void on_scope_unmute(NotificationSettingsScope scope);
static string get_notification_settings_scope_database_key(NotificationSettingsScope scope);
static void save_scope_notification_settings(NotificationSettingsScope scope,
const ScopeNotificationSettings &new_settings);
bool update_scope_notification_settings(NotificationSettingsScope scope, ScopeNotificationSettings *current_settings,
ScopeNotificationSettings &&new_settings);
static uint64 save_update_scope_notification_settings_on_server_log_event(NotificationSettingsScope scope);
void update_scope_notification_settings_on_server(NotificationSettingsScope scope, uint64 log_event_id);
void schedule_scope_unmute(NotificationSettingsScope scope, int32 mute_until);
void update_scope_unmute_timeout(NotificationSettingsScope scope, int32 &old_mute_until, int32 new_mute_until);
Td *td_;
ActorShared<> parent_;
bool is_inited_ = false;
bool are_saved_ringtones_loaded_ = false;
bool are_saved_ringtones_reloaded_ = false;
ScopeNotificationSettings users_notification_settings_;
ScopeNotificationSettings chats_notification_settings_;
ScopeNotificationSettings channels_notification_settings_;
MultiTimeout scope_unmute_timeout_{"ScopeUnmuteTimeout"};
int64 saved_ringtone_hash_ = 0;
vector<FileId> saved_ringtone_file_ids_;
vector<FileId> sorted_saved_ringtone_file_ids_;
FileSourceId saved_ringtones_file_source_id_;
std::shared_ptr<UploadRingtoneCallback> upload_ringtone_callback_;
struct UploadedRingtone {
bool is_reupload;
Promise<td_api::object_ptr<td_api::notificationSound>> promise;
UploadedRingtone(bool is_reupload, Promise<td_api::object_ptr<td_api::notificationSound>> promise)
: is_reupload(is_reupload), promise(std::move(promise)) {
}
};
FlatHashMap<FileId, UploadedRingtone, FileIdHash> being_uploaded_ringtones_;
vector<Promise<Unit>> reload_saved_ringtones_queries_;
vector<Promise<Unit>> repair_saved_ringtones_queries_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_notification_settings_queries_;
};
} // namespace td

View File

@ -0,0 +1,299 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/NotificationSound.h"
#include "td/utils/tl_helpers.h"
namespace td {
class NotificationSoundNone final : public NotificationSound {
public:
NotificationSoundNone() = default;
NotificationSoundType get_type() const final {
return NotificationSoundType::None;
}
};
class NotificationSoundLocal final : public NotificationSound {
public:
string title_;
string data_;
NotificationSoundLocal() = default;
NotificationSoundLocal(string title, string data) : title_(std::move(title)), data_(std::move(data)) {
}
NotificationSoundType get_type() const final {
return NotificationSoundType::Local;
}
};
class NotificationSoundRingtone final : public NotificationSound {
public:
int64 ringtone_id_;
NotificationSoundRingtone() = default;
explicit NotificationSoundRingtone(int64 ringtone_id) : ringtone_id_(ringtone_id) {
}
NotificationSoundType get_type() const final {
return NotificationSoundType::Ringtone;
}
};
template <class StorerT>
static void store(const NotificationSound *notification_sound, StorerT &storer) {
CHECK(notification_sound != nullptr);
auto sound_type = notification_sound->get_type();
store(sound_type, storer);
switch (sound_type) {
case NotificationSoundType::None:
break;
case NotificationSoundType::Local: {
const auto *sound = static_cast<const NotificationSoundLocal *>(notification_sound);
store(sound->title_, storer);
store(sound->data_, storer);
break;
}
case NotificationSoundType::Ringtone: {
const auto *sound = static_cast<const NotificationSoundRingtone *>(notification_sound);
store(sound->ringtone_id_, storer);
break;
}
default:
UNREACHABLE();
}
}
template <class ParserT>
static void parse(unique_ptr<NotificationSound> &notification_sound, ParserT &parser) {
NotificationSoundType sound_type;
parse(sound_type, parser);
switch (sound_type) {
case NotificationSoundType::None:
notification_sound = make_unique<NotificationSoundNone>();
break;
case NotificationSoundType::Local: {
auto sound = make_unique<NotificationSoundLocal>();
parse(sound->title_, parser);
parse(sound->data_, parser);
notification_sound = std::move(sound);
break;
}
case NotificationSoundType::Ringtone: {
auto sound = make_unique<NotificationSoundRingtone>();
parse(sound->ringtone_id_, parser);
notification_sound = std::move(sound);
break;
}
default:
LOG(FATAL) << "Have unknown notification sound type " << static_cast<int32>(sound_type);
}
}
void store_notification_sound(const NotificationSound *notification_sound, LogEventStorerCalcLength &storer) {
store(notification_sound, storer);
}
void store_notification_sound(const NotificationSound *notification_sound, LogEventStorerUnsafe &storer) {
store(notification_sound, storer);
}
void parse_notification_sound(unique_ptr<NotificationSound> &notification_sound, LogEventParser &parser) {
parse(notification_sound, parser);
}
StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<NotificationSound> &notification_sound) {
if (notification_sound == nullptr) {
return string_builder << "DefaultSound";
}
switch (notification_sound->get_type()) {
case NotificationSoundType::None:
return string_builder << "NoSound";
case NotificationSoundType::Local: {
const auto *sound = static_cast<const NotificationSoundLocal *>(notification_sound.get());
return string_builder << "LocalSound[" << sound->title_ << '|' << sound->data_ << ']';
}
case NotificationSoundType::Ringtone: {
const auto *sound = static_cast<const NotificationSoundRingtone *>(notification_sound.get());
return string_builder << "Ringtone[" << sound->ringtone_id_ << ']';
}
default:
UNREACHABLE();
return string_builder;
}
}
bool is_notification_sound_default(const unique_ptr<NotificationSound> &notification_sound) {
if (notification_sound == nullptr) {
return true;
}
return notification_sound->get_type() == NotificationSoundType::Local;
}
bool are_equivalent_notification_sounds(const unique_ptr<NotificationSound> &lhs,
const unique_ptr<NotificationSound> &rhs) {
if (is_notification_sound_default(lhs)) {
return is_notification_sound_default(rhs);
}
if (is_notification_sound_default(rhs)) {
return false;
}
auto sound_type = lhs->get_type();
if (sound_type != rhs->get_type()) {
return false;
}
switch (sound_type) {
case NotificationSoundType::None:
return true;
case NotificationSoundType::Ringtone:
return static_cast<const NotificationSoundRingtone *>(lhs.get())->ringtone_id_ ==
static_cast<const NotificationSoundRingtone *>(rhs.get())->ringtone_id_;
case NotificationSoundType::Local:
default:
UNREACHABLE();
break;
}
return false;
}
bool are_different_equivalent_notification_sounds(const unique_ptr<NotificationSound> &lhs,
const unique_ptr<NotificationSound> &rhs) {
if (lhs == nullptr) {
return rhs != nullptr && rhs->get_type() == NotificationSoundType::Local;
}
if (rhs == nullptr) {
return lhs->get_type() == NotificationSoundType::Local;
}
if (lhs->get_type() != NotificationSoundType::Local || rhs->get_type() != NotificationSoundType::Local) {
return false;
}
const auto *lhs_local = static_cast<const NotificationSoundLocal *>(lhs.get());
const auto *rhs_local = static_cast<const NotificationSoundLocal *>(rhs.get());
return lhs_local->title_ != rhs_local->title_ || lhs_local->data_ != rhs_local->data_;
}
int64 get_notification_sound_ringtone_id(const unique_ptr<NotificationSound> &notification_sound) {
if (notification_sound == nullptr) {
return -1;
}
switch (notification_sound->get_type()) {
case NotificationSoundType::None:
return 0;
case NotificationSoundType::Local:
return -1;
case NotificationSoundType::Ringtone: {
return static_cast<const NotificationSoundRingtone *>(notification_sound.get())->ringtone_id_;
default:
UNREACHABLE();
return -1;
}
}
}
unique_ptr<NotificationSound> get_legacy_notification_sound(const string &sound) {
if (sound == "default") {
return nullptr;
}
if (sound.empty()) {
return make_unique<NotificationSoundNone>();
}
return td::make_unique<NotificationSoundLocal>(sound, sound);
}
unique_ptr<NotificationSound> get_notification_sound(bool use_default_sound, int64 ringtone_id) {
if (use_default_sound || ringtone_id == -1) {
return nullptr;
}
if (ringtone_id == 0) {
return make_unique<NotificationSoundNone>();
}
return td::make_unique<NotificationSoundRingtone>(ringtone_id);
}
unique_ptr<NotificationSound> get_notification_sound(telegram_api::NotificationSound *notification_sound) {
if (notification_sound == nullptr) {
return nullptr;
}
switch (notification_sound->get_id()) {
case telegram_api::notificationSoundDefault::ID:
return nullptr;
case telegram_api::notificationSoundNone::ID:
return make_unique<NotificationSoundNone>();
case telegram_api::notificationSoundLocal::ID: {
const auto *sound = static_cast<telegram_api::notificationSoundLocal *>(notification_sound);
return td::make_unique<NotificationSoundLocal>(std::move(sound->title_), std::move(sound->data_));
}
case telegram_api::notificationSoundRingtone::ID: {
const auto *sound = static_cast<telegram_api::notificationSoundRingtone *>(notification_sound);
if (sound->id_ == 0 || sound->id_ == -1) {
LOG(ERROR) << "Receive ringtone with id = " << sound->id_;
return make_unique<NotificationSoundNone>();
}
return td::make_unique<NotificationSoundRingtone>(sound->id_);
}
default:
UNREACHABLE();
return nullptr;
}
}
telegram_api::object_ptr<telegram_api::NotificationSound> get_input_notification_sound(
const unique_ptr<NotificationSound> &notification_sound) {
if (notification_sound == nullptr) {
return nullptr;
}
// must not return nullptr if notification_sound != nullptr
switch (notification_sound->get_type()) {
case NotificationSoundType::None:
return telegram_api::make_object<telegram_api::notificationSoundNone>();
case NotificationSoundType::Local: {
const auto *sound = static_cast<const NotificationSoundLocal *>(notification_sound.get());
return telegram_api::make_object<telegram_api::notificationSoundLocal>(sound->title_, sound->data_);
}
case NotificationSoundType::Ringtone: {
const auto *sound = static_cast<const NotificationSoundRingtone *>(notification_sound.get());
return telegram_api::make_object<telegram_api::notificationSoundRingtone>(sound->ringtone_id_);
}
default:
UNREACHABLE();
return nullptr;
}
}
unique_ptr<NotificationSound> dup_notification_sound(const unique_ptr<NotificationSound> &notification_sound) {
if (notification_sound == nullptr) {
return nullptr;
}
switch (notification_sound->get_type()) {
case NotificationSoundType::None:
return make_unique<NotificationSoundNone>();
case NotificationSoundType::Local: {
const auto *sound = static_cast<const NotificationSoundLocal *>(notification_sound.get());
return td::make_unique<NotificationSoundLocal>(sound->title_, sound->data_);
}
case NotificationSoundType::Ringtone: {
const auto *sound = static_cast<const NotificationSoundRingtone *>(notification_sound.get());
return td::make_unique<NotificationSoundRingtone>(sound->ringtone_id_);
}
default:
UNREACHABLE();
return nullptr;
}
}
} // namespace td

View File

@ -0,0 +1,68 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/logevent/LogEvent.h"
#include "td/telegram/NotificationSoundType.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
namespace td {
class NotificationSound {
public:
NotificationSound() = default;
NotificationSound(const NotificationSound &) = delete;
NotificationSound &operator=(const NotificationSound &) = delete;
NotificationSound(NotificationSound &&) = delete;
NotificationSound &operator=(NotificationSound &&) = delete;
virtual NotificationSoundType get_type() const = 0;
virtual ~NotificationSound() = default;
template <class StorerT>
void store(StorerT &storer) const;
};
StringBuilder &operator<<(StringBuilder &string_builder, const unique_ptr<NotificationSound> &notification_sound);
void store_notification_sound(const NotificationSound *notification_sound, LogEventStorerCalcLength &storer);
void store_notification_sound(const NotificationSound *notification_sound, LogEventStorerUnsafe &storer);
template <class StorerT>
void NotificationSound::store(StorerT &storer) const {
store_notification_sound(this, storer);
}
void parse_notification_sound(unique_ptr<NotificationSound> &notification_sound, LogEventParser &parser);
bool is_notification_sound_default(const unique_ptr<NotificationSound> &notification_sound);
bool are_equivalent_notification_sounds(const unique_ptr<NotificationSound> &lhs,
const unique_ptr<NotificationSound> &rhs);
bool are_different_equivalent_notification_sounds(const unique_ptr<NotificationSound> &lhs,
const unique_ptr<NotificationSound> &rhs);
int64 get_notification_sound_ringtone_id(const unique_ptr<NotificationSound> &notification_sound);
unique_ptr<NotificationSound> get_legacy_notification_sound(const string &sound);
unique_ptr<NotificationSound> get_notification_sound(bool use_default_sound, int64 ringtone_id);
unique_ptr<NotificationSound> get_notification_sound(telegram_api::NotificationSound *notification_sound);
telegram_api::object_ptr<telegram_api::NotificationSound> get_input_notification_sound(
const unique_ptr<NotificationSound> &notification_sound);
unique_ptr<NotificationSound> dup_notification_sound(const unique_ptr<NotificationSound> &notification_sound);
} // namespace td

View File

@ -0,0 +1,15 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
namespace td {
enum class NotificationSoundType : int32 { None, Local, Ringtone };
} // namespace td

View File

@ -12,6 +12,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/VideoNotesManager.h"

View File

@ -7,6 +7,7 @@
#include "td/telegram/OptionManager.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/AttachMenuManager.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ConfigShared.h"
@ -70,6 +71,46 @@ class SetDefaultReactionQuery final : public Td::ResultHandler {
OptionManager::OptionManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
send_unix_time_update();
if (G()->shared_config().have_option("language_database_path")) {
G()->shared_config().set_option_string("language_pack_database_path",
G()->shared_config().get_option_string("language_database_path"));
G()->shared_config().set_option_empty("language_database_path");
}
if (G()->shared_config().have_option("language_pack")) {
G()->shared_config().set_option_string("localization_target",
G()->shared_config().get_option_string("language_pack"));
G()->shared_config().set_option_empty("language_pack");
}
if (G()->shared_config().have_option("language_code")) {
G()->shared_config().set_option_string("language_pack_id", G()->shared_config().get_option_string("language_code"));
G()->shared_config().set_option_empty("language_code");
}
if (!G()->shared_config().have_option("message_text_length_max")) {
G()->shared_config().set_option_integer("message_text_length_max", 4096);
}
if (!G()->shared_config().have_option("message_caption_length_max")) {
G()->shared_config().set_option_integer("message_caption_length_max", 1024);
}
if (!G()->shared_config().have_option("suggested_video_note_length")) {
G()->shared_config().set_option_integer("suggested_video_note_length", 384);
}
if (!G()->shared_config().have_option("suggested_video_note_video_bitrate")) {
G()->shared_config().set_option_integer("suggested_video_note_video_bitrate", 1000);
}
if (!G()->shared_config().have_option("suggested_video_note_audio_bitrate")) {
G()->shared_config().set_option_integer("suggested_video_note_audio_bitrate", 64);
}
if (!G()->shared_config().have_option("notification_sound_duration_max")) {
G()->shared_config().set_option_integer("notification_sound_duration_max", 5);
}
if (!G()->shared_config().have_option("notification_sound_size_max")) {
G()->shared_config().set_option_integer("notification_sound_size_max", 307200);
}
if (!G()->shared_config().have_option("notification_sound_count_max")) {
G()->shared_config().set_option_integer("notification_sound_count_max", G()->is_test_dc() ? 5 : 100);
}
G()->shared_config().set_option_integer("utc_time_offset", Clocks::tz_offset());
}
void OptionManager::tear_down() {
@ -226,6 +267,7 @@ void OptionManager::on_option_updated(const string &name) {
if (G()->mtproto_header().set_language_code(G()->shared_config().get_option_string(name))) {
G()->net_query_dispatcher().update_mtproto_header();
}
send_closure(td_->attach_menu_manager_actor_, &AttachMenuManager::reload_attach_menu_bots, Promise<Unit>());
}
if (name == "language_pack_version") {
send_closure(td_->language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, false, -1);

View File

@ -14,8 +14,10 @@
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/PasswordManager.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/Td.h"
#include "td/telegram/ThemeManager.h"
#include "td/telegram/UpdatesManager.h"
#include "td/utils/algorithm.h"
@ -1145,24 +1147,14 @@ void answer_pre_checkout_query(Td *td, int64 pre_checkout_query_id, const string
td->create_handler<SetBotPreCheckoutAnswerQuery>(std::move(promise))->send(pre_checkout_query_id, error_message);
}
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::paymentFormTheme> &theme,
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<tl_object_ptr<td_api::paymentForm>> &&promise) {
TRY_RESULT_PROMISE(promise, server_message_id, td->messages_manager_->get_invoice_message_id(full_message_id));
tl_object_ptr<telegram_api::dataJSON> theme_parameters;
if (theme != nullptr) {
theme_parameters = make_tl_object<telegram_api::dataJSON>(string());
theme_parameters->data_ = json_encode<string>(json_object([&theme](auto &o) {
auto get_color = [](int32 color) {
return static_cast<int64>(static_cast<uint32>(color) | 0x000000FF);
};
o("bg_color", get_color(theme->background_color_));
o("text_color", get_color(theme->text_color_));
o("hint_color", get_color(theme->hint_color_));
o("link_color", get_color(theme->link_color_));
o("button_color", get_color(theme->button_color_));
o("button_text_color", get_color(theme->button_text_color_));
}));
theme_parameters->data_ = ThemeManager::get_theme_parameters_json_string(theme, false);
}
td->create_handler<GetPaymentFormQuery>(std::move(promise))
->send(full_message_id.get_dialog_id(), server_message_id, std::move(theme_parameters));

View File

@ -180,7 +180,7 @@ void answer_shipping_query(Td *td, int64 shipping_query_id,
void answer_pre_checkout_query(Td *td, int64 pre_checkout_query_id, const string &error_message,
Promise<Unit> &&promise);
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::paymentFormTheme> &theme,
void get_payment_form(Td *td, FullMessageId full_message_id, const td_api::object_ptr<td_api::themeParameters> &theme,
Promise<tl_object_ptr<td_api::paymentForm>> &&promise);
void validate_order_info(Td *td, FullMessageId full_message_id, tl_object_ptr<td_api::orderInfo> order_info,

View File

@ -9,153 +9,23 @@
#include "td/telegram/files/FileEncryptionKey.h"
#include "td/telegram/files/FileLocation.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/ConfigShared.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/utils/algorithm.h"
#include "td/utils/base64.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/SliceBuilder.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace td {
static uint16 get_dimension(int32 size, const char *source) {
if (size < 0 || size > 65535) {
LOG(ERROR) << "Wrong image dimension = " << size << " from " << source;
return 0;
}
return narrow_cast<uint16>(size);
}
Dimensions get_dimensions(int32 width, int32 height, const char *source) {
Dimensions result;
result.width = get_dimension(width, source);
result.height = get_dimension(height, source);
if (result.width == 0 || result.height == 0) {
result.width = 0;
result.height = 0;
}
return result;
}
static uint32 get_pixel_count(const Dimensions &dimensions) {
return static_cast<uint32>(dimensions.width) * static_cast<uint32>(dimensions.height);
}
bool operator==(const Dimensions &lhs, const Dimensions &rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
bool operator!=(const Dimensions &lhs, const Dimensions &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions) {
return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")";
}
td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string &packed) {
if (G()->shared_config().get_option_boolean("disable_minithumbnails")) {
return nullptr;
}
if (packed.size() < 3) {
return nullptr;
}
if (packed[0] == '\x01') {
static const string header =
base64_decode(
"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////"
"m8H///"
"/6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/"
"wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/"
"8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0R"
"FRkd"
"ISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2"
"uHi4"
"+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/"
"8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkN"
"ERUZ"
"HSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2"
"Nna4"
"uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=")
.move_as_ok();
static const string footer = base64_decode("/9k=").move_as_ok();
auto result = td_api::make_object<td_api::minithumbnail>();
result->height_ = static_cast<unsigned char>(packed[1]);
result->width_ = static_cast<unsigned char>(packed[2]);
result->data_ = PSTRING() << header.substr(0, 164) << packed[1] << header[165] << packed[2] << header.substr(167)
<< packed.substr(3) << footer;
return result;
}
return nullptr;
}
static td_api::object_ptr<td_api::ThumbnailFormat> get_thumbnail_format_object(PhotoFormat format) {
switch (format) {
case PhotoFormat::Jpeg:
return td_api::make_object<td_api::thumbnailFormatJpeg>();
case PhotoFormat::Png:
return td_api::make_object<td_api::thumbnailFormatPng>();
case PhotoFormat::Webp:
return td_api::make_object<td_api::thumbnailFormatWebp>();
case PhotoFormat::Gif:
return td_api::make_object<td_api::thumbnailFormatGif>();
case PhotoFormat::Tgs:
return td_api::make_object<td_api::thumbnailFormatTgs>();
case PhotoFormat::Mpeg4:
return td_api::make_object<td_api::thumbnailFormatMpeg4>();
case PhotoFormat::Webm:
return td_api::make_object<td_api::thumbnailFormatWebm>();
default:
UNREACHABLE();
return nullptr;
}
}
static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) {
switch (format) {
case PhotoFormat::Jpeg:
return string_builder << "jpg";
case PhotoFormat::Png:
return string_builder << "png";
case PhotoFormat::Webp:
return string_builder << "webp";
case PhotoFormat::Gif:
return string_builder << "gif";
case PhotoFormat::Tgs:
return string_builder << "tgs";
case PhotoFormat::Mpeg4:
return string_builder << "mp4";
case PhotoFormat::Webm:
return string_builder << "webm";
default:
UNREACHABLE();
return string_builder;
}
}
static FileId register_photo(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash,
std::string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id,
PhotoFormat format) {
LOG(DEBUG) << "Receive " << format << " photo " << id << " of type " << source.get_file_type("register_photo")
<< " from " << dc_id;
auto suggested_name = PSTRING() << source.get_unique_name(id) << '.' << format;
auto file_location_source = owner_dialog_id.get_type() == DialogType::SecretChat ? FileLocationSource::FromUser
: FileLocationSource::FromServer;
return file_manager->register_remote(
FullRemoteFileLocation(source, id, access_hash, dc_id, std::move(file_reference)), file_location_source,
owner_dialog_id, file_size, 0, std::move(suggested_name));
}
ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash,
tl_object_ptr<telegram_api::UserProfilePhoto> &&profile_photo_ptr) {
ProfilePhoto result;
@ -173,10 +43,10 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64
if (!G()->shared_config().get_option_boolean("disable_minithumbnails")) {
result.minithumbnail = profile_photo->stripped_thumb_.as_slice().str();
}
result.small_file_id = register_photo(
result.small_file_id = register_photo_size(
file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, false), result.id,
0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg);
result.big_file_id = register_photo(
result.big_file_id = register_photo_size(
file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, true), result.id,
0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg);
break;
@ -235,11 +105,11 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6
result.has_animation = chat_photo->has_video_;
result.minithumbnail = chat_photo->stripped_thumb_.as_slice().str();
result.small_file_id =
register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, false),
chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg);
register_photo_size(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, false),
chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg);
result.big_file_id =
register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, true),
chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg);
register_photo_size(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, true),
chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg);
break;
}
@ -333,266 +203,6 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dial
<< ", has_animation = " << dialog_photo.has_animation << ">";
}
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
int32 width, int32 height) {
if (bytes.empty()) {
return PhotoSize();
}
PhotoSize res;
res.type = 't';
res.dimensions = get_dimensions(width, height, "get_secret_thumbnail_photo_size");
res.size = narrow_cast<int32>(bytes.size());
// generate some random remote location to save
auto dc_id = DcId::invalid();
auto photo_id = -(Random::secure_int64() & std::numeric_limits<int64>::max());
res.file_id = file_manager->register_remote(
FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id,
string()),
FileLocationSource::FromServer, owner_dialog_id, res.size, 0,
PSTRING() << static_cast<uint64>(photo_id) << ".jpg");
file_manager->set_content(res.file_id, std::move(bytes));
return res;
}
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
int64 access_hash, std::string file_reference, DcId dc_id,
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
PhotoFormat format) {
CHECK(size_ptr != nullptr);
string type;
PhotoSize res;
BufferSlice content;
switch (size_ptr->get_id()) {
case telegram_api::photoSizeEmpty::ID:
return std::move(res);
case telegram_api::photoSize::ID: {
auto size = move_tl_object_as<telegram_api::photoSize>(size_ptr);
type = std::move(size->type_);
res.dimensions = get_dimensions(size->w_, size->h_, "photoSize");
res.size = size->size_;
break;
}
case telegram_api::photoCachedSize::ID: {
auto size = move_tl_object_as<telegram_api::photoCachedSize>(size_ptr);
type = std::move(size->type_);
CHECK(size->bytes_.size() <= static_cast<size_t>(std::numeric_limits<int32>::max()));
res.dimensions = get_dimensions(size->w_, size->h_, "photoCachedSize");
res.size = static_cast<int32>(size->bytes_.size());
content = std::move(size->bytes_);
break;
}
case telegram_api::photoStrippedSize::ID: {
auto size = move_tl_object_as<telegram_api::photoStrippedSize>(size_ptr);
if (format != PhotoFormat::Jpeg) {
if (G()->shared_config().get_option_boolean("disable_minithumbnails")) {
LOG(DEBUG) << "Receive unexpected JPEG minithumbnail";
} else {
LOG(ERROR) << "Receive unexpected JPEG minithumbnail in photo " << id << " from " << source << " of format "
<< format;
}
if (G()->shared_config().get_option_boolean("disable_minithumbnails")) {
return std::string("");
} else {
return std::move(res);
}
}
if (G()->shared_config().get_option_boolean("disable_minithumbnails")) {
return std::string("");
} else {
return size->bytes_.as_slice().str();
}
}
case telegram_api::photoSizeProgressive::ID: {
auto size = move_tl_object_as<telegram_api::photoSizeProgressive>(size_ptr);
if (size->sizes_.empty()) {
LOG(ERROR) << "Receive photo " << id << " from " << source << " with empty size " << to_string(size);
return std::move(res);
}
std::sort(size->sizes_.begin(), size->sizes_.end());
type = std::move(size->type_);
res.dimensions = get_dimensions(size->w_, size->h_, "photoSizeProgressive");
res.size = size->sizes_.back();
size->sizes_.pop_back();
res.progressive_sizes = std::move(size->sizes_);
break;
}
case telegram_api::photoPathSize::ID: {
auto size = move_tl_object_as<telegram_api::photoPathSize>(size_ptr);
if (format != PhotoFormat::Tgs && format != PhotoFormat::Webp && format != PhotoFormat::Webm) {
LOG(ERROR) << "Receive unexpected SVG minithumbnail in photo " << id << " from " << source << " of format "
<< format;
return std::move(res);
}
return size->bytes_.as_slice().str();
}
default:
UNREACHABLE();
break;
}
if (type.size() != 1) {
LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res;
res.type = 0;
} else {
res.type = static_cast<uint8>(type[0]);
if (res.type >= 128) {
LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res;
res.type = 0;
}
}
if (source.get_type("get_photo_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
res.file_id = register_photo(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, format);
if (!content.empty()) {
file_manager->set_content(res.file_id, std::move(content));
}
return std::move(res);
}
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
std::string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size) {
CHECK(size != nullptr);
AnimationSize res;
if (size->type_ != "v" && size->type_ != "u") {
LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size);
}
res.type = static_cast<uint8>(size->type_[0]);
if (res.type >= 128) {
LOG(ERROR) << "Wrong videoSize \"" << res.type << "\" " << res;
res.type = 0;
}
res.dimensions = get_dimensions(size->w_, size->h_, "get_animation_size");
res.size = size->size_;
if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) {
res.main_frame_timestamp = size->video_start_ts_;
}
if (source.get_type("get_animation_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
res.file_id = register_photo(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, PhotoFormat::Mpeg4);
return res;
}
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::WebDocument> web_document_ptr) {
if (web_document_ptr == nullptr) {
return {};
}
FileId file_id;
vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
int32 size = 0;
string mime_type;
switch (web_document_ptr->get_id()) {
case telegram_api::webDocument::ID: {
auto web_document = move_tl_object_as<telegram_api::webDocument>(web_document_ptr);
auto r_http_url = parse_url(web_document->url_);
if (r_http_url.is_error()) {
LOG(ERROR) << "Can't parse URL " << web_document->url_;
return {};
}
auto http_url = r_http_url.move_as_ok();
auto url = http_url.get_url();
file_id = file_manager->register_remote(FullRemoteFileLocation(file_type, url, web_document->access_hash_),
FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_,
get_url_query_file_name(http_url.query_));
size = web_document->size_;
mime_type = std::move(web_document->mime_type_);
attributes = std::move(web_document->attributes_);
break;
}
case telegram_api::webDocumentNoProxy::ID: {
auto web_document = move_tl_object_as<telegram_api::webDocumentNoProxy>(web_document_ptr);
if (web_document->url_.find('.') == string::npos) {
LOG(ERROR) << "Receive invalid URL " << web_document->url_;
return {};
}
auto r_file_id = file_manager->from_persistent_id(web_document->url_, file_type);
if (r_file_id.is_error()) {
LOG(ERROR) << "Can't register URL: " << r_file_id.error();
return {};
}
file_id = r_file_id.move_as_ok();
size = web_document->size_;
mime_type = std::move(web_document->mime_type_);
attributes = std::move(web_document->attributes_);
break;
}
default:
UNREACHABLE();
}
CHECK(file_id.is_valid());
bool is_animation = mime_type == "video/mp4";
bool is_gif = mime_type == "image/gif";
Dimensions dimensions;
for (auto &attribute : attributes) {
switch (attribute->get_id()) {
case telegram_api::documentAttributeImageSize::ID: {
auto image_size = move_tl_object_as<telegram_api::documentAttributeImageSize>(attribute);
dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize");
break;
}
case telegram_api::documentAttributeAnimated::ID:
case telegram_api::documentAttributeHasStickers::ID:
case telegram_api::documentAttributeSticker::ID:
case telegram_api::documentAttributeVideo::ID:
case telegram_api::documentAttributeAudio::ID:
LOG(ERROR) << "Unexpected web document attribute " << to_string(attribute);
break;
case telegram_api::documentAttributeFilename::ID:
break;
default:
UNREACHABLE();
}
}
PhotoSize s;
s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'n'));
s.dimensions = dimensions;
s.size = size;
s.file_id = file_id;
return s;
}
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
PhotoFormat format) {
if (!photo_size.file_id.is_valid()) {
return nullptr;
}
if (format == PhotoFormat::Jpeg && photo_size.type == 'g') {
format = PhotoFormat::Gif;
}
return td_api::make_object<td_api::thumbnail>(get_thumbnail_format_object(format), photo_size.dimensions.width,
photo_size.dimensions.height,
file_manager->get_file_object(photo_size.file_id));
}
static tl_object_ptr<td_api::photoSize> get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size) {
if (photo_size == nullptr || !photo_size->file_id.is_valid()) {
return nullptr;
@ -623,41 +233,6 @@ static vector<td_api::object_ptr<td_api::photoSize>> get_photo_sizes_object(File
return sizes;
}
bool operator==(const PhotoSize &lhs, const PhotoSize &rhs) {
return lhs.type == rhs.type && lhs.dimensions == rhs.dimensions && lhs.size == rhs.size &&
lhs.file_id == rhs.file_id && lhs.progressive_sizes == rhs.progressive_sizes;
}
bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs) {
return !(lhs == rhs);
}
bool operator<(const PhotoSize &lhs, const PhotoSize &rhs) {
if (lhs.size != rhs.size) {
return lhs.size < rhs.size;
}
auto lhs_pixels = get_pixel_count(lhs.dimensions);
auto rhs_pixels = get_pixel_count(rhs.dimensions);
if (lhs_pixels != rhs_pixels) {
return lhs_pixels < rhs_pixels;
}
int32 lhs_type = lhs.type == 't' ? -1 : lhs.type;
int32 rhs_type = rhs.type == 't' ? -1 : rhs.type;
if (lhs_type != rhs_type) {
return lhs_type < rhs_type;
}
if (lhs.file_id != rhs.file_id) {
return lhs.file_id.get() < rhs.file_id.get();
}
return lhs.dimensions.width < rhs.dimensions.width;
}
StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size) {
return string_builder << "{type = " << photo_size.type << ", dimensions = " << photo_size.dimensions
<< ", size = " << photo_size.size << ", file_id = " << photo_size.file_id
<< ", progressive_sizes = " << photo_size.progressive_sizes << "}";
}
static tl_object_ptr<td_api::animatedChatPhoto> get_animated_chat_photo_object(FileManager *file_manager,
const AnimationSize *animation_size) {
if (animation_size == nullptr || !animation_size->file_id.is_valid()) {
@ -669,20 +244,6 @@ static tl_object_ptr<td_api::animatedChatPhoto> get_animated_chat_photo_object(F
animation_size->main_frame_timestamp);
}
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs) {
return static_cast<const PhotoSize &>(lhs) == static_cast<const PhotoSize &>(rhs) &&
fabs(lhs.main_frame_timestamp - rhs.main_frame_timestamp) < 1e-3;
}
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size) {
return string_builder << static_cast<const PhotoSize &>(animation_size) << " from "
<< animation_size.main_frame_timestamp;
}
Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr<EncryptedFile> &&file,
tl_object_ptr<secret_api::decryptedMessageMediaPhoto> &&photo,
DialogId owner_dialog_id) {

View File

@ -9,9 +9,7 @@
#include "td/telegram/DialogId.h"
#include "td/telegram/EncryptedFile.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/td_api.h"
@ -22,17 +20,11 @@
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Variant.h"
namespace td {
class FileManager;
struct Dimensions {
uint16 width = 0;
uint16 height = 0;
};
struct DialogPhoto {
FileId small_file_id;
FileId big_file_id;
@ -44,18 +36,6 @@ struct ProfilePhoto final : public DialogPhoto {
int64 id = 0;
};
struct PhotoSize {
int32 type = 0;
Dimensions dimensions;
int32 size = 0;
FileId file_id;
vector<int32> progressive_sizes;
};
struct AnimationSize final : public PhotoSize {
double main_frame_timestamp = 0.0;
};
struct Photo {
MovableValue<int64, -2> id;
int32 date = 0;
@ -72,15 +52,6 @@ struct Photo {
}
};
Dimensions get_dimensions(int32 width, int32 height, const char *source);
bool operator==(const Dimensions &lhs, const Dimensions &rhs);
bool operator!=(const Dimensions &lhs, const Dimensions &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions);
td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string &packed);
ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash,
tl_object_ptr<telegram_api::UserProfilePhoto> &&profile_photo_ptr);
tl_object_ptr<td_api::profilePhoto> get_profile_photo_object(FileManager *file_manager,
@ -107,34 +78,6 @@ bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo);
enum class PhotoFormat : int32 { Jpeg, Png, Webp, Gif, Tgs, Mpeg4, Webm };
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
int32 width, int32 height);
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
int64 access_hash, string file_reference, DcId dc_id,
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
PhotoFormat format);
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size);
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::WebDocument> web_document_ptr);
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
PhotoFormat format);
bool operator==(const PhotoSize &lhs, const PhotoSize &rhs);
bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs);
bool operator<(const PhotoSize &lhs, const PhotoSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size);
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs);
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size);
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::Photo> &&photo, DialogId owner_dialog_id);
Photo get_photo(FileManager *file_manager, tl_object_ptr<telegram_api::photo> &&photo, DialogId owner_dialog_id);
Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr<EncryptedFile> &&file,

View File

@ -8,27 +8,14 @@
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoSize.hpp"
#include "td/telegram/Version.h"
#include "td/telegram/ConfigShared.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void store(Dimensions dimensions, StorerT &storer) {
store(static_cast<uint32>((static_cast<uint32>(dimensions.width) << 16) | dimensions.height), storer);
}
template <class ParserT>
void parse(Dimensions &dimensions, ParserT &parser) {
uint32 width_height;
parse(width_height, parser);
dimensions.width = static_cast<uint16>(width_height >> 16);
dimensions.height = static_cast<uint16>(width_height & 0xFFFF);
}
template <class StorerT>
void store(const DialogPhoto &dialog_photo, StorerT &storer) {
bool has_file_ids = dialog_photo.small_file_id.is_valid() || dialog_photo.big_file_id.is_valid();
@ -83,50 +70,6 @@ void parse(ProfilePhoto &profile_photo, ParserT &parser) {
parse(profile_photo.id, parser);
}
template <class StorerT>
void store(const PhotoSize &photo_size, StorerT &storer) {
LOG(DEBUG) << "Store photo size " << photo_size;
store(photo_size.type, storer);
store(photo_size.dimensions, storer);
store(photo_size.size, storer);
store(photo_size.file_id, storer);
store(photo_size.progressive_sizes, storer);
}
template <class ParserT>
void parse(PhotoSize &photo_size, ParserT &parser) {
parse(photo_size.type, parser);
parse(photo_size.dimensions, parser);
parse(photo_size.size, parser);
parse(photo_size.file_id, parser);
if (parser.version() >= static_cast<int32>(Version::AddPhotoProgressiveSizes)) {
parse(photo_size.progressive_sizes, parser);
} else {
photo_size.progressive_sizes.clear();
}
if (photo_size.type < 0 || photo_size.type >= 128) {
parser.set_error("Wrong PhotoSize type");
return;
}
LOG(DEBUG) << "Parsed photo size " << photo_size;
}
template <class StorerT>
void store(const AnimationSize &animation_size, StorerT &storer) {
store(static_cast<const PhotoSize &>(animation_size), storer);
store(animation_size.main_frame_timestamp, storer);
}
template <class ParserT>
void parse(AnimationSize &animation_size, ParserT &parser) {
parse(static_cast<PhotoSize &>(animation_size), parser);
if (parser.version() >= static_cast<int32>(Version::AddDialogPhotoHasAnimation)) {
parse(animation_size.main_frame_timestamp, parser);
} else {
animation_size.main_frame_timestamp = 0;
}
}
template <class StorerT>
void store(const Photo &photo, StorerT &storer) {
bool has_minithumbnail = !photo.minithumbnail.empty();

15
td/telegram/PhotoFormat.h Normal file
View File

@ -0,0 +1,15 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
namespace td {
enum class PhotoFormat : int32 { Jpeg, Png, Webp, Gif, Tgs, Mpeg4, Webm };
} // namespace td

447
td/telegram/PhotoSize.cpp Normal file
View File

@ -0,0 +1,447 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/PhotoSize.h"
#include "td/telegram/files/FileLocation.h"
#include "td/telegram/files/FileManager.h"
#include "td/utils/base64.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/SliceBuilder.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace td {
static uint16 get_dimension(int32 size, const char *source) {
if (size < 0 || size > 65535) {
LOG(ERROR) << "Wrong image dimension = " << size << " from " << source;
return 0;
}
return narrow_cast<uint16>(size);
}
Dimensions get_dimensions(int32 width, int32 height, const char *source) {
Dimensions result;
result.width = get_dimension(width, source);
result.height = get_dimension(height, source);
if (result.width == 0 || result.height == 0) {
result.width = 0;
result.height = 0;
}
return result;
}
static uint32 get_pixel_count(const Dimensions &dimensions) {
return static_cast<uint32>(dimensions.width) * static_cast<uint32>(dimensions.height);
}
bool operator==(const Dimensions &lhs, const Dimensions &rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
bool operator!=(const Dimensions &lhs, const Dimensions &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions) {
return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")";
}
td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string &packed) {
if (packed.size() < 3) {
return nullptr;
}
if (packed[0] == '\x01') {
static const string header =
base64_decode(
"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////"
"m8H///"
"/6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/"
"wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/"
"8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0R"
"FRkd"
"ISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2"
"uHi4"
"+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/"
"8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkN"
"ERUZ"
"HSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2"
"Nna4"
"uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=")
.move_as_ok();
static const string footer = base64_decode("/9k=").move_as_ok();
auto result = td_api::make_object<td_api::minithumbnail>();
result->height_ = static_cast<unsigned char>(packed[1]);
result->width_ = static_cast<unsigned char>(packed[2]);
result->data_ = PSTRING() << header.substr(0, 164) << packed[1] << header[165] << packed[2] << header.substr(167)
<< packed.substr(3) << footer;
return result;
}
return nullptr;
}
static td_api::object_ptr<td_api::ThumbnailFormat> get_thumbnail_format_object(PhotoFormat format) {
switch (format) {
case PhotoFormat::Jpeg:
return td_api::make_object<td_api::thumbnailFormatJpeg>();
case PhotoFormat::Png:
return td_api::make_object<td_api::thumbnailFormatPng>();
case PhotoFormat::Webp:
return td_api::make_object<td_api::thumbnailFormatWebp>();
case PhotoFormat::Gif:
return td_api::make_object<td_api::thumbnailFormatGif>();
case PhotoFormat::Tgs:
return td_api::make_object<td_api::thumbnailFormatTgs>();
case PhotoFormat::Mpeg4:
return td_api::make_object<td_api::thumbnailFormatMpeg4>();
case PhotoFormat::Webm:
return td_api::make_object<td_api::thumbnailFormatWebm>();
default:
UNREACHABLE();
return nullptr;
}
}
static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) {
switch (format) {
case PhotoFormat::Jpeg:
return string_builder << "jpg";
case PhotoFormat::Png:
return string_builder << "png";
case PhotoFormat::Webp:
return string_builder << "webp";
case PhotoFormat::Gif:
return string_builder << "gif";
case PhotoFormat::Tgs:
return string_builder << "tgs";
case PhotoFormat::Mpeg4:
return string_builder << "mp4";
case PhotoFormat::Webm:
return string_builder << "webm";
default:
UNREACHABLE();
return string_builder;
}
}
FileId register_photo_size(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash,
string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id,
PhotoFormat format) {
LOG(DEBUG) << "Receive " << format << " photo " << id << " of type " << source.get_file_type("register_photo_size")
<< " from " << dc_id;
auto suggested_name = PSTRING() << source.get_unique_name(id) << '.' << format;
auto file_location_source = owner_dialog_id.get_type() == DialogType::SecretChat ? FileLocationSource::FromUser
: FileLocationSource::FromServer;
return file_manager->register_remote(
FullRemoteFileLocation(source, id, access_hash, dc_id, std::move(file_reference)), file_location_source,
owner_dialog_id, file_size, 0, std::move(suggested_name));
}
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
int32 width, int32 height) {
if (bytes.empty()) {
return PhotoSize();
}
PhotoSize res;
res.type = 't';
res.dimensions = get_dimensions(width, height, "get_secret_thumbnail_photo_size");
res.size = narrow_cast<int32>(bytes.size());
// generate some random remote location to save
auto dc_id = DcId::invalid();
auto photo_id = -(Random::secure_int64() & std::numeric_limits<int64>::max());
res.file_id = file_manager->register_remote(
FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id,
string()),
FileLocationSource::FromServer, owner_dialog_id, res.size, 0,
PSTRING() << static_cast<uint64>(photo_id) << ".jpg");
file_manager->set_content(res.file_id, std::move(bytes));
return res;
}
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
int64 access_hash, std::string file_reference, DcId dc_id,
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
PhotoFormat format) {
CHECK(size_ptr != nullptr);
string type;
PhotoSize res;
BufferSlice content;
switch (size_ptr->get_id()) {
case telegram_api::photoSizeEmpty::ID:
return std::move(res);
case telegram_api::photoSize::ID: {
auto size = move_tl_object_as<telegram_api::photoSize>(size_ptr);
type = std::move(size->type_);
res.dimensions = get_dimensions(size->w_, size->h_, "photoSize");
res.size = size->size_;
break;
}
case telegram_api::photoCachedSize::ID: {
auto size = move_tl_object_as<telegram_api::photoCachedSize>(size_ptr);
type = std::move(size->type_);
CHECK(size->bytes_.size() <= static_cast<size_t>(std::numeric_limits<int32>::max()));
res.dimensions = get_dimensions(size->w_, size->h_, "photoCachedSize");
res.size = static_cast<int32>(size->bytes_.size());
content = std::move(size->bytes_);
break;
}
case telegram_api::photoStrippedSize::ID: {
auto size = move_tl_object_as<telegram_api::photoStrippedSize>(size_ptr);
if (format != PhotoFormat::Jpeg) {
LOG(ERROR) << "Receive unexpected JPEG minithumbnail in photo " << id << " from " << source << " of format "
<< format;
return std::move(res);
}
return size->bytes_.as_slice().str();
}
case telegram_api::photoSizeProgressive::ID: {
auto size = move_tl_object_as<telegram_api::photoSizeProgressive>(size_ptr);
if (size->sizes_.empty()) {
LOG(ERROR) << "Receive photo " << id << " from " << source << " with empty size " << to_string(size);
return std::move(res);
}
std::sort(size->sizes_.begin(), size->sizes_.end());
type = std::move(size->type_);
res.dimensions = get_dimensions(size->w_, size->h_, "photoSizeProgressive");
res.size = size->sizes_.back();
size->sizes_.pop_back();
res.progressive_sizes = std::move(size->sizes_);
break;
}
case telegram_api::photoPathSize::ID: {
auto size = move_tl_object_as<telegram_api::photoPathSize>(size_ptr);
if (format != PhotoFormat::Tgs && format != PhotoFormat::Webp && format != PhotoFormat::Webm) {
LOG(ERROR) << "Receive unexpected SVG minithumbnail in photo " << id << " from " << source << " of format "
<< format;
return std::move(res);
}
return size->bytes_.as_slice().str();
}
default:
UNREACHABLE();
break;
}
if (type.size() != 1) {
LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res;
res.type = 0;
} else {
res.type = static_cast<uint8>(type[0]);
if (res.type >= 128) {
LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res;
res.type = 0;
}
}
if (source.get_type("get_photo_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, format);
if (!content.empty()) {
file_manager->set_content(res.file_id, std::move(content));
}
return std::move(res);
}
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
std::string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size) {
CHECK(size != nullptr);
AnimationSize res;
if (size->type_ != "v" && size->type_ != "u") {
LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size);
}
res.type = static_cast<uint8>(size->type_[0]);
if (res.type >= 128) {
LOG(ERROR) << "Wrong videoSize \"" << res.type << "\" " << res;
res.type = 0;
}
res.dimensions = get_dimensions(size->w_, size->h_, "get_animation_size");
res.size = size->size_;
if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) {
res.main_frame_timestamp = size->video_start_ts_;
}
if (source.get_type("get_animation_size") == PhotoSizeSource::Type::Thumbnail) {
source.thumbnail().thumbnail_type = res.type;
}
res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id,
res.size, dc_id, PhotoFormat::Mpeg4);
return res;
}
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::WebDocument> web_document_ptr) {
if (web_document_ptr == nullptr) {
return {};
}
FileId file_id;
vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
int32 size = 0;
string mime_type;
switch (web_document_ptr->get_id()) {
case telegram_api::webDocument::ID: {
auto web_document = move_tl_object_as<telegram_api::webDocument>(web_document_ptr);
auto r_http_url = parse_url(web_document->url_);
if (r_http_url.is_error()) {
LOG(ERROR) << "Can't parse URL " << web_document->url_;
return {};
}
auto http_url = r_http_url.move_as_ok();
auto url = http_url.get_url();
file_id = file_manager->register_remote(FullRemoteFileLocation(file_type, url, web_document->access_hash_),
FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_,
get_url_query_file_name(http_url.query_));
size = web_document->size_;
mime_type = std::move(web_document->mime_type_);
attributes = std::move(web_document->attributes_);
break;
}
case telegram_api::webDocumentNoProxy::ID: {
auto web_document = move_tl_object_as<telegram_api::webDocumentNoProxy>(web_document_ptr);
if (web_document->url_.find('.') == string::npos) {
LOG(ERROR) << "Receive invalid URL " << web_document->url_;
return {};
}
auto r_file_id = file_manager->from_persistent_id(web_document->url_, file_type);
if (r_file_id.is_error()) {
LOG(ERROR) << "Can't register URL: " << r_file_id.error();
return {};
}
file_id = r_file_id.move_as_ok();
size = web_document->size_;
mime_type = std::move(web_document->mime_type_);
attributes = std::move(web_document->attributes_);
break;
}
default:
UNREACHABLE();
}
CHECK(file_id.is_valid());
bool is_animation = mime_type == "video/mp4";
bool is_gif = mime_type == "image/gif";
Dimensions dimensions;
for (auto &attribute : attributes) {
switch (attribute->get_id()) {
case telegram_api::documentAttributeImageSize::ID: {
auto image_size = move_tl_object_as<telegram_api::documentAttributeImageSize>(attribute);
dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize");
break;
}
case telegram_api::documentAttributeAnimated::ID:
case telegram_api::documentAttributeHasStickers::ID:
case telegram_api::documentAttributeSticker::ID:
case telegram_api::documentAttributeVideo::ID:
case telegram_api::documentAttributeAudio::ID:
LOG(ERROR) << "Unexpected web document attribute " << to_string(attribute);
break;
case telegram_api::documentAttributeFilename::ID:
break;
default:
UNREACHABLE();
}
}
PhotoSize s;
s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'n'));
s.dimensions = dimensions;
s.size = size;
s.file_id = file_id;
return s;
}
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
PhotoFormat format) {
if (!photo_size.file_id.is_valid()) {
return nullptr;
}
if (format == PhotoFormat::Jpeg && photo_size.type == 'g') {
format = PhotoFormat::Gif;
}
return td_api::make_object<td_api::thumbnail>(get_thumbnail_format_object(format), photo_size.dimensions.width,
photo_size.dimensions.height,
file_manager->get_file_object(photo_size.file_id));
}
bool operator==(const PhotoSize &lhs, const PhotoSize &rhs) {
return lhs.type == rhs.type && lhs.dimensions == rhs.dimensions && lhs.size == rhs.size &&
lhs.file_id == rhs.file_id && lhs.progressive_sizes == rhs.progressive_sizes;
}
bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs) {
return !(lhs == rhs);
}
bool operator<(const PhotoSize &lhs, const PhotoSize &rhs) {
if (lhs.size != rhs.size) {
return lhs.size < rhs.size;
}
auto lhs_pixels = get_pixel_count(lhs.dimensions);
auto rhs_pixels = get_pixel_count(rhs.dimensions);
if (lhs_pixels != rhs_pixels) {
return lhs_pixels < rhs_pixels;
}
int32 lhs_type = lhs.type == 't' ? -1 : lhs.type;
int32 rhs_type = rhs.type == 't' ? -1 : rhs.type;
if (lhs_type != rhs_type) {
return lhs_type < rhs_type;
}
if (lhs.file_id != rhs.file_id) {
return lhs.file_id.get() < rhs.file_id.get();
}
return lhs.dimensions.width < rhs.dimensions.width;
}
StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size) {
return string_builder << "{type = " << photo_size.type << ", dimensions = " << photo_size.dimensions
<< ", size = " << photo_size.size << ", file_id = " << photo_size.file_id
<< ", progressive_sizes = " << photo_size.progressive_sizes << "}";
}
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs) {
return static_cast<const PhotoSize &>(lhs) == static_cast<const PhotoSize &>(rhs) &&
fabs(lhs.main_frame_timestamp - rhs.main_frame_timestamp) < 1e-3;
}
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size) {
return string_builder << static_cast<const PhotoSize &>(animation_size) << " from "
<< animation_size.main_frame_timestamp;
}
} // namespace td

87
td/telegram/PhotoSize.h Normal file
View File

@ -0,0 +1,87 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/Variant.h"
namespace td {
class FileManager;
struct Dimensions {
uint16 width = 0;
uint16 height = 0;
};
struct PhotoSize {
int32 type = 0;
Dimensions dimensions;
int32 size = 0;
FileId file_id;
vector<int32> progressive_sizes;
};
struct AnimationSize final : public PhotoSize {
double main_frame_timestamp = 0.0;
};
Dimensions get_dimensions(int32 width, int32 height, const char *source);
bool operator==(const Dimensions &lhs, const Dimensions &rhs);
bool operator!=(const Dimensions &lhs, const Dimensions &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions);
td_api::object_ptr<td_api::minithumbnail> get_minithumbnail_object(const string &packed);
FileId register_photo_size(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash,
string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id,
PhotoFormat format);
PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id,
int32 width, int32 height);
Variant<PhotoSize, string> get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id,
int64 access_hash, string file_reference, DcId dc_id,
DialogId owner_dialog_id, tl_object_ptr<telegram_api::PhotoSize> &&size_ptr,
PhotoFormat format);
AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash,
string file_reference, DcId dc_id, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::videoSize> &&size);
PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id,
tl_object_ptr<telegram_api::WebDocument> web_document_ptr);
td_api::object_ptr<td_api::thumbnail> get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size,
PhotoFormat format);
bool operator==(const PhotoSize &lhs, const PhotoSize &rhs);
bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs);
bool operator<(const PhotoSize &lhs, const PhotoSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size);
bool operator==(const AnimationSize &lhs, const AnimationSize &rhs);
bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size);
} // namespace td

75
td/telegram/PhotoSize.hpp Normal file
View File

@ -0,0 +1,75 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/files/FileId.hpp"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/Version.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void store(Dimensions dimensions, StorerT &storer) {
store(static_cast<uint32>((static_cast<uint32>(dimensions.width) << 16) | dimensions.height), storer);
}
template <class ParserT>
void parse(Dimensions &dimensions, ParserT &parser) {
uint32 width_height;
parse(width_height, parser);
dimensions.width = static_cast<uint16>(width_height >> 16);
dimensions.height = static_cast<uint16>(width_height & 0xFFFF);
}
template <class StorerT>
void store(const PhotoSize &photo_size, StorerT &storer) {
LOG(DEBUG) << "Store photo size " << photo_size;
store(photo_size.type, storer);
store(photo_size.dimensions, storer);
store(photo_size.size, storer);
store(photo_size.file_id, storer);
store(photo_size.progressive_sizes, storer);
}
template <class ParserT>
void parse(PhotoSize &photo_size, ParserT &parser) {
parse(photo_size.type, parser);
parse(photo_size.dimensions, parser);
parse(photo_size.size, parser);
parse(photo_size.file_id, parser);
if (parser.version() >= static_cast<int32>(Version::AddPhotoProgressiveSizes)) {
parse(photo_size.progressive_sizes, parser);
} else {
photo_size.progressive_sizes.clear();
}
if (photo_size.type < 0 || photo_size.type >= 128) {
parser.set_error("Wrong PhotoSize type");
return;
}
LOG(DEBUG) << "Parsed photo size " << photo_size;
}
template <class StorerT>
void store(const AnimationSize &animation_size, StorerT &storer) {
store(static_cast<const PhotoSize &>(animation_size), storer);
store(animation_size.main_frame_timestamp, storer);
}
template <class ParserT>
void parse(AnimationSize &animation_size, ParserT &parser) {
parse(static_cast<PhotoSize &>(animation_size), parser);
if (parser.version() >= static_cast<int32>(Version::AddDialogPhotoHasAnimation)) {
parse(animation_size.main_frame_timestamp, parser);
} else {
animation_size.main_frame_timestamp = 0;
}
}
} // namespace td

View File

@ -886,8 +886,10 @@ void PollManager::on_set_poll_answer_finished(PollId poll_id, Result<Unit> &&res
}
}
for (auto &promise : promises) {
promise.set_result(result.clone());
if (result.is_ok()) {
set_promises(promises);
} else {
fail_promises(promises, result.move_as_error());
}
}
@ -1021,9 +1023,7 @@ void PollManager::on_get_poll_voters(PollId poll_id, int32 option_id, string off
return;
}
if (result.is_error()) {
for (auto &promise : promises) {
promise.set_error(result.error().clone());
}
fail_promises(promises, result.move_as_error());
return;
}
@ -1424,10 +1424,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
auto it = poll_voters_.find(poll_id);
if (it != poll_voters_.end()) {
for (auto &voters : it->second) {
auto promises = std::move(voters.pending_queries);
for (auto &promise : promises) {
promise.set_error(Status::Error(500, "The poll was changed"));
}
fail_promises(voters.pending_queries, Status::Error(500, "The poll was changed"));
}
poll_voters_.erase(it);
}

View File

@ -159,7 +159,7 @@ void PrivacyManager::UserPrivacySettingRule::set_chat_ids(const vector<int64> &d
break;
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
if (td->contacts_manager_->get_channel_type(channel_id) != ContactsManager::ChannelType::Megagroup) {
if (!td->contacts_manager_->is_megagroup_channel(channel_id)) {
LOG(ERROR) << "Ignore broadcast " << channel_id;
break;
}
@ -487,19 +487,19 @@ void PrivacyManager::update_privacy(tl_object_ptr<telegram_api::updatePrivacy> u
}
void PrivacyManager::on_get_result(UserPrivacySetting user_privacy_setting,
Result<UserPrivacySettingRules> privacy_rules) {
Result<UserPrivacySettingRules> r_privacy_rules) {
auto &info = get_info(user_privacy_setting);
auto promises = std::move(info.get_promises);
reset_to_empty(info.get_promises);
for (auto &promise : promises) {
if (privacy_rules.is_error()) {
promise.set_error(privacy_rules.error().clone());
if (r_privacy_rules.is_error()) {
promise.set_error(r_privacy_rules.error().clone());
} else {
promise.set_value(privacy_rules.ok().get_user_privacy_setting_rules_object());
promise.set_value(r_privacy_rules.ok().get_user_privacy_setting_rules_object());
}
}
if (privacy_rules.is_ok()) {
do_update_privacy(user_privacy_setting, privacy_rules.move_as_ok(), false);
if (r_privacy_rules.is_ok()) {
do_update_privacy(user_privacy_setting, r_privacy_rules.move_as_ok(), false);
}
}

View File

@ -151,7 +151,7 @@ class PrivacyManager final : public NetQueryCallback {
return info_[static_cast<size_t>(key.type())];
}
void on_get_result(UserPrivacySetting user_privacy_setting, Result<UserPrivacySettingRules> privacy_rules);
void on_get_result(UserPrivacySetting user_privacy_setting, Result<UserPrivacySettingRules> r_privacy_rules);
void do_update_privacy(UserPrivacySetting user_privacy_setting, UserPrivacySettingRules &&privacy_rules,
bool from_update);

View File

@ -68,12 +68,10 @@ void QueryCombiner::on_get_query_result(int64 query_id, Result<Unit> &&result) {
auto promises = std::move(it->second.promises);
queries_.erase(it);
for (auto &promise : promises) {
if (result.is_ok()) {
promise.set_value(Unit());
} else {
promise.set_error(result.error().clone());
}
if (result.is_ok()) {
set_promises(promises);
} else {
fail_promises(promises, result.move_as_error());
}
loop();
}

View File

@ -130,9 +130,7 @@ void RecentDialogList::on_load_dialogs(vector<string> &&found_dialogs) {
CHECK(!promises.empty());
if (G()->close_flag()) {
for (auto &promise : promises) {
promise.set_error(Global::request_aborted_error());
}
fail_promises(promises, Global::request_aborted_error());
return;
}
@ -162,9 +160,7 @@ void RecentDialogList::on_load_dialogs(vector<string> &&found_dialogs) {
save_dialogs();
}
for (auto &promise : promises) {
promise.set_value(Unit());
}
set_promises(promises);
}
void RecentDialogList::add_dialog(DialogId dialog_id) {

View File

@ -54,6 +54,9 @@ static StringBuilder &operator<<(StringBuilder &string_builder, const KeyboardBu
case KeyboardButton::Type::RequestPollRegular:
string_builder << "RequestPollRegular";
break;
case KeyboardButton::Type::WebView:
string_builder << "WebView";
break;
default:
UNREACHABLE();
}
@ -94,6 +97,9 @@ static StringBuilder &operator<<(StringBuilder &string_builder, const InlineKeyb
case InlineKeyboardButton::Type::User:
string_builder << "User " << keyboard_button.user_id.get();
break;
case InlineKeyboardButton::Type::WebView:
string_builder << "WebView";
break;
default:
UNREACHABLE();
}
@ -214,6 +220,13 @@ static KeyboardButton get_keyboard_button(tl_object_ptr<telegram_api::KeyboardBu
button.text = std::move(keyboard_button->text_);
break;
}
case telegram_api::keyboardButtonSimpleWebView::ID: {
auto keyboard_button = move_tl_object_as<telegram_api::keyboardButtonSimpleWebView>(keyboard_button_ptr);
button.type = KeyboardButton::Type::WebView;
button.text = std::move(keyboard_button->text_);
button.url = std::move(keyboard_button->url_);
break;
}
default:
LOG(ERROR) << "Unsupported keyboard button: " << to_string(keyboard_button_ptr);
}
@ -278,6 +291,13 @@ static InlineKeyboardButton get_inline_keyboard_button(
button.user_id = UserId(keyboard_button->user_id_);
break;
}
case telegram_api::keyboardButtonWebView::ID: {
auto keyboard_button = move_tl_object_as<telegram_api::keyboardButtonWebView>(keyboard_button_ptr);
button.type = InlineKeyboardButton::Type::WebView;
button.text = std::move(keyboard_button->text_);
button.data = std::move(keyboard_button->url_);
break;
}
default:
LOG(ERROR) << "Unsupported inline keyboard button: " << to_string(keyboard_button_ptr);
}
@ -420,6 +440,25 @@ static Result<KeyboardButton> get_keyboard_button(tl_object_ptr<td_api::keyboard
}
break;
}
case td_api::keyboardButtonTypeWebApp::ID: {
if (!request_buttons_allowed) {
return Status::Error(400, "Web app can be used in private chats only");
}
auto button_type = move_tl_object_as<td_api::keyboardButtonTypeWebApp>(button->type_);
auto user_id = LinkManager::get_link_user_id(button_type->url_);
if (user_id.is_valid()) {
return Status::Error(400, "Link to a user can't be used in web app URL buttons");
}
auto r_url = LinkManager::check_link(button_type->url_, true, !G()->is_test_dc());
if (r_url.is_error()) {
return Status::Error(400, PSLICE() << "Inline keyboard button web app URL '" << button_type->url_
<< "' is invalid: " << r_url.error().message());
}
current_button.type = KeyboardButton::Type::WebView;
current_button.url = std::move(button_type->url_);
break;
}
default:
UNREACHABLE();
}
@ -452,7 +491,8 @@ static Result<InlineKeyboardButton> get_inline_keyboard_button(tl_object_ptr<td_
}
auto r_url = LinkManager::check_link(button_type->url_);
if (r_url.is_error()) {
return Status::Error(400, "Inline keyboard button URL is invalid");
return Status::Error(400, PSLICE() << "Inline keyboard button URL '" << button_type->url_
<< "' is invalid: " << r_url.error().message());
}
current_button.type = InlineKeyboardButton::Type::Url;
current_button.data = r_url.move_as_ok();
@ -499,9 +539,10 @@ static Result<InlineKeyboardButton> get_inline_keyboard_button(tl_object_ptr<td_
if (user_id.is_valid()) {
return Status::Error(400, "Link to a user can't be used in login URL buttons");
}
auto r_url = LinkManager::check_link(button_type->url_);
auto r_url = LinkManager::check_link(button_type->url_, true);
if (r_url.is_error()) {
return Status::Error(400, "Inline keyboard button login URL is invalid");
return Status::Error(400, PSLICE() << "Inline keyboard button login URL '" << button_type->url_
<< "' is invalid: " << r_url.error().message());
}
current_button.type = InlineKeyboardButton::Type::UrlAuth;
current_button.data = r_url.move_as_ok();
@ -528,6 +569,24 @@ static Result<InlineKeyboardButton> get_inline_keyboard_button(tl_object_ptr<td_
}
break;
}
case td_api::inlineKeyboardButtonTypeWebApp::ID: {
auto button_type = move_tl_object_as<td_api::inlineKeyboardButtonTypeWebApp>(button->type_);
auto user_id = LinkManager::get_link_user_id(button_type->url_);
if (user_id.is_valid()) {
return Status::Error(400, "Link to a user can't be used in web app URL buttons");
}
auto r_url = LinkManager::check_link(button_type->url_, true, !G()->is_test_dc());
if (r_url.is_error()) {
return Status::Error(400, PSLICE() << "Inline keyboard button web app URL '" << button_type->url_
<< "' is invalid: " << r_url.error().message());
}
current_button.type = InlineKeyboardButton::Type::WebView;
current_button.data = r_url.move_as_ok();
if (!clean_input_string(current_button.data)) {
return Status::Error(400, "Inline keyboard button web app URL must be encoded in UTF-8");
}
break;
}
default:
UNREACHABLE();
}
@ -662,6 +721,8 @@ static tl_object_ptr<telegram_api::KeyboardButton> get_keyboard_button(const Key
return make_tl_object<telegram_api::keyboardButtonRequestPoll>(1, true, keyboard_button.text);
case KeyboardButton::Type::RequestPollRegular:
return make_tl_object<telegram_api::keyboardButtonRequestPoll>(1, false, keyboard_button.text);
case KeyboardButton::Type::WebView:
return make_tl_object<telegram_api::keyboardButtonSimpleWebView>(keyboard_button.text, keyboard_button.url);
default:
UNREACHABLE();
return nullptr;
@ -721,6 +782,8 @@ static tl_object_ptr<telegram_api::KeyboardButton> get_inline_keyboard_button(
return make_tl_object<telegram_api::inputKeyboardButtonUserProfile>(keyboard_button.text,
r_input_user.move_as_ok());
}
case InlineKeyboardButton::Type::WebView:
return make_tl_object<telegram_api::keyboardButtonWebView>(keyboard_button.text, keyboard_button.data);
default:
UNREACHABLE();
return nullptr;
@ -798,6 +861,9 @@ static tl_object_ptr<td_api::keyboardButton> get_keyboard_button_object(const Ke
case KeyboardButton::Type::RequestPollRegular:
type = make_tl_object<td_api::keyboardButtonTypeRequestPoll>(true, false);
break;
case KeyboardButton::Type::WebView:
type = make_tl_object<td_api::keyboardButtonTypeWebApp>(keyboard_button.url);
break;
default:
UNREACHABLE();
return nullptr;
@ -839,6 +905,9 @@ static tl_object_ptr<td_api::inlineKeyboardButton> get_inline_keyboard_button_ob
G()->td().get_actor_unsafe()->contacts_manager_->get_user_id_object(keyboard_button.user_id,
"get_inline_keyboard_button_object"));
break;
case InlineKeyboardButton::Type::WebView:
type = make_tl_object<td_api::inlineKeyboardButtonTypeWebApp>(keyboard_button.data);
break;
default:
UNREACHABLE();
return nullptr;

View File

@ -26,10 +26,12 @@ struct KeyboardButton {
RequestLocation,
RequestPoll,
RequestPollQuiz,
RequestPollRegular
RequestPollRegular,
WebView
};
Type type;
string text;
string url; // WebView only
};
struct InlineKeyboardButton {
@ -43,7 +45,8 @@ struct InlineKeyboardButton {
Buy,
UrlAuth,
CallbackWithPassword,
User
User,
WebView
};
Type type;
int64 id = 0; // UrlAuth only, button_id or (2 * request_write_access - 1) * bot_user_id

Some files were not shown because too many files have changed in this diff Show More