diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 867fc17494..f06bbc9561 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -537,7 +537,7 @@ final class CallListControllerNode: ASDisplayNode { |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId, canCreateGroupCall) -> Signal in let (update, type) = updateAndType - let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, canCreateGroupCall: canCreateGroupCall, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) + let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, canCreateGroupCall: canCreateGroupCall && mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) let previous = previousView.swap(processedView) let previousType = previousType.swap(type) diff --git a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift index 9c049ea141..e64aa14847 100644 --- a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift @@ -300,6 +300,12 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem } } + @objc private func justCreatedCallTextTap(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.item?.openCallAction?() + } + } + public func asyncLayout() -> (_ item: ItemListPermanentInviteLinkItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { let makeAddressLayout = TextNode.asyncLayout(self.addressNode) let makeInvitedPeersLayout = TextNode.asyncLayout(self.invitedPeersNode) @@ -587,23 +593,8 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem strongSelf.justCreatedCallTextNode = justCreatedCallTextNode strongSelf.addSubnode(justCreatedCallTextNode.textNode) - } - - justCreatedCallTextNode.linkHighlightColor = item.presentationData.theme.actionSheet.controlAccentColor.withAlphaComponent(0.1) - justCreatedCallTextNode.highlightAttributeAction = { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) - } else { - return nil - } - } - justCreatedCallTextNode.tapAttributeAction = { [weak strongSelf] attributes, _ in - guard let strongSelf else { - return - } - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { - strongSelf.item?.openCallAction?() - } + + justCreatedCallTextNode.textNode.view.addGestureRecognizer(UITapGestureRecognizer(target: strongSelf, action: #selector(strongSelf.justCreatedCallTextTap(_:)))) } let justCreatedCallTextNodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - justCreatedCallTextNodeLayout.0.size.width) / 2.0), y: shareButtonNode.frame.maxY + justCreatedCallTextSpacing), size: CGSize(width: justCreatedCallTextNodeLayout.0.size.width, height: justCreatedCallTextNodeLayout.0.size.height)) diff --git a/submodules/TelegramCore/Sources/Network/FetchV2.swift b/submodules/TelegramCore/Sources/Network/FetchV2.swift index 073b7236a9..72703446ee 100644 --- a/submodules/TelegramCore/Sources/Network/FetchV2.swift +++ b/submodules/TelegramCore/Sources/Network/FetchV2.swift @@ -973,6 +973,7 @@ private final class FetchImpl { } let queue = self.queue + hashRange.disposable?.dispose() hashRange.disposable = (fetchRequest |> deliverOn(self.queue)).startStrict(next: { [weak self, weak state, weak hashRange] result in queue.async { diff --git a/third-party/td/td/CMakeLists.txt b/third-party/td/td/CMakeLists.txt index 46ae3e1e36..d6bbc903b6 100644 --- a/third-party/td/td/CMakeLists.txt +++ b/third-party/td/td/CMakeLists.txt @@ -350,6 +350,7 @@ set(TDLIB_SOURCE_PART1 td/telegram/BusinessAwayMessage.cpp td/telegram/BusinessAwayMessageSchedule.cpp td/telegram/BusinessBotManageBar.cpp + td/telegram/BusinessBotRights.cpp td/telegram/BusinessChatLink.cpp td/telegram/BusinessConnectedBot.cpp td/telegram/BusinessConnectionManager.cpp @@ -398,6 +399,7 @@ set(TDLIB_SOURCE_PART1 td/telegram/DialogParticipantManager.cpp td/telegram/DialogSource.cpp td/telegram/Dimensions.cpp + td/telegram/DisallowedGiftsSettings.cpp td/telegram/Document.cpp td/telegram/DocumentsManager.cpp td/telegram/DownloadManager.cpp @@ -577,6 +579,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/StarGiftAttribute.cpp td/telegram/StarGiftId.cpp td/telegram/StarGiftManager.cpp + td/telegram/StarGiftSettings.cpp td/telegram/StarManager.cpp td/telegram/StarSubscription.cpp td/telegram/StarSubscriptionPricing.cpp @@ -664,6 +667,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/BusinessAwayMessage.h td/telegram/BusinessAwayMessageSchedule.h td/telegram/BusinessBotManageBar.h + td/telegram/BusinessBotRights.h td/telegram/BusinessChatLink.h td/telegram/BusinessConnectedBot.h td/telegram/BusinessConnectionId.h @@ -725,6 +729,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/DialogParticipantManager.h td/telegram/DialogSource.h td/telegram/Dimensions.h + td/telegram/DisallowedGiftsSettings.h td/telegram/Document.h td/telegram/DocumentsManager.h td/telegram/DownloadManager.h @@ -950,6 +955,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/StarGiftAttribute.h td/telegram/StarGiftId.h td/telegram/StarGiftManager.h + td/telegram/StarGiftSettings.h td/telegram/StarManager.h td/telegram/StarSubscription.h td/telegram/StarSubscriptionPricing.h @@ -1024,6 +1030,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/BotVerifierSettings.hpp td/telegram/BusinessAwayMessage.hpp td/telegram/BusinessAwayMessageSchedule.hpp + td/telegram/BusinessBotRights.hpp td/telegram/BusinessConnectedBot.hpp td/telegram/BusinessGreetingMessage.hpp td/telegram/BusinessInfo.hpp @@ -1035,6 +1042,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/DialogInviteLink.hpp td/telegram/DialogNotificationSettings.hpp td/telegram/Dimensions.hpp + td/telegram/DisallowedGiftsSettings.hpp td/telegram/Document.hpp td/telegram/DocumentsManager.hpp td/telegram/DraftMessage.hpp @@ -1093,6 +1101,7 @@ set(TDLIB_SOURCE_PART2 td/telegram/StarGift.hpp td/telegram/StarGiftAttribute.hpp td/telegram/StarGiftId.hpp + td/telegram/StarGiftSettings.hpp td/telegram/StarSubscriptionPricing.hpp td/telegram/StickerMaskPosition.hpp td/telegram/StickerPhotoSize.hpp diff --git a/third-party/td/td/SplitSource.php b/third-party/td/td/SplitSource.php index 06fe95093d..0e36342f35 100644 --- a/third-party/td/td/SplitSource.php +++ b/third-party/td/td/SplitSource.php @@ -316,6 +316,7 @@ function split_file($file, $chunks, $undo) { 'BotVerification' => 'BotVerification', 'BotVerifierSettings' => 'BotVerifierSettings', 'BusinessAwayMessage' => 'BusinessAwayMessage', + 'BusinessBotRights' => 'BusinessBotRights', 'BusinessChatLink' => 'BusinessChatLink', 'BusinessConnectedBot' => 'BusinessConnectedBot', 'BusinessConnectionId' => 'BusinessConnectionId', @@ -351,6 +352,7 @@ function split_file($file, $chunks, $undo) { 'DialogParticipantFilter' => 'DialogParticipantFilter', 'dialog_participant_manager[_(-](?![.]get[(][)])|DialogParticipantManager' => 'DialogParticipantManager', 'DialogSource' => 'DialogSource', + 'DisallowedGiftsSettings' => 'DisallowedGiftsSettings', 'documents_manager[_(-](?![.]get[(][)])|DocumentsManager' => 'DocumentsManager', 'download_manager[_(-](?![.]get[(][)])|DownloadManager[^C]' => 'DownloadManager', 'DownloadManagerCallback' => 'DownloadManagerCallback', @@ -426,6 +428,7 @@ function split_file($file, $chunks, $undo) { 'StarGiftAttribute' => 'StarGiftAttribute', 'StarGiftId' => 'StarGiftId', 'star_gift_manager[_(-](?![.]get[(][)])|StarGiftManager' => 'StarGiftManager', + 'StarGiftSettings' => 'StarGiftSettings', 'star_manager[_(-](?![.]get[(][)])|StarManager' => 'StarManager', 'StarSubscription[^P]' => 'StarSubscription', 'StarSubscriptionPricing' => 'StarSubscriptionPricing', diff --git a/third-party/td/td/benchmark/bench_crypto.cpp b/third-party/td/td/benchmark/bench_crypto.cpp index 74ba466820..ae4dbaec7f 100644 --- a/third-party/td/td/benchmark/bench_crypto.cpp +++ b/third-party/td/td/benchmark/bench_crypto.cpp @@ -127,7 +127,8 @@ class HmacSha256ShortBench final : public td::Benchmark { void run(int n) final { unsigned char md[32]; for (int i = 0; i < n; i++) { - td::hmac_sha256(td::Slice(data, 32), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32)); + td::hmac_sha256(td::Slice(data, td::min(static_cast(32), SHORT_DATA_SIZE)), + td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32)); } } }; @@ -147,7 +148,8 @@ class HmacSha512ShortBench final : public td::Benchmark { void run(int n) final { unsigned char md[64]; for (int i = 0; i < n; i++) { - td::hmac_sha512(td::Slice(data, 32), td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64)); + td::hmac_sha512(td::Slice(data, td::min(static_cast(64), SHORT_DATA_SIZE)), + td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64)); } } }; @@ -491,11 +493,6 @@ class Crc64Bench final : public td::Benchmark { int main() { td::init_openssl_threads(); - td::bench(HmacSha256ShortBench()); - td::bench(HmacSha512ShortBench()); - td::bench(SHA1ShortBench()); - td::bench(SHA256ShortBench()); - td::bench(SHA512ShortBench()); td::bench(AesCtrBench()); #if OPENSSL_VERSION_NUMBER >= 0x10100000L td::bench(AesCtrOpenSSLBench()); @@ -521,6 +518,11 @@ int main() { #if OPENSSL_VERSION_NUMBER <= 0x10100000L td::bench(SHA1Bench()); #endif + td::bench(SHA1ShortBench()); + td::bench(SHA256ShortBench()); + td::bench(SHA512ShortBench()); + td::bench(HmacSha256ShortBench()); + td::bench(HmacSha512ShortBench()); td::bench(Crc32Bench()); td::bench(Crc64Bench()); } diff --git a/third-party/td/td/td/generate/scheme/td_api.tl b/third-party/td/td/td/generate/scheme/td_api.tl index c1bdb666c4..81180de4ef 100644 --- a/third-party/td/td/td/generate/scheme/td_api.tl +++ b/third-party/td/td/td/generate/scheme/td_api.tl @@ -680,11 +680,28 @@ businessAwayMessageSettings shortcut_id:int32 recipients:businessRecipients sche //@inactivity_days The number of days after which a chat will be considered as inactive; currently, must be on of 7, 14, 21, or 28 businessGreetingMessageSettings shortcut_id:int32 recipients:businessRecipients inactivity_days:int32 = BusinessGreetingMessageSettings; +//@description Describes rights of a business bot +//@can_reply True, if the bot can send and edit messages in the private chats that had incoming messages in the last 24 hours +//@can_read_messages True, if the bot can mark incoming private messages as read +//@can_delete_sent_messages True, if the bot can delete sent messages +//@can_delete_all_messages True, if the bot can delete any message +//@can_edit_name True, if the bot can edit name of the business account +//@can_edit_bio True, if the bot can edit bio of the business account +//@can_edit_profile_photo True, if the bot can edit profile photo of the business account +//@can_edit_username True, if the bot can edit username of the business account +//@can_view_gifts_and_stars True, if the bot can view gifts and amount of Telegram Stars owned by the business account +//@can_sell_gifts True, if the bot can sell regular gifts received by the business account +//@can_change_gift_settings True, if the bot can change gift receiving settings of the business account +//@can_transfer_and_upgrade_gifts True, if the bot can transfer and upgrade gifts received by the business account +//@can_transfer_stars True, if the bot can transfer Telegram Stars received by the business account to account of the bot, or use them to upgrade and transfer gifts +//@can_manage_stories True, if the bot can send, edit and delete stories +businessBotRights can_reply:Bool can_read_messages:Bool can_delete_sent_messages:Bool can_delete_all_messages:Bool can_edit_name:Bool can_edit_bio:Bool can_edit_profile_photo:Bool can_edit_username:Bool can_view_gifts_and_stars:Bool can_sell_gifts:Bool can_change_gift_settings:Bool can_transfer_and_upgrade_gifts:Bool can_transfer_stars:Bool can_manage_stories:Bool = BusinessBotRights; + //@description Describes a bot connected to a business account //@bot_user_id User identifier of the bot //@recipients Private chats that will be accessible to the bot -//@can_reply True, if the bot can send messages to the private chats; false otherwise -businessConnectedBot bot_user_id:int53 recipients:businessRecipients can_reply:Bool = BusinessConnectedBot; +//@rights Rights of the bot +businessConnectedBot bot_user_id:int53 recipients:businessRecipients rights:businessBotRights = BusinessConnectedBot; //@description Describes settings for a business account start page //@title Title text of the start page @@ -1036,16 +1053,28 @@ starGiveawayPaymentOption currency:string amount:int53 star_count:int53 store_pr starGiveawayPaymentOptions options:vector = StarGiveawayPaymentOptions; +//@description Describes gift types that are accepted by a user +//@unlimited_gifts True, if unlimited regular gifts are accepted +//@limited_gifts True, if limited regular gifts are accepted +//@upgraded_gifts True, if upgraded gifts and regular gifts that can be upgraded for free are accepted +//@premium_subscription True, if Telegram Premium subscription is accepted +acceptedGiftTypes unlimited_gifts:Bool limited_gifts:Bool upgraded_gifts:Bool premium_subscription:Bool = AcceptedGiftTypes; + +//@description Contains settings for gift receiving for a user +//@show_gift_button True, if a button for sending a gift to the user or by the user must always be shown in the input field +//@accepted_gift_types Types of gifts accepted by the user; for Telegram Premium users only +giftSettings show_gift_button:Bool accepted_gift_types:acceptedGiftTypes = GiftSettings; + //@description Describes a model of an upgraded gift //@name Name of the model //@sticker The sticker representing the upgraded gift -//@rarity_per_mille The number of upgraded gift that receive this model for each 1000 gifts upgraded +//@rarity_per_mille The number of upgraded gifts that receive this model for each 1000 gifts upgraded upgradedGiftModel name:string sticker:sticker rarity_per_mille:int32 = UpgradedGiftModel; //@description Describes a symbol shown on the pattern of an upgraded gift //@name Name of the symbol -//@sticker The sticker representing the upgraded gift -//@rarity_per_mille The number of upgraded gift that receive this symbol for each 1000 gifts upgraded +//@sticker The sticker representing the symbol +//@rarity_per_mille The number of upgraded gifts that receive this symbol for each 1000 gifts upgraded upgradedGiftSymbol name:string sticker:sticker rarity_per_mille:int32 = UpgradedGiftSymbol; //@description Describes colors of a backdrop of an upgraded gift @@ -1058,7 +1087,7 @@ upgradedGiftBackdropColors center_color:int32 edge_color:int32 symbol_color:int3 //@description Describes a backdrop of an upgraded gift //@name Name of the backdrop //@colors Colors of the backdrop -//@rarity_per_mille The number of upgraded gift that receive this backdrop for each 1000 gifts upgraded +//@rarity_per_mille The number of upgraded gifts that receive this backdrop for each 1000 gifts upgraded upgradedGiftBackdrop name:string colors:upgradedGiftBackdropColors rarity_per_mille:int32 = UpgradedGiftBackdrop; //@description Describes the original details about the gift @@ -1092,9 +1121,9 @@ gifts gifts:vector = Gifts; //@total_upgraded_count Total number of gifts that were upgraded from the same gift //@max_upgraded_count The maximum number of gifts that can be upgraded from the same gift //@owner_id Identifier of the user or the chat that owns the upgraded gift; may be null if none or unknown -//@owner_address Address of the gift NFT owner in TON blockchain; may be empty if none +//@owner_address Address of the gift NFT owner in TON blockchain; may be empty if none. Append the address to getOption("ton_blockchain_explorer_url") to get a link with information about the address //@owner_name Name of the owner for the case when owner identifier and address aren't known -//@gift_address Address of the gift NFT in TON blockchain; may be empty if none +//@gift_address Address of the gift NFT in TON blockchain; may be empty if none. Append the address to getOption("ton_blockchain_explorer_url") to get a link with information about the address //@model Model of the upgraded gift //@symbol Symbol of the upgraded gift //@backdrop Backdrop of the upgraded gift @@ -1292,12 +1321,18 @@ starTransactionTypePaidMessageSend chat_id:int53 message_count:int32 = StarTrans //@commission_star_amount The amount of Telegram Stars that were received by Telegram; can be negative for refunds starTransactionTypePaidMessageReceive sender_id:MessageSender message_count:int32 commission_per_mille:int32 commission_star_amount:starAmount = StarTransactionType; -//@description The transaction is a purchase of Telegram Premium subscription; for regular users only +//@description The transaction is a purchase of Telegram Premium subscription; for regular users and bots only //@user_id Identifier of the user that received the Telegram Premium subscription //@month_count Number of months the Telegram Premium subscription will be active //@sticker A sticker to be shown in the transaction information; may be null if unknown starTransactionTypePremiumPurchase user_id:int53 month_count:int32 sticker:sticker = StarTransactionType; +//@description The transaction is a transfer of Telegram Stars to a business bot; for regular users only @user_id Identifier of the bot that received Telegram Stars +starTransactionTypeBusinessBotTransferSend user_id:int53 = StarTransactionType; + +//@description The transaction is a transfer of Telegram Stars from a business account; for bots only @user_id Identifier of the user that sent Telegram Stars +starTransactionTypeBusinessBotTransferReceive user_id:int53 = StarTransactionType; + //@description The transaction is a transaction of an unsupported type starTransactionTypeUnsupported = StarTransactionType; @@ -1502,10 +1537,11 @@ botInfo short_description:string description:string photo:photo animation:animat //@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 //@incoming_paid_message_star_count Number of Telegram Stars that must be paid by the user for each sent message to the current user //@outgoing_paid_message_star_count Number of Telegram Stars that must be paid by the current user for each sent message to the user +//@gift_settings Settings for gift receiving for the user //@bot_verification Information about verification status of the user provided by a bot; may be null if none or unknown //@business_info Information about business settings for Telegram Business accounts; may be null if none //@bot_info For bots, information about the bot; may be null if the user isn't a bot -userFullInfo personal_photo:chatPhoto photo:chatPhoto public_photo:chatPhoto block_list:BlockList can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool has_posted_to_profile_stories:Bool has_sponsored_messages_enabled:Bool need_phone_number_privacy_exception:Bool set_chat_background:Bool bio:formattedText birthdate:birthdate personal_chat_id:int53 gift_count:int32 group_in_common_count:int32 incoming_paid_message_star_count:int53 outgoing_paid_message_star_count:int53 bot_verification:botVerification business_info:businessInfo bot_info:botInfo = UserFullInfo; +userFullInfo personal_photo:chatPhoto photo:chatPhoto public_photo:chatPhoto block_list:BlockList can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool has_posted_to_profile_stories:Bool has_sponsored_messages_enabled:Bool need_phone_number_privacy_exception:Bool set_chat_background:Bool bio:formattedText birthdate:birthdate personal_chat_id:int53 gift_count:int32 group_in_common_count:int32 incoming_paid_message_star_count:int53 outgoing_paid_message_star_count:int53 gift_settings:giftSettings bot_verification:botVerification business_info:businessInfo 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 = Users; @@ -2187,27 +2223,37 @@ sponsoredMessage message_id:int53 is_recommended:Bool can_be_reported:Bool conte //@description Contains a list of sponsored messages @messages List of sponsored messages @messages_between The minimum number of messages between shown sponsored messages, or 0 if only one sponsored message must be shown after all ordinary messages sponsoredMessages messages:vector messages_between:int32 = SponsoredMessages; +//@description Describes a sponsored chat +//@unique_id Unique identifier of this result +//@chat_id Chat identifier +//@sponsor_info Additional optional information about the sponsor to be shown along with the chat +//@additional_info If non-empty, additional information about the sponsored chat to be shown along with the chat +sponsoredChat unique_id:int53 chat_id:int53 sponsor_info:string additional_info:string = SponsoredChat; + +//@description Contains a list of sponsored chats @chats List of sponsored chats +sponsoredChats chats:vector = SponsoredChats; + //@description Describes an option to report an entity to Telegram @id Unique identifier of the option @text Text of the option reportOption id:bytes text:string = ReportOption; -//@class ReportChatSponsoredMessageResult @description Describes result of sponsored message report +//@class ReportSponsoredResult @description Describes result of sponsored message or chat report //@description The message was reported successfully -reportChatSponsoredMessageResultOk = ReportChatSponsoredMessageResult; +reportSponsoredResultOk = ReportSponsoredResult; //@description The sponsored message is too old or not found -reportChatSponsoredMessageResultFailed = ReportChatSponsoredMessageResult; +reportSponsoredResultFailed = ReportSponsoredResult; //@description The user must choose an option to report the message and repeat request with the chosen option @title Title for the option choice @options List of available options -reportChatSponsoredMessageResultOptionRequired title:string options:vector = ReportChatSponsoredMessageResult; +reportSponsoredResultOptionRequired title:string options:vector = ReportSponsoredResult; //@description Sponsored messages were hidden for the user in all chats -reportChatSponsoredMessageResultAdsHidden = ReportChatSponsoredMessageResult; +reportSponsoredResultAdsHidden = ReportSponsoredResult; //@description The user asked to hide sponsored messages, but Telegram Premium is required for this -reportChatSponsoredMessageResultPremiumRequired = ReportChatSponsoredMessageResult; +reportSponsoredResultPremiumRequired = ReportSponsoredResult; //@description Describes a file added to file download list @@ -4132,6 +4178,12 @@ messageUpgradedGift gift:upgradedGift sender_id:MessageSender received_gift_id:s //@is_upgrade True, if the gift was obtained by upgrading of a previously received gift messageRefundedUpgradedGift gift:gift sender_id:MessageSender is_upgrade:Bool = MessageContent; +//@description Paid messages were refunded @message_count The number of refunded messages @star_count The number of refunded Telegram Stars +messagePaidMessagesRefunded message_count:int32 star_count:int53 = MessageContent; + +//@description A price for paid messages was changed in the supergroup chat @paid_message_star_count The new number of Telegram Stars that must be paid by non-administrator users of the supergroup chat for each sent message +messagePaidMessagePriceChanged paid_message_star_count:int53 = MessageContent; + //@description A contact has registered with Telegram messageContactRegistered = MessageContent; @@ -5463,9 +5515,9 @@ speechRecognitionResultError error:error = SpeechRecognitionResult; //@user_id Identifier of the business user that created the connection //@user_chat_id Chat identifier of the private chat with the user //@date Point in time (Unix timestamp) when the connection was established -//@can_reply True, if the bot can send messages to the connected user; false otherwise +//@rights Rights of the bot; may be null if the connection was disabled //@is_enabled True, if the connection is enabled; false otherwise -businessConnection id:string user_id:int53 user_chat_id:int53 date:int32 can_reply:Bool is_enabled:Bool = BusinessConnection; +businessConnection id:string user_id:int53 user_chat_id:int53 date:int32 rights:businessBotRights is_enabled:Bool = BusinessConnection; //@description Describes a color to highlight a bot added to attachment menu @light_color Color in the RGB format for light themes @dark_color Color in the RGB format for dark themes @@ -6347,7 +6399,7 @@ storePaymentPurposeGiftedStars user_id:int53 currency:string amount:int53 star_c //@class TelegramPaymentPurpose @description Describes a purpose of a payment toward Telegram //@description The user gifting Telegram Premium to another user -//@currency ISO 4217 currency code of the payment currency +//@currency ISO 4217 currency code of the payment currency, or "XTR" for payments in Telegram Stars //@amount Paid amount, in the smallest units of the currency //@user_id Identifier of the user which will receive Telegram Premium //@month_count Number of months the Telegram Premium subscription will be active for the user @@ -6970,7 +7022,8 @@ readDatePrivacySettings show_read_date:Bool = ReadDatePrivacySettings; //@description Contains privacy settings for chats with non-contacts //@allow_new_chats_from_unknown_users True, if non-contacts users are able to write first to the current user. Telegram Premium subscribers are able to write first regardless of this setting //@incoming_paid_message_star_count Number of Telegram Stars that must be paid for every incoming private message by non-contacts; 0-getOption("paid_message_star_count_max"). -//-If positive, then allow_new_chats_from_unknown_users must be true. The current user will receive getOption("paid_message_earnings_per_mille") Telegram Stars for each 1000 Telegram Stars paid for message sending +//-If positive, then allow_new_chats_from_unknown_users must be true. The current user will receive getOption("paid_message_earnings_per_mille") Telegram Stars for each 1000 Telegram Stars paid for message sending. +//-Can be positive, only if getOption("can_enable_paid_messages") is true newChatPrivacySettings allow_new_chats_from_unknown_users:Bool incoming_paid_message_star_count:int53 = NewChatPrivacySettings; @@ -8505,6 +8558,13 @@ updateLanguagePackStrings localization_target:string language_pack_id:string str //@description The connection state has changed. This update must be used only to show a human-readable description of the connection state @state The new connection state updateConnectionState state:ConnectionState = Update; +//@description The freeze state of the current user's account has changed +//@is_frozen True, if the account is frozen +//@freezing_date Point in time (Unix timestamp) when the account was frozen; 0 if the account isn't frozen +//@deletion_date Point in time (Unix timestamp) when the account will be deleted and can't be unfrozen; 0 if the account isn't frozen +//@appeal_link The link to open to send an appeal to unfreeze the account +updateFreezeState is_frozen:Bool freezing_date:int32 deletion_date:int32 appeal_link:string = Update; + //@description New terms of service must be accepted by the user. If the terms of service are declined, then the deleteAccount method must be called with the reason "Decline ToS update" @terms_of_service_id Identifier of the terms of service @terms_of_service The new terms of service updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService = Update; @@ -9339,7 +9399,21 @@ clickChatSponsoredMessage chat_id:int53 message_id:int53 is_media_click:Bool fro //@chat_id Chat identifier of the sponsored message //@message_id Identifier of the sponsored message //@option_id Option identifier chosen by the user; leave empty for the initial request -reportChatSponsoredMessage chat_id:int53 message_id:int53 option_id:bytes = ReportChatSponsoredMessageResult; +reportChatSponsoredMessage chat_id:int53 message_id:int53 option_id:bytes = ReportSponsoredResult; + +//@description Returns sponsored chats to be shown in the search results @query Query the user searches for +getSearchSponsoredChats query:string = SponsoredChats; + +//@description Informs TDLib that the user fully viewed a sponsored chat @sponsored_chat_unique_id Unique identifier of the sponsored chat +viewSponsoredChat sponsored_chat_unique_id:int53 = Ok; + +//@description Informs TDLib that the user opened a sponsored chat @sponsored_chat_unique_id Unique identifier of the sponsored chat +openSponsoredChat sponsored_chat_unique_id:int53 = Ok; + +//@description Reports a sponsored chat to Telegram moderators +//@sponsored_chat_unique_id Unique identifier of the sponsored chat +//@option_id Option identifier chosen by the user; leave empty for the initial request +reportSponsoredChat sponsored_chat_unique_id:int53 option_id:bytes = ReportSponsoredResult; //@description Removes an active notification from notification list. Needs to be called only if the notification is removed by the current user @notification_group_id Identifier of notification group to which the notification belongs @notification_id Identifier of removed notification @@ -9655,6 +9729,66 @@ stopBusinessPoll business_connection_id:string chat_id:int53 message_id:int53 re //@is_pinned Pass true to pin the message, pass false to unpin it setBusinessMessageIsPinned business_connection_id:string chat_id:int53 message_id:int53 is_pinned:Bool = Ok; +//@description Reads a message on behalf of a business account; for bots only +//@business_connection_id Unique identifier of business connection through which the message was received +//@chat_id The chat the message belongs to +//@message_id Identifier of the message +readBusinessMessage business_connection_id:string chat_id:int53 message_id:int53 = Ok; + +//@description Deletes messages on behalf of a business account; for bots only +//@business_connection_id Unique identifier of business connection through which the messages were received +//@message_ids Identifier of the messages +deleteBusinessMessages business_connection_id:string message_ids:vector = Ok; + +//@description Changes a story sent by the bot on behalf of a business account; for bots only +//@story_sender_chat_id Identifier of the chat that posted the story +//@story_id Identifier of the story to edit +//@content New content of the story +//@areas New clickable rectangle areas to be shown on the story media +//@caption New story caption +//@privacy_settings The new privacy settings for the story +editBusinessStory story_sender_chat_id:int53 story_id:int32 content:InputStoryContent areas:inputStoryAreas caption:formattedText privacy_settings:StoryPrivacySettings = Story; + +//@description Deletes a story sent by the bot on behalf of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@story_id Identifier of the story to delete +deleteBusinessStory business_connection_id:string story_id:int32 = Ok; + +//@description Changes the first and last name of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@first_name The new value of the first name for the business account; 1-64 characters +//@last_name The new value of the optional last name for the business account; 0-64 characters +setBusinessAccountName business_connection_id:string first_name:string last_name:string = Ok; + +//@description Changes the bio of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@bio The new value of the bio; 0-getOption("bio_length_max") characters without line feeds +setBusinessAccountBio business_connection_id:string bio:string = Ok; + +//@description Changes a profile photo of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@photo Profile photo to set; pass null to remove the photo +//@is_public Pass true to set the public photo, which will be visible even the main photo is hidden by privacy settings +setBusinessAccountProfilePhoto business_connection_id:string photo:InputChatPhoto is_public:Bool = Ok; + +//@description Changes the editable username of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@username The new value of the username +setBusinessAccountUsername business_connection_id:string username:string = Ok; + +//@description Changes settings for gift receiving of a business account; for bots only +//@business_connection_id Unique identifier of business connection +//@settings The new settings +setBusinessAccountGiftSettings business_connection_id:string settings:giftSettings = Ok; + +//@description Returns the amount of Telegram Stars owned by a business account; for bots only @business_connection_id Unique identifier of business connection +getBusinessAccountStarAmount business_connection_id:string = StarAmount; + +//@description Transfer Telegram Stars from the business account to the business bot; for bots only +//@business_connection_id Unique identifier of business connection +//@star_count Number of Telegram Stars to transfer +transferBusinessAccountStars business_connection_id:string star_count:int53 = Ok; + //@description Checks validness of a name for a quick reply shortcut. Can be called synchronously @name The name of the shortcut; 1-32 characters checkQuickReplyShortcutName name:string = Ok; @@ -11406,7 +11540,7 @@ getWebPageInstantView url:string only_local:Bool = WebPageInstantView; //@description Changes a profile photo for the current user //@photo Profile photo to set -//@is_public Pass true to set a public photo, which will be visible even the main photo is hidden by privacy settings +//@is_public Pass true to set the public photo, which will be visible even the main photo is hidden by privacy settings setProfilePhoto photo:InputChatPhoto is_public:Bool = Ok; //@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete @@ -11845,6 +11979,9 @@ deleteSavedOrderInfo = Ok; deleteSavedCredentials = Ok; +//@description Changes settings for gift receiving for the current user @settings The new settings +setGiftSettings settings:giftSettings = Ok; + //@description Returns gifts that can be sent to other users and channel chats getAvailableGifts = Gifts; @@ -11857,8 +11994,10 @@ getAvailableGifts = Gifts; //@pay_for_upgrade Pass true to additionally pay for the gift upgrade and allow the receiver to upgrade it for free sendGift gift_id:int64 owner_id:MessageSender text:formattedText is_private:Bool pay_for_upgrade:Bool = Ok; -//@description Sells a gift for Telegram Stars @received_gift_id Identifier of the gift -sellGift received_gift_id:string = Ok; +//@description Sells a gift for Telegram Stars +//@business_connection_id Unique identifier of business connection on behalf of which to send the request; for bots only +//@received_gift_id Identifier of the gift +sellGift business_connection_id:string received_gift_id:string = Ok; //@description Toggles whether a gift is shown on the current user's or the channel's profile page; requires can_post_messages administrator right in the channel chat //@received_gift_id Identifier of the gift @@ -11879,18 +12018,21 @@ toggleChatGiftNotifications chat_id:int53 are_enabled:Bool = Ok; getGiftUpgradePreview gift_id:int64 = GiftUpgradePreview; //@description Upgrades a regular gift +//@business_connection_id Unique identifier of business connection on behalf of which to send the request; for bots only //@received_gift_id Identifier of the gift //@keep_original_details Pass true to keep the original gift text, sender and receiver in the upgraded gift //@star_count The amount of Telegram Stars required to pay for the upgrade. It the gift has prepaid_upgrade_star_count > 0, then pass 0, otherwise, pass gift.upgrade_star_count -upgradeGift received_gift_id:string keep_original_details:Bool star_count:int53 = UpgradeGiftResult; +upgradeGift business_connection_id:string received_gift_id:string keep_original_details:Bool star_count:int53 = UpgradeGiftResult; //@description Sends an upgraded gift to another user or a channel chat +//@business_connection_id Unique identifier of business connection on behalf of which to send the request; for bots only //@received_gift_id Identifier of the gift //@new_owner_id Identifier of the user or the channel chat that will receive the gift //@star_count The amount of Telegram Stars required to pay for the transfer -transferGift received_gift_id:string new_owner_id:MessageSender star_count:int53 = Ok; +transferGift business_connection_id:string received_gift_id:string new_owner_id:MessageSender star_count:int53 = Ok; //@description Returns gifts received by the given user or chat +//@business_connection_id Unique identifier of business connection on behalf of which to send the request; for bots only //@owner_id Identifier of the gift receiver //@exclude_unsaved Pass true to exclude gifts that aren't saved to the chat's profile page. Always true for gifts received by other users and channel chats without can_post_messages administrator right //@exclude_saved Pass true to exclude gifts that are saved to the chat's profile page. Always false for gifts received by other users and channel chats without can_post_messages administrator right @@ -11900,7 +12042,7 @@ transferGift received_gift_id:string new_owner_id:MessageSender star_count:int53 //@sort_by_price Pass true to sort results by gift price instead of send date //@offset Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results //@limit The maximum number of gifts to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned objects is chosen by TDLib and can be smaller than the specified limit -getReceivedGifts owner_id:MessageSender exclude_unsaved:Bool exclude_saved:Bool exclude_unlimited:Bool exclude_limited:Bool exclude_upgraded:Bool sort_by_price:Bool offset:string limit:int32 = ReceivedGifts; +getReceivedGifts business_connection_id:string owner_id:MessageSender exclude_unsaved:Bool exclude_saved:Bool exclude_unlimited:Bool exclude_limited:Bool exclude_upgraded:Bool sort_by_price:Bool offset:string limit:int32 = ReceivedGifts; //@description Returns information about a received gift @received_gift_id Identifier of the gift getReceivedGift received_gift_id:string = ReceivedGift; @@ -12393,6 +12535,13 @@ checkPremiumGiftCode code:string = PremiumGiftCodeInfo; //@description Applies a Telegram Premium gift code @code The code to apply applyPremiumGiftCode code:string = Ok; +//@description Allows to buy a Telegram Premium subscription for another user with payment in Telegram Stars; for bots only +//@user_id Identifier of the user which will receive Telegram Premium +//@star_count The number of Telegram Stars to pay for subscription +//@month_count Number of months the Telegram Premium subscription will be active for the user +//@text Text to show to the user receiving Telegram Premium; 0-getOption("gift_text_length_max") characters. Only Bold, Italic, Underline, Strikethrough, Spoiler, and CustomEmoji entities are allowed +giftPremiumWithStars user_id:int53 star_count:int53 month_count:int32 text:formattedText = Ok; + //@description Launches a prepaid giveaway //@giveaway_id Unique identifier of the prepaid giveaway //@parameters Giveaway parameters diff --git a/third-party/td/td/td/generate/scheme/telegram_api.tl b/third-party/td/td/td/generate/scheme/telegram_api.tl index f979f161db..76f2b9189b 100644 --- a/third-party/td/td/td/generate/scheme/telegram_api.tl +++ b/third-party/td/td/td/generate/scheme/telegram_api.tl @@ -210,6 +210,8 @@ messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long c messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction; messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction; messageActionStarGiftUnique#acdfcb81 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long = MessageAction; +messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction; +messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction; dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?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 ttl_period:flags.5?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; @@ -229,6 +231,7 @@ geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radiu auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode; +auth.sentCodePaymentRequired#d7cef980 store_product:string phone_code_hash:string = auth.SentCode; auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization; auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; @@ -261,7 +264,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason; -userFull#d2234ea0 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 voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?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 wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long = UserFull; +userFull#99e78045 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 voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?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 wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings = UserFull; contact#145ade0b user_id:long mutual:Bool = Contact; @@ -452,6 +455,7 @@ updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long conne updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update; updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update; updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update; +updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -922,7 +926,6 @@ phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason; phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason; phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason; phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason; -phoneCallDiscardReasonAllowGroupCall#afe2b839 encrypted_key:bytes = PhoneCallDiscardReason; dataJSON#7d748d04 data:string = DataJSON; @@ -1336,7 +1339,7 @@ statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInvite stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector top_admins:Vector top_inviters:Vector users:Vector = stats.MegagroupStats; -globalPrivacySettings#c9d8df1c flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true noncontact_peers_paid_stars:flags.5?long = GlobalPrivacySettings; +globalPrivacySettings#fe41b34f flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true display_gifts_button:flags.7?true noncontact_peers_paid_stars:flags.5?long disallowed_gifts:flags.6?DisallowedGiftsSettings = GlobalPrivacySettings; help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector patterns:flags.1?Vector = help.CountryCode; @@ -1506,6 +1509,7 @@ inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:fla inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice; inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice; inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice; +inputInvoiceBusinessBotTransferStars#f4997e42 bot:InputUser stars:long = InputInvoice; payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; @@ -1520,6 +1524,7 @@ inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?t inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose; inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose; inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector countries_iso2:flags.2?Vector prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose; +inputStorePaymentAuthCode#9bb2636d flags:# restore:flags.0?true phone_number:string phone_code_hash:string currency:string amount:long = InputStorePaymentPurpose; paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod; @@ -1778,7 +1783,7 @@ inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut; messages.quickReplies#c68d6695 quick_replies:Vector messages:Vector chats:Vector users:Vector = messages.QuickReplies; messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies; -connectedBot#bd068601 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessBotRecipients = ConnectedBot; +connectedBot#cd64636c flags:# bot_id:long recipients:BusinessBotRecipients rights:BusinessBotRights = ConnectedBot; account.connectedBots#17d7f87b connected_bots:Vector users:Vector = account.ConnectedBots; @@ -1786,7 +1791,7 @@ messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday; -botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection; +botBusinessConnection#8f34b2f5 flags:# disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int rights:flags.2?BusinessBotRights = BotBusinessConnection; inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro; @@ -1864,7 +1869,7 @@ starsTransactionPeerAPI#f9677aad = StarsTransactionPeer; starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption; -starsTransaction#a39fd94a flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true id:string stars:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int = StarsTransaction; +starsTransaction#a39fd94a flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true id:string stars:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int = StarsTransaction; payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector next_offset:flags.0?string chats:Vector users:Vector = payments.StarsStatus; @@ -1970,6 +1975,15 @@ requirementToContactEmpty#50a9839 = RequirementToContact; requirementToContactPremium#e581e4e9 = RequirementToContact; requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact; +businessBotRights#a0624cf7 flags:# reply:flags.0?true read_messages:flags.1?true delete_sent_messages:flags.2?true delete_received_messages:flags.3?true edit_name:flags.4?true edit_bio:flags.5?true edit_profile_photo:flags.6?true edit_username:flags.7?true view_gifts:flags.8?true sell_gifts:flags.9?true change_gift_settings:flags.10?true transfer_and_upgrade_gifts:flags.11?true transfer_stars:flags.12?true manage_stories:flags.13?true = BusinessBotRights; + +disallowedGiftsSettings#71f276c4 flags:# disallow_unlimited_stargifts:flags.0?true disallow_limited_stargifts:flags.1?true disallow_unique_stargifts:flags.2?true disallow_premium_gifts:flags.3?true = DisallowedGiftsSettings; + +sponsoredPeer#c69708d3 flags:# random_id:bytes peer:Peer sponsor_info:flags.0?string additional_info:flags.1?string = SponsoredPeer; + +contacts.sponsoredPeersEmpty#ea32b4b1 = contacts.SponsoredPeers; +contacts.sponsoredPeers#eb032884 peers:Vector chats:Vector users:Vector = contacts.SponsoredPeers; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -2104,7 +2118,7 @@ account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?Bus account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool; account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool; account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool; -account.updateConnectedBot#43d8521d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessBotRecipients = Updates; +account.updateConnectedBot#66a08c7e flags:# deleted:flags.1?true rights:flags.0?BusinessBotRights bot:InputUser recipients:InputBusinessBotRecipients = Updates; account.getConnectedBots#4ea4c80f = account.ConnectedBots; account.getBotBusinessConnection#76a86270 connection_id:string = Updates; account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool; @@ -2155,6 +2169,7 @@ contacts.importContactToken#13005788 token:string = User; contacts.editCloseFriends#ba6705f0 id:Vector = Bool; contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector limit:int = Bool; contacts.getBirthdays#daeda864 = contacts.ContactBirthdays; +contacts.getSponsoredPeers#b6c8c393 q:string = contacts.SponsoredPeers; messages.getMessages#63c66506 id:Vector = messages.Messages; messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs; @@ -2373,9 +2388,9 @@ messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:fla messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates; messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool; messages.getPaidReactionPrivacy#472455aa = Updates; -messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool; -messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool; -messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:bytes = channels.SponsoredMessageReportResult; +messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool; +messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool; +messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult; messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages; messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector = messages.BotPreparedInlineMessage; messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage; @@ -2531,7 +2546,6 @@ payments.getBankCardData#2e79d779 number:string = payments.BankCardData; payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice; payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates; payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates; -payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool; payments.getPremiumGiftCodeOptions#2757ba54 flags:# boost_peer:flags.0?InputPeer = Vector; payments.checkGiftCode#8e51b4c1 slug:string = payments.CheckedGiftCode; payments.applyGiftCode#f6e26854 slug:string = Updates; @@ -2569,6 +2583,7 @@ payments.getSavedStarGift#b455a106 stargift:Vector = payment payments.getStarGiftWithdrawalUrl#d06e93a8 stargift:InputSavedStarGift password:InputCheckPasswordSRP = payments.StarGiftWithdrawalUrl; payments.toggleChatStarGiftNotifications#60eaefa1 flags:# enabled:flags.0?true peer:InputPeer = Bool; payments.toggleStarGiftsPinnedToTop#1513e7b0 peer:InputPeer stargift:Vector = Bool; +payments.canPurchaseStore#4fdc5ea7 purpose:InputStorePaymentPurpose = Bool; stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; diff --git a/third-party/td/td/td/telegram/AccountManager.cpp b/third-party/td/td/td/telegram/AccountManager.cpp index b27e629f7b..9bc0b29ef6 100644 --- a/third-party/td/td/td/telegram/AccountManager.cpp +++ b/third-party/td/td/td/telegram/AccountManager.cpp @@ -387,13 +387,10 @@ class ChangeAuthorizationSettingsQuery final : public Td::ResultHandler { if (set_call_requests_disabled) { flags |= telegram_api::account_changeAuthorizationSettings::CALL_REQUESTS_DISABLED_MASK; } - if (confirm) { - flags |= telegram_api::account_changeAuthorizationSettings::CONFIRMED_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::account_changeAuthorizationSettings(flags, false /*ignored*/, hash, encrypted_requests_disabled, - call_requests_disabled), - {{"me"}})); + send_query( + G()->net_query_creator().create(telegram_api::account_changeAuthorizationSettings( + flags, confirm, hash, encrypted_requests_disabled, call_requests_disabled), + {{"me"}})); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/AnimationsManager.cpp b/third-party/td/td/td/telegram/AnimationsManager.cpp index 24e30ce708..5cdaa6d45b 100644 --- a/third-party/td/td/td/telegram/AnimationsManager.cpp +++ b/third-party/td/td/td/telegram/AnimationsManager.cpp @@ -298,21 +298,21 @@ tl_object_ptr AnimationsManager::get_input_media( const Animation *animation = get_animation(file_id); CHECK(animation != nullptr); - vector> attributes; + vector> attributes; if (!animation->file_name.empty()) { - attributes.push_back(make_tl_object(animation->file_name)); + attributes.push_back(telegram_api::make_object(animation->file_name)); } string mime_type = animation->mime_type; if (mime_type == "video/mp4") { - attributes.push_back(make_tl_object( - 0, false /*ignored*/, false /*ignored*/, false /*ignored*/, animation->duration, animation->dimensions.width, - animation->dimensions.height, 0, 0.0, string())); + attributes.push_back(telegram_api::make_object( + 0, false, false, false, animation->duration, animation->dimensions.width, animation->dimensions.height, 0, + 0.0, string())); } else if (animation->dimensions.width != 0 && animation->dimensions.height != 0) { if (!begins_with(mime_type, "image/")) { mime_type = "image/gif"; } - attributes.push_back(make_tl_object(animation->dimensions.width, - animation->dimensions.height)); + attributes.push_back(telegram_api::make_object( + animation->dimensions.width, animation->dimensions.height)); } int32 flags = 0; vector> added_stickers; @@ -352,19 +352,19 @@ SecretInputMedia AnimationsManager::get_secret_input_media( if (animation->thumbnail.file_id.is_valid() && thumbnail.empty()) { return SecretInputMedia{}; } - vector> attributes; + vector> attributes; if (!animation->file_name.empty()) { - attributes.push_back(make_tl_object(animation->file_name)); + attributes.push_back(secret_api::make_object(animation->file_name)); } if (animation->duration != 0 && animation->mime_type == "video/mp4") { - attributes.push_back(make_tl_object( + attributes.push_back(secret_api::make_object( 0, false, animation->duration, animation->dimensions.width, animation->dimensions.height)); } if (animation->dimensions.width != 0 && animation->dimensions.height != 0) { - attributes.push_back(make_tl_object(animation->dimensions.width, - animation->dimensions.height)); + attributes.push_back(secret_api::make_object(animation->dimensions.width, + animation->dimensions.height)); } - attributes.push_back(make_tl_object()); + attributes.push_back(secret_api::make_object()); return {std::move(input_file), std::move(thumbnail), diff --git a/third-party/td/td/td/telegram/AudiosManager.cpp b/third-party/td/td/td/telegram/AudiosManager.cpp index f02e0ece00..6538dc3862 100644 --- a/third-party/td/td/td/telegram/AudiosManager.cpp +++ b/third-party/td/td/td/telegram/AudiosManager.cpp @@ -228,13 +228,13 @@ SecretInputMedia AudiosManager::get_secret_input_media( if (audio->thumbnail.file_id.is_valid() && thumbnail.empty()) { return SecretInputMedia{}; } - vector> attributes; + vector> attributes; if (!audio->file_name.empty()) { - attributes.push_back(make_tl_object(audio->file_name)); + attributes.push_back(secret_api::make_object(audio->file_name)); } - attributes.push_back(make_tl_object( - secret_api::documentAttributeAudio::TITLE_MASK | secret_api::documentAttributeAudio::PERFORMER_MASK, - false /*ignored*/, audio->duration, audio->title, audio->performer, BufferSlice())); + attributes.push_back(secret_api::make_object( + secret_api::documentAttributeAudio::TITLE_MASK | secret_api::documentAttributeAudio::PERFORMER_MASK, false, + audio->duration, audio->title, audio->performer, BufferSlice())); return {std::move(input_file), std::move(thumbnail), @@ -267,12 +267,12 @@ tl_object_ptr AudiosManager::get_input_media( const Audio *audio = get_audio(file_id); CHECK(audio != nullptr); - vector> attributes; - attributes.push_back(make_tl_object( - telegram_api::documentAttributeAudio::TITLE_MASK | telegram_api::documentAttributeAudio::PERFORMER_MASK, - false /*ignored*/, audio->duration, audio->title, audio->performer, BufferSlice())); + vector> attributes; + attributes.push_back(telegram_api::make_object( + telegram_api::documentAttributeAudio::TITLE_MASK | telegram_api::documentAttributeAudio::PERFORMER_MASK, false, + audio->duration, audio->title, audio->performer, BufferSlice())); if (!audio->file_name.empty()) { - attributes.push_back(make_tl_object(audio->file_name)); + attributes.push_back(telegram_api::make_object(audio->file_name)); } string mime_type = audio->mime_type; if (!begins_with(mime_type, "audio/")) { diff --git a/third-party/td/td/td/telegram/AuthManager.cpp b/third-party/td/td/td/telegram/AuthManager.cpp index e270b140a4..220670ac4a 100644 --- a/third-party/td/td/td/telegram/AuthManager.cpp +++ b/third-party/td/td/td/telegram/AuthManager.cpp @@ -661,12 +661,8 @@ void AuthManager::register_user(uint64 query_id, string first_name, string last_ } last_name = clean_name(last_name, MAX_NAME_LENGTH); - int32 flags = 0; - if (disable_notification) { - flags |= telegram_api::auth_signUp::NO_JOINED_NOTIFICATIONS_MASK; - } start_net_query(NetQueryType::SignUp, G()->net_query_creator().create_unauth(telegram_api::auth_signUp( - flags, false /*ignored*/, send_code_helper_.phone_number().str(), + 0, disable_notification, send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), first_name, last_name))); } @@ -849,6 +845,9 @@ void AuthManager::on_sent_code(telegram_api::object_ptrget_id(); if (sent_code_id != telegram_api::auth_sentCode::ID) { + if (sent_code_id == telegram_api::auth_sentCodePaymentRequired::ID) { + return on_current_query_error(Status::Error(500, "Receive unsupported response")); + } CHECK(sent_code_id == telegram_api::auth_sentCodeSuccess::ID); auto sent_code_success = move_tl_object_as(sent_code_ptr); return on_get_authorization(std::move(sent_code_success->authorization_)); @@ -874,8 +873,8 @@ void AuthManager::on_sent_code(telegram_api::object_ptrreset_pending_date_ > 0) { reset_pending_date_ = code_type->reset_pending_date_; - } else if ((code_type->flags_ & telegram_api::auth_sentCodeTypeEmailCode::RESET_AVAILABLE_PERIOD_MASK) != 0) { - reset_available_period_ = max(code_type->reset_available_period_, 0); + } else if (code_type->reset_available_period_ > 0) { + reset_available_period_ = code_type->reset_available_period_; } if (email_code_info_.is_empty()) { email_code_info_ = SentEmailCode("", code_type->length_); diff --git a/third-party/td/td/td/telegram/AutoDownloadSettings.cpp b/third-party/td/td/td/telegram/AutoDownloadSettings.cpp index bb96e46989..a591a6072d 100644 --- a/third-party/td/td/td/telegram/AutoDownloadSettings.cpp +++ b/third-party/td/td/td/telegram/AutoDownloadSettings.cpp @@ -21,19 +21,14 @@ namespace td { static td_api::object_ptr convert_auto_download_settings( const telegram_api::object_ptr &settings) { CHECK(settings != nullptr); - auto flags = settings->flags_; - auto disabled = (flags & telegram_api::autoDownloadSettings::DISABLED_MASK) != 0; - auto video_preload_large = (flags & telegram_api::autoDownloadSettings::VIDEO_PRELOAD_LARGE_MASK) != 0; - auto audio_preload_next = (flags & telegram_api::autoDownloadSettings::AUDIO_PRELOAD_NEXT_MASK) != 0; - auto stories_preload = (flags & telegram_api::autoDownloadSettings::STORIES_PRELOAD_MASK) != 0; - auto phonecalls_less_data = (flags & telegram_api::autoDownloadSettings::PHONECALLS_LESS_DATA_MASK) != 0; constexpr int32 MAX_PHOTO_SIZE = 10 * (1 << 20) /* 10 MB */; constexpr int64 MAX_DOCUMENT_SIZE = (static_cast(1) << 52); return td_api::make_object( - !disabled, clamp(settings->photo_size_max_, static_cast(0), MAX_PHOTO_SIZE), + !settings->disabled_, clamp(settings->photo_size_max_, static_cast(0), MAX_PHOTO_SIZE), clamp(settings->video_size_max_, static_cast(0), MAX_DOCUMENT_SIZE), clamp(settings->file_size_max_, static_cast(0), MAX_DOCUMENT_SIZE), settings->video_upload_maxbitrate_, - video_preload_large, audio_preload_next, stories_preload, phonecalls_less_data); + settings->video_preload_large_, settings->audio_preload_next_, settings->stories_preload_, + settings->phonecalls_less_data_); } class GetAutoDownloadSettingsQuery final : public Td::ResultHandler { @@ -67,26 +62,10 @@ class GetAutoDownloadSettingsQuery final : public Td::ResultHandler { static telegram_api::object_ptr get_input_auto_download_settings( const AutoDownloadSettings &settings) { - int32 flags = 0; - if (!settings.is_enabled) { - flags |= telegram_api::autoDownloadSettings::DISABLED_MASK; - } - if (settings.preload_large_videos) { - flags |= telegram_api::autoDownloadSettings::VIDEO_PRELOAD_LARGE_MASK; - } - if (settings.preload_next_audio) { - flags |= telegram_api::autoDownloadSettings::AUDIO_PRELOAD_NEXT_MASK; - } - if (settings.preload_stories) { - flags |= telegram_api::autoDownloadSettings::STORIES_PRELOAD_MASK; - } - if (settings.use_less_data_for_calls) { - flags |= telegram_api::autoDownloadSettings::PHONECALLS_LESS_DATA_MASK; - } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - settings.max_photo_file_size, settings.max_video_file_size, settings.max_other_file_size, - settings.video_upload_bitrate, 0, 0); + 0, !settings.is_enabled, settings.preload_large_videos, settings.preload_next_audio, + settings.use_less_data_for_calls, settings.preload_stories, settings.max_photo_file_size, + settings.max_video_file_size, settings.max_other_file_size, settings.video_upload_bitrate, 0, 0); } class SaveAutoDownloadSettingsQuery final : public Td::ResultHandler { @@ -97,15 +76,8 @@ class SaveAutoDownloadSettingsQuery final : public Td::ResultHandler { } void send(NetType type, const AutoDownloadSettings &settings) { - int32 flags = 0; - if (type == NetType::MobileRoaming) { - flags |= telegram_api::account_saveAutoDownloadSettings::LOW_MASK; - } - if (type == NetType::WiFi) { - flags |= telegram_api::account_saveAutoDownloadSettings::HIGH_MASK; - } send_query(G()->net_query_creator().create(telegram_api::account_saveAutoDownloadSettings( - flags, false /*ignored*/, false /*ignored*/, get_input_auto_download_settings(settings)))); + 0, type == NetType::MobileRoaming, type == NetType::WiFi, get_input_auto_download_settings(settings)))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/AutosaveManager.cpp b/third-party/td/td/td/telegram/AutosaveManager.cpp index 6c344cdfc7..85c2e2d77f 100644 --- a/third-party/td/td/td/telegram/AutosaveManager.cpp +++ b/third-party/td/td/td/telegram/AutosaveManager.cpp @@ -68,20 +68,14 @@ class SaveAutoSaveSettingsQuery final : public Td::ResultHandler { telegram_api::object_ptr settings) { int32 flags = 0; telegram_api::object_ptr input_peer; - if (users) { - flags |= telegram_api::account_saveAutoSaveSettings::USERS_MASK; - } else if (chats) { - flags |= telegram_api::account_saveAutoSaveSettings::CHATS_MASK; - } else if (broadcasts) { - flags |= telegram_api::account_saveAutoSaveSettings::BROADCASTS_MASK; - } else { + if (!users && !chats && !broadcasts) { flags |= telegram_api::account_saveAutoSaveSettings::PEER_MASK; input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); } send_query(G()->net_query_creator().create( - telegram_api::account_saveAutoSaveSettings(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_peer), std::move(settings)), + telegram_api::account_saveAutoSaveSettings(flags, users, chats, broadcasts, std::move(input_peer), + std::move(settings)), {{"me"}})); } @@ -154,16 +148,10 @@ AutosaveManager::DialogAutosaveSettings::DialogAutosaveSettings(const td_api::sc telegram_api::object_ptr AutosaveManager::DialogAutosaveSettings::get_input_auto_save_settings() const { int32 flags = 0; - if (autosave_photos_) { - flags |= telegram_api::autoSaveSettings::PHOTOS_MASK; - } - if (autosave_videos_) { - flags |= telegram_api::autoSaveSettings::VIDEOS_MASK; - } if (are_inited_) { flags |= telegram_api::autoSaveSettings::VIDEO_MAX_SIZE_MASK; } - return telegram_api::make_object(flags, false /*ignored*/, false /*ignored*/, + return telegram_api::make_object(flags, autosave_photos_, autosave_videos_, max_video_file_size_); } diff --git a/third-party/td/td/td/telegram/BackgroundManager.cpp b/third-party/td/td/td/telegram/BackgroundManager.cpp index 6d9e3ecd1e..b9925f840f 100644 --- a/third-party/td/td/td/telegram/BackgroundManager.cpp +++ b/third-party/td/td/td/telegram/BackgroundManager.cpp @@ -219,12 +219,8 @@ class UploadBackgroundQuery final : public Td::ResultHandler { type_ = type; dialog_id_ = dialog_id; for_dark_theme_ = for_dark_theme; - int32 flags = 0; - if (dialog_id.is_valid()) { - flags |= telegram_api::account_uploadWallPaper::FOR_CHAT_MASK; - } send_query(G()->net_query_creator().create(telegram_api::account_uploadWallPaper( - flags, false /*ignored*/, std::move(input_file), type_.get_mime_type(), type_.get_input_wallpaper_settings()))); + 0, dialog_id.is_valid(), std::move(input_file), type_.get_mime_type(), type_.get_input_wallpaper_settings()))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/BackgroundType.cpp b/third-party/td/td/td/telegram/BackgroundType.cpp index 20d470e7b7..48684e4b0c 100644 --- a/third-party/td/td/td/telegram/BackgroundType.cpp +++ b/third-party/td/td/td/telegram/BackgroundType.cpp @@ -422,13 +422,13 @@ BackgroundType::BackgroundType(bool has_no_file, bool is_pattern, type_ = Type::Pattern; if (settings != nullptr) { fill_ = BackgroundFill(settings.get()); - is_moving_ = (settings->flags_ & telegram_api::wallPaperSettings::MOTION_MASK) != 0; + is_moving_ = settings->motion_; } } else { type_ = Type::Wallpaper; if (settings != nullptr) { - is_blurred_ = (settings->flags_ & telegram_api::wallPaperSettings::BLUR_MASK) != 0; - is_moving_ = (settings->flags_ & telegram_api::wallPaperSettings::MOTION_MASK) != 0; + is_blurred_ = settings->blur_; + is_moving_ = settings->motion_; } } } @@ -471,12 +471,6 @@ td_api::object_ptr BackgroundType::get_background_type_o telegram_api::object_ptr BackgroundType::get_input_wallpaper_settings() const { int32 flags = 0; - if (is_blurred_) { - flags |= telegram_api::wallPaperSettings::BLUR_MASK; - } - if (is_moving_) { - flags |= telegram_api::wallPaperSettings::MOTION_MASK; - } switch (fill_.get_type()) { case BackgroundFill::Type::FreeformGradient: if (fill_.fourth_color_ != -1) { diff --git a/third-party/td/td/td/telegram/BoostManager.cpp b/third-party/td/td/td/telegram/BoostManager.cpp index 6fcd719f5f..2753359f0d 100644 --- a/third-party/td/td/td/telegram/BoostManager.cpp +++ b/third-party/td/td/td/telegram/BoostManager.cpp @@ -274,12 +274,8 @@ class GetBoostsListQuery final : public Td::ResultHandler { dialog_id_ = dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); CHECK(input_peer != nullptr); - int32 flags = 0; - if (only_gift_codes) { - flags |= telegram_api::premium_getBoostsList::GIFTS_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::premium_getBoostsList(flags, false /*ignored*/, std::move(input_peer), offset, limit))); + telegram_api::premium_getBoostsList(0, only_gift_codes, std::move(input_peer), offset, limit))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/BotInfoManager.cpp b/third-party/td/td/td/telegram/BotInfoManager.cpp index 9034f87b14..d3b2c4fa09 100644 --- a/third-party/td/td/td/telegram/BotInfoManager.cpp +++ b/third-party/td/td/td/telegram/BotInfoManager.cpp @@ -598,14 +598,11 @@ class SetCustomVerificationQuery final : public Td::ResultHandler { if (input_user != nullptr) { flags |= telegram_api::bots_setCustomVerification::BOT_MASK; } - if (is_verified) { - flags |= telegram_api::bots_setCustomVerification::ENABLED_MASK; - } if (!custom_description.empty()) { flags |= telegram_api::bots_setCustomVerification::CUSTOM_DESCRIPTION_MASK; } send_query(G()->net_query_creator().create( - telegram_api::bots_setCustomVerification(flags, false /*ignored*/, std::move(input_user), std::move(input_peer), + telegram_api::bots_setCustomVerification(flags, is_verified, std::move(input_user), std::move(input_peer), custom_description), {{dialog_id}})); } diff --git a/third-party/td/td/td/telegram/BusinessAwayMessage.cpp b/third-party/td/td/td/telegram/BusinessAwayMessage.cpp index de3c00198a..6345680b3a 100644 --- a/third-party/td/td/td/telegram/BusinessAwayMessage.cpp +++ b/third-party/td/td/td/telegram/BusinessAwayMessage.cpp @@ -42,12 +42,8 @@ td_api::object_ptr BusinessAwayMessage::get telegram_api::object_ptr BusinessAwayMessage::get_input_business_away_message( Td *td) const { - int32 flags = 0; - if (offline_only_) { - flags |= telegram_api::inputBusinessAwayMessage::OFFLINE_ONLY_MASK; - } return telegram_api::make_object( - flags, false /*ignored*/, shortcut_id_.get(), schedule_.get_input_business_away_message_schedule(), + 0, offline_only_, shortcut_id_.get(), schedule_.get_input_business_away_message_schedule(), recipients_.get_input_business_recipients(td)); } diff --git a/third-party/td/td/td/telegram/BusinessBotRights.cpp b/third-party/td/td/td/telegram/BusinessBotRights.cpp new file mode 100644 index 0000000000..082af7379d --- /dev/null +++ b/third-party/td/td/td/telegram/BusinessBotRights.cpp @@ -0,0 +1,130 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/BusinessBotRights.h" + +namespace td { + +BusinessBotRights::BusinessBotRights(const telegram_api::object_ptr &bot_rights) { + if (bot_rights == nullptr) { + return; + } + can_reply_ = bot_rights->reply_; + can_read_messages_ = bot_rights->read_messages_; + can_delete_sent_messages_ = bot_rights->delete_sent_messages_; + can_delete_received_messages_ = bot_rights->delete_received_messages_; + can_edit_name_ = bot_rights->edit_name_; + can_edit_bio_ = bot_rights->edit_bio_; + can_edit_profile_photo_ = bot_rights->edit_profile_photo_; + can_edit_username_ = bot_rights->edit_username_; + can_view_gifts_ = bot_rights->view_gifts_; + can_sell_gifts_ = bot_rights->sell_gifts_; + can_change_gift_settings_ = bot_rights->change_gift_settings_; + can_transfer_and_upgrade_gifts_ = bot_rights->transfer_and_upgrade_gifts_; + can_transfer_stars_ = bot_rights->transfer_stars_; + can_manage_stories_ = bot_rights->manage_stories_; +} + +BusinessBotRights::BusinessBotRights(const td_api::object_ptr &bot_rights) { + if (bot_rights == nullptr) { + return; + } + can_reply_ = bot_rights->can_reply_; + can_read_messages_ = bot_rights->can_read_messages_; + can_delete_sent_messages_ = bot_rights->can_delete_sent_messages_; + can_delete_received_messages_ = bot_rights->can_delete_all_messages_; + can_edit_name_ = bot_rights->can_edit_name_; + can_edit_bio_ = bot_rights->can_edit_bio_; + can_edit_profile_photo_ = bot_rights->can_edit_profile_photo_; + can_edit_username_ = bot_rights->can_edit_username_; + can_view_gifts_ = bot_rights->can_view_gifts_and_stars_; + can_sell_gifts_ = bot_rights->can_sell_gifts_; + can_change_gift_settings_ = bot_rights->can_change_gift_settings_; + can_transfer_and_upgrade_gifts_ = bot_rights->can_transfer_and_upgrade_gifts_; + can_transfer_stars_ = bot_rights->can_transfer_stars_; + can_manage_stories_ = bot_rights->can_manage_stories_; +} + +BusinessBotRights BusinessBotRights::legacy(bool can_reply) { + BusinessBotRights result; + result.can_reply_ = can_reply; + return result; +} + +td_api::object_ptr BusinessBotRights::get_business_bot_rights_object() const { + return td_api::make_object( + can_reply_, can_read_messages_, can_delete_sent_messages_, can_delete_received_messages_, can_edit_name_, + can_edit_bio_, can_edit_profile_photo_, can_edit_username_, can_view_gifts_, can_sell_gifts_, + can_change_gift_settings_, can_transfer_and_upgrade_gifts_, can_transfer_stars_, can_manage_stories_); +} + +telegram_api::object_ptr BusinessBotRights::get_input_business_bot_rights() const { + return telegram_api::make_object( + 0, can_reply_, can_read_messages_, can_delete_sent_messages_, can_delete_received_messages_, can_edit_name_, + can_edit_bio_, can_edit_profile_photo_, can_edit_username_, can_view_gifts_, can_sell_gifts_, + can_change_gift_settings_, can_transfer_and_upgrade_gifts_, can_transfer_stars_, can_manage_stories_); +} + +bool operator==(const BusinessBotRights &lhs, const BusinessBotRights &rhs) { + return lhs.can_reply_ == rhs.can_reply_ && lhs.can_read_messages_ == rhs.can_read_messages_ && + lhs.can_delete_sent_messages_ == rhs.can_delete_sent_messages_ && + lhs.can_delete_received_messages_ == rhs.can_delete_received_messages_ && + lhs.can_edit_name_ == rhs.can_edit_name_ && lhs.can_edit_bio_ == rhs.can_edit_bio_ && + lhs.can_edit_profile_photo_ == rhs.can_edit_profile_photo_ && + lhs.can_edit_username_ == rhs.can_edit_username_ && lhs.can_view_gifts_ == rhs.can_view_gifts_ && + lhs.can_sell_gifts_ == rhs.can_sell_gifts_ && lhs.can_change_gift_settings_ == rhs.can_change_gift_settings_ && + lhs.can_transfer_and_upgrade_gifts_ == rhs.can_transfer_and_upgrade_gifts_ && + lhs.can_transfer_stars_ == rhs.can_transfer_stars_ && lhs.can_manage_stories_ == rhs.can_manage_stories_; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const BusinessBotRights &bot_rights) { + string_builder << "BusinessBotRights"; + if (bot_rights.can_reply_) { + string_builder << "(reply)"; + } + if (bot_rights.can_read_messages_) { + string_builder << "(read_messages)"; + } + if (bot_rights.can_delete_sent_messages_) { + string_builder << "(delete_sent_messages)"; + } + if (bot_rights.can_delete_received_messages_) { + string_builder << "(delete_received_messages)"; + } + if (bot_rights.can_edit_name_) { + string_builder << "(edit_name)"; + } + if (bot_rights.can_edit_bio_) { + string_builder << "(edit_bio)"; + } + if (bot_rights.can_edit_profile_photo_) { + string_builder << "(edit_profile_photo)"; + } + if (bot_rights.can_edit_username_) { + string_builder << "(edit_username)"; + } + if (bot_rights.can_view_gifts_) { + string_builder << "(view_gifts)"; + } + if (bot_rights.can_sell_gifts_) { + string_builder << "(sell_gifts)"; + } + if (bot_rights.can_change_gift_settings_) { + string_builder << "(change_gift_settings)"; + } + if (bot_rights.can_transfer_and_upgrade_gifts_) { + string_builder << "(transfer_and_upgrade_gifts)"; + } + if (bot_rights.can_transfer_stars_) { + string_builder << "(transfer_stars)"; + } + if (bot_rights.can_manage_stories_) { + string_builder << "(manage_stories)"; + } + return string_builder << ']'; +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/BusinessBotRights.h b/third-party/td/td/td/telegram/BusinessBotRights.h new file mode 100644 index 0000000000..9e6d175ed3 --- /dev/null +++ b/third-party/td/td/td/telegram/BusinessBotRights.h @@ -0,0 +1,66 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class BusinessBotRights { + public: + BusinessBotRights() = default; + + explicit BusinessBotRights(const telegram_api::object_ptr &bot_rights); + + explicit BusinessBotRights(const td_api::object_ptr &bot_rights); + + static BusinessBotRights legacy(bool can_reply); + + td_api::object_ptr get_business_bot_rights_object() const; + + telegram_api::object_ptr get_input_business_bot_rights() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + + private: + bool can_reply_ = false; + bool can_read_messages_ = false; + bool can_delete_sent_messages_ = false; + bool can_delete_received_messages_ = false; + bool can_edit_name_ = false; + bool can_edit_bio_ = false; + bool can_edit_profile_photo_ = false; + bool can_edit_username_ = false; + bool can_view_gifts_ = false; + bool can_sell_gifts_ = false; + bool can_change_gift_settings_ = false; + bool can_transfer_and_upgrade_gifts_ = false; + bool can_transfer_stars_ = false; + bool can_manage_stories_ = false; + + friend bool operator==(const BusinessBotRights &lhs, const BusinessBotRights &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const BusinessBotRights &bot_rights); +}; + +bool operator==(const BusinessBotRights &lhs, const BusinessBotRights &rhs); + +inline bool operator!=(const BusinessBotRights &lhs, const BusinessBotRights &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const BusinessBotRights &bot_rights); + +} // namespace td diff --git a/third-party/td/td/td/telegram/BusinessBotRights.hpp b/third-party/td/td/td/telegram/BusinessBotRights.hpp new file mode 100644 index 0000000000..bc7b23f4d2 --- /dev/null +++ b/third-party/td/td/td/telegram/BusinessBotRights.hpp @@ -0,0 +1,56 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/BusinessBotRights.h" + +#include "td/utils/common.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void BusinessBotRights::store(StorerT &storer) const { + BEGIN_STORE_FLAGS(); + STORE_FLAG(can_reply_); + STORE_FLAG(can_read_messages_); + STORE_FLAG(can_delete_sent_messages_); + STORE_FLAG(can_delete_received_messages_); + STORE_FLAG(can_edit_name_); + STORE_FLAG(can_edit_bio_); + STORE_FLAG(can_edit_profile_photo_); + STORE_FLAG(can_edit_username_); + STORE_FLAG(can_view_gifts_); + STORE_FLAG(can_sell_gifts_); + STORE_FLAG(can_change_gift_settings_); + STORE_FLAG(can_transfer_and_upgrade_gifts_); + STORE_FLAG(can_transfer_stars_); + STORE_FLAG(can_manage_stories_); + END_STORE_FLAGS(); +} + +template +void BusinessBotRights::parse(ParserT &parser) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(can_reply_); + PARSE_FLAG(can_read_messages_); + PARSE_FLAG(can_delete_sent_messages_); + PARSE_FLAG(can_delete_received_messages_); + PARSE_FLAG(can_edit_name_); + PARSE_FLAG(can_edit_bio_); + PARSE_FLAG(can_edit_profile_photo_); + PARSE_FLAG(can_edit_username_); + PARSE_FLAG(can_view_gifts_); + PARSE_FLAG(can_sell_gifts_); + PARSE_FLAG(can_change_gift_settings_); + PARSE_FLAG(can_transfer_and_upgrade_gifts_); + PARSE_FLAG(can_transfer_stars_); + PARSE_FLAG(can_manage_stories_); + END_PARSE_FLAGS(); +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/BusinessConnectedBot.cpp b/third-party/td/td/td/telegram/BusinessConnectedBot.cpp index 0c49919757..07210469ad 100644 --- a/third-party/td/td/td/telegram/BusinessConnectedBot.cpp +++ b/third-party/td/td/td/telegram/BusinessConnectedBot.cpp @@ -15,7 +15,7 @@ BusinessConnectedBot::BusinessConnectedBot(telegram_api::object_ptrbot_id_); recipients_ = BusinessRecipients(std::move(connected_bot->recipients_)); - can_reply_ = connected_bot->can_reply_; + rights_ = BusinessBotRights(connected_bot->rights_); } BusinessConnectedBot::BusinessConnectedBot(td_api::object_ptr connected_bot) { @@ -24,23 +24,23 @@ BusinessConnectedBot::BusinessConnectedBot(td_api::object_ptrbot_user_id_); recipients_ = BusinessRecipients(std::move(connected_bot->recipients_), true); - can_reply_ = connected_bot->can_reply_; + rights_ = BusinessBotRights(connected_bot->rights_); } td_api::object_ptr BusinessConnectedBot::get_business_connected_bot_object(Td *td) const { CHECK(is_valid()); return td_api::make_object( td->user_manager_->get_user_id_object(user_id_, "businessConnectedBot"), - recipients_.get_business_recipients_object(td), can_reply_); + recipients_.get_business_recipients_object(td), rights_.get_business_bot_rights_object()); } bool operator==(const BusinessConnectedBot &lhs, const BusinessConnectedBot &rhs) { - return lhs.user_id_ == rhs.user_id_ && lhs.recipients_ == rhs.recipients_ && lhs.can_reply_ == rhs.can_reply_; + return lhs.user_id_ == rhs.user_id_ && lhs.recipients_ == rhs.recipients_ && lhs.rights_ == rhs.rights_; } StringBuilder &operator<<(StringBuilder &string_builder, const BusinessConnectedBot &connected_bot) { - return string_builder << "connected bot " << connected_bot.user_id_ << ' ' << connected_bot.recipients_ << ' ' - << (connected_bot.can_reply_ ? " that can reply" : " read-only"); + return string_builder << "connected bot " << connected_bot.user_id_ << ' ' << connected_bot.recipients_ << " with " + << connected_bot.rights_; } } // namespace td diff --git a/third-party/td/td/td/telegram/BusinessConnectedBot.h b/third-party/td/td/td/telegram/BusinessConnectedBot.h index be089de815..60fe4e3fd6 100644 --- a/third-party/td/td/td/telegram/BusinessConnectedBot.h +++ b/third-party/td/td/td/telegram/BusinessConnectedBot.h @@ -6,6 +6,7 @@ // #pragma once +#include "td/telegram/BusinessBotRights.h" #include "td/telegram/BusinessRecipients.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -40,8 +41,8 @@ class BusinessConnectedBot { return recipients_; } - bool get_can_reply() const { - return can_reply_; + const BusinessBotRights &get_rights() const { + return rights_; } template @@ -53,7 +54,7 @@ class BusinessConnectedBot { private: UserId user_id_; BusinessRecipients recipients_; - bool can_reply_ = false; + BusinessBotRights rights_; friend bool operator==(const BusinessConnectedBot &lhs, const BusinessConnectedBot &rhs); diff --git a/third-party/td/td/td/telegram/BusinessConnectedBot.hpp b/third-party/td/td/td/telegram/BusinessConnectedBot.hpp index 4c8643dc4c..27f7e36a16 100644 --- a/third-party/td/td/td/telegram/BusinessConnectedBot.hpp +++ b/third-party/td/td/td/telegram/BusinessConnectedBot.hpp @@ -6,6 +6,8 @@ // #pragma once +#include "td/telegram/BusinessBotRights.h" +#include "td/telegram/BusinessBotRights.hpp" #include "td/telegram/BusinessConnectedBot.h" #include "td/telegram/BusinessRecipients.hpp" @@ -16,20 +18,32 @@ namespace td { template void BusinessConnectedBot::store(StorerT &storer) const { + bool can_reply = false; + bool has_rights = true; BEGIN_STORE_FLAGS(); STORE_FLAG(can_reply_); + STORE_FLAG(has_rights); END_STORE_FLAGS(); td::store(user_id_, storer); td::store(recipients_, storer); + td::store(rights_, storer); } template void BusinessConnectedBot::parse(ParserT &parser) { + bool can_reply; + bool has_rights; BEGIN_PARSE_FLAGS(); - PARSE_FLAG(can_reply_); + PARSE_FLAG(can_reply); + PARSE_FLAG(has_rights); END_PARSE_FLAGS(); td::parse(user_id_, parser); td::parse(recipients_, parser); + if (has_rights) { + td::parse(rights_, parser); + } else { + rights_ = BusinessBotRights::legacy(can_reply); + } } } // namespace td diff --git a/third-party/td/td/td/telegram/BusinessConnectionManager.cpp b/third-party/td/td/td/telegram/BusinessConnectionManager.cpp index 5c9dc5e02d..ff34cb8e3c 100644 --- a/third-party/td/td/td/telegram/BusinessConnectionManager.cpp +++ b/third-party/td/td/td/telegram/BusinessConnectionManager.cpp @@ -8,6 +8,7 @@ #include "td/telegram/AccessRights.h" #include "td/telegram/AuthManager.h" +#include "td/telegram/BusinessBotRights.h" #include "td/telegram/ChatManager.h" #include "td/telegram/DialogManager.h" #include "td/telegram/files/FileManager.h" @@ -24,10 +25,12 @@ #include "td/telegram/MessageQuote.h" #include "td/telegram/MessageSelfDestructType.h" #include "td/telegram/MessagesManager.h" +#include "td/telegram/misc.h" #include "td/telegram/OptionManager.h" #include "td/telegram/Photo.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/ServerMessageId.h" +#include "td/telegram/StarAmount.h" #include "td/telegram/Td.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -75,7 +78,7 @@ struct BusinessConnectionManager::BusinessConnection { UserId user_id_; DcId dc_id_; int32 connection_date_ = 0; - bool can_reply_ = false; + BusinessBotRights rights_; bool is_disabled_ = false; explicit BusinessConnection(const telegram_api::object_ptr &connection) @@ -83,7 +86,7 @@ struct BusinessConnectionManager::BusinessConnection { , user_id_(connection->user_id_) , dc_id_(DcId::create(connection->dc_id_)) , connection_date_(connection->date_) - , can_reply_(connection->can_reply_) + , rights_(connection->rights_) , is_disabled_(connection->disabled_) { } @@ -102,8 +105,8 @@ struct BusinessConnectionManager::BusinessConnection { td->dialog_manager_->force_create_dialog(user_dialog_id, "get_business_connection_object"); return td_api::make_object( connection_id_.get(), td->user_manager_->get_user_id_object(user_id_, "businessConnection"), - td->dialog_manager_->get_chat_id_object(user_dialog_id, "businessConnection"), connection_date_, can_reply_, - !is_disabled_); + td->dialog_manager_->get_chat_id_object(user_dialog_id, "businessConnection"), connection_date_, + is_disabled_ ? nullptr : rights_.get_business_bot_rights_object(), !is_disabled_); } }; @@ -514,8 +517,8 @@ class BusinessConnectionManager::StopBusinessPollQuery final : public Td::Result } auto poll = telegram_api::make_object( - 0, telegram_api::poll::CLOSED_MASK, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - telegram_api::make_object(string(), Auto()), Auto(), 0, 0); + 0, 0, true, false, false, false, telegram_api::make_object(string(), Auto()), + Auto(), 0, 0); auto input_media = telegram_api::make_object(0, std::move(poll), vector(), string(), Auto()); int32 server_message_id = message_id.get_server_message_id().get(); @@ -544,6 +547,382 @@ class BusinessConnectionManager::StopBusinessPollQuery final : public Td::Result } }; +class ReadBusinessMessageQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit ReadBusinessMessageQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, DialogId dialog_id, MessageId message_id) { + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Know); + CHECK(input_peer != nullptr); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::messages_readHistory(std::move(input_peer), message_id.get_server_message_id().get()), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id), {{dialog_id}})); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(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 ReadBusinessMessageQuery: " << to_string(ptr); + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class DeleteBusinessMessagesQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit DeleteBusinessMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, const vector &message_ids) { + int32 flags = telegram_api::messages_deleteMessages::REVOKE_MASK; + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::messages_deleteMessages(flags, false /*ignored*/, MessageId::get_server_message_ids(message_ids)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto affected_messages = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for DeleteBusinessMessagesQuery: " << to_string(affected_messages); + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class DeleteBusinessStoriesQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit DeleteBusinessStoriesQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, const vector &story_ids) { + auto user_id = td_->business_connection_manager_->get_business_connection_user_id(business_connection_id); + auto input_peer = td_->dialog_manager_->get_input_peer(DialogId(user_id), AccessRights::Read); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::stories_deleteStories(std::move(input_peer), StoryId::get_input_story_ids(story_ids)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(DEBUG) << "Receive result for DeleteBusinessStoriesQuery: " << ptr; + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class UpdateBusinessProfileQuery final : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + bool set_name_ = false; + bool set_about_ = false; + string first_name_; + string last_name_; + + public: + explicit UpdateBusinessProfileQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(const BusinessConnectionId &business_connection_id, UserId user_id, bool set_name, const string &first_name, + const string &last_name, bool set_about, const string &about) { + user_id_ = user_id; + int32 flags = 0; + if (set_name) { + flags |= telegram_api::account_updateProfile::FIRST_NAME_MASK; + flags |= telegram_api::account_updateProfile::LAST_NAME_MASK; + set_name_ = true; + first_name_ = first_name; + last_name_ = last_name; + } + if (set_about) { + set_about_ = true; + flags |= telegram_api::account_updateProfile::ABOUT_MASK; + } + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::account_updateProfile(flags, first_name, last_name, about), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + LOG(DEBUG) << "Receive result for UpdateBusinessProfileQuery: " << to_string(result_ptr.ok()); + + if (set_name_ && user_id_.is_valid()) { + td_->user_manager_->on_update_user_name(user_id_, std::move(first_name_), std::move(last_name_)); + } + if (set_about_ && user_id_.is_valid()) { + td_->user_manager_->invalidate_user_full(user_id_); + } + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class UpdateBusinessUsernameQuery final : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + + public: + explicit UpdateBusinessUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(const BusinessConnectionId &business_connection_id, UserId user_id, const string &username) { + user_id_ = user_id; + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), telegram_api::account_updateUsername(username), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + LOG(DEBUG) << "Receive result for UpdateBusinessUsernameQuery: " << to_string(result_ptr.ok()); + td_->user_manager_->on_get_user(result_ptr.move_as_ok(), "UpdateBusinessUsernameQuery"); + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class UpdateBusinessGiftSettingsQuery final : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + + public: + explicit UpdateBusinessGiftSettingsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(const BusinessConnectionId &business_connection_id, UserId user_id, const StarGiftSettings &settings) { + user_id_ = user_id; + + int32 flags = 0; + auto gifts_settings = settings.get_disallowed_gifts().get_input_disallowed_gifts_settings(); + if (gifts_settings != nullptr) { + flags |= telegram_api::globalPrivacySettings::DISALLOWED_GIFTS_MASK; + } + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::account_setGlobalPrivacySettings(telegram_api::make_object( + flags, false, false, false, false, false, settings.get_display_gifts_button(), 0, + std::move(gifts_settings))), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + LOG(DEBUG) << "Receive result for UpdateBusinessGiftSettingsQuery: " << to_string(result_ptr.ok()); + td_->user_manager_->invalidate_user_full(user_id_); + + promise_.set_value(Unit()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class GetBusinessStarsStatusQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetBusinessStarsStatusQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const BusinessConnectionId &business_connection_id) { + auto user_id = td_->business_connection_manager_->get_business_connection_user_id(business_connection_id); + auto input_peer = td_->dialog_manager_->get_input_peer(DialogId(user_id), AccessRights::Read); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), telegram_api::payments_getStarsStatus(std::move(input_peer)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto result = result_ptr.move_as_ok(); + LOG(DEBUG) << "Receive result for GetBusinessStarsStatusQuery: " << to_string(result); + + auto star_amount = StarAmount(std::move(result->balance_), true); + promise_.set_value(star_amount.get_star_amount_object()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +class TransferBusinessStarsQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit TransferBusinessStarsQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, int64 payment_form_id, int64 star_count) { + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_sendStarsForm( + payment_form_id, telegram_api::make_object( + telegram_api::make_object(), star_count)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto payment_result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for TransferBusinessStarsQuery: " << to_string(payment_result); + switch (payment_result->get_id()) { + case telegram_api::payments_paymentResult::ID: { + auto result = telegram_api::move_object_as(payment_result); + promise_.set_value(Unit()); + break; + } + case telegram_api::payments_paymentVerificationNeeded::ID: + LOG(ERROR) << "Receive " << to_string(payment_result); + break; + default: + UNREACHABLE(); + } + } + + void on_error(Status status) final { + if (status.message() == "FORM_SUBMIT_DUPLICATE") { + LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; + } + promise_.set_error(std::move(status)); + } +}; + +class GetBusinessStarTransferPaymentFormQuery final : public Td::ResultHandler { + Promise promise_; + BusinessConnectionId business_connection_id_; + int64 star_count_; + + public: + explicit GetBusinessStarTransferPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, int64 star_count) { + business_connection_id_ = business_connection_id; + star_count_ = star_count; + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_getPaymentForm( + 0, + telegram_api::make_object( + telegram_api::make_object(), star_count_), + nullptr), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto payment_form_ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetBusinessStarTransferPaymentFormQuery: " << to_string(payment_form_ptr); + switch (payment_form_ptr->get_id()) { + case telegram_api::payments_paymentForm::ID: + LOG(ERROR) << "Receive " << to_string(payment_form_ptr); + promise_.set_error(Status::Error(500, "Unsupported")); + break; + case telegram_api::payments_paymentFormStars::ID: { + auto payment_form = static_cast(payment_form_ptr.get()); + if (payment_form->invoice_->prices_.size() != 1u || + payment_form->invoice_->prices_[0]->amount_ != star_count_) { + return promise_.set_error(Status::Error(400, "Wrong transfer price specified")); + } + td_->create_handler(std::move(promise_)) + ->send(business_connection_id_, payment_form->form_id_, star_count_); + break; + } + case telegram_api::payments_paymentFormStarGift::ID: { + auto payment_form = static_cast(payment_form_ptr.get()); + if (payment_form->invoice_->prices_.size() != 1u || + payment_form->invoice_->prices_[0]->amount_ != star_count_) { + return promise_.set_error(Status::Error(400, "Wrong transfer price specified")); + } + td_->create_handler(std::move(promise_)) + ->send(business_connection_id_, payment_form->form_id_, star_count_); + break; + } + default: + UNREACHABLE(); + } + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class BusinessConnectionManager::UploadMediaCallback final : public FileManager::UploadCallback { public: void on_upload_ok(FileUploadId file_upload_id, telegram_api::object_ptr input_file) final { @@ -584,6 +963,16 @@ void BusinessConnectionManager::tear_down() { parent_.reset(); } +Status BusinessConnectionManager::check_business_connection(const BusinessConnectionId &connection_id) const { + CHECK(td_->auth_manager_->is_bot()); + auto connection = business_connections_.get_pointer(connection_id); + if (connection == nullptr) { + return Status::Error(400, "Business connection not found"); + } + // no need to check connection->rights_ and connection->is_disabled_ + return Status::OK(); +} + Status BusinessConnectionManager::check_business_connection(const BusinessConnectionId &connection_id, DialogId dialog_id) const { CHECK(td_->auth_manager_->is_bot()); @@ -597,7 +986,7 @@ Status BusinessConnectionManager::check_business_connection(const BusinessConnec if (dialog_id == DialogId(connection->user_id_)) { return Status::Error(400, "Messages must not be sent to self"); } - // no need to check connection->can_reply_ and connection->is_disabled_ + // no need to check connection->rights_ and connection->is_disabled_ return Status::OK(); } @@ -611,6 +1000,22 @@ Status BusinessConnectionManager::check_business_message_id(MessageId message_id return Status::OK(); } +Status BusinessConnectionManager::check_business_story_id(StoryId story_id) const { + if (!story_id.is_valid()) { + return Status::Error(400, "Invalid story identifier specified"); + } + if (!story_id.is_server()) { + return Status::Error(400, "Wrong story identifier specified"); + } + return Status::OK(); +} + +UserId BusinessConnectionManager::get_business_connection_user_id(const BusinessConnectionId &connection_id) const { + auto connection = business_connections_.get_pointer(connection_id); + CHECK(connection != nullptr); + return connection->user_id_; +} + DcId BusinessConnectionManager::get_business_connection_dc_id(const BusinessConnectionId &connection_id) const { if (connection_id.is_empty()) { return DcId::main(); @@ -1513,6 +1918,92 @@ void BusinessConnectionManager::stop_poll(BusinessConnectionId business_connecti ->send(business_connection_id, dialog_id, message_id, std::move(new_reply_markup)); } +void BusinessConnectionManager::read_business_message(BusinessConnectionId business_connection_id, DialogId dialog_id, + MessageId message_id, Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id, dialog_id)); + TRY_STATUS_PROMISE(promise, check_business_message_id(message_id)); + + td_->create_handler(std::move(promise)) + ->send(business_connection_id, dialog_id, message_id); +} + +void BusinessConnectionManager::delete_business_messages(BusinessConnectionId business_connection_id, + const vector &message_ids, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + for (auto message_id : message_ids) { + TRY_STATUS_PROMISE(promise, check_business_message_id(message_id)); + } + if (message_ids.size() > 100u) { + return promise.set_error(Status::Error(400, "Too many messages identifiers specified")); + } + + td_->create_handler(std::move(promise))->send(business_connection_id, message_ids); +} + +void BusinessConnectionManager::delete_business_story(BusinessConnectionId business_connection_id, StoryId story_id, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + TRY_STATUS_PROMISE(promise, check_business_story_id(story_id)); + td_->create_handler(std::move(promise))->send(business_connection_id, {story_id}); +} + +void BusinessConnectionManager::set_business_name(BusinessConnectionId business_connection_id, const string &first_name, + const string &last_name, Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + auto user_id = get_business_connection_user_id(business_connection_id); + + td_->create_handler(std::move(promise)) + ->send(business_connection_id, user_id, true, clean_name(first_name, MAX_NAME_LENGTH), + clean_name(last_name, MAX_NAME_LENGTH), false, string()); +} + +void BusinessConnectionManager::set_business_about(BusinessConnectionId business_connection_id, const string &about, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + auto user_id = get_business_connection_user_id(business_connection_id); + + td_->create_handler(std::move(promise)) + ->send(business_connection_id, user_id, false, string(), string(), true, about); +} + +void BusinessConnectionManager::set_business_username(BusinessConnectionId business_connection_id, + const string &username, Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + if (!username.empty() && !is_allowed_username(username)) { + return promise.set_error(Status::Error(400, "Username is invalid")); + } + auto user_id = get_business_connection_user_id(business_connection_id); + + td_->create_handler(std::move(promise))->send(business_connection_id, user_id, username); +} + +void BusinessConnectionManager::set_business_gift_settings(BusinessConnectionId business_connection_id, + StarGiftSettings settings, Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + auto user_id = get_business_connection_user_id(business_connection_id); + + td_->create_handler(std::move(promise)) + ->send(business_connection_id, user_id, settings); +} + +void BusinessConnectionManager::get_business_star_status(BusinessConnectionId business_connection_id, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + td_->create_handler(std::move(promise))->send(business_connection_id); +} + +void BusinessConnectionManager::transfer_business_stars(BusinessConnectionId business_connection_id, int64 star_count, + Promise &&promise) { + TRY_STATUS_PROMISE(promise, check_business_connection(business_connection_id)); + if (star_count <= 0 || star_count > 1000000000) { + return promise.set_error(Status::Error(400, "Invalid amount of Telegram Stars to transfer specified")); + } + + td_->create_handler(std::move(promise)) + ->send(business_connection_id, star_count); +} + td_api::object_ptr BusinessConnectionManager::get_update_business_connection( const BusinessConnection *connection) const { return td_api::make_object(connection->get_business_connection_object(td_)); diff --git a/third-party/td/td/td/telegram/BusinessConnectionManager.h b/third-party/td/td/td/telegram/BusinessConnectionManager.h index b8b0ed7b28..35c17c170c 100644 --- a/third-party/td/td/td/telegram/BusinessConnectionManager.h +++ b/third-party/td/td/td/telegram/BusinessConnectionManager.h @@ -13,8 +13,11 @@ #include "td/telegram/MessageId.h" #include "td/telegram/MessageInputReplyTo.h" #include "td/telegram/net/DcId.h" +#include "td/telegram/StarGiftSettings.h" +#include "td/telegram/StoryId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" +#include "td/telegram/UserId.h" #include "td/actor/actor.h" @@ -41,8 +44,12 @@ class BusinessConnectionManager final : public Actor { BusinessConnectionManager &operator=(BusinessConnectionManager &&) = delete; ~BusinessConnectionManager() final; + Status check_business_connection(const BusinessConnectionId &connection_id) const; + Status check_business_connection(const BusinessConnectionId &connection_id, DialogId dialog_id) const; + UserId get_business_connection_user_id(const BusinessConnectionId &connection_id) const; + DcId get_business_connection_dc_id(const BusinessConnectionId &connection_id) const; void on_update_bot_business_connect(telegram_api::object_ptr &&connection); @@ -103,9 +110,35 @@ class BusinessConnectionManager final : public Actor { td_api::object_ptr &&reply_markup, Promise> &&promise); + void read_business_message(BusinessConnectionId business_connection_id, DialogId dialog_id, MessageId message_id, + Promise &&promise); + + void delete_business_messages(BusinessConnectionId business_connection_id, const vector &message_ids, + Promise &&promise); + + void delete_business_story(BusinessConnectionId business_connection_id, StoryId story_id, Promise &&promise); + + void set_business_name(BusinessConnectionId business_connection_id, const string &first_name, const string &last_name, + Promise &&promise); + + void set_business_about(BusinessConnectionId business_connection_id, const string &about, Promise &&promise); + + void set_business_username(BusinessConnectionId business_connection_id, const string &username, + Promise &&promise); + + void set_business_gift_settings(BusinessConnectionId business_connection_id, StarGiftSettings settings, + Promise &&promise); + + void get_business_star_status(BusinessConnectionId business_connection_id, + Promise> &&promise); + + void transfer_business_stars(BusinessConnectionId business_connection_id, int64 star_count, Promise &&promise); + void get_current_state(vector> &updates) const; private: + static constexpr size_t MAX_NAME_LENGTH = 64; // server side limit for first/last name + struct BusinessConnection; struct PendingMessage; class SendBusinessMessageQuery; @@ -140,6 +173,8 @@ class BusinessConnectionManager final : public Actor { Status check_business_message_id(MessageId message_id) const; + Status check_business_story_id(StoryId story_id) const; + void on_get_business_connection(const BusinessConnectionId &connection_id, Result> r_updates); diff --git a/third-party/td/td/td/telegram/BusinessManager.cpp b/third-party/td/td/td/telegram/BusinessManager.cpp index 154b63d1de..5337b6278e 100644 --- a/third-party/td/td/td/telegram/BusinessManager.cpp +++ b/third-party/td/td/td/telegram/BusinessManager.cpp @@ -80,20 +80,17 @@ class UpdateConnectedBotQuery final : public Td::ResultHandler { } void send(const BusinessConnectedBot &bot, telegram_api::object_ptr &&input_user) { - int32 flags = 0; - if (bot.get_can_reply()) { - flags |= telegram_api::account_updateConnectedBot::CAN_REPLY_MASK; - } + int32 flags = telegram_api::account_updateConnectedBot::RIGHTS_MASK; + auto rights = bot.get_rights().get_input_business_bot_rights(); send_query(G()->net_query_creator().create( - telegram_api::account_updateConnectedBot(flags, false /*ignored*/, false /*ignored*/, std::move(input_user), + telegram_api::account_updateConnectedBot(flags, false, std::move(rights), std::move(input_user), bot.get_recipients().get_input_business_bot_recipients(td_)), {{"me"}})); } void send(telegram_api::object_ptr &&input_user) { - int32 flags = telegram_api::account_updateConnectedBot::DELETED_MASK; send_query(G()->net_query_creator().create( - telegram_api::account_updateConnectedBot(flags, false /*ignored*/, false /*ignored*/, std::move(input_user), + telegram_api::account_updateConnectedBot(0, true, nullptr, std::move(input_user), BusinessRecipients().get_input_business_bot_recipients(td_)), {{"me"}})); } diff --git a/third-party/td/td/td/telegram/BusinessRecipients.cpp b/third-party/td/td/td/telegram/BusinessRecipients.cpp index 010b499122..378e375a5c 100644 --- a/third-party/td/td/td/telegram/BusinessRecipients.cpp +++ b/third-party/td/td/td/telegram/BusinessRecipients.cpp @@ -86,22 +86,6 @@ td_api::object_ptr BusinessRecipients::get_business_ telegram_api::object_ptr BusinessRecipients::get_input_business_recipients( Td *td) const { - int32 flags = 0; - if (existing_chats_) { - flags |= telegram_api::inputBusinessRecipients::EXISTING_CHATS_MASK; - } - if (new_chats_) { - flags |= telegram_api::inputBusinessRecipients::NEW_CHATS_MASK; - } - if (contacts_) { - flags |= telegram_api::inputBusinessRecipients::CONTACTS_MASK; - } - if (non_contacts_) { - flags |= telegram_api::inputBusinessRecipients::NON_CONTACTS_MASK; - } - if (exclude_selected_) { - flags |= telegram_api::inputBusinessRecipients::EXCLUDE_SELECTED_MASK; - } vector> input_users; for (auto user_id : user_ids_) { auto r_input_user = td->user_manager_->get_input_user(user_id); @@ -109,32 +93,16 @@ telegram_api::object_ptr BusinessRecipien input_users.push_back(r_input_user.move_as_ok()); } } + int32 flags = 0; if (!input_users.empty()) { flags |= telegram_api::inputBusinessRecipients::USERS_MASK; } - return telegram_api::make_object(flags, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, - false /*ignored*/, std::move(input_users)); + return telegram_api::make_object( + flags, existing_chats_, new_chats_, contacts_, non_contacts_, exclude_selected_, std::move(input_users)); } telegram_api::object_ptr BusinessRecipients::get_input_business_bot_recipients(Td *td) const { - int32 flags = 0; - if (existing_chats_) { - flags |= telegram_api::inputBusinessBotRecipients::EXISTING_CHATS_MASK; - } - if (new_chats_) { - flags |= telegram_api::inputBusinessBotRecipients::NEW_CHATS_MASK; - } - if (contacts_) { - flags |= telegram_api::inputBusinessBotRecipients::CONTACTS_MASK; - } - if (non_contacts_) { - flags |= telegram_api::inputBusinessBotRecipients::NON_CONTACTS_MASK; - } - if (exclude_selected_) { - flags |= telegram_api::inputBusinessBotRecipients::EXCLUDE_SELECTED_MASK; - } vector> input_users; for (auto user_id : user_ids_) { auto r_input_user = td->user_manager_->get_input_user(user_id); @@ -142,9 +110,6 @@ BusinessRecipients::get_input_business_bot_recipients(Td *td) const { input_users.push_back(r_input_user.move_as_ok()); } } - if (!input_users.empty()) { - flags |= telegram_api::inputBusinessBotRecipients::USERS_MASK; - } vector> excluded_input_users; for (auto user_id : excluded_user_ids_) { auto r_input_user = td->user_manager_->get_input_user(user_id); @@ -152,12 +117,16 @@ BusinessRecipients::get_input_business_bot_recipients(Td *td) const { excluded_input_users.push_back(r_input_user.move_as_ok()); } } + int32 flags = 0; + if (!input_users.empty()) { + flags |= telegram_api::inputBusinessBotRecipients::USERS_MASK; + } if (!excluded_input_users.empty()) { flags |= telegram_api::inputBusinessBotRecipients::EXCLUDE_USERS_MASK; } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_users), std::move(excluded_input_users)); + flags, existing_chats_, new_chats_, contacts_, non_contacts_, exclude_selected_, std::move(input_users), + std::move(excluded_input_users)); } void BusinessRecipients::add_dependencies(Dependencies &dependencies) const { diff --git a/third-party/td/td/td/telegram/CallActor.cpp b/third-party/td/td/td/telegram/CallActor.cpp index a37c40ea31..1720f7deda 100644 --- a/third-party/td/td/td/telegram/CallActor.cpp +++ b/third-party/td/td/td/telegram/CallActor.cpp @@ -44,16 +44,9 @@ CallProtocol::CallProtocol(const telegram_api::phoneCallProtocol &protocol) , library_versions(protocol.library_versions_) { } -tl_object_ptr CallProtocol::get_input_phone_call_protocol() const { - int32 flags = 0; - if (udp_p2p) { - flags |= telegram_api::phoneCallProtocol::UDP_P2P_MASK; - } - if (udp_reflector) { - flags |= telegram_api::phoneCallProtocol::UDP_REFLECTOR_MASK; - } - return make_tl_object(flags, udp_p2p, udp_reflector, min_layer, max_layer, - vector(library_versions)); +telegram_api::object_ptr CallProtocol::get_input_phone_call_protocol() const { + return telegram_api::make_object(0, udp_p2p, udp_reflector, min_layer, max_layer, + vector(library_versions)); } CallProtocol::CallProtocol(const td_api::callProtocol &protocol) @@ -95,9 +88,9 @@ CallConnection::CallConnection(const telegram_api::PhoneConnection &connection) } } -tl_object_ptr CallProtocol::get_call_protocol_object() const { - return make_tl_object(udp_p2p, udp_reflector, min_layer, max_layer, - vector(library_versions)); +td_api::object_ptr CallProtocol::get_call_protocol_object() const { + return td_api::make_object(udp_p2p, udp_reflector, min_layer, max_layer, + vector(library_versions)); } tl_object_ptr CallConnection::get_call_server_object() const { diff --git a/third-party/td/td/td/telegram/CallActor.h b/third-party/td/td/td/telegram/CallActor.h index 1c489bfa39..801c90bdea 100644 --- a/third-party/td/td/td/telegram/CallActor.h +++ b/third-party/td/td/td/telegram/CallActor.h @@ -46,9 +46,9 @@ struct CallProtocol { explicit CallProtocol(const telegram_api::phoneCallProtocol &protocol); - tl_object_ptr get_input_phone_call_protocol() const; + telegram_api::object_ptr get_input_phone_call_protocol() const; - tl_object_ptr get_call_protocol_object() const; + td_api::object_ptr get_call_protocol_object() const; }; struct CallConnection { diff --git a/third-party/td/td/td/telegram/CallDiscardReason.cpp b/third-party/td/td/td/telegram/CallDiscardReason.cpp index 019a682071..2d0c3a0dda 100644 --- a/third-party/td/td/td/telegram/CallDiscardReason.cpp +++ b/third-party/td/td/td/telegram/CallDiscardReason.cpp @@ -28,12 +28,14 @@ CallDiscardReason get_call_discard_reason( case telegram_api::phoneCallDiscardReasonBusy::ID: result.type_ = CallDiscardReason::Type::Declined; break; + /* case telegram_api::phoneCallDiscardReasonAllowGroupCall::ID: result.type_ = CallDiscardReason::Type::AllowGroupCall; result.encrypted_key_ = static_cast(reason.get()) ->encrypted_key_.as_slice() .str(); break; + */ default: UNREACHABLE(); break; @@ -55,9 +57,11 @@ telegram_api::object_ptr get_input_phone_c return telegram_api::make_object(); case CallDiscardReason::Type::Declined: return telegram_api::make_object(); + /* case CallDiscardReason::Type::AllowGroupCall: return telegram_api::make_object( BufferSlice(reason.encrypted_key_)); + */ default: UNREACHABLE(); return nullptr; @@ -76,8 +80,10 @@ td_api::object_ptr get_call_discard_reason_object(Cal return td_api::make_object(); case CallDiscardReason::Type::Declined: return td_api::make_object(); + /* case CallDiscardReason::Type::AllowGroupCall: return td_api::make_object(reason.encrypted_key_); + */ default: UNREACHABLE(); return nullptr; diff --git a/third-party/td/td/td/telegram/CallDiscardReason.h b/third-party/td/td/td/telegram/CallDiscardReason.h index 06be06b1a2..329787a1bc 100644 --- a/third-party/td/td/td/telegram/CallDiscardReason.h +++ b/third-party/td/td/td/telegram/CallDiscardReason.h @@ -14,7 +14,7 @@ namespace td { struct CallDiscardReason { - enum class Type : int32 { Empty, Missed, Disconnected, HungUp, Declined, AllowGroupCall }; + enum class Type : int32 { Empty, Missed, Disconnected, HungUp, Declined }; Type type_ = Type::Empty; string encrypted_key_; }; diff --git a/third-party/td/td/td/telegram/ChatManager.cpp b/third-party/td/td/td/telegram/ChatManager.cpp index 66937d0881..70d4d4ac3b 100644 --- a/third-party/td/td/td/telegram/ChatManager.cpp +++ b/third-party/td/td/td/telegram/ChatManager.cpp @@ -116,22 +116,13 @@ class CreateChannelQuery final : public Td::ResultHandler { void send(const string &title, bool is_forum, bool is_megagroup, const string &about, const DialogLocation &location, bool for_import, MessageTtl message_ttl) { int32 flags = telegram_api::channels_createChannel::TTL_PERIOD_MASK; - if (is_forum) { - flags |= telegram_api::channels_createChannel::FORUM_MASK; - } else if (is_megagroup) { - flags |= telegram_api::channels_createChannel::MEGAGROUP_MASK; - } else { - flags |= telegram_api::channels_createChannel::BROADCAST_MASK; - } if (!location.empty()) { flags |= telegram_api::channels_createChannel::GEO_POINT_MASK; } - if (for_import) { - flags |= telegram_api::channels_createChannel::FOR_IMPORT_MASK; - } + auto is_broadcast = !is_forum && !is_megagroup; send_query(G()->net_query_creator().create(telegram_api::channels_createChannel( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, title, about, + flags, is_broadcast, is_megagroup && !is_forum, is_forum, for_import, title, about, location.get_input_geo_point(), location.get_address(), message_ttl.get_input_ttl_period()))); } @@ -340,9 +331,6 @@ class UpdateChannelColorQuery final : public Td::ResultHandler { auto input_channel = td_->chat_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); int32 flags = 0; - if (for_profile) { - flags |= telegram_api::channels_updateColor::FOR_PROFILE_MASK; - } if (accent_color_id.is_valid()) { flags |= telegram_api::channels_updateColor::COLOR_MASK; } @@ -350,7 +338,7 @@ class UpdateChannelColorQuery final : public Td::ResultHandler { flags |= telegram_api::channels_updateColor::BACKGROUND_EMOJI_ID_MASK; } send_query(G()->net_query_creator().create( - telegram_api::channels_updateColor(flags, false /*ignored*/, std::move(input_channel), accent_color_id.get(), + telegram_api::channels_updateColor(flags, for_profile, std::move(input_channel), accent_color_id.get(), background_custom_emoji_id.get()), {{channel_id}})); } @@ -578,15 +566,8 @@ class ToggleChannelSignaturesQuery final : public Td::ResultHandler { channel_id_ = channel_id; auto input_channel = td_->chat_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - int32 flags = 0; - if (sign_messages) { - flags |= telegram_api::channels_toggleSignatures::SIGNATURES_ENABLED_MASK; - } - if (show_message_sender) { - flags |= telegram_api::channels_toggleSignatures::PROFILES_ENABLED_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::channels_toggleSignatures(flags, false /*ignored*/, false /*ignored*/, std::move(input_channel)), + telegram_api::channels_toggleSignatures(0, sign_messages, show_message_sender, std::move(input_channel)), {{channel_id}})); } @@ -1360,19 +1341,8 @@ class GetCreatedPublicChannelsQuery final : public Td::ResultHandler { void send(PublicDialogType type, bool check_limit) { type_ = type; - int32 flags = 0; - if (type_ == PublicDialogType::IsLocationBased) { - flags |= telegram_api::channels_getAdminedPublicChannels::BY_LOCATION_MASK; - } - if (type_ == PublicDialogType::ForPersonalDialog) { - CHECK(!check_limit); - flags |= telegram_api::channels_getAdminedPublicChannels::FOR_PERSONAL_MASK; - } - if (check_limit) { - flags |= telegram_api::channels_getAdminedPublicChannels::CHECK_LIMIT_MASK; - } send_query(G()->net_query_creator().create(telegram_api::channels_getAdminedPublicChannels( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/))); + 0, type_ == PublicDialogType::IsLocationBased, check_limit, type_ == PublicDialogType::ForPersonalDialog))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/ChatReactions.cpp b/third-party/td/td/td/telegram/ChatReactions.cpp index 4f73cbb309..c87c064cc0 100644 --- a/third-party/td/td/td/telegram/ChatReactions.cpp +++ b/third-party/td/td/td/telegram/ChatReactions.cpp @@ -120,11 +120,7 @@ td_api::object_ptr ChatReactions::get_chat_avail telegram_api::object_ptr ChatReactions::get_input_chat_reactions() const { if (allow_all_regular_) { - int32 flags = 0; - if (allow_all_custom_) { - flags |= telegram_api::chatReactionsAll::ALLOW_CUSTOM_MASK; - } - return telegram_api::make_object(flags, allow_all_custom_); + return telegram_api::make_object(0, allow_all_custom_); } if (!reaction_types_.empty()) { return telegram_api::make_object( diff --git a/third-party/td/td/td/telegram/ConfigManager.cpp b/third-party/td/td/td/telegram/ConfigManager.cpp index ad0c51d9fc..a91881e785 100644 --- a/third-party/td/td/td/telegram/ConfigManager.cpp +++ b/third-party/td/td/td/telegram/ConfigManager.cpp @@ -1001,12 +1001,9 @@ void ConfigManager::set_content_settings(bool ignore_sensitive_content_restricti queries.push_back(std::move(promise)); if (!is_set_content_settings_request_sent_) { is_set_content_settings_request_sent_ = true; - int32 flags = 0; - if (ignore_sensitive_content_restrictions) { - flags |= telegram_api::account_setContentSettings::SENSITIVE_ENABLED_MASK; - } G()->net_query_dispatcher().dispatch_with_callback( - G()->net_query_creator().create(telegram_api::account_setContentSettings(flags, false /*ignored*/)), + G()->net_query_creator().create( + telegram_api::account_setContentSettings(0, ignore_sensitive_content_restrictions)), actor_shared(this, 3 + static_cast(ignore_sensitive_content_restrictions))); } } @@ -1376,6 +1373,9 @@ void ConfigManager::process_app_config(tl_object_ptr &c bool channel_revenue_withdrawal_enabled = false; bool can_edit_fact_check = false; vector starref_start_param_prefixes; + int32 freeze_since_date = 0; + int32 freeze_until_date = 0; + string freeze_appeal_url; if (config->get_id() == telegram_api::jsonObject::ID) { for (auto &key_value : static_cast(config.get())->value_) { Slice key = key_value->key_; @@ -2075,6 +2075,18 @@ void ConfigManager::process_app_config(tl_object_ptr &c G()->set_option_integer("pinned_gift_count_max", get_json_value_int(std::move(key_value->value_), key)); continue; } + if (key == "freeze_since_date") { + freeze_since_date = get_json_value_int(std::move(key_value->value_), key); + continue; + } + if (key == "freeze_until_date") { + freeze_until_date = get_json_value_int(std::move(key_value->value_), key); + continue; + } + if (key == "freeze_appeal_url") { + freeze_appeal_url = get_json_value_string(std::move(key_value->value_), key); + continue; + } new_values.push_back(std::move(key_value)); } @@ -2090,6 +2102,9 @@ void ConfigManager::process_app_config(tl_object_ptr &c transcribe_audio_trial_weekly_number, transcribe_audio_trial_duration_max, transcribe_audio_trial_cooldown_until); + send_closure(G()->user_manager(), &UserManager::on_update_freeze_state, freeze_since_date, freeze_until_date, + std::move(freeze_appeal_url)); + Global &options = *G(); if (ignored_restriction_reasons.empty()) { diff --git a/third-party/td/td/td/telegram/ConfigManager.h b/third-party/td/td/td/telegram/ConfigManager.h index 953bf6542d..de1527a6e3 100644 --- a/third-party/td/td/td/telegram/ConfigManager.h +++ b/third-party/td/td/td/telegram/ConfigManager.h @@ -77,7 +77,7 @@ class ConfigManager final : public NetQueryCallback { private: struct AppConfig { - static constexpr int32 CURRENT_VERSION = 70; + static constexpr int32 CURRENT_VERSION = 71; int32 version_ = 0; int32 hash_ = 0; telegram_api::object_ptr config_; diff --git a/third-party/td/td/td/telegram/DeviceTokenManager.cpp b/third-party/td/td/td/telegram/DeviceTokenManager.cpp index b9110f3ffc..361716e100 100644 --- a/third-party/td/td/td/telegram/DeviceTokenManager.cpp +++ b/third-party/td/td/td/telegram/DeviceTokenManager.cpp @@ -400,9 +400,9 @@ void DeviceTokenManager::loop() { net_query = G()->net_query_creator().create( telegram_api::account_unregisterDevice(token_type, info.token, vector(info.other_user_ids))); } else { - int32 flags = telegram_api::account_registerDevice::NO_MUTED_MASK; + bool no_muted = true; net_query = G()->net_query_creator().create( - telegram_api::account_registerDevice(flags, false /*ignored*/, token_type, info.token, info.is_app_sandbox, + telegram_api::account_registerDevice(0, no_muted, token_type, info.token, info.is_app_sandbox, BufferSlice(info.encryption_key), vector(info.other_user_ids))); } info.net_query_id = net_query->id(); diff --git a/third-party/td/td/td/telegram/DialogAction.cpp b/third-party/td/td/td/telegram/DialogAction.cpp index 379d27893e..0edbf1eb91 100644 --- a/third-party/td/td/td/telegram/DialogAction.cpp +++ b/third-party/td/td/td/telegram/DialogAction.cpp @@ -428,6 +428,8 @@ bool DialogAction::is_canceled_by_message_of_type(MessageContentType message_con case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); diff --git a/third-party/td/td/td/telegram/DialogEventLog.cpp b/third-party/td/td/td/telegram/DialogEventLog.cpp index 6bd5e868a8..a412c6dee4 100644 --- a/third-party/td/td/td/telegram/DialogEventLog.cpp +++ b/third-party/td/td/td/telegram/DialogEventLog.cpp @@ -593,60 +593,12 @@ static telegram_api::object_ptr get_i if (filters == nullptr) { return nullptr; } - - int32 flags = 0; - if (filters->message_edits_) { - flags |= telegram_api::channelAdminLogEventsFilter::EDIT_MASK; - } - if (filters->message_deletions_) { - flags |= telegram_api::channelAdminLogEventsFilter::DELETE_MASK; - } - if (filters->message_pins_) { - flags |= telegram_api::channelAdminLogEventsFilter::PINNED_MASK; - } - if (filters->member_joins_) { - flags |= telegram_api::channelAdminLogEventsFilter::JOIN_MASK; - } - if (filters->member_leaves_) { - flags |= telegram_api::channelAdminLogEventsFilter::LEAVE_MASK; - } - if (filters->member_invites_) { - flags |= telegram_api::channelAdminLogEventsFilter::INVITE_MASK; - } - if (filters->member_promotions_) { - flags |= telegram_api::channelAdminLogEventsFilter::PROMOTE_MASK; - flags |= telegram_api::channelAdminLogEventsFilter::DEMOTE_MASK; - } - if (filters->member_restrictions_) { - flags |= telegram_api::channelAdminLogEventsFilter::BAN_MASK; - flags |= telegram_api::channelAdminLogEventsFilter::UNBAN_MASK; - flags |= telegram_api::channelAdminLogEventsFilter::KICK_MASK; - flags |= telegram_api::channelAdminLogEventsFilter::UNKICK_MASK; - } - if (filters->info_changes_) { - flags |= telegram_api::channelAdminLogEventsFilter::INFO_MASK; - } - if (filters->setting_changes_) { - flags |= telegram_api::channelAdminLogEventsFilter::SETTINGS_MASK; - } - if (filters->invite_link_changes_) { - flags |= telegram_api::channelAdminLogEventsFilter::INVITES_MASK; - } - if (filters->video_chat_changes_) { - flags |= telegram_api::channelAdminLogEventsFilter::GROUP_CALL_MASK; - } - if (filters->forum_changes_) { - flags |= telegram_api::channelAdminLogEventsFilter::FORUMS_MASK; - } - if (filters->subscription_extensions_) { - flags |= telegram_api::channelAdminLogEventsFilter::SUB_EXTEND_MASK; - } - return telegram_api::make_object( - 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*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/); + 0, filters->member_joins_, filters->member_leaves_, filters->member_invites_, filters->member_restrictions_, + filters->member_restrictions_, filters->member_restrictions_, filters->member_restrictions_, + filters->member_promotions_, filters->member_promotions_, filters->info_changes_, filters->setting_changes_, + filters->message_pins_, filters->message_edits_, filters->message_deletions_, filters->video_chat_changes_, + filters->invite_link_changes_, false /*send*/, filters->forum_changes_, filters->subscription_extensions_); } void get_dialog_event_log(Td *td, DialogId dialog_id, const string &query, int64 from_event_id, int32 limit, diff --git a/third-party/td/td/td/telegram/DialogFilter.cpp b/third-party/td/td/td/telegram/DialogFilter.cpp index efbd0ba9e1..4e397d90a4 100644 --- a/third-party/td/td/td/telegram/DialogFilter.cpp +++ b/third-party/td/td/td/telegram/DialogFilter.cpp @@ -57,15 +57,14 @@ unique_ptr DialogFilter::get_dialog_filter( InputDialogId::get_input_dialog_ids(filter->include_peers_, &added_dialog_ids); dialog_filter->excluded_dialog_ids_ = InputDialogId::get_input_dialog_ids(filter->exclude_peers_, &added_dialog_ids); - auto flags = filter->flags_; - dialog_filter->exclude_muted_ = (flags & telegram_api::dialogFilter::EXCLUDE_MUTED_MASK) != 0; - dialog_filter->exclude_read_ = (flags & telegram_api::dialogFilter::EXCLUDE_READ_MASK) != 0; - dialog_filter->exclude_archived_ = (flags & telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK) != 0; - dialog_filter->include_contacts_ = (flags & telegram_api::dialogFilter::CONTACTS_MASK) != 0; - dialog_filter->include_non_contacts_ = (flags & telegram_api::dialogFilter::NON_CONTACTS_MASK) != 0; - dialog_filter->include_bots_ = (flags & telegram_api::dialogFilter::BOTS_MASK) != 0; - dialog_filter->include_groups_ = (flags & telegram_api::dialogFilter::GROUPS_MASK) != 0; - dialog_filter->include_channels_ = (flags & telegram_api::dialogFilter::BROADCASTS_MASK) != 0; + dialog_filter->exclude_muted_ = filter->exclude_muted_; + dialog_filter->exclude_read_ = filter->exclude_read_; + dialog_filter->exclude_archived_ = filter->exclude_archived_; + dialog_filter->include_contacts_ = filter->contacts_; + dialog_filter->include_non_contacts_ = filter->non_contacts_; + dialog_filter->include_bots_ = filter->bots_; + dialog_filter->include_groups_ = filter->groups_; + dialog_filter->include_channels_ = filter->broadcasts_; if (!is_valid_color_id(dialog_filter->color_id_)) { LOG(ERROR) << "Receive color " << dialog_filter->color_id_; dialog_filter->color_id_ = -1; @@ -444,52 +443,18 @@ telegram_api::object_ptr DialogFilter::get_input_dia if (color_id_ != -1) { flags |= telegram_api::dialogFilterChatlist::COLOR_MASK; } - if (has_my_invites_) { - flags |= telegram_api::dialogFilterChatlist::HAS_MY_INVITES_MASK; - } - if (!animate_title_) { - flags |= telegram_api::dialogFilterChatlist::TITLE_NOANIMATE_MASK; - } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, dialog_filter_id_.get(), + flags, has_my_invites_, !animate_title_, dialog_filter_id_.get(), get_input_text_with_entities(nullptr, title_, "dialogFilterChatlist"), emoji_, color_id_, InputDialogId::get_input_peers(pinned_dialog_ids_), InputDialogId::get_input_peers(included_dialog_ids_)); } int32 flags = telegram_api::dialogFilter::EMOTICON_MASK; - if (!animate_title_) { - flags |= telegram_api::dialogFilter::TITLE_NOANIMATE_MASK; - } if (color_id_ != -1) { flags |= telegram_api::dialogFilter::COLOR_MASK; } - if (exclude_muted_) { - flags |= telegram_api::dialogFilter::EXCLUDE_MUTED_MASK; - } - if (exclude_read_) { - flags |= telegram_api::dialogFilter::EXCLUDE_READ_MASK; - } - if (exclude_archived_) { - flags |= telegram_api::dialogFilter::EXCLUDE_ARCHIVED_MASK; - } - if (include_contacts_) { - flags |= telegram_api::dialogFilter::CONTACTS_MASK; - } - if (include_non_contacts_) { - flags |= telegram_api::dialogFilter::NON_CONTACTS_MASK; - } - if (include_bots_) { - flags |= telegram_api::dialogFilter::BOTS_MASK; - } - if (include_groups_) { - flags |= telegram_api::dialogFilter::GROUPS_MASK; - } - if (include_channels_) { - flags |= telegram_api::dialogFilter::BROADCASTS_MASK; - } - return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, dialog_filter_id_.get(), + flags, include_contacts_, include_non_contacts_, include_groups_, include_channels_, include_bots_, + exclude_muted_, exclude_read_, exclude_archived_, !animate_title_, dialog_filter_id_.get(), get_input_text_with_entities(nullptr, title_, "dialogFilter"), emoji_, color_id_, InputDialogId::get_input_peers(pinned_dialog_ids_), InputDialogId::get_input_peers(included_dialog_ids_), InputDialogId::get_input_peers(excluded_dialog_ids_)); diff --git a/third-party/td/td/td/telegram/DialogManager.cpp b/third-party/td/td/td/telegram/DialogManager.cpp index 5329883eb7..fb5b8337ec 100644 --- a/third-party/td/td/td/telegram/DialogManager.cpp +++ b/third-party/td/td/td/telegram/DialogManager.cpp @@ -724,14 +724,8 @@ class GetBlockedDialogsQuery final : public Td::ResultHandler { void send(BlockListId block_list_id, int32 offset, int32 limit) { offset_ = offset; limit_ = limit; - - int32 flags = 0; - if (block_list_id == BlockListId::stories()) { - flags |= telegram_api::contacts_getBlocked::MY_STORIES_FROM_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::contacts_getBlocked(flags, false /*ignored*/, offset, limit), {{"me"}})); + telegram_api::contacts_getBlocked(0, block_list_id == BlockListId::stories(), offset, limit), {{"me"}})); } void on_result(BufferSlice packet) final { @@ -1040,17 +1034,13 @@ class ToggleDialogIsBlockedQuery final : public Td::ResultHandler { auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Know); CHECK(input_peer != nullptr && input_peer->get_id() != telegram_api::inputPeerEmpty::ID); - int32 flags = 0; - if (is_blocked_for_stories) { - flags |= telegram_api::contacts_block::MY_STORIES_FROM_MASK; - } vector chain_ids{{dialog_id, MessageContentType::Photo}, {dialog_id, MessageContentType::Text}, {"me"}}; auto query = is_blocked || is_blocked_for_stories ? G()->net_query_creator().create( - telegram_api::contacts_block(flags, false /*ignored*/, std::move(input_peer)), std::move(chain_ids)) + telegram_api::contacts_block(0, is_blocked_for_stories, std::move(input_peer)), std::move(chain_ids)) : G()->net_query_creator().create( - telegram_api::contacts_unblock(flags, false /*ignored*/, std::move(input_peer)), + telegram_api::contacts_unblock(0, is_blocked_for_stories, std::move(input_peer)), std::move(chain_ids)); send_query(std::move(query)); } diff --git a/third-party/td/td/td/telegram/DialogParticipant.cpp b/third-party/td/td/td/telegram/DialogParticipant.cpp index 462198413a..afb2c8dcee 100644 --- a/third-party/td/td/td/telegram/DialogParticipant.cpp +++ b/third-party/td/td/td/telegram/DialogParticipant.cpp @@ -92,57 +92,11 @@ AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dial } telegram_api::object_ptr AdministratorRights::get_chat_admin_rights() const { - int32 flags = 0; - if (can_change_info_and_settings()) { - flags |= telegram_api::chatAdminRights::CHANGE_INFO_MASK; - } - if (can_post_messages()) { - flags |= telegram_api::chatAdminRights::POST_MESSAGES_MASK; - } - if (can_edit_messages()) { - flags |= telegram_api::chatAdminRights::EDIT_MESSAGES_MASK; - } - if (can_delete_messages()) { - flags |= telegram_api::chatAdminRights::DELETE_MESSAGES_MASK; - } - if (can_invite_users()) { - flags |= telegram_api::chatAdminRights::INVITE_USERS_MASK; - } - if (can_restrict_members()) { - flags |= telegram_api::chatAdminRights::BAN_USERS_MASK; - } - if (can_pin_messages()) { - flags |= telegram_api::chatAdminRights::PIN_MESSAGES_MASK; - } - if (can_manage_topics()) { - flags |= telegram_api::chatAdminRights::MANAGE_TOPICS_MASK; - } - if (can_promote_members()) { - flags |= telegram_api::chatAdminRights::ADD_ADMINS_MASK; - } - if (can_manage_calls()) { - flags |= telegram_api::chatAdminRights::MANAGE_CALL_MASK; - } - if (can_manage_dialog()) { - flags |= telegram_api::chatAdminRights::OTHER_MASK; - } - if (can_post_stories()) { - flags |= telegram_api::chatAdminRights::POST_STORIES_MASK; - } - if (can_edit_stories()) { - flags |= telegram_api::chatAdminRights::EDIT_STORIES_MASK; - } - if (can_delete_stories()) { - flags |= telegram_api::chatAdminRights::DELETE_STORIES_MASK; - } - if (is_anonymous()) { - flags |= telegram_api::chatAdminRights::ANONYMOUS_MASK; - } - return telegram_api::make_object( - 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*/); + 0, can_change_info_and_settings(), can_post_messages(), can_edit_messages(), can_delete_messages(), + can_restrict_members(), can_invite_users(), can_pin_messages(), can_promote_members(), is_anonymous(), + can_manage_calls(), can_manage_dialog(), can_manage_topics(), can_post_stories(), can_edit_stories(), + can_delete_stories()); } td_api::object_ptr AdministratorRights::get_chat_administrator_rights_object() const { @@ -284,66 +238,13 @@ td_api::object_ptr RestrictedRights::get_chat_permissio can_manage_topics()); } -tl_object_ptr RestrictedRights::get_chat_banned_rights() const { - int32 flags = 0; - if (!can_send_messages()) { - flags |= telegram_api::chatBannedRights::SEND_PLAIN_MASK; - } - if (!can_send_audios()) { - flags |= telegram_api::chatBannedRights::SEND_AUDIOS_MASK; - } - if (!can_send_documents()) { - flags |= telegram_api::chatBannedRights::SEND_DOCS_MASK; - } - if (!can_send_photos()) { - flags |= telegram_api::chatBannedRights::SEND_PHOTOS_MASK; - } - if (!can_send_videos()) { - flags |= telegram_api::chatBannedRights::SEND_VIDEOS_MASK; - } - if (!can_send_video_notes()) { - flags |= telegram_api::chatBannedRights::SEND_ROUNDVIDEOS_MASK; - } - if (!can_send_voice_notes()) { - flags |= telegram_api::chatBannedRights::SEND_VOICES_MASK; - } - if (!can_send_stickers()) { - flags |= telegram_api::chatBannedRights::SEND_STICKERS_MASK; - } - if (!can_send_animations()) { - flags |= telegram_api::chatBannedRights::SEND_GIFS_MASK; - } - if (!can_send_games()) { - flags |= telegram_api::chatBannedRights::SEND_GAMES_MASK; - } - if (!can_use_inline_bots()) { - flags |= telegram_api::chatBannedRights::SEND_INLINE_MASK; - } - if (!can_add_web_page_previews()) { - flags |= telegram_api::chatBannedRights::EMBED_LINKS_MASK; - } - if (!can_send_polls()) { - flags |= telegram_api::chatBannedRights::SEND_POLLS_MASK; - } - if (!can_change_info_and_settings()) { - flags |= telegram_api::chatBannedRights::CHANGE_INFO_MASK; - } - if (!can_invite_users()) { - flags |= telegram_api::chatBannedRights::INVITE_USERS_MASK; - } - if (!can_pin_messages()) { - flags |= telegram_api::chatBannedRights::PIN_MESSAGES_MASK; - } - if (!can_manage_topics()) { - flags |= telegram_api::chatBannedRights::MANAGE_TOPICS_MASK; - } - - LOG(INFO) << "Create chat banned rights " << flags; - return make_tl_object( - 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*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, 0); +telegram_api::object_ptr RestrictedRights::get_chat_banned_rights() const { + return telegram_api::make_object( + 0, false /*view_messages*/, false /*send_messages*/, false /*send_media*/, !can_send_stickers(), + !can_send_animations(), !can_send_games(), !can_use_inline_bots(), !can_add_web_page_previews(), + !can_send_polls(), !can_change_info_and_settings(), !can_invite_users(), !can_pin_messages(), + !can_manage_topics(), !can_send_photos(), !can_send_videos(), !can_send_video_notes(), !can_send_audios(), + !can_send_voice_notes(), !can_send_documents(), !can_send_messages(), 0); } bool operator==(const RestrictedRights &lhs, const RestrictedRights &rhs) { @@ -515,7 +416,7 @@ RestrictedRights DialogParticipantStatus::get_effective_restricted_rights() cons can_create_topics(), ChannelType::Unknown); } -tl_object_ptr DialogParticipantStatus::get_chat_member_status_object() const { +td_api::object_ptr DialogParticipantStatus::get_chat_member_status_object() const { switch (type_) { case Type::Creator: return td_api::make_object(rank_, is_anonymous(), is_member()); diff --git a/third-party/td/td/td/telegram/DialogParticipant.h b/third-party/td/td/td/telegram/DialogParticipant.h index 2d611cffca..db5cfdb6ba 100644 --- a/third-party/td/td/td/telegram/DialogParticipant.h +++ b/third-party/td/td/td/telegram/DialogParticipant.h @@ -376,11 +376,11 @@ class DialogParticipantStatus { DialogParticipantStatus apply_restrictions(RestrictedRights default_restrictions, bool is_booster, bool is_bot) const; - tl_object_ptr get_chat_member_status_object() const; + td_api::object_ptr get_chat_member_status_object() const; - tl_object_ptr get_chat_admin_rights() const; + telegram_api::object_ptr get_chat_admin_rights() const; - tl_object_ptr get_chat_banned_rights() const; + telegram_api::object_ptr get_chat_banned_rights() const; // unrestricts user if restriction time expired. Should be called before all privileges checks void update_restrictions() const; diff --git a/third-party/td/td/td/telegram/DisallowedGiftsSettings.cpp b/third-party/td/td/td/telegram/DisallowedGiftsSettings.cpp new file mode 100644 index 0000000000..c9781dab4b --- /dev/null +++ b/third-party/td/td/td/telegram/DisallowedGiftsSettings.cpp @@ -0,0 +1,68 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/DisallowedGiftsSettings.h" + +namespace td { + +DisallowedGiftsSettings::DisallowedGiftsSettings( + telegram_api::object_ptr &&settings) { + if (settings != nullptr) { + disallow_unlimited_stargifts_ = settings->disallow_unlimited_stargifts_; + disallow_limited_stargifts_ = settings->disallow_limited_stargifts_; + disallow_unique_stargifts_ = settings->disallow_unique_stargifts_; + disallow_premium_gifts_ = settings->disallow_premium_gifts_; + } +} + +DisallowedGiftsSettings::DisallowedGiftsSettings(const td_api::object_ptr &types) { + if (types != nullptr) { + disallow_unlimited_stargifts_ = !types->unlimited_gifts_; + disallow_limited_stargifts_ = !types->limited_gifts_; + disallow_unique_stargifts_ = !types->upgraded_gifts_; + disallow_premium_gifts_ = !types->premium_subscription_; + } +} + +td_api::object_ptr DisallowedGiftsSettings::get_accepted_gift_types_object() const { + return td_api::make_object(!disallow_unlimited_stargifts_, !disallow_limited_stargifts_, + !disallow_unique_stargifts_, !disallow_premium_gifts_); +} + +telegram_api::object_ptr +DisallowedGiftsSettings::get_input_disallowed_gifts_settings() const { + if (is_default()) { + return nullptr; + } + return telegram_api::make_object( + 0, disallow_unlimited_stargifts_, disallow_limited_stargifts_, disallow_unique_stargifts_, + disallow_premium_gifts_); +} + +bool operator==(const DisallowedGiftsSettings &lhs, const DisallowedGiftsSettings &rhs) { + return lhs.disallow_unlimited_stargifts_ == rhs.disallow_unlimited_stargifts_ && + lhs.disallow_limited_stargifts_ == rhs.disallow_limited_stargifts_ && + lhs.disallow_unique_stargifts_ == rhs.disallow_unique_stargifts_ && + lhs.disallow_premium_gifts_ == rhs.disallow_premium_gifts_; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DisallowedGiftsSettings &settings) { + if (!settings.disallow_unlimited_stargifts_) { + string_builder << "(unlimited)"; + } + if (!settings.disallow_limited_stargifts_) { + string_builder << "(limited)"; + } + if (!settings.disallow_unique_stargifts_) { + string_builder << "(unique)"; + } + if (!settings.disallow_premium_gifts_) { + string_builder << "(premium)"; + } + return string_builder; +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/DisallowedGiftsSettings.h b/third-party/td/td/td/telegram/DisallowedGiftsSettings.h new file mode 100644 index 0000000000..c326dd24f5 --- /dev/null +++ b/third-party/td/td/td/telegram/DisallowedGiftsSettings.h @@ -0,0 +1,58 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/utils/common.h" +#include "td/utils/StringBuilder.h" + +namespace td { + +class DisallowedGiftsSettings { + bool disallow_unlimited_stargifts_ = false; + bool disallow_limited_stargifts_ = false; + bool disallow_unique_stargifts_ = false; + bool disallow_premium_gifts_ = false; + + friend bool operator==(const DisallowedGiftsSettings &lhs, const DisallowedGiftsSettings &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const DisallowedGiftsSettings &settings); + + public: + DisallowedGiftsSettings() = default; + + explicit DisallowedGiftsSettings(telegram_api::object_ptr &&settings); + + explicit DisallowedGiftsSettings(const td_api::object_ptr &types); + + td_api::object_ptr get_accepted_gift_types_object() const; + + telegram_api::object_ptr get_input_disallowed_gifts_settings() const; + + bool is_default() const { + return !disallow_unlimited_stargifts_ && !disallow_limited_stargifts_ && !disallow_unique_stargifts_ && + !disallow_premium_gifts_; + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +bool operator==(const DisallowedGiftsSettings &lhs, const DisallowedGiftsSettings &rhs); + +inline bool operator!=(const DisallowedGiftsSettings &lhs, const DisallowedGiftsSettings &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const DisallowedGiftsSettings &settings); + +} // namespace td diff --git a/third-party/td/td/td/telegram/DisallowedGiftsSettings.hpp b/third-party/td/td/td/telegram/DisallowedGiftsSettings.hpp new file mode 100644 index 0000000000..8b7feab9f8 --- /dev/null +++ b/third-party/td/td/td/telegram/DisallowedGiftsSettings.hpp @@ -0,0 +1,36 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/DisallowedGiftsSettings.h" + +#include "td/utils/common.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void DisallowedGiftsSettings::store(StorerT &storer) const { + BEGIN_STORE_FLAGS(); + STORE_FLAG(disallow_unlimited_stargifts_); + STORE_FLAG(disallow_limited_stargifts_); + STORE_FLAG(disallow_unique_stargifts_); + STORE_FLAG(disallow_premium_gifts_); + END_STORE_FLAGS(); +} + +template +void DisallowedGiftsSettings::parse(ParserT &parser) { + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(disallow_unlimited_stargifts_); + PARSE_FLAG(disallow_limited_stargifts_); + PARSE_FLAG(disallow_unique_stargifts_); + PARSE_FLAG(disallow_premium_gifts_); + END_PARSE_FLAGS(); +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/DocumentsManager.cpp b/third-party/td/td/td/telegram/DocumentsManager.cpp index c54e750528..e0e682fad3 100644 --- a/third-party/td/td/td/telegram/DocumentsManager.cpp +++ b/third-party/td/td/td/telegram/DocumentsManager.cpp @@ -74,11 +74,11 @@ tl_object_ptr DocumentsManager::get_document_object(FileId fil Document DocumentsManager::on_get_document(RemoteDocument remote_document, DialogId owner_dialog_id, bool is_self_destructing, MultiPromiseActor *load_data_multipromise_ptr, Document::Type default_document_type, Subtype document_subtype) { - tl_object_ptr animated; - tl_object_ptr video; - tl_object_ptr audio; - tl_object_ptr sticker; - tl_object_ptr custom_emoji; + telegram_api::object_ptr animated; + telegram_api::object_ptr video; + telegram_api::object_ptr audio; + telegram_api::object_ptr sticker; + telegram_api::object_ptr custom_emoji; Dimensions dimensions; string file_name; bool has_stickers = false; @@ -86,25 +86,25 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo for (auto &attribute : remote_document.attributes) { switch (attribute->get_id()) { case telegram_api::documentAttributeImageSize::ID: { - auto image_size = move_tl_object_as(attribute); + auto image_size = telegram_api::move_object_as(attribute); dimensions = get_dimensions(image_size->w_, image_size->h_, oneline(to_string(remote_document.document)).c_str()); break; } case telegram_api::documentAttributeAnimated::ID: - animated = move_tl_object_as(attribute); + animated = telegram_api::move_object_as(attribute); type_attributes++; break; case telegram_api::documentAttributeSticker::ID: - sticker = move_tl_object_as(attribute); + sticker = telegram_api::move_object_as(attribute); type_attributes++; break; case telegram_api::documentAttributeVideo::ID: - video = move_tl_object_as(attribute); + video = telegram_api::move_object_as(attribute); type_attributes++; break; case telegram_api::documentAttributeAudio::ID: - audio = move_tl_object_as(attribute); + audio = telegram_api::move_object_as(attribute); type_attributes++; break; case telegram_api::documentAttributeFilename::ID: @@ -114,7 +114,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo has_stickers = true; break; case telegram_api::documentAttributeCustomEmoji::ID: - custom_emoji = move_tl_object_as(attribute); + custom_emoji = telegram_api::move_object_as(attribute); type_attributes++; break; default: @@ -154,7 +154,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo if (animated != nullptr) { type_attributes--; - if ((video->flags_ & telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK) != 0) { + if (video->round_message_) { // video note without sound animated = nullptr; } else if (sticker != nullptr || custom_emoji != nullptr) { @@ -209,7 +209,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo default_document_type == Document::Type::VoiceNote) { bool is_voice_note = default_document_type == Document::Type::VoiceNote; if (audio != nullptr) { - is_voice_note = (audio->flags_ & telegram_api::documentAttributeAudio::VOICE_MASK) != 0; + is_voice_note = audio->voice_; } if (is_voice_note) { document_type = Document::Type::VoiceNote; @@ -232,9 +232,9 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo default_document_type == Document::Type::VideoNote) { bool is_video_note = default_document_type == Document::Type::VideoNote; if (video != nullptr) { - is_video_note = (video->flags_ & telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK) != 0; + is_video_note = video->round_message_; if (!is_video_note) { - supports_streaming = (video->flags_ & telegram_api::documentAttributeVideo::SUPPORTS_STREAMING_MASK) != 0; + supports_streaming = video->supports_streaming_; video_codec = std::move(video->video_codec_); } } @@ -638,9 +638,9 @@ SecretInputMedia DocumentsManager::get_secret_input_media( if (document->thumbnail.file_id.is_valid() && thumbnail.empty()) { return SecretInputMedia{}; } - vector> attributes; + vector> attributes; if (!document->file_name.empty()) { - attributes.push_back(make_tl_object(document->file_name)); + attributes.push_back(secret_api::make_object(document->file_name)); } return {std::move(input_file), std::move(thumbnail), @@ -673,9 +673,9 @@ tl_object_ptr DocumentsManager::get_input_media( const GeneralDocument *document = get_document(file_id); CHECK(document != nullptr); - vector> attributes; + vector> attributes; if (!document->file_name.empty()) { - attributes.push_back(make_tl_object(document->file_name)); + attributes.push_back(telegram_api::make_object(document->file_name)); } int32 flags = 0; if (input_thumbnail != nullptr) { diff --git a/third-party/td/td/td/telegram/DocumentsManager.h b/third-party/td/td/td/telegram/DocumentsManager.h index d48077ea67..72a64002b0 100644 --- a/third-party/td/td/td/telegram/DocumentsManager.h +++ b/third-party/td/td/td/telegram/DocumentsManager.h @@ -48,7 +48,7 @@ class DocumentsManager { tl_object_ptr web_document; PhotoSize thumbnail; - vector> attributes; + vector> attributes; RemoteDocument(tl_object_ptr &&server_document) : document(std::move(server_document)) @@ -60,7 +60,7 @@ class DocumentsManager { } RemoteDocument(tl_object_ptr &&web_document, PhotoSize thumbnail, - vector> &&attributes) + vector> &&attributes) : document(nullptr) , secret_file(nullptr) , secret_document(nullptr) @@ -71,7 +71,7 @@ class DocumentsManager { RemoteDocument(unique_ptr &&secret_file, tl_object_ptr &&secret_document, - vector> &&attributes) + vector> &&attributes) : document(nullptr) , secret_file(std::move(secret_file)) , secret_document(std::move(secret_document)) diff --git a/third-party/td/td/td/telegram/EmojiStatus.cpp b/third-party/td/td/td/telegram/EmojiStatus.cpp index c3c5ad0589..ddcff8f2f8 100644 --- a/third-party/td/td/td/telegram/EmojiStatus.cpp +++ b/third-party/td/td/td/telegram/EmojiStatus.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/EmojiStatus.h" +#include "td/telegram/AuthManager.h" #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/StickersManager.h" @@ -623,6 +624,10 @@ void clear_recent_emoji_statuses(Td *td, Promise &&promise) { } void get_upgraded_gift_emoji_statuses(Td *td, Promise> &&promise) { + if (td->auth_manager_->is_bot()) { + CHECK(!promise); + return; + } auto statuses = load_emoji_statuses(get_upgraded_gift_emoji_statuses_database_key()); if (statuses.hash_ != -1 && promise) { promise.set_value(statuses.get_emoji_statuses_object()); diff --git a/third-party/td/td/td/telegram/ForumTopicManager.cpp b/third-party/td/td/td/telegram/ForumTopicManager.cpp index ee5198ed93..b45d39470d 100644 --- a/third-party/td/td/td/telegram/ForumTopicManager.cpp +++ b/third-party/td/td/td/telegram/ForumTopicManager.cpp @@ -258,9 +258,8 @@ class ReorderPinnedForumTopicsQuery final : public Td::ResultHandler { auto input_channel = td_->chat_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - int32 flags = telegram_api::channels_reorderPinnedForumTopics::FORCE_MASK; send_query(G()->net_query_creator().create( - telegram_api::channels_reorderPinnedForumTopics(flags, true /*ignored*/, std::move(input_channel), + telegram_api::channels_reorderPinnedForumTopics(0, true, std::move(input_channel), MessageId::get_server_message_ids(top_thread_message_ids)), {{channel_id}})); } diff --git a/third-party/td/td/td/telegram/GiveawayParameters.cpp b/third-party/td/td/td/telegram/GiveawayParameters.cpp index e1dc39121c..49a447d1bf 100644 --- a/third-party/td/td/td/telegram/GiveawayParameters.cpp +++ b/third-party/td/td/td/telegram/GiveawayParameters.cpp @@ -103,12 +103,6 @@ GiveawayParameters::get_input_store_payment_premium_giveaway(Td *td, const strin } int32 flags = 0; - if (only_new_subscribers_) { - flags |= telegram_api::inputStorePaymentPremiumGiveaway::ONLY_NEW_SUBSCRIBERS_MASK; - } - if (winners_are_visible_) { - flags |= telegram_api::inputStorePaymentPremiumGiveaway::WINNERS_ARE_VISIBLE_MASK; - } if (!additional_input_peers.empty()) { flags |= telegram_api::inputStorePaymentPremiumGiveaway::ADDITIONAL_PEERS_MASK; } @@ -119,8 +113,9 @@ GiveawayParameters::get_input_store_payment_premium_giveaway(Td *td, const strin flags |= telegram_api::inputStorePaymentPremiumGiveaway::PRIZE_DESCRIPTION_MASK; } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, std::move(boost_input_peer), std::move(additional_input_peers), - vector(country_codes_), prize_description_, random_id, date_, currency, amount); + flags, only_new_subscribers_, winners_are_visible_, std::move(boost_input_peer), + std::move(additional_input_peers), vector(country_codes_), prize_description_, random_id, date_, currency, + amount); } telegram_api::object_ptr @@ -142,12 +137,6 @@ GiveawayParameters::get_input_store_payment_stars_giveaway(Td *td, const string } int32 flags = 0; - if (only_new_subscribers_) { - flags |= telegram_api::inputStorePaymentStarsGiveaway::ONLY_NEW_SUBSCRIBERS_MASK; - } - if (winners_are_visible_) { - flags |= telegram_api::inputStorePaymentStarsGiveaway::WINNERS_ARE_VISIBLE_MASK; - } if (!additional_input_peers.empty()) { flags |= telegram_api::inputStorePaymentStarsGiveaway::ADDITIONAL_PEERS_MASK; } @@ -158,7 +147,7 @@ GiveawayParameters::get_input_store_payment_stars_giveaway(Td *td, const string flags |= telegram_api::inputStorePaymentStarsGiveaway::PRIZE_DESCRIPTION_MASK; } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, star_count, std::move(boost_input_peer), + flags, only_new_subscribers_, winners_are_visible_, star_count, std::move(boost_input_peer), std::move(additional_input_peers), vector(country_codes_), prize_description_, random_id, date_, currency, amount, user_count); } diff --git a/third-party/td/td/td/telegram/Global.h b/third-party/td/td/td/telegram/Global.h index 4dd906a1cb..c7d35b7679 100644 --- a/third-party/td/td/td/telegram/Global.h +++ b/third-party/td/td/td/telegram/Global.h @@ -679,6 +679,10 @@ class Global final : public ActorContext { // flood wait return true; } + if (error.code() == 406 && error.message() == "FROZEN_METHOD_INVALID") { + // the account is frozen + return true; + } return close_flag(); } diff --git a/third-party/td/td/td/telegram/GlobalPrivacySettings.cpp b/third-party/td/td/td/telegram/GlobalPrivacySettings.cpp index 4f68741e2f..db189a4fb0 100644 --- a/third-party/td/td/td/telegram/GlobalPrivacySettings.cpp +++ b/third-party/td/td/td/telegram/GlobalPrivacySettings.cpp @@ -81,7 +81,8 @@ GlobalPrivacySettings::GlobalPrivacySettings(telegram_api::object_ptrkeep_archived_folders_) , hide_read_marks_(settings->hide_read_marks_) , new_noncontact_peers_require_premium_(settings->new_noncontact_peers_require_premium_) - , noncontact_peers_paid_star_count_(StarManager::get_star_count(settings->noncontact_peers_paid_stars_)) { + , noncontact_peers_paid_star_count_(StarManager::get_star_count(settings->noncontact_peers_paid_stars_)) + , gift_settings_(settings->display_gifts_button_, std::move(settings->disallowed_gifts_)) { } GlobalPrivacySettings::GlobalPrivacySettings(td_api::object_ptr &&settings) @@ -106,6 +107,11 @@ GlobalPrivacySettings::GlobalPrivacySettings(td_api::object_ptr(0), static_cast(1000000)); } +GlobalPrivacySettings::GlobalPrivacySettings(td_api::object_ptr &&settings) + : set_type_(SetType::Gift) { + gift_settings_ = StarGiftSettings(settings); +} + void GlobalPrivacySettings::apply_changes(const GlobalPrivacySettings &set_settings) { CHECK(set_type_ == SetType::None); switch (set_settings.set_type_) { @@ -121,6 +127,9 @@ void GlobalPrivacySettings::apply_changes(const GlobalPrivacySettings &set_setti new_noncontact_peers_require_premium_ = set_settings.new_noncontact_peers_require_premium_; noncontact_peers_paid_star_count_ = set_settings.noncontact_peers_paid_star_count_; break; + case SetType::Gift: + gift_settings_ = set_settings.gift_settings_; + break; default: UNREACHABLE(); break; @@ -131,27 +140,17 @@ telegram_api::object_ptr GlobalPrivacySetti const { CHECK(set_type_ == SetType::None); int32 flags = 0; - if (archive_and_mute_new_noncontact_peers_) { - flags |= telegram_api::globalPrivacySettings::ARCHIVE_AND_MUTE_NEW_NONCONTACT_PEERS_MASK; - } - if (keep_archived_unmuted_) { - flags |= telegram_api::globalPrivacySettings::KEEP_ARCHIVED_UNMUTED_MASK; - } - if (keep_archived_folders_) { - flags |= telegram_api::globalPrivacySettings::KEEP_ARCHIVED_FOLDERS_MASK; - } - if (hide_read_marks_) { - flags |= telegram_api::globalPrivacySettings::HIDE_READ_MARKS_MASK; - } - if (new_noncontact_peers_require_premium_) { - flags |= telegram_api::globalPrivacySettings::NEW_NONCONTACT_PEERS_REQUIRE_PREMIUM_MASK; - } if (noncontact_peers_paid_star_count_ > 0) { flags |= telegram_api::globalPrivacySettings::NONCONTACT_PEERS_PAID_STARS_MASK; } + auto gifts_settings = gift_settings_.get_disallowed_gifts().get_input_disallowed_gifts_settings(); + if (gifts_settings != nullptr) { + flags |= telegram_api::globalPrivacySettings::DISALLOWED_GIFTS_MASK; + } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - noncontact_peers_paid_star_count_); + flags, archive_and_mute_new_noncontact_peers_, keep_archived_unmuted_, keep_archived_folders_, hide_read_marks_, + new_noncontact_peers_require_premium_, gift_settings_.get_display_gifts_button(), + noncontact_peers_paid_star_count_, std::move(gifts_settings)); } td_api::object_ptr GlobalPrivacySettings::get_archive_chat_list_settings_object() diff --git a/third-party/td/td/td/telegram/GlobalPrivacySettings.h b/third-party/td/td/td/telegram/GlobalPrivacySettings.h index f179f6cccf..0d0680a31a 100644 --- a/third-party/td/td/td/telegram/GlobalPrivacySettings.h +++ b/third-party/td/td/td/telegram/GlobalPrivacySettings.h @@ -6,6 +6,7 @@ // #pragma once +#include "td/telegram/StarGiftSettings.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -17,7 +18,7 @@ namespace td { class Td; class GlobalPrivacySettings { - enum class SetType : int32 { None, Archive, ReadDate, NewChat }; + enum class SetType : int32 { None, Archive, ReadDate, NewChat, Gift }; SetType set_type_ = SetType::None; bool archive_and_mute_new_noncontact_peers_ = false; bool keep_archived_unmuted_ = false; @@ -25,6 +26,7 @@ class GlobalPrivacySettings { bool hide_read_marks_ = false; bool new_noncontact_peers_require_premium_ = false; int64 noncontact_peers_paid_star_count_ = 0; + StarGiftSettings gift_settings_; void apply_changes(const GlobalPrivacySettings &set_settings); @@ -37,6 +39,8 @@ class GlobalPrivacySettings { explicit GlobalPrivacySettings(td_api::object_ptr &&settings); + explicit GlobalPrivacySettings(td_api::object_ptr &&settings); + telegram_api::object_ptr get_input_global_privacy_settings() const; td_api::object_ptr get_archive_chat_list_settings_object() const; diff --git a/third-party/td/td/td/telegram/GroupCallManager.cpp b/third-party/td/td/td/telegram/GroupCallManager.cpp index 1c3b2f26f9..92ad90976e 100644 --- a/third-party/td/td/td/telegram/GroupCallManager.cpp +++ b/third-party/td/td/td/telegram/GroupCallManager.cpp @@ -83,16 +83,15 @@ class GetGroupCallStreamQuery final : public Td::ResultHandler { void send(InputGroupCallId input_group_call_id, DcId stream_dc_id, int64 time_offset, int32 scale, int32 channel_id, int32 video_quality) { - int32 stream_flags = 0; - if (channel_id != 0) { - stream_flags |= telegram_api::inputGroupCallStream::VIDEO_CHANNEL_MASK; - } - auto input_stream = make_tl_object( - stream_flags, input_group_call_id.get_input_group_call(), time_offset, scale, channel_id, video_quality); int32 flags = 0; + if (channel_id != 0) { + flags |= telegram_api::inputGroupCallStream::VIDEO_CHANNEL_MASK; + } + auto input_stream = telegram_api::make_object( + flags, input_group_call_id.get_input_group_call(), time_offset, scale, channel_id, video_quality); auto query = G()->net_query_creator().create( - telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, std::move(input_stream), 0, 1 << 20), - {}, stream_dc_id, NetQuery::Type::DownloadSmall); + telegram_api::upload_getFile(0, false, false, std::move(input_stream), 0, 1 << 20), {}, stream_dc_id, + NetQuery::Type::DownloadSmall); query->total_timeout_limit_ = 0; send_query(std::move(query)); } diff --git a/third-party/td/td/td/telegram/InlineQueriesManager.cpp b/third-party/td/td/td/telegram/InlineQueriesManager.cpp index 461e6ad0d1..e6a5bcb4ae 100644 --- a/third-party/td/td/td/telegram/InlineQueriesManager.cpp +++ b/third-party/td/td/td/telegram/InlineQueriesManager.cpp @@ -468,38 +468,22 @@ Result> InlineQueriesManager: if (!entities.empty()) { flags |= telegram_api::inputBotInlineMessageMediaWebPage::ENTITIES_MASK; } - if (input_message_text.force_small_media) { - flags |= telegram_api::inputBotInlineMessageMediaWebPage::FORCE_SMALL_MEDIA_MASK; - } - if (input_message_text.force_large_media) { - flags |= telegram_api::inputBotInlineMessageMediaWebPage::FORCE_LARGE_MEDIA_MASK; - } - if (input_message_text.show_above_text) { - flags |= telegram_api::inputBotInlineMessageMediaWebPage::INVERT_MEDIA_MASK; - } - if (!input_message_text.text.text.empty()) { - flags |= telegram_api::inputBotInlineMessageMediaWebPage::OPTIONAL_MASK; - } + bool is_optional = !input_message_text.text.text.empty(); return make_tl_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_message_text.text.text), std::move(entities), input_message_text.web_page_url, - std::move(input_reply_markup)); + flags, input_message_text.show_above_text, input_message_text.force_large_media, + input_message_text.force_small_media, is_optional, std::move(input_message_text.text.text), + std::move(entities), input_message_text.web_page_url, std::move(input_reply_markup)); } int32 flags = 0; if (input_reply_markup != nullptr) { flags |= telegram_api::inputBotInlineMessageText::REPLY_MARKUP_MASK; } - if (input_message_text.disable_web_page_preview) { - flags |= telegram_api::inputBotInlineMessageText::NO_WEBPAGE_MASK; - } else if (input_message_text.show_above_text) { - flags |= telegram_api::inputBotInlineMessageText::INVERT_MEDIA_MASK; - } if (!entities.empty()) { flags |= telegram_api::inputBotInlineMessageText::ENTITIES_MASK; } - return make_tl_object(flags, false /*ignored*/, false /*ignored*/, - std::move(input_message_text.text.text), - std::move(entities), std::move(input_reply_markup)); + return make_tl_object( + flags, input_message_text.disable_web_page_preview, input_message_text.show_above_text, + std::move(input_message_text.text.text), std::move(entities), std::move(input_reply_markup)); } if (constructor_id == td_api::inputMessageContact::ID) { TRY_RESULT(contact, process_input_message_contact(td_, std::move(input_message_content))); @@ -542,11 +526,9 @@ Result> InlineQueriesManager: if (!entities.empty()) { flags |= telegram_api::inputBotInlineMessageMediaAuto::ENTITIES_MASK; } - if (extract_input_invert_media(input_message_content)) { - flags |= telegram_api::inputBotInlineMessageMediaAuto::INVERT_MEDIA_MASK; - } + bool invert_media = extract_input_invert_media(input_message_content); return make_tl_object( - flags, false /*ignored*/, caption.text, std::move(entities), std::move(input_reply_markup)); + flags, invert_media, caption.text, std::move(entities), std::move(input_reply_markup)); } return Status::Error(400, "Unallowed inline message content type"); } @@ -1092,9 +1074,10 @@ Result> InlineQueriesManager:: if (!clean_input_string(thumbnail_url)) { return Status::Error(400, "Strings must be encoded in UTF-8"); } - vector> attributes; + vector> attributes; if (thumbnail_width > 0 && thumbnail_height > 0) { - attributes.push_back(make_tl_object(thumbnail_width, thumbnail_height)); + attributes.push_back( + telegram_api::make_object(thumbnail_width, thumbnail_height)); } thumbnail = make_tl_object(thumbnail_url, 0, thumbnail_type, std::move(attributes)); } @@ -1108,28 +1091,30 @@ Result> InlineQueriesManager:: return Status::Error(400, "Strings must be encoded in UTF-8"); } - vector> attributes; + vector> attributes; if (width > 0 && height > 0) { if ((duration > 0 || type == "video" || content_type == "video/mp4") && !begins_with(content_type, "image/")) { - attributes.push_back(make_tl_object( - 0, false /*ignored*/, false /*ignored*/, false /*ignored*/, duration, width, height, 0, 0.0, string())); + attributes.push_back(telegram_api::make_object( + 0, false, false, false, duration, width, height, 0, 0.0, string())); } else { - attributes.push_back(make_tl_object(width, height)); + attributes.push_back(telegram_api::make_object(width, height)); } } else if (type == "audio") { - attributes.push_back(make_tl_object( + attributes.push_back(telegram_api::make_object( telegram_api::documentAttributeAudio::TITLE_MASK | telegram_api::documentAttributeAudio::PERFORMER_MASK, - false /*ignored*/, duration, title, description, BufferSlice())); + false, duration, title, description, BufferSlice())); } else if (type == "voice") { - attributes.push_back(make_tl_object( - telegram_api::documentAttributeAudio::VOICE_MASK, false /*ignored*/, duration, "", "", BufferSlice())); + attributes.push_back(telegram_api::make_object(0, true, duration, string(), + string(), BufferSlice())); } - attributes.push_back(make_tl_object(get_url_file_name(content_url))); + attributes.push_back( + telegram_api::make_object(get_url_file_name(content_url))); - content = make_tl_object(content_url, 0, content_type, std::move(attributes)); + content = + telegram_api::make_object(content_url, 0, content_type, std::move(attributes)); } - return make_tl_object( + return telegram_api::make_object( flags, id, type, title, description, url, std::move(thumbnail), std::move(content), std::move(inline_message)); } diff --git a/third-party/td/td/td/telegram/InputInvoice.cpp b/third-party/td/td/td/telegram/InputInvoice.cpp index f9eb4f1d7f..10f2000273 100644 --- a/third-party/td/td/td/telegram/InputInvoice.cpp +++ b/third-party/td/td/td/telegram/InputInvoice.cpp @@ -267,39 +267,16 @@ td_api::object_ptr InputInvoice::get_message_invoice_obj tl_object_ptr InputInvoice::Invoice::get_input_invoice() const { int32 flags = 0; - if (is_test_) { - flags |= telegram_api::invoice::TEST_MASK; - } - if (need_name_) { - flags |= telegram_api::invoice::NAME_REQUESTED_MASK; - } - if (need_phone_number_) { - flags |= telegram_api::invoice::PHONE_REQUESTED_MASK; - } - if (need_email_address_) { - flags |= telegram_api::invoice::EMAIL_REQUESTED_MASK; - } - if (need_shipping_address_) { - flags |= telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK; - } - if (send_phone_number_to_provider_) { - flags |= telegram_api::invoice::PHONE_TO_PROVIDER_MASK; - } - if (send_email_address_to_provider_) { - flags |= telegram_api::invoice::EMAIL_TO_PROVIDER_MASK; - } - if (is_flexible_) { - flags |= telegram_api::invoice::FLEXIBLE_MASK; - } if (max_tip_amount_ != 0) { flags |= telegram_api::invoice::MAX_TIP_AMOUNT_MASK; } if (subscription_period_ != 0) { flags |= telegram_api::invoice::SUBSCRIPTION_PERIOD_MASK; } + bool is_recurring = false; string terms_of_service_url; if (!recurring_payment_terms_of_service_url_.empty()) { - flags |= telegram_api::invoice::RECURRING_MASK; + is_recurring = true; flags |= telegram_api::invoice::TERMS_URL_MASK; terms_of_service_url = recurring_payment_terms_of_service_url_; } else if (!terms_of_service_url_.empty()) { @@ -310,9 +287,9 @@ tl_object_ptr InputInvoice::Invoice::get_input_invoice() auto prices = transform(price_parts_, [](const LabeledPricePart &price) { return telegram_api::make_object(price.label, price.amount); }); - return make_tl_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, currency_, std::move(prices), + return telegram_api::make_object( + flags, is_test_, need_name_, need_phone_number_, need_email_address_, need_shipping_address_, is_flexible_, + send_phone_number_to_provider_, send_email_address_to_provider_, is_recurring, currency_, std::move(prices), max_tip_amount_, vector(suggested_tip_amounts_), terms_of_service_url, subscription_period_); } @@ -326,10 +303,10 @@ static telegram_api::object_ptr get_input_web_do const PhotoSize &size = photo.photos[0]; CHECK(size.file_id.is_valid()); - vector> attributes; + vector> attributes; if (size.dimensions.width != 0 && size.dimensions.height != 0) { - attributes.push_back( - make_tl_object(size.dimensions.width, size.dimensions.height)); + attributes.push_back(telegram_api::make_object(size.dimensions.width, + size.dimensions.height)); } auto file_view = file_manager->get_file_view(size.file_id); diff --git a/third-party/td/td/td/telegram/MediaArea.cpp b/third-party/td/td/td/telegram/MediaArea.cpp index 5e9703f375..1358895f66 100644 --- a/third-party/td/td/td/telegram/MediaArea.cpp +++ b/third-party/td/td/td/telegram/MediaArea.cpp @@ -335,18 +335,10 @@ telegram_api::object_ptr MediaArea::get_input_media_are coordinates_.get_input_media_area_coordinates(), input_query_id_, input_result_id_); } return venue_.get_input_media_area_venue(coordinates_.get_input_media_area_coordinates()); - case Type::Reaction: { - int32 flags = 0; - if (is_dark_) { - flags |= telegram_api::mediaAreaSuggestedReaction::DARK_MASK; - } - if (is_flipped_) { - flags |= telegram_api::mediaAreaSuggestedReaction::FLIPPED_MASK; - } + case Type::Reaction: return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, coordinates_.get_input_media_area_coordinates(), + 0, is_dark_, is_flipped_, coordinates_.get_input_media_area_coordinates(), reaction_type_.get_input_reaction()); - } case Type::Message: { auto channel_id = message_full_id_.get_dialog_id().get_channel_id(); auto server_message_id = message_full_id_.get_message_id().get_server_message_id(); diff --git a/third-party/td/td/td/telegram/MessageContent.cpp b/third-party/td/td/td/telegram/MessageContent.cpp index 81079db7ee..73d5adc92f 100644 --- a/third-party/td/td/td/telegram/MessageContent.cpp +++ b/third-party/td/td/td/telegram/MessageContent.cpp @@ -518,7 +518,7 @@ class MessageChatSetTtl final : public MessageContent { class MessageUnsupported final : public MessageContent { public: - static constexpr int32 CURRENT_VERSION = 39; + static constexpr int32 CURRENT_VERSION = 41; int32 version = CURRENT_VERSION; MessageUnsupported() = default; @@ -1326,6 +1326,34 @@ class MessageStarGiftUnique final : public MessageContent { } }; +class MessagePaidMessagesRefunded final : public MessageContent { + public: + int32 message_count = 0; + int64 star_count = 0; + + MessagePaidMessagesRefunded() = default; + MessagePaidMessagesRefunded(int32 message_count, int64 star_count) + : message_count(message_count), star_count(star_count) { + } + + MessageContentType get_type() const final { + return MessageContentType::PaidMessagesRefunded; + } +}; + +class MessagePaidMessagesPrice final : public MessageContent { + public: + int64 star_count = 0; + + MessagePaidMessagesPrice() = default; + explicit MessagePaidMessagesPrice(int64 star_count) : star_count(star_count) { + } + + MessageContentType get_type() const final { + return MessageContentType::PaidMessagesPrice; + } +}; + template static void store(const MessageContent *content, StorerT &storer) { CHECK(content != nullptr); @@ -2097,6 +2125,21 @@ static void store(const MessageContent *content, StorerT &storer) { } break; } + case MessageContentType::PaidMessagesRefunded: { + const auto *m = static_cast(content); + BEGIN_STORE_FLAGS(); + END_STORE_FLAGS(); + store(m->message_count, storer); + store(m->star_count, storer); + break; + } + case MessageContentType::PaidMessagesPrice: { + const auto *m = static_cast(content); + BEGIN_STORE_FLAGS(); + END_STORE_FLAGS(); + store(m->star_count, storer); + break; + } default: UNREACHABLE(); } @@ -3090,6 +3133,23 @@ static void parse(unique_ptr &content, ParserT &parser) { content = std::move(m); break; } + case MessageContentType::PaidMessagesRefunded: { + auto m = make_unique(); + BEGIN_PARSE_FLAGS(); + END_PARSE_FLAGS(); + parse(m->message_count, parser); + parse(m->star_count, parser); + content = std::move(m); + break; + } + case MessageContentType::PaidMessagesPrice: { + auto m = make_unique(); + BEGIN_PARSE_FLAGS(); + END_PARSE_FLAGS(); + parse(m->star_count, parser); + content = std::move(m); + break; + } default: is_bad = true; @@ -3875,6 +3935,8 @@ bool can_message_content_have_input_media(const Td *td, const MessageContent *co case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; case MessageContentType::Animation: case MessageContentType::Audio: @@ -4023,6 +4085,8 @@ SecretInputMedia get_message_content_secret_input_media( case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: break; default: UNREACHABLE(); @@ -4198,6 +4262,8 @@ static telegram_api::object_ptr get_message_content_in case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: break; default: UNREACHABLE(); @@ -4440,6 +4506,8 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td, int32 med case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: break; default: UNREACHABLE(); @@ -4662,6 +4730,8 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: UNREACHABLE(); } return Status::OK(); @@ -4816,6 +4886,8 @@ static int32 get_message_content_media_index_mask(const MessageContent *content, case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return 0; default: UNREACHABLE(); @@ -5124,6 +5196,10 @@ vector get_message_content_min_user_ids(const Td *td, const MessageConte case MessageContentType::StarGiftUnique: // private chats only break; + case MessageContentType::PaidMessagesRefunded: + break; + case MessageContentType::PaidMessagesPrice: + break; default: UNREACHABLE(); break; @@ -5367,8 +5443,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo const auto *old_ = static_cast(old_content); const auto *new_ = static_cast(new_content); auto get_content_object = [td, dialog_id](const MessageContent *content) { - return to_string(get_message_content_object(content, td, dialog_id, MessageId(), false, -1, false, false, - std::numeric_limits::max(), false, false)); + return to_string(get_message_content_object(content, td, dialog_id, MessageId(), false, DialogId(), -1, false, + false, std::numeric_limits::max(), false, false)); }; if (old_->text.text != new_->text.text) { if (need_message_changed_warning && need_message_text_changed_warning(old_, new_)) { @@ -5555,6 +5631,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: break; default: UNREACHABLE(); @@ -5712,6 +5790,8 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type; break; default: @@ -6342,6 +6422,22 @@ void compare_message_contents(Td *td, const MessageContent *old_content, const M } break; } + case MessageContentType::PaidMessagesRefunded: { + const auto *lhs = static_cast(old_content); + const auto *rhs = static_cast(new_content); + if (lhs->message_count != rhs->message_count || lhs->star_count != rhs->star_count) { + need_update = true; + } + break; + } + case MessageContentType::PaidMessagesPrice: { + const auto *lhs = static_cast(old_content); + const auto *rhs = static_cast(new_content); + if (lhs->star_count != rhs->star_count) { + need_update = true; + } + break; + } default: UNREACHABLE(); break; @@ -6691,17 +6787,17 @@ static auto secret_to_telegram(secret_api::photoCachedSize &photo_size) { // documentAttributeImageSize w:int h:int = DocumentAttribute; static auto secret_to_telegram(secret_api::documentAttributeImageSize &image_size) { - return make_tl_object(image_size.w_, image_size.h_); + return telegram_api::make_object(image_size.w_, image_size.h_); } // documentAttributeAnimated = DocumentAttribute; static auto secret_to_telegram(secret_api::documentAttributeAnimated &animated) { - return make_tl_object(); + return telegram_api::make_object(); } // documentAttributeSticker23 = DocumentAttribute; static auto secret_to_telegram(secret_api::documentAttributeSticker23 &sticker) { - return make_tl_object( + return telegram_api::make_object( 0, false, "", make_tl_object(), nullptr); } @@ -6721,14 +6817,14 @@ static auto secret_to_telegram(secret_api::documentAttributeSticker &sticker) { if (!clean_input_string(sticker.alt_)) { sticker.alt_.clear(); } - return make_tl_object( + return telegram_api::make_object( 0, false, sticker.alt_, secret_to_telegram(*sticker.stickerset_), nullptr); } // documentAttributeVideo23 duration:int w:int h:int = DocumentAttribute; static auto secret_to_telegram(secret_api::documentAttributeVideo23 &video) { - return make_tl_object(0, false, false, false, video.duration_, video.w_, - video.h_, 0, 0.0, string()); + return telegram_api::make_object(0, false, false, false, video.duration_, + video.w_, video.h_, 0, 0.0, string()); } // documentAttributeFilename file_name:string = DocumentAttribute; @@ -6736,14 +6832,13 @@ static auto secret_to_telegram(secret_api::documentAttributeFilename &filename) if (!clean_input_string(filename.file_name_)) { filename.file_name_.clear(); } - return make_tl_object(filename.file_name_); + return telegram_api::make_object(filename.file_name_); } // documentAttributeVideo flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute; static auto secret_to_telegram(secret_api::documentAttributeVideo &video) { - return make_tl_object( - video.round_message_ ? telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK : 0, video.round_message_, false, - false, video.duration_, video.w_, video.h_, 0, 0.0, string()); + return telegram_api::make_object( + 0, video.round_message_, false, false, video.duration_, video.w_, video.h_, 0, 0.0, string()); } static auto telegram_documentAttributeAudio(bool is_voice_note, int duration, string title, string performer, @@ -6754,22 +6849,8 @@ static auto telegram_documentAttributeAudio(bool is_voice_note, int duration, st if (!clean_input_string(performer)) { performer.clear(); } - - int32 flags = 0; - if (is_voice_note) { - flags |= telegram_api::documentAttributeAudio::VOICE_MASK; - } - if (!title.empty()) { - flags |= telegram_api::documentAttributeAudio::TITLE_MASK; - } - if (!performer.empty()) { - flags |= telegram_api::documentAttributeAudio::PERFORMER_MASK; - } - if (!waveform.empty()) { - flags |= telegram_api::documentAttributeAudio::WAVEFORM_MASK; - } - return make_tl_object(flags, is_voice_note, duration, std::move(title), - std::move(performer), std::move(waveform)); + return telegram_api::make_object(0, is_voice_note, duration, std::move(title), + std::move(performer), std::move(waveform)); } // documentAttributeAudio23 duration:int = DocumentAttribute; @@ -6788,8 +6869,8 @@ static auto secret_to_telegram(secret_api::documentAttributeAudio &audio) { audio.waveform_.clone()); } -static auto secret_to_telegram(std::vector> &attributes) { - std::vector> res; +static auto secret_to_telegram(std::vector> &attributes) { + std::vector> res; for (auto &attribute : attributes) { auto telegram_attribute = secret_to_telegram(*attribute); if (telegram_attribute) { @@ -6922,7 +7003,7 @@ unique_ptr get_secret_message_content( switch (constructor_id) { case secret_api::decryptedMessageMediaDocument46::ID: { auto media = secret_api::move_object_as(media_ptr); - media_ptr = make_tl_object( + media_ptr = secret_api::make_object( std::move(media->thumb_), media->thumb_w_, media->thumb_h_, media->mime_type_, media->size_, std::move(media->key_), std::move(media->iv_), std::move(media->attributes_), string()); @@ -6931,10 +7012,10 @@ unique_ptr get_secret_message_content( } case secret_api::decryptedMessageMediaVideo::ID: { auto media = secret_api::move_object_as(media_ptr); - vector> attributes; - attributes.emplace_back( - make_tl_object(0, false, media->duration_, media->w_, media->h_)); - media_ptr = make_tl_object( + vector> attributes; + attributes.emplace_back(secret_api::make_object(0, false, media->duration_, + media->w_, media->h_)); + media_ptr = secret_api::make_object( std::move(media->thumb_), media->thumb_w_, media->thumb_h_, media->mime_type_, media->size_, std::move(media->key_), std::move(media->iv_), std::move(attributes), string()); @@ -7065,7 +7146,7 @@ unique_ptr get_secret_message_content( auto attribute_sticker = static_cast(attribute.get()); CHECK(attribute_sticker->stickerset_ != nullptr); if (attribute_sticker->stickerset_->get_id() != telegram_api::inputStickerSetEmpty::ID) { - attribute_sticker->stickerset_ = make_tl_object(); + attribute_sticker->stickerset_ = telegram_api::make_object(); } } } @@ -7614,6 +7695,8 @@ unique_ptr dup_message_content(Td *td, DialogId dialog_id, const case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return nullptr; default: UNREACHABLE(); @@ -7655,6 +7738,7 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr get_action_message_content(Td *td, tl_object_ptr get_action_message_content(Td *td, tl_object_ptrupgrade_, (action->flags_ & telegram_api::messageActionStarGiftUnique::TRANSFER_STARS_MASK) != 0, action->transferred_, action->refunded_); } + case telegram_api::messageActionPaidMessagesRefunded::ID: { + auto action = move_tl_object_as(action_ptr); + return td::make_unique(max(0, action->count_), + StarManager::get_star_count(action->stars_)); + } + case telegram_api::messageActionPaidMessagesPrice::ID: { + auto action = move_tl_object_as(action_ptr); + return td::make_unique(StarManager::get_star_count(action->stars_)); + } default: UNREACHABLE(); } @@ -8202,12 +8296,10 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr(FormattedText(), WebPageId(), false, false, false, string()); } -td_api::object_ptr get_message_content_object(const MessageContent *content, Td *td, - DialogId dialog_id, MessageId message_id, - bool is_outgoing, int32 message_date, - bool is_content_secret, bool skip_bot_commands, - int32 max_media_timestamp, bool invert_media, - bool disable_web_page_preview) { +td_api::object_ptr get_message_content_object( + const MessageContent *content, Td *td, DialogId dialog_id, MessageId message_id, bool is_outgoing, + DialogId sender_dialog_id, int32 message_date, bool is_content_secret, bool skip_bot_commands, + int32 max_media_timestamp, bool invert_media, bool disable_web_page_preview) { CHECK(content != nullptr); auto is_server = message_id != MessageId() && message_id.is_any_server() && dialog_id.get_type() != DialogType::SecretChat; @@ -8693,15 +8785,8 @@ td_api::object_ptr get_message_content_object(const Mess star_gift_id = StarGiftId(message_id.get_server_message_id()); } } - DialogId sender_dialog_id; if (m->sender_dialog_id != DialogId()) { sender_dialog_id = m->sender_dialog_id; - } else { - if (is_outgoing) { - sender_dialog_id = td->dialog_manager_->get_my_dialog_id(); - } else { - sender_dialog_id = dialog_id; - } } return td_api::make_object( m->star_gift.get_gift_object(td), get_message_sender_object(td, sender_dialog_id, "messageGift"), @@ -8711,15 +8796,8 @@ td_api::object_ptr get_message_content_object(const Mess } case MessageContentType::StarGiftUnique: { const auto *m = static_cast(content); - DialogId sender_dialog_id; if (m->sender_dialog_id != DialogId()) { sender_dialog_id = m->sender_dialog_id; - } else { - if (is_outgoing) { - sender_dialog_id = td->dialog_manager_->get_my_dialog_id(); - } else { - sender_dialog_id = dialog_id; - } } if (m->was_refunded) { return td_api::make_object( @@ -8744,6 +8822,14 @@ td_api::object_ptr get_message_content_object(const Mess star_gift_id.get_star_gift_id(), m->is_upgrade, m->is_saved, m->can_transfer, m->was_transferred, m->transfer_star_count, m->can_export_at); } + case MessageContentType::PaidMessagesRefunded: { + const auto *m = static_cast(content); + return td_api::make_object(m->message_count, m->star_count); + } + case MessageContentType::PaidMessagesPrice: { + const auto *m = static_cast(content); + return td_api::make_object(m->star_count); + } default: UNREACHABLE(); return nullptr; @@ -9849,6 +9935,10 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC dependencies.add_dialog_and_dependencies(content->owner_dialog_id); break; } + case MessageContentType::PaidMessagesRefunded: + break; + case MessageContentType::PaidMessagesPrice: + break; default: UNREACHABLE(); break; diff --git a/third-party/td/td/td/telegram/MessageContent.h b/third-party/td/td/td/telegram/MessageContent.h index d0b72e4e20..d379c04819 100644 --- a/third-party/td/td/td/telegram/MessageContent.h +++ b/third-party/td/td/td/telegram/MessageContent.h @@ -270,10 +270,10 @@ unique_ptr get_action_message_content(Td *td, tl_object_ptr get_message_content_object(const MessageContent *content, Td *td, DialogId dialog_id, MessageId message_id, - bool is_outgoing, int32 message_date, - bool is_content_secret, bool skip_bot_commands, - int32 max_media_timestamp, bool invert_media, - bool disable_web_page_preview); + bool is_outgoing, DialogId sender_dialog_id, + int32 message_date, bool is_content_secret, + bool skip_bot_commands, int32 max_media_timestamp, + bool invert_media, bool disable_web_page_preview); td_api::object_ptr get_message_content_upgrade_gift_result_object( const MessageContent *content, Td *td, DialogId dialog_id, MessageId message_id); diff --git a/third-party/td/td/td/telegram/MessageContentType.cpp b/third-party/td/td/td/telegram/MessageContentType.cpp index c9c446bad7..d91849e48f 100644 --- a/third-party/td/td/td/telegram/MessageContentType.cpp +++ b/third-party/td/td/td/telegram/MessageContentType.cpp @@ -158,6 +158,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType cont return string_builder << "StarGift"; case MessageContentType::StarGiftUnique: return string_builder << "UpgradedStarGift"; + case MessageContentType::PaidMessagesRefunded: + return string_builder << "PaidMessagesRefunded"; + case MessageContentType::PaidMessagesPrice: + return string_builder << "PaidMessagesPrice"; default: return string_builder << "Invalid type " << static_cast(content_type); } @@ -251,6 +255,8 @@ bool is_allowed_media_group_content(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); @@ -338,6 +344,8 @@ bool can_be_secret_message_content(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); @@ -421,6 +429,8 @@ bool can_be_local_message_content(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); @@ -504,6 +514,8 @@ bool is_service_message_content(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return true; default: UNREACHABLE(); @@ -587,6 +599,8 @@ bool is_editable_message_content(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); @@ -734,6 +748,8 @@ bool can_have_message_content_caption(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return false; default: UNREACHABLE(); @@ -819,6 +835,8 @@ bool can_send_message_content_to_secret_chat(MessageContentType content_type) { case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: default: UNREACHABLE(); return false; @@ -919,6 +937,8 @@ bool get_default_service_message_content_reactions_are_possible(MessageContentTy case MessageContentType::PrizeStars: case MessageContentType::StarGift: case MessageContentType::StarGiftUnique: + case MessageContentType::PaidMessagesRefunded: + case MessageContentType::PaidMessagesPrice: return true; default: UNREACHABLE(); diff --git a/third-party/td/td/td/telegram/MessageContentType.h b/third-party/td/td/td/telegram/MessageContentType.h index 95fd3cb3e9..809a0d59bb 100644 --- a/third-party/td/td/td/telegram/MessageContentType.h +++ b/third-party/td/td/td/telegram/MessageContentType.h @@ -86,7 +86,9 @@ enum class MessageContentType : int32 { GiftStars, PrizeStars, StarGift, - StarGiftUnique + StarGiftUnique, + PaidMessagesRefunded, + PaidMessagesPrice }; // increase MessageUnsupported::CURRENT_VERSION each time a new message content type is added diff --git a/third-party/td/td/td/telegram/MessageEntity.cpp b/third-party/td/td/td/telegram/MessageEntity.cpp index e9de236897..d19ef204a2 100644 --- a/third-party/td/td/td/telegram/MessageEntity.cpp +++ b/third-party/td/td/td/telegram/MessageEntity.cpp @@ -3559,7 +3559,7 @@ vector> get_input_secret_message_entiti break; case MessageEntity::Type::BlockQuote: if (layer >= static_cast(SecretChatLayer::NewEntities)) { - // result.push_back(make_tl_object(0, false /*ignored*/, entity.offset, entity.length)); + // result.push_back(make_tl_object(0, false, entity.offset, entity.length)); } break; case MessageEntity::Type::Code: @@ -3592,8 +3592,8 @@ vector> get_input_secret_message_entiti break; case MessageEntity::Type::ExpandableBlockQuote: if (layer >= static_cast(SecretChatLayer::NewEntities)) { - // result.push_back(make_tl_object( - // secret_api::messageEntityBlockquote::COLLAPSED_MASK, false /*ignored*/, entity.offset, entity.length)); + // result.push_back(make_tl_object(0, true, entity.offset, + // entity.length)); } break; default: @@ -4684,8 +4684,7 @@ vector> get_input_message_entities(co user_entity_count++; switch (entity.type) { case MessageEntity::Type::BlockQuote: - result.push_back( - make_tl_object(0, false /*ignored*/, entity.offset, entity.length)); + result.push_back(make_tl_object(0, false, entity.offset, entity.length)); break; case MessageEntity::Type::Code: result.push_back(make_tl_object(entity.offset, entity.length)); @@ -4708,8 +4707,7 @@ vector> get_input_message_entities(co break; } case MessageEntity::Type::ExpandableBlockQuote: - result.push_back(make_tl_object( - telegram_api::messageEntityBlockquote::COLLAPSED_MASK, false /*ignored*/, entity.offset, entity.length)); + result.push_back(make_tl_object(0, true, entity.offset, entity.length)); break; default: UNREACHABLE(); diff --git a/third-party/td/td/td/telegram/MessageQueryManager.cpp b/third-party/td/td/td/telegram/MessageQueryManager.cpp index d93f763d24..979ed45b57 100644 --- a/third-party/td/td/td/telegram/MessageQueryManager.cpp +++ b/third-party/td/td/td/telegram/MessageQueryManager.cpp @@ -850,18 +850,8 @@ class BlockFromRepliesQuery final : public Td::ResultHandler { } void send(MessageId message_id, bool need_delete_message, bool need_delete_all_messages, bool report_spam) { - int32 flags = 0; - if (need_delete_message) { - flags |= telegram_api::contacts_blockFromReplies::DELETE_MESSAGE_MASK; - } - if (need_delete_all_messages) { - flags |= telegram_api::contacts_blockFromReplies::DELETE_HISTORY_MASK; - } - if (report_spam) { - flags |= telegram_api::contacts_blockFromReplies::REPORT_SPAM_MASK; - } send_query(G()->net_query_creator().create(telegram_api::contacts_blockFromReplies( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, message_id.get_server_message_id().get()))); + 0, need_delete_message, need_delete_all_messages, report_spam, message_id.get_server_message_id().get()))); } void on_result(BufferSlice packet) final { @@ -1021,13 +1011,8 @@ class DeleteChannelHistoryQuery final : public Td::ResultHandler { return on_error(Status::Error(400, "Can't access the chat")); } - int32 flags = 0; - if (revoke) { - flags |= telegram_api::channels_deleteHistory::FOR_EVERYONE_MASK; - } - send_query(G()->net_query_creator().create(telegram_api::channels_deleteHistory( - flags, false /*ignored*/, std::move(input_channel), max_message_id.get_server_message_id().get()))); + 0, revoke, std::move(input_channel), max_message_id.get_server_message_id().get()))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/MessagesManager.cpp b/third-party/td/td/td/telegram/MessagesManager.cpp index 8be5060ae9..a626b9e3e4 100644 --- a/third-party/td/td/td/telegram/MessagesManager.cpp +++ b/third-party/td/td/td/telegram/MessagesManager.cpp @@ -242,7 +242,7 @@ class GetChannelMessagesQuery final : public Td::ResultHandler { explicit GetChannelMessagesQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(ChannelId channel_id, tl_object_ptr &&input_channel, + void send(ChannelId channel_id, tl_object_ptr input_channel, vector> &&message_ids, MessageId last_new_message_id) { channel_id_ = channel_id; last_new_message_id_ = last_new_message_id; @@ -353,7 +353,9 @@ class UpdateDialogPinnedMessageQuery final : public Td::ResultHandler { business_connection_id_ = business_connection_id; dialog_id_ = dialog_id; message_id_ = message_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Write); + + auto input_peer = td_->dialog_manager_->get_input_peer( + dialog_id, business_connection_id_.is_valid() ? AccessRights::Know : AccessRights::Write); if (input_peer == nullptr) { LOG(INFO) << "Can't update pinned message in " << dialog_id; return on_error(Status::Error(400, "Can't update pinned message")); @@ -392,7 +394,7 @@ class UpdateDialogPinnedMessageQuery final : public Td::ResultHandler { } void on_error(Status status) final { - if (!business_connection_id_.is_empty()) { + if (business_connection_id_.is_empty()) { td_->messages_manager_->on_get_message_error(dialog_id_, message_id_, status, "UpdateDialogPinnedMessageQuery"); } promise_.set_error(std::move(status)); @@ -419,13 +421,9 @@ class ExportChannelMessageLinkQuery final : public Td::ResultHandler { if (input_channel == nullptr) { return on_error(Status::Error(400, "Can't access the chat")); } - int32 flags = 0; - if (for_group) { - flags |= telegram_api::channels_exportMessageLink::GROUPED_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::channels_exportMessageLink(flags, false /*ignored*/, false /*ignored*/, std::move(input_channel), - message_id.get_server_message_id().get()))); + bool for_thread = false; + send_query(G()->net_query_creator().create(telegram_api::channels_exportMessageLink( + 0, for_group, for_thread, std::move(input_channel), message_id.get_server_message_id().get()))); } void on_result(BufferSlice packet) final { @@ -2230,14 +2228,8 @@ class GetChannelDifferenceQuery final : public Td::ResultHandler { pts_ = pts; limit_ = limit; CHECK(input_channel != nullptr); - - int32 flags = 0; - if (force) { - flags |= telegram_api::updates_getChannelDifference::FORCE_MASK; - } send_query(G()->net_query_creator().create(telegram_api::updates_getChannelDifference( - flags, false /*ignored*/, std::move(input_channel), make_tl_object(), - pts, limit))); + 0, force, std::move(input_channel), make_tl_object(), pts, limit))); } void on_result(BufferSlice packet) final { @@ -4214,9 +4206,10 @@ void MessagesManager::on_update_service_notification(tl_object_ptrpopup_) { send_closure(G()->td(), &Td::send_update, td_api::make_object( - update->type_, get_message_content_object( - content.get(), td_, owner_dialog_id, MessageId(ServerMessageId(1)), false, date, - is_content_secret, true, -1, update->invert_media_, disable_web_page_preview))); + update->type_, + get_message_content_object(content.get(), td_, owner_dialog_id, MessageId(ServerMessageId(1)), + false, owner_dialog_id, date, is_content_secret, true, -1, + update->invert_media_, disable_web_page_preview))); } if (has_date && is_user) { Dialog *d = get_service_notifications_dialog(); @@ -14508,6 +14501,10 @@ void MessagesManager::get_messages_from_server(vector &&message_i } const auto *d = get_dialog_force(DialogId(it.first)); for (auto &slice_message_ids : vector_split(std::move(it.second), MAX_SLICE_SIZE)) { + if (input_channel == nullptr) { + input_channel = td_->chat_manager_->get_input_channel(it.first); + CHECK(input_channel != nullptr); + } td_->create_handler(mpas.get_promise()) ->send(it.first, std::move(input_channel), std::move(slice_message_ids), d == nullptr ? MessageId() : d->last_new_message_id); @@ -19513,9 +19510,11 @@ tl_object_ptr MessagesManager::get_message_sched td_api::object_ptr MessagesManager::get_message_message_content_object(DialogId dialog_id, const Message *m) const { auto live_location_date = m->is_failed_to_send ? 0 : m->date; - return get_message_content_object(m->content.get(), td_, dialog_id, m->message_id, m->is_outgoing, live_location_date, - m->is_content_secret, need_skip_bot_commands(dialog_id, m), - get_message_max_media_timestamp(m), m->invert_media, m->disable_web_page_preview); + return get_message_content_object( + m->content.get(), td_, dialog_id, m->message_id, m->is_outgoing, + m->sender_dialog_id != DialogId() ? m->sender_dialog_id : DialogId(m->sender_user_id), live_location_date, + m->is_content_secret, need_skip_bot_commands(dialog_id, m), get_message_max_media_timestamp(m), m->invert_media, + m->disable_web_page_preview); } td_api::object_ptr MessagesManager::get_dialog_event_log_message_object( @@ -19541,9 +19540,10 @@ td_api::object_ptr MessagesManager::get_dialog_event_log_messag td_->user_manager_->get_user_id_object(m->via_bot_user_id, "get_dialog_event_log_message_object via_bot_user_id"); auto edit_date = m->hide_edit_date ? 0 : m->edit_date; auto reply_markup = get_reply_markup_object(td_->user_manager_.get(), m->reply_markup); - auto content = - get_message_content_object(m->content.get(), td_, dialog_id, m->message_id, m->is_outgoing, 0, false, true, - get_message_own_max_media_timestamp(m), m->invert_media, m->disable_web_page_preview); + auto content = get_message_content_object( + m->content.get(), td_, dialog_id, m->message_id, m->is_outgoing, + m->sender_dialog_id != DialogId() ? m->sender_dialog_id : DialogId(m->sender_user_id), 0, false, true, + get_message_own_max_media_timestamp(m), m->invert_media, m->disable_web_page_preview); return td_api::make_object( m->message_id.get(), std::move(sender), get_chat_id_object(dialog_id, "get_dialog_event_log_message_object"), nullptr, nullptr, m->is_outgoing, m->is_pinned, m->is_from_offline, can_be_saved, true, m->is_channel_post, @@ -19843,7 +19843,9 @@ unique_ptr MessagesManager::create_message_to_send( } else { m->sender_dialog_id = send_as_dialog_id; } - m->has_explicit_sender = true; + if (dialog_type == DialogType::Channel) { + m->has_explicit_sender = true; + } } else if (d->default_send_message_as_dialog_id.is_valid()) { if (d->default_send_message_as_dialog_id.get_type() == DialogType::User) { m->sender_user_id = my_id; @@ -26519,9 +26521,12 @@ MessageFullId MessagesManager::on_send_message_success(int64 random_id, MessageI // upload was canceled in delete_message const auto *input_reply_to = get_message_input_reply_to(sent_message.get()); - if (input_reply_to != nullptr && input_reply_to->is_valid() && - input_reply_to->get_reply_message_full_id(dialog_id).get_message_id().is_yet_unsent()) { - set_message_reply(d, sent_message.get(), MessageInputReplyTo(), false); + if (input_reply_to != nullptr) { + auto reply_message_full_id = input_reply_to->get_reply_message_full_id(dialog_id); + auto reply_message_id = reply_message_full_id.get_message_id(); + if (reply_message_id.is_valid() && reply_message_id.is_yet_unsent()) { + set_message_reply(d, sent_message.get(), MessageInputReplyTo(), false); + } } sent_message->message_id = new_message_id; @@ -27982,7 +27987,7 @@ void MessagesManager::on_create_new_dialog(telegram_api::object_ptr> &&chat_promise, Promise> &&channel_promise) { - LOG(INFO) << "Receive result for creation of a chat: " << to_string(updates); + LOG(INFO) << "Receive result for creating of a chat: " << to_string(updates); auto fail = [&](Slice message) { chat_promise.set_error(Status::Error(500, message)); diff --git a/third-party/td/td/td/telegram/NotificationSettingsManager.cpp b/third-party/td/td/td/telegram/NotificationSettingsManager.cpp index 5abed5853b..90672bbf90 100644 --- a/third-party/td/td/td/telegram/NotificationSettingsManager.cpp +++ b/third-party/td/td/td/telegram/NotificationSettingsManager.cpp @@ -233,16 +233,13 @@ class GetNotifySettingsExceptionsQuery final : public Td::ResultHandler { void send(NotificationSettingsScope scope, bool filter_scope, bool compare_sound) { int32 flags = 0; - tl_object_ptr input_notify_peer; + telegram_api::object_ptr input_notify_peer; if (filter_scope) { flags |= telegram_api::account_getNotifyExceptions::PEER_MASK; input_notify_peer = get_input_notify_peer(scope); } - if (compare_sound) { - flags |= telegram_api::account_getNotifyExceptions::COMPARE_SOUND_MASK; - } - send_query(G()->net_query_creator().create(telegram_api::account_getNotifyExceptions( - flags, false /*ignored*/, false /*ignored*/, std::move(input_notify_peer)))); + send_query(G()->net_query_creator().create( + telegram_api::account_getNotifyExceptions(flags, compare_sound, false, std::move(input_notify_peer)))); } void on_result(BufferSlice packet) final { @@ -295,9 +292,7 @@ class GetStoryNotifySettingsExceptionsQuery final : public Td::ResultHandler { } void send() { - int32 flags = telegram_api::account_getNotifyExceptions::COMPARE_STORIES_MASK; - send_query(G()->net_query_creator().create( - telegram_api::account_getNotifyExceptions(flags, false /*ignored*/, false /*ignored*/, nullptr))); + send_query(G()->net_query_creator().create(telegram_api::account_getNotifyExceptions(0, false, true, nullptr))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/PasswordManager.cpp b/third-party/td/td/td/telegram/PasswordManager.cpp index fb7fb5f99f..bdf0d78a06 100644 --- a/third-party/td/td/td/telegram/PasswordManager.cpp +++ b/third-party/td/td/td/telegram/PasswordManager.cpp @@ -813,8 +813,8 @@ void PasswordManager::do_get_state(Promise promise) { state.unconfirmed_recovery_email_code = {std::move(password->email_unconfirmed_pattern_), code_length}; state.login_email_pattern = std::move(password->login_email_pattern_); - if (password->flags_ & telegram_api::account_password::PENDING_RESET_DATE_MASK) { - state.pending_reset_date = td::max(password->pending_reset_date_, 0); + if (password->pending_reset_date_ > 0) { + state.pending_reset_date = password->pending_reset_date_; } auto &new_state = state.new_state; diff --git a/third-party/td/td/td/telegram/Payments.cpp b/third-party/td/td/td/telegram/Payments.cpp index 6ef3d7f491..b8177971d8 100644 --- a/third-party/td/td/td/telegram/Payments.cpp +++ b/third-party/td/td/td/telegram/Payments.cpp @@ -295,14 +295,12 @@ static tl_object_ptr convert_invoice(tl_object_ptrprices_), convert_labeled_price); - bool is_test = (invoice->flags_ & telegram_api::invoice::TEST_MASK) != 0; - bool need_name = (invoice->flags_ & telegram_api::invoice::NAME_REQUESTED_MASK) != 0; - bool need_phone_number = (invoice->flags_ & telegram_api::invoice::PHONE_REQUESTED_MASK) != 0; - bool need_email_address = (invoice->flags_ & telegram_api::invoice::EMAIL_REQUESTED_MASK) != 0; - bool need_shipping_address = (invoice->flags_ & telegram_api::invoice::SHIPPING_ADDRESS_REQUESTED_MASK) != 0; - bool send_phone_number_to_provider = (invoice->flags_ & telegram_api::invoice::PHONE_TO_PROVIDER_MASK) != 0; - bool send_email_address_to_provider = (invoice->flags_ & telegram_api::invoice::EMAIL_TO_PROVIDER_MASK) != 0; - bool is_flexible = (invoice->flags_ & telegram_api::invoice::FLEXIBLE_MASK) != 0; + bool need_phone_number = invoice->phone_requested_; + bool need_email_address = invoice->email_requested_; + bool need_shipping_address = invoice->shipping_address_requested_; + bool send_phone_number_to_provider = invoice->phone_to_provider_; + bool send_email_address_to_provider = invoice->email_to_provider_; + bool is_flexible = invoice->flexible_; if (send_phone_number_to_provider) { need_phone_number = true; } @@ -332,9 +330,9 @@ static tl_object_ptr convert_invoice(tl_object_ptr( std::move(invoice->currency_), std::move(labeled_prices), max(invoice->subscription_period_, 0), - invoice->max_tip_amount_, std::move(invoice->suggested_tip_amounts_), recurring_terms_url, terms_url, is_test, - need_name, need_phone_number, need_email_address, need_shipping_address, send_phone_number_to_provider, - send_email_address_to_provider, is_flexible); + invoice->max_tip_amount_, std::move(invoice->suggested_tip_amounts_), recurring_terms_url, terms_url, + invoice->test_, invoice->name_requested_, need_phone_number, need_email_address, need_shipping_address, + send_phone_number_to_provider, send_email_address_to_provider, is_flexible); } static tl_object_ptr convert_payment_provider( @@ -1150,13 +1148,8 @@ void send_payment_form(Td *td, td_api::object_ptr &&input_ } case td_api::inputCredentialsNew::ID: { auto credentials_new = static_cast(credentials.get()); - int32 flags = 0; - if (credentials_new->allow_save_) { - flags |= telegram_api::inputPaymentCredentials::SAVE_MASK; - } - input_credentials = make_tl_object( - flags, false /*ignored*/, make_tl_object(credentials_new->data_)); + 0, credentials_new->allow_save_, make_tl_object(credentials_new->data_)); break; } case td_api::inputCredentialsGooglePay::ID: { diff --git a/third-party/td/td/td/telegram/PhoneNumberManager.cpp b/third-party/td/td/td/telegram/PhoneNumberManager.cpp index c502cd7214..fc04fdcda4 100644 --- a/third-party/td/td/td/telegram/PhoneNumberManager.cpp +++ b/third-party/td/td/td/telegram/PhoneNumberManager.cpp @@ -40,6 +40,7 @@ class SendCodeQuery final : public Td::ResultHandler { auto ptr = result_ptr.move_as_ok(); switch (ptr->get_id()) { case telegram_api::auth_sentCodeSuccess::ID: + case telegram_api::auth_sentCodePaymentRequired::ID: return on_error(Status::Error(500, "Receive invalid response")); case telegram_api::auth_sentCode::ID: return promise_.set_value(telegram_api::move_object_as(ptr)); diff --git a/third-party/td/td/td/telegram/PhotoSize.cpp b/third-party/td/td/td/telegram/PhotoSize.cpp index 89b885a854..90695074a0 100644 --- a/third-party/td/td/td/telegram/PhotoSize.cpp +++ b/third-party/td/td/td/telegram/PhotoSize.cpp @@ -338,7 +338,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t } FileId file_id; - vector> attributes; + vector> attributes; int32 size = 0; string mime_type; switch (web_document_ptr->get_id()) { @@ -389,7 +389,7 @@ PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_t for (auto &attribute : attributes) { switch (attribute->get_id()) { case telegram_api::documentAttributeImageSize::ID: { - auto image_size = move_tl_object_as(attribute); + auto image_size = telegram_api::move_object_as(attribute); dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize"); break; } diff --git a/third-party/td/td/td/telegram/PollManager.cpp b/third-party/td/td/td/telegram/PollManager.cpp index c2888c9397..32500efa0c 100644 --- a/third-party/td/td/td/telegram/PollManager.cpp +++ b/third-party/td/td/td/telegram/PollManager.cpp @@ -205,8 +205,8 @@ class StopPollQuery final : public Td::ResultHandler { auto message_id = message_full_id.get_message_id().get_server_message_id().get(); auto poll = telegram_api::make_object( - poll_id.get(), telegram_api::poll::CLOSED_MASK, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, telegram_api::make_object(string(), Auto()), Auto(), 0, 0); + poll_id.get(), 0, true, false, false, false, + telegram_api::make_object(string(), Auto()), Auto(), 0, 0); auto input_media = telegram_api::make_object(0, std::move(poll), vector(), string(), Auto()); send_query(G()->net_query_creator().create( @@ -1532,24 +1532,12 @@ tl_object_ptr PollManager::get_input_media(PollId poll CHECK(poll != nullptr); int32 poll_flags = 0; - if (!poll->is_anonymous_) { - poll_flags |= telegram_api::poll::PUBLIC_VOTERS_MASK; - } - if (poll->allow_multiple_answers_) { - poll_flags |= telegram_api::poll::MULTIPLE_CHOICE_MASK; - } - if (poll->is_quiz_) { - poll_flags |= telegram_api::poll::QUIZ_MASK; - } if (poll->open_period_ != 0) { poll_flags |= telegram_api::poll::CLOSE_PERIOD_MASK; } if (poll->close_date_ != 0) { poll_flags |= telegram_api::poll::CLOSE_DATE_MASK; } - if (poll->is_closed_) { - poll_flags |= telegram_api::poll::CLOSED_MASK; - } int32 flags = 0; vector correct_answers; @@ -1566,7 +1554,7 @@ tl_object_ptr PollManager::get_input_media(PollId poll return telegram_api::make_object( flags, telegram_api::make_object( - 0, poll_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, + 0, poll_flags, poll->is_closed_, !poll->is_anonymous_, poll->allow_multiple_answers_, poll->is_quiz_, get_input_text_with_entities(nullptr, poll->question_, "get_input_media_poll"), transform(poll->options_, get_input_poll_option), poll->open_period_, poll->close_date_), std::move(correct_answers), poll->explanation_.text, @@ -1694,7 +1682,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptrquestion_ = std::move(question); is_changed = true; } - poll_server_is_closed = (poll_server->flags_ & telegram_api::poll::CLOSED_MASK) != 0; + poll_server_is_closed = poll_server->closed_; if (poll_server_is_closed && !poll->is_closed_) { poll->is_closed_ = poll_server_is_closed; is_changed = true; @@ -1734,13 +1722,13 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptrflags_ & telegram_api::poll::PUBLIC_VOTERS_MASK) == 0; + bool is_anonymous = !poll_server->public_voters_; if (is_anonymous != poll->is_anonymous_) { poll->is_anonymous_ = is_anonymous; is_changed = true; } - bool allow_multiple_answers = (poll_server->flags_ & telegram_api::poll::MULTIPLE_CHOICE_MASK) != 0; - bool is_quiz = (poll_server->flags_ & telegram_api::poll::QUIZ_MASK) != 0; + bool allow_multiple_answers = poll_server->multiple_choice_; + bool is_quiz = poll_server->quiz_; if (is_quiz && allow_multiple_answers) { LOG(ERROR) << "Receive quiz " << poll_id << " from " << source << " allowing multiple answers"; allow_multiple_answers = false; diff --git a/third-party/td/td/td/telegram/Premium.cpp b/third-party/td/td/td/telegram/Premium.cpp index 50d1b99457..89f6c5e903 100644 --- a/third-party/td/td/td/telegram/Premium.cpp +++ b/third-party/td/td/td/telegram/Premium.cpp @@ -212,15 +212,7 @@ static Result> get_input_s switch (purpose->get_id()) { case td_api::storePaymentPurposePremiumSubscription::ID: { auto p = static_cast(purpose.get()); - int32 flags = 0; - if (p->is_restore_) { - flags |= telegram_api::inputStorePaymentPremiumSubscription::RESTORE_MASK; - } - if (p->is_upgrade_) { - flags |= telegram_api::inputStorePaymentPremiumSubscription::UPGRADE_MASK; - } - return make_tl_object(flags, false /*ignored*/, - false /*ignored*/); + return make_tl_object(0, p->is_restore_, p->is_upgrade_); } case td_api::storePaymentPurposePremiumGift::ID: { auto p = static_cast(purpose.get()); @@ -585,6 +577,99 @@ class ApplyGiftCodeQuery final : public Td::ResultHandler { } }; +class SendPremiumGiftQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit SendPremiumGiftQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(telegram_api::object_ptr input_invoice, int64 payment_form_id) { + send_query(G()->net_query_creator().create( + telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto payment_result = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for SendPremiumGiftQuery: " << to_string(payment_result); + switch (payment_result->get_id()) { + case telegram_api::payments_paymentResult::ID: { + auto result = telegram_api::move_object_as(payment_result); + td_->updates_manager_->on_get_updates(std::move(result->updates_), std::move(promise_)); + break; + } + case telegram_api::payments_paymentVerificationNeeded::ID: + LOG(ERROR) << "Receive " << to_string(payment_result); + break; + default: + UNREACHABLE(); + } + } + + void on_error(Status status) final { + if (status.message() == "FORM_SUBMIT_DUPLICATE") { + LOG(ERROR) << "Receive FORM_SUBMIT_DUPLICATE"; + } + promise_.set_error(std::move(status)); + } +}; + +class GetPremiumGiftPaymentFormQuery final : public Td::ResultHandler { + Promise promise_; + int64 star_count_; + telegram_api::object_ptr send_input_invoice_; + + public: + explicit GetPremiumGiftPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(telegram_api::object_ptr input_invoice, + telegram_api::object_ptr send_input_invoice, int64 star_count) { + star_count_ = star_count; + send_input_invoice_ = std::move(send_input_invoice); + send_query( + G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto payment_form_ptr = result_ptr.move_as_ok(); + LOG(INFO) << "Receive result for GetPremiumGiftPaymentFormQuery: " << to_string(payment_form_ptr); + switch (payment_form_ptr->get_id()) { + case telegram_api::payments_paymentForm::ID: + case telegram_api::payments_paymentFormStarGift::ID: + LOG(ERROR) << "Receive " << to_string(payment_form_ptr); + promise_.set_error(Status::Error(500, "Unsupported")); + break; + case telegram_api::payments_paymentFormStars::ID: { + auto payment_form = static_cast(payment_form_ptr.get()); + if (payment_form->invoice_->prices_.size() != 1u || + payment_form->invoice_->prices_[0]->amount_ != star_count_) { + return promise_.set_error(Status::Error(400, "Wrong purchase price specified")); + } + td_->create_handler(std::move(promise_)) + ->send(std::move(send_input_invoice_), payment_form->form_id_); + break; + } + default: + UNREACHABLE(); + } + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class LaunchPrepaidGiveawayQuery final : public Td::ResultHandler { Promise promise_; @@ -724,12 +809,11 @@ class CanPurchasePremiumQuery final : public Td::ResultHandler { return on_error(r_input_purpose.move_as_error()); } - send_query( - G()->net_query_creator().create(telegram_api::payments_canPurchasePremium(r_input_purpose.move_as_ok()))); + send_query(G()->net_query_creator().create(telegram_api::payments_canPurchaseStore(r_input_purpose.move_as_ok()))); } void on_result(BufferSlice packet) final { - auto result_ptr = fetch_result(packet); + auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(result_ptr.move_as_error()); } @@ -1264,6 +1348,26 @@ void apply_premium_gift_code(Td *td, const string &code, Promise &&promise td->create_handler(std::move(promise))->send(code); } +void gift_premium_with_stars(Td *td, UserId user_id, int64 star_count, int32 month_count, + td_api::object_ptr &&text, Promise &&promise) { + TRY_RESULT_PROMISE(promise, input_user, td->user_manager_->get_input_user(user_id)); + string currency = "XTR"; + TRY_STATUS_PROMISE(promise, check_payment_amount(currency, star_count)); + TRY_RESULT_PROMISE(promise, message, get_premium_gift_text(td, std::move(text))); + + int32 flags = 0; + if (message != nullptr) { + flags |= telegram_api::inputInvoicePremiumGiftStars::MESSAGE_MASK; + } + auto input_invoice = telegram_api::make_object(0, std::move(input_user), + month_count, nullptr); + auto send_input_invoice = telegram_api::make_object( + flags, td->user_manager_->get_input_user(user_id).move_as_ok(), month_count, std::move(message)); + + td->create_handler(std::move(promise)) + ->send(std::move(input_invoice), std::move(send_input_invoice), star_count); +} + void launch_prepaid_premium_giveaway(Td *td, int64 giveaway_id, td_api::object_ptr &¶meters, int32 user_count, int64 star_count, Promise &&promise) { diff --git a/third-party/td/td/td/telegram/Premium.h b/third-party/td/td/td/telegram/Premium.h index d34797839e..42a0e02e31 100644 --- a/third-party/td/td/td/telegram/Premium.h +++ b/third-party/td/td/td/telegram/Premium.h @@ -10,6 +10,7 @@ #include "td/telegram/MessageFullId.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/Promise.h" @@ -54,6 +55,9 @@ void check_premium_gift_code(Td *td, const string &code, void apply_premium_gift_code(Td *td, const string &code, Promise &&promise); +void gift_premium_with_stars(Td *td, UserId user_id, int64 star_count, int32 month_count, + td_api::object_ptr &&text, Promise &&promise); + void launch_prepaid_premium_giveaway(Td *td, int64 giveaway_id, td_api::object_ptr &¶meters, int32 user_count, int64 star_count, Promise &&promise); diff --git a/third-party/td/td/td/telegram/PrivacyManager.cpp b/third-party/td/td/td/telegram/PrivacyManager.cpp index 21dc973066..4b27adc83f 100644 --- a/third-party/td/td/td/telegram/PrivacyManager.cpp +++ b/third-party/td/td/td/telegram/PrivacyManager.cpp @@ -89,12 +89,8 @@ class AddNoPaidMessageExceptionQuery final : public Td::ResultHandler { void send(UserId user_id, telegram_api::object_ptr &&input_user, bool refund_charged) { user_id_ = user_id; - int32 flags = 0; - if (refund_charged) { - flags |= telegram_api::account_addNoPaidMessagesException::REFUND_CHARGED_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::account_addNoPaidMessagesException(flags, false /*ignored*/, std::move(input_user)))); + telegram_api::account_addNoPaidMessagesException(0, refund_charged, std::move(input_user)))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/QuickReplyManager.cpp b/third-party/td/td/td/telegram/QuickReplyManager.cpp index 957df8f3d4..7f51f618cd 100644 --- a/third-party/td/td/td/telegram/QuickReplyManager.cpp +++ b/third-party/td/td/td/telegram/QuickReplyManager.cpp @@ -1233,11 +1233,11 @@ td_api::object_ptr QuickReplyManager::get_message_s td_api::object_ptr QuickReplyManager::get_quick_reply_message_message_content_object( const QuickReplyMessage *m) const { if (m->edited_content != nullptr) { - return get_message_content_object(m->edited_content.get(), td_, DialogId(), MessageId(), false, 0, false, true, -1, - m->edited_invert_media, m->edited_disable_web_page_preview); + return get_message_content_object(m->edited_content.get(), td_, DialogId(), MessageId(), false, DialogId(), 0, + false, true, -1, m->edited_invert_media, m->edited_disable_web_page_preview); } - return get_message_content_object(m->content.get(), td_, DialogId(), m->message_id, false, 0, false, true, -1, - m->invert_media, m->disable_web_page_preview); + return get_message_content_object(m->content.get(), td_, DialogId(), m->message_id, false, DialogId(), 0, false, true, + -1, m->invert_media, m->disable_web_page_preview); } td_api::object_ptr QuickReplyManager::get_quick_reply_message_object( diff --git a/third-party/td/td/td/telegram/RepliedMessageInfo.cpp b/third-party/td/td/td/telegram/RepliedMessageInfo.cpp index 21c85e3855..0d2cd97608 100644 --- a/third-party/td/td/td/telegram/RepliedMessageInfo.cpp +++ b/third-party/td/td/td/telegram/RepliedMessageInfo.cpp @@ -291,8 +291,8 @@ td_api::object_ptr RepliedMessageInfo::get_messag td_api::object_ptr content; if (content_ != nullptr) { - content = - get_message_content_object(content_.get(), td, dialog_id, message_id, false, 0, false, true, -1, false, false); + content = get_message_content_object(content_.get(), td, dialog_id, message_id, false, dialog_id, 0, false, true, + -1, false, false); switch (content->get_id()) { case td_api::messageUnsupported::ID: content = nullptr; diff --git a/third-party/td/td/td/telegram/ReplyMarkup.cpp b/third-party/td/td/td/telegram/ReplyMarkup.cpp index e5d045fe94..d5129a238b 100644 --- a/third-party/td/td/td/telegram/ReplyMarkup.cpp +++ b/third-party/td/td/td/telegram/ReplyMarkup.cpp @@ -900,15 +900,16 @@ static tl_object_ptr get_input_keyboard_button( } case InlineKeyboardButton::Type::SwitchInlineCurrentDialog: return make_tl_object( - telegram_api::keyboardButtonSwitchInline::SAME_PEER_MASK, true, keyboard_button.text, keyboard_button.data, + 0, true, keyboard_button.text, keyboard_button.data, vector>()); case InlineKeyboardButton::Type::Buy: return make_tl_object(keyboard_button.text); case InlineKeyboardButton::Type::UrlAuth: { int32 flags = 0; + bool request_write_access = false; int64 bot_user_id = keyboard_button.id; if (bot_user_id > 0) { - flags |= telegram_api::inputKeyboardButtonUrlAuth::REQUEST_WRITE_ACCESS_MASK; + request_write_access = true; } else { bot_user_id = -bot_user_id; } @@ -920,7 +921,7 @@ static tl_object_ptr get_input_keyboard_button( LOG(ERROR) << "Failed to get InputUser for " << bot_user_id << ": " << r_input_user.error(); return make_tl_object(keyboard_button.text, keyboard_button.data); } - return make_tl_object(flags, false /*ignored*/, keyboard_button.text, + return make_tl_object(flags, request_write_access, keyboard_button.text, keyboard_button.forward_text, keyboard_button.data, r_input_user.move_as_ok()); } @@ -973,46 +974,22 @@ tl_object_ptr ReplyMarkup::get_input_reply_markup(Use rows.push_back(make_tl_object(std::move(buttons))); } int32 flags = 0; - if (is_persistent) { - flags |= telegram_api::replyKeyboardMarkup::PERSISTENT_MASK; - } - if (need_resize_keyboard) { - flags |= telegram_api::replyKeyboardMarkup::RESIZE_MASK; - } - if (is_one_time_keyboard) { - flags |= telegram_api::replyKeyboardMarkup::SINGLE_USE_MASK; - } - if (is_personal) { - flags |= telegram_api::replyKeyboardMarkup::SELECTIVE_MASK; - } if (!placeholder.empty()) { flags |= telegram_api::replyKeyboardMarkup::PLACEHOLDER_MASK; } - return make_tl_object(flags, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, std::move(rows), - placeholder); + return make_tl_object( + flags, need_resize_keyboard, is_one_time_keyboard, is_personal, is_persistent, std::move(rows), placeholder); } case ReplyMarkup::Type::ForceReply: { int32 flags = 0; - if (is_one_time_keyboard) { - flags |= telegram_api::replyKeyboardForceReply::SINGLE_USE_MASK; - } - if (is_personal) { - flags |= telegram_api::replyKeyboardForceReply::SELECTIVE_MASK; - } if (!placeholder.empty()) { flags |= telegram_api::replyKeyboardForceReply::PLACEHOLDER_MASK; } - return make_tl_object(flags, false /*ignored*/, false /*ignored*/, + return make_tl_object(flags, is_one_time_keyboard, is_personal, placeholder); } - case ReplyMarkup::Type::RemoveKeyboard: { - int32 flags = 0; - if (is_personal) { - flags |= telegram_api::replyKeyboardHide::SELECTIVE_MASK; - } - return make_tl_object(flags, false /*ignored*/); - } + case ReplyMarkup::Type::RemoveKeyboard: + return make_tl_object(0, is_personal); default: UNREACHABLE(); return nullptr; diff --git a/third-party/td/td/td/telegram/RequestedDialogType.cpp b/third-party/td/td/td/telegram/RequestedDialogType.cpp index 0f9040187e..a8e7aa5505 100644 --- a/third-party/td/td/td/telegram/RequestedDialogType.cpp +++ b/third-party/td/td/td/telegram/RequestedDialogType.cpp @@ -130,15 +130,9 @@ telegram_api::object_ptr RequestedDialogType::get if (restrict_is_forum_) { flags |= telegram_api::requestPeerTypeChat::FORUM_MASK; } - if (bot_is_participant_) { - flags |= telegram_api::requestPeerTypeChat::BOT_PARTICIPANT_MASK; - } if (restrict_has_username_) { flags |= telegram_api::requestPeerTypeChat::HAS_USERNAME_MASK; } - if (is_created_) { - flags |= telegram_api::requestPeerTypeChat::CREATOR_MASK; - } if (restrict_user_administrator_rights_) { flags |= telegram_api::requestPeerTypeChat::USER_ADMIN_RIGHTS_MASK; } @@ -150,7 +144,7 @@ telegram_api::object_ptr RequestedDialogType::get auto bot_admin_rights = restrict_bot_administrator_rights_ ? bot_administrator_rights_.get_chat_admin_rights() : nullptr; return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, has_username_, is_forum_, std::move(user_admin_rights), + flags, is_created_, bot_is_participant_, has_username_, is_forum_, std::move(user_admin_rights), std::move(bot_admin_rights)); } case Type::Channel: { @@ -158,9 +152,6 @@ telegram_api::object_ptr RequestedDialogType::get if (restrict_has_username_) { flags |= telegram_api::requestPeerTypeBroadcast::HAS_USERNAME_MASK; } - if (is_created_) { - flags |= telegram_api::requestPeerTypeBroadcast::CREATOR_MASK; - } if (restrict_user_administrator_rights_) { flags |= telegram_api::requestPeerTypeBroadcast::USER_ADMIN_RIGHTS_MASK; } @@ -172,7 +163,7 @@ telegram_api::object_ptr RequestedDialogType::get auto bot_admin_rights = restrict_bot_administrator_rights_ ? bot_administrator_rights_.get_chat_admin_rights() : nullptr; return telegram_api::make_object( - flags, false /*ignored*/, has_username_, std::move(user_admin_rights), std::move(bot_admin_rights)); + flags, is_created_, has_username_, std::move(user_admin_rights), std::move(bot_admin_rights)); } default: UNREACHABLE(); @@ -182,19 +173,9 @@ telegram_api::object_ptr RequestedDialogType::get telegram_api::object_ptr RequestedDialogType::get_input_keyboard_button_request_peer(const string &text) const { - int32 flags = 0; - if (request_name_) { - flags |= telegram_api::inputKeyboardButtonRequestPeer::NAME_REQUESTED_MASK; - } - if (request_username_) { - flags |= telegram_api::inputKeyboardButtonRequestPeer::USERNAME_REQUESTED_MASK; - } - if (request_photo_) { - flags |= telegram_api::inputKeyboardButtonRequestPeer::PHOTO_REQUESTED_MASK; - } return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, text, button_id_, - get_input_request_peer_type_object(), max_quantity_); + 0, request_name_, request_username_, request_photo_, text, button_id_, get_input_request_peer_type_object(), + max_quantity_); } int32 RequestedDialogType::get_button_id() const { diff --git a/third-party/td/td/td/telegram/Requests.cpp b/third-party/td/td/td/telegram/Requests.cpp index b76366b6ee..b03e574b45 100644 --- a/third-party/td/td/td/telegram/Requests.cpp +++ b/third-party/td/td/td/telegram/Requests.cpp @@ -139,6 +139,7 @@ #include "td/telegram/SponsoredMessageManager.h" #include "td/telegram/StarGiftId.h" #include "td/telegram/StarGiftManager.h" +#include "td/telegram/StarGiftSettings.h" #include "td/telegram/StarManager.h" #include "td/telegram/StarSubscriptionPricing.h" #include "td/telegram/StateManager.h" @@ -1936,6 +1937,10 @@ Promise Requests::create_http_url_request_promise(uint64 id) { if (td_->auth_manager_->is_bot()) { \ return send_error_raw(id, 400, "The method is not available to bots"); \ } +#define CHECK_IS_USER_OR_BUSINESS() \ + if (td_->auth_manager_->is_bot() && request.business_connection_id_.empty()) { \ + return send_error_raw(id, 400, "The method is not available to bots"); \ + } #define CREATE_NO_ARGS_REQUEST(name) \ auto slot_id = td_->request_actors_.create(ActorOwn<>(), Td::RequestActorIdType); \ @@ -2566,6 +2571,32 @@ void Requests::on_request(uint64 id, const td_api::reportChatSponsoredMessage &r request.option_id_, std::move(promise)); } +void Requests::on_request(uint64 id, td_api::getSearchSponsoredChats &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.query_); + CREATE_REQUEST_PROMISE(); + td_->sponsored_message_manager_->get_search_sponsored_dialogs(request.query_, std::move(promise)); +} + +void Requests::on_request(uint64 id, const td_api::viewSponsoredChat &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + td_->sponsored_message_manager_->view_sponsored_dialog(request.sponsored_chat_unique_id_, std::move(promise)); +} + +void Requests::on_request(uint64 id, const td_api::openSponsoredChat &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + td_->sponsored_message_manager_->open_sponsored_dialog(request.sponsored_chat_unique_id_, std::move(promise)); +} + +void Requests::on_request(uint64 id, const td_api::reportSponsoredChat &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + td_->sponsored_message_manager_->report_sponsored_dialog(request.sponsored_chat_unique_id_, request.option_id_, + std::move(promise)); +} + void Requests::on_request(uint64 id, const td_api::getMessageThread &request) { CHECK_IS_USER(); CREATE_REQUEST(GetMessageThreadRequest, request.chat_id_, request.message_id_); @@ -3819,6 +3850,91 @@ void Requests::on_request(uint64 id, td_api::setBusinessMessageIsPinned &request !request.is_pinned_, std::move(promise)); } +void Requests::on_request(uint64 id, td_api::readBusinessMessage &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->read_business_message( + BusinessConnectionId(std::move(request.business_connection_id_)), DialogId(request.chat_id_), + MessageId(request.message_id_), std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::deleteBusinessMessages &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->delete_business_messages( + BusinessConnectionId(std::move(request.business_connection_id_)), + MessageId::get_message_ids(request.message_ids_), std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::editBusinessStory &request) { + CHECK_IS_BOT(); + CREATE_REQUEST_PROMISE(); + td_->story_manager_->edit_business_story( + DialogId(request.story_sender_chat_id_), StoryId(request.story_id_), std::move(request.content_), + std::move(request.areas_), std::move(request.caption_), std::move(request.privacy_settings_), std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::deleteBusinessStory &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->delete_business_story( + BusinessConnectionId(std::move(request.business_connection_id_)), StoryId(request.story_id_), std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::setBusinessAccountName &request) { + CHECK_IS_BOT(); + CLEAN_INPUT_STRING(request.first_name_); + CLEAN_INPUT_STRING(request.last_name_); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->set_business_name(BusinessConnectionId(std::move(request.business_connection_id_)), + request.first_name_, request.last_name_, std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::setBusinessAccountBio &request) { + CHECK_IS_BOT(); + CLEAN_INPUT_STRING(request.bio_); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->set_business_about( + BusinessConnectionId(std::move(request.business_connection_id_)), request.bio_, std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::setBusinessAccountProfilePhoto &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->user_manager_->set_business_profile_photo(BusinessConnectionId(std::move(request.business_connection_id_)), + request.photo_, request.is_public_, std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::setBusinessAccountUsername &request) { + CHECK_IS_BOT(); + CLEAN_INPUT_STRING(request.username_); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->set_business_username( + BusinessConnectionId(std::move(request.business_connection_id_)), request.username_, std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::setBusinessAccountGiftSettings &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->set_business_gift_settings( + BusinessConnectionId(std::move(request.business_connection_id_)), StarGiftSettings(request.settings_), + std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::getBusinessAccountStarAmount &request) { + CHECK_IS_BOT(); + CREATE_REQUEST_PROMISE(); + td_->business_connection_manager_->get_business_star_status( + BusinessConnectionId(std::move(request.business_connection_id_)), std::move(promise)); +} + +void Requests::on_request(uint64 id, td_api::transferBusinessAccountStars &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + td_->business_connection_manager_->transfer_business_stars( + BusinessConnectionId(std::move(request.business_connection_id_)), request.star_count_, std::move(promise)); +} + void Requests::on_request(uint64 id, const td_api::loadQuickReplyShortcuts &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); @@ -3943,7 +4059,6 @@ void Requests::on_request(uint64 id, const td_api::canSendStory &request) { } void Requests::on_request(uint64 id, td_api::sendStory &request) { - CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); td_->story_manager_->send_story(DialogId(request.chat_id_), std::move(request.content_), std::move(request.areas_), std::move(request.caption_), std::move(request.privacy_settings_), @@ -4113,7 +4228,6 @@ void Requests::on_request(uint64 id, const td_api::deleteChatReplyMarkup &reques } void Requests::on_request(uint64 id, td_api::sendChatAction &request) { - CLEAN_INPUT_STRING(request.business_connection_id_); CREATE_OK_REQUEST_PROMISE(); td_->dialog_action_manager_->send_dialog_action(DialogId(request.chat_id_), MessageId(request.message_thread_id_), BusinessConnectionId(std::move(request.business_connection_id_)), @@ -7156,6 +7270,21 @@ void Requests::on_request(uint64 id, const td_api::deleteSavedCredentials &reque delete_saved_credentials(td_, std::move(promise)); } +void Requests::on_request(uint64 id, td_api::setGiftSettings &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + auto query_promise = PromiseCreator::lambda( + [settings = StarGiftSettings(request.settings_), promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + send_closure(G()->user_manager(), &UserManager::on_update_my_user_gift_settings, std::move(settings), + std::move(promise)); + }); + GlobalPrivacySettings::set_global_privacy_settings(td_, GlobalPrivacySettings(std::move(request.settings_)), + std::move(query_promise)); +} + void Requests::on_request(uint64 id, const td_api::getAvailableGifts &request) { CREATE_REQUEST_PROMISE(); td_->star_gift_manager_->get_gift_payment_options(std::move(promise)); @@ -7168,10 +7297,11 @@ void Requests::on_request(uint64 id, td_api::sendGift &request) { request.pay_for_upgrade_, std::move(promise)); } -void Requests::on_request(uint64 id, const td_api::sellGift &request) { - CHECK_IS_USER(); +void Requests::on_request(uint64 id, td_api::sellGift &request) { + CHECK_IS_USER_OR_BUSINESS(); CREATE_OK_REQUEST_PROMISE(); - td_->star_gift_manager_->convert_gift(StarGiftId(request.received_gift_id_), std::move(promise)); + td_->star_gift_manager_->convert_gift(BusinessConnectionId(std::move(request.business_connection_id_)), + StarGiftId(request.received_gift_id_), std::move(promise)); } void Requests::on_request(uint64 id, const td_api::toggleGiftIsSaved &request) { @@ -7204,30 +7334,32 @@ void Requests::on_request(uint64 id, const td_api::getGiftUpgradePreview &reques td_->star_gift_manager_->get_gift_upgrade_preview(request.gift_id_, std::move(promise)); } -void Requests::on_request(uint64 id, const td_api::upgradeGift &request) { - CHECK_IS_USER(); +void Requests::on_request(uint64 id, td_api::upgradeGift &request) { + CHECK_IS_USER_OR_BUSINESS(); CREATE_REQUEST_PROMISE(); - td_->star_gift_manager_->upgrade_gift(StarGiftId(request.received_gift_id_), request.keep_original_details_, + td_->star_gift_manager_->upgrade_gift(BusinessConnectionId(std::move(request.business_connection_id_)), + StarGiftId(request.received_gift_id_), request.keep_original_details_, request.star_count_, std::move(promise)); } -void Requests::on_request(uint64 id, const td_api::transferGift &request) { - CHECK_IS_USER(); +void Requests::on_request(uint64 id, td_api::transferGift &request) { + CHECK_IS_USER_OR_BUSINESS(); CREATE_OK_REQUEST_PROMISE(); TRY_RESULT_PROMISE(promise, owner_dialog_id, get_message_sender_dialog_id(td_, request.new_owner_id_, true, false)); - td_->star_gift_manager_->transfer_gift(StarGiftId(request.received_gift_id_), owner_dialog_id, request.star_count_, + td_->star_gift_manager_->transfer_gift(BusinessConnectionId(std::move(request.business_connection_id_)), + StarGiftId(request.received_gift_id_), owner_dialog_id, request.star_count_, std::move(promise)); } void Requests::on_request(uint64 id, td_api::getReceivedGifts &request) { - CHECK_IS_USER(); + CHECK_IS_USER_OR_BUSINESS(); CLEAN_INPUT_STRING(request.offset_); CREATE_REQUEST_PROMISE(); TRY_RESULT_PROMISE(promise, owner_dialog_id, get_message_sender_dialog_id(td_, request.owner_id_, true, false)); - td_->star_gift_manager_->get_saved_star_gifts(owner_dialog_id, request.exclude_unsaved_, request.exclude_saved_, - request.exclude_unlimited_, request.exclude_limited_, - request.exclude_upgraded_, request.sort_by_price_, request.offset_, - request.limit_, std::move(promise)); + td_->star_gift_manager_->get_saved_star_gifts( + BusinessConnectionId(std::move(request.business_connection_id_)), owner_dialog_id, request.exclude_unsaved_, + request.exclude_saved_, request.exclude_unlimited_, request.exclude_limited_, request.exclude_upgraded_, + request.sort_by_price_, request.offset_, request.limit_, std::move(promise)); } void Requests::on_request(uint64 id, const td_api::getReceivedGift &request) { @@ -7251,7 +7383,6 @@ void Requests::on_request(uint64 id, const td_api::getUpgradedGiftWithdrawalUrl } void Requests::on_request(uint64 id, td_api::createInvoiceLink &request) { - CLEAN_INPUT_STRING(request.business_connection_id_); CHECK_IS_BOT(); CREATE_HTTP_URL_REQUEST_PROMISE(); export_invoice(td_, BusinessConnectionId(std::move(request.business_connection_id_)), std::move(request.invoice_), @@ -7582,6 +7713,13 @@ void Requests::on_request(uint64 id, td_api::applyPremiumGiftCode &request) { apply_premium_gift_code(td_, request.code_, std::move(promise)); } +void Requests::on_request(uint64 id, td_api::giftPremiumWithStars &request) { + CHECK_IS_BOT(); + CREATE_OK_REQUEST_PROMISE(); + gift_premium_with_stars(td_, UserId(request.user_id_), request.star_count_, request.month_count_, + std::move(request.text_), std::move(promise)); +} + void Requests::on_request(uint64 id, td_api::launchPrepaidGiveaway &request) { CHECK_IS_USER(); CREATE_OK_REQUEST_PROMISE(); diff --git a/third-party/td/td/td/telegram/Requests.h b/third-party/td/td/td/telegram/Requests.h index d1f1ca767c..e0c5f5b932 100644 --- a/third-party/td/td/td/telegram/Requests.h +++ b/third-party/td/td/td/telegram/Requests.h @@ -275,6 +275,14 @@ class Requests { void on_request(uint64 id, const td_api::reportChatSponsoredMessage &request); + void on_request(uint64 id, td_api::getSearchSponsoredChats &request); + + void on_request(uint64 id, const td_api::viewSponsoredChat &request); + + void on_request(uint64 id, const td_api::openSponsoredChat &request); + + void on_request(uint64 id, const td_api::reportSponsoredChat &request); + void on_request(uint64 id, const td_api::getMessageLink &request); void on_request(uint64 id, const td_api::getMessageEmbeddingCode &request); @@ -567,6 +575,28 @@ class Requests { void on_request(uint64 id, td_api::setBusinessMessageIsPinned &request); + void on_request(uint64 id, td_api::readBusinessMessage &request); + + void on_request(uint64 id, td_api::deleteBusinessMessages &request); + + void on_request(uint64 id, td_api::editBusinessStory &request); + + void on_request(uint64 id, td_api::deleteBusinessStory &request); + + void on_request(uint64 id, td_api::setBusinessAccountName &request); + + void on_request(uint64 id, td_api::setBusinessAccountBio &request); + + void on_request(uint64 id, td_api::setBusinessAccountProfilePhoto &request); + + void on_request(uint64 id, td_api::setBusinessAccountUsername &request); + + void on_request(uint64 id, td_api::setBusinessAccountGiftSettings &request); + + void on_request(uint64 id, td_api::getBusinessAccountStarAmount &request); + + void on_request(uint64 id, td_api::transferBusinessAccountStars &request); + void on_request(uint64 id, const td_api::loadQuickReplyShortcuts &request); void on_request(uint64 id, const td_api::setQuickReplyShortcutName &request); @@ -1485,11 +1515,13 @@ class Requests { void on_request(uint64 id, const td_api::deleteSavedCredentials &request); + void on_request(uint64 id, td_api::setGiftSettings &request); + void on_request(uint64 id, const td_api::getAvailableGifts &request); void on_request(uint64 id, td_api::sendGift &request); - void on_request(uint64 id, const td_api::sellGift &request); + void on_request(uint64 id, td_api::sellGift &request); void on_request(uint64 id, const td_api::toggleGiftIsSaved &request); @@ -1499,9 +1531,9 @@ class Requests { void on_request(uint64 id, const td_api::getGiftUpgradePreview &request); - void on_request(uint64 id, const td_api::upgradeGift &request); + void on_request(uint64 id, td_api::upgradeGift &request); - void on_request(uint64 id, const td_api::transferGift &request); + void on_request(uint64 id, td_api::transferGift &request); void on_request(uint64 id, td_api::getReceivedGifts &request); @@ -1591,6 +1623,8 @@ class Requests { void on_request(uint64 id, td_api::applyPremiumGiftCode &request); + void on_request(uint64 id, td_api::giftPremiumWithStars &request); + void on_request(uint64 id, td_api::launchPrepaidGiveaway &request); void on_request(uint64 id, const td_api::getGiveawayInfo &request); diff --git a/third-party/td/td/td/telegram/SecretInputMedia.cpp b/third-party/td/td/td/telegram/SecretInputMedia.cpp index 9c7029b8ab..20761029d5 100644 --- a/third-party/td/td/td/telegram/SecretInputMedia.cpp +++ b/third-party/td/td/td/telegram/SecretInputMedia.cpp @@ -16,7 +16,7 @@ namespace td { SecretInputMedia::SecretInputMedia(telegram_api::object_ptr input_file, BufferSlice &&thumbnail, Dimensions thumbnail_dimensions, const string &mime_type, const FileView &file_view, - vector> &&attributes, + vector> &&attributes, const string &caption, int32 layer) : input_file_(std::move(input_file)) { auto &encryption_key = file_view.encryption_key(); diff --git a/third-party/td/td/td/telegram/SecretInputMedia.h b/third-party/td/td/td/telegram/SecretInputMedia.h index 7c07a0da0b..e8ab8442cc 100644 --- a/third-party/td/td/td/telegram/SecretInputMedia.h +++ b/third-party/td/td/td/telegram/SecretInputMedia.h @@ -19,18 +19,18 @@ class FileView; struct SecretInputMedia { telegram_api::object_ptr input_file_; - tl_object_ptr decrypted_media_; + secret_api::object_ptr decrypted_media_; SecretInputMedia() = default; SecretInputMedia(telegram_api::object_ptr input_file, - tl_object_ptr decrypted_media) + secret_api::object_ptr decrypted_media) : input_file_(std::move(input_file)), decrypted_media_(std::move(decrypted_media)) { } SecretInputMedia(telegram_api::object_ptr input_file, BufferSlice &&thumbnail, Dimensions thumbnail_dimensions, const string &mime_type, const FileView &file_view, - vector> &&attributes, const string &caption, + vector> &&attributes, const string &caption, int32 layer); bool empty() const { diff --git a/third-party/td/td/td/telegram/SendCodeHelper.cpp b/third-party/td/td/td/telegram/SendCodeHelper.cpp index 52872ef6c7..a12ee73faa 100644 --- a/third-party/td/td/td/telegram/SendCodeHelper.cpp +++ b/third-party/td/td/td/telegram/SendCodeHelper.cpp @@ -65,25 +65,21 @@ telegram_api::object_ptr SendCodeHelper::get_input_c int32 flags = 0; vector logout_tokens; string device_token; + bool allow_flashcall = false; + bool current_number = false; + bool allow_app_hash = false; + bool allow_missed_call = false; + bool allow_firebase = false; + bool unknown_number = false; bool is_app_sandbox = false; if (settings != nullptr) { - if (settings->allow_flash_call_) { - flags |= telegram_api::codeSettings::ALLOW_FLASHCALL_MASK; - } - if (settings->allow_missed_call_) { - flags |= telegram_api::codeSettings::ALLOW_MISSED_CALL_MASK; - } - if (settings->is_current_phone_number_) { - flags |= telegram_api::codeSettings::CURRENT_NUMBER_MASK; - } - if (settings->has_unknown_phone_number_) { - flags |= telegram_api::codeSettings::UNKNOWN_NUMBER_MASK; - } - if (settings->allow_sms_retriever_api_) { - flags |= telegram_api::codeSettings::ALLOW_APP_HASH_MASK; - } + allow_flashcall = settings->allow_flash_call_; + current_number = settings->is_current_phone_number_; + allow_app_hash = settings->allow_sms_retriever_api_; + allow_missed_call = settings->allow_missed_call_; + unknown_number = settings->has_unknown_phone_number_; if (settings->firebase_authentication_settings_ != nullptr) { - flags |= telegram_api::codeSettings::ALLOW_FIREBASE_MASK; + allow_firebase = true; if (settings->firebase_authentication_settings_->get_id() == td_api::firebaseAuthenticationSettingsIos::ID) { flags |= telegram_api::codeSettings::TOKEN_MASK; auto ios_settings = static_cast( @@ -106,9 +102,9 @@ telegram_api::object_ptr SendCodeHelper::get_input_c flags |= telegram_api::codeSettings::LOGOUT_TOKENS_MASK; } } - return telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, std::move(logout_tokens), device_token, is_app_sandbox); + return telegram_api::make_object(flags, allow_flashcall, current_number, allow_app_hash, + allow_missed_call, allow_firebase, unknown_number, + std::move(logout_tokens), device_token, is_app_sandbox); } telegram_api::auth_sendCode SendCodeHelper::send_code(string phone_number, const Settings &settings, int32 api_id, diff --git a/third-party/td/td/td/telegram/SponsoredMessageManager.cpp b/third-party/td/td/td/telegram/SponsoredMessageManager.cpp index a4b6b82965..9010e2600e 100644 --- a/third-party/td/td/td/telegram/SponsoredMessageManager.cpp +++ b/third-party/td/td/td/telegram/SponsoredMessageManager.cpp @@ -68,17 +68,9 @@ class GetSponsoredMessagesQuery final : public Td::ResultHandler { }; class ViewSponsoredMessageQuery final : public Td::ResultHandler { - DialogId dialog_id_; - public: - void send(DialogId dialog_id, const string &message_id) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); - if (input_peer == nullptr) { - return; - } - send_query(G()->net_query_creator().create( - telegram_api::messages_viewSponsoredMessage(std::move(input_peer), BufferSlice(message_id)))); + void send(const string &message_id) { + send_query(G()->net_query_creator().create(telegram_api::messages_viewSponsoredMessage(BufferSlice(message_id)))); } void on_result(BufferSlice packet) final { @@ -89,24 +81,17 @@ class ViewSponsoredMessageQuery final : public Td::ResultHandler { } void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ViewSponsoredMessageQuery"); } }; class ClickSponsoredMessageQuery final : public Td::ResultHandler { Promise promise_; - DialogId dialog_id_; public: explicit ClickSponsoredMessageQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, const string &message_id, bool is_media_click, bool from_fullscreen) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); - if (input_peer == nullptr) { - return promise_.set_value(Unit()); - } + void send(const string &message_id, bool is_media_click, bool from_fullscreen) { int32 flags = 0; if (is_media_click) { flags |= telegram_api::messages_clickSponsoredMessage::MEDIA_MASK; @@ -115,7 +100,7 @@ class ClickSponsoredMessageQuery final : public Td::ResultHandler { flags |= telegram_api::messages_clickSponsoredMessage::FULLSCREEN_MASK; } send_query(G()->net_query_creator().create(telegram_api::messages_clickSponsoredMessage( - flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), BufferSlice(message_id)))); + flags, false /*ignored*/, false /*ignored*/, BufferSlice(message_id)))); } void on_result(BufferSlice packet) final { @@ -127,28 +112,21 @@ class ClickSponsoredMessageQuery final : public Td::ResultHandler { } void on_error(Status status) final { - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ClickSponsoredMessageQuery"); promise_.set_error(std::move(status)); } }; class ReportSponsoredMessageQuery final : public Td::ResultHandler { - Promise> promise_; - DialogId dialog_id_; + Promise> promise_; public: - explicit ReportSponsoredMessageQuery(Promise> &&promise) + explicit ReportSponsoredMessageQuery(Promise> &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, const string &message_id, const string &option_id) { - dialog_id_ = dialog_id; - auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); - if (input_peer == nullptr) { - return promise_.set_value(td_api::make_object()); - } - send_query(G()->net_query_creator().create(telegram_api::messages_reportSponsoredMessage( - std::move(input_peer), BufferSlice(message_id), BufferSlice(option_id)))); + void send(const string &message_id, const string &option_id) { + send_query(G()->net_query_creator().create( + telegram_api::messages_reportSponsoredMessage(BufferSlice(message_id), BufferSlice(option_id)))); } void on_result(BufferSlice packet) final { @@ -161,21 +139,21 @@ class ReportSponsoredMessageQuery final : public Td::ResultHandler { LOG(DEBUG) << "Receive result for ReportSponsoredMessageQuery: " << to_string(ptr); switch (ptr->get_id()) { case telegram_api::channels_sponsoredMessageReportResultReported::ID: - return promise_.set_value(td_api::make_object()); + return promise_.set_value(td_api::make_object()); case telegram_api::channels_sponsoredMessageReportResultAdsHidden::ID: - return promise_.set_value(td_api::make_object()); + return promise_.set_value(td_api::make_object()); case telegram_api::channels_sponsoredMessageReportResultChooseOption::ID: { auto options = telegram_api::move_object_as(ptr); if (options->options_.empty()) { - return promise_.set_value(td_api::make_object()); + return promise_.set_value(td_api::make_object()); } vector> report_options; for (auto &option : options->options_) { report_options.push_back( td_api::make_object(option->option_.as_slice().str(), option->text_)); } - return promise_.set_value(td_api::make_object( + return promise_.set_value(td_api::make_object( options->title_, std::move(report_options))); } default: @@ -185,16 +163,49 @@ class ReportSponsoredMessageQuery final : public Td::ResultHandler { void on_error(Status status) final { if (status.message() == "AD_EXPIRED") { - return promise_.set_value(td_api::make_object()); + return promise_.set_value(td_api::make_object()); } if (status.message() == "PREMIUM_ACCOUNT_REQUIRED") { - return promise_.set_value(td_api::make_object()); + return promise_.set_value(td_api::make_object()); } - td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "ReportSponsoredMessageQuery"); promise_.set_error(std::move(status)); } }; +class GetSponsoredPeersQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetSponsoredPeersQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(const string &query) { + send_query(G()->net_query_creator().create(telegram_api::contacts_getSponsoredPeers(query))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + LOG(DEBUG) << "Receive result for GetSponsoredPeersQuery: " << to_string(ptr); + promise_.set_value(std::move(ptr)); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + +struct SponsoredMessageManager::SponsoredContentInfo { + string random_id_; + bool is_viewed_ = false; + bool is_clicked_ = false; +}; + struct SponsoredMessageManager::SponsoredMessage { int64 local_id = 0; bool is_recommended = false; @@ -225,24 +236,43 @@ struct SponsoredMessageManager::SponsoredMessage { } }; -struct SponsoredMessageManager::SponsoredMessageInfo { - string random_id_; - bool is_viewed_ = false; - bool is_clicked_ = false; -}; - struct SponsoredMessageManager::DialogSponsoredMessages { vector>> promises; vector messages; - FlatHashMap message_infos; + FlatHashMap message_infos; int32 messages_between = 0; bool is_premium = false; bool sponsored_enabled = false; }; +struct SponsoredMessageManager::SponsoredDialog { + int64 local_id = 0; + DialogId dialog_id; + string sponsor_info; + string additional_info; + + SponsoredDialog(int64 local_id, DialogId dialog_id, string &&sponsor_info, string &&additional_info) + : local_id(local_id) + , dialog_id(dialog_id) + , sponsor_info(std::move(sponsor_info)) + , additional_info(std::move(additional_info)) { + } +}; + +struct SponsoredMessageManager::SponsoredDialogs { + int64 local_id = 0; + vector>> promises; + vector dialogs; + bool is_premium = false; + bool sponsored_enabled = false; +}; + SponsoredMessageManager::SponsoredMessageManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { delete_cached_sponsored_messages_timeout_.set_callback(on_delete_cached_sponsored_messages_timeout_callback); delete_cached_sponsored_messages_timeout_.set_callback_data(static_cast(this)); + + delete_cached_sponsored_dialogs_timeout_.set_callback(on_delete_cached_sponsored_dialogs_timeout_callback); + delete_cached_sponsored_dialogs_timeout_.set_callback_data(static_cast(this)); } SponsoredMessageManager::~SponsoredMessageManager() = default; @@ -273,6 +303,38 @@ void SponsoredMessageManager::delete_cached_sponsored_messages(DialogId dialog_i } } +void SponsoredMessageManager::on_delete_cached_sponsored_dialogs_timeout_callback(void *sponsored_message_manager_ptr, + int64 local_id) { + if (G()->close_flag()) { + return; + } + + auto sponsored_message_manager = static_cast(sponsored_message_manager_ptr); + send_closure_later(sponsored_message_manager->actor_id(sponsored_message_manager), + &SponsoredMessageManager::delete_cached_sponsored_dialogs, local_id); +} + +void SponsoredMessageManager::delete_cached_sponsored_dialogs(int64 local_id) { + if (G()->close_flag()) { + return; + } + + auto query_it = local_id_to_search_query_.find(local_id); + if (query_it == local_id_to_search_query_.end()) { + return; + } + + auto it = search_sponsored_dialogs_.find(query_it->second); + CHECK(it != search_sponsored_dialogs_.end()); + if (it->second->promises.empty()) { + for (auto &dialog : it->second->dialogs) { + dialog_infos_.erase(dialog.local_id); + } + search_sponsored_dialogs_.erase(it); + local_id_to_search_query_.erase(query_it); + } +} + td_api::object_ptr SponsoredMessageManager::get_message_sponsor_object( const SponsoredMessage &sponsored_message) const { return td_api::make_object( @@ -289,7 +351,7 @@ td_api::object_ptr SponsoredMessageManager::get_sponso return td_api::make_object( sponsored_message.local_id, sponsored_message.is_recommended, sponsored_message.can_be_reported, get_message_content_object(sponsored_message.content.get(), td_, dialog_id, MessageId(ServerMessageId(1)), false, - 0, false, true, -1, false, true), + dialog_id, 0, false, true, -1, false, true), std::move(sponsor), sponsored_message.title, sponsored_message.button_text, td_->theme_manager_->get_accent_color_id_object(sponsored_message.peer_color.accent_color_id_, AccentColorId()), sponsored_message.peer_color.background_custom_emoji_id_.get(), sponsored_message.additional_info); @@ -304,6 +366,19 @@ td_api::object_ptr SponsoredMessageManager::get_spons return td_api::make_object(std::move(messages), sponsored_messages.messages_between); } +td_api::object_ptr SponsoredMessageManager::get_sponsored_chat_object( + const SponsoredDialog &sponsored_dialog) const { + return td_api::make_object( + sponsored_dialog.local_id, td_->dialog_manager_->get_chat_id_object(sponsored_dialog.dialog_id, "sponsoredChat"), + sponsored_dialog.sponsor_info, sponsored_dialog.additional_info); +} + +td_api::object_ptr SponsoredMessageManager::get_sponsored_chats_object( + const SponsoredDialogs &sponsored_dialogs) const { + return td_api::make_object(transform( + sponsored_dialogs.dialogs, [this](const SponsoredDialog &dialog) { return get_sponsored_chat_object(dialog); })); +} + void SponsoredMessageManager::get_dialog_sponsored_messages( DialogId dialog_id, Promise> &&promise) { TRY_STATUS_PROMISE(promise, td_->dialog_manager_->check_dialog_access(dialog_id, false, AccessRights::Read, @@ -329,7 +404,7 @@ void SponsoredMessageManager::get_dialog_sponsored_messages( if (messages->promises.size() == 1) { auto query_promise = PromiseCreator::lambda( [actor_id = actor_id(this), - dialog_id](Result> &&result) mutable { + dialog_id](Result> &&result) { send_closure(actor_id, &SponsoredMessageManager::on_get_dialog_sponsored_messages, dialog_id, std::move(result)); }); @@ -401,7 +476,7 @@ void SponsoredMessageManager::on_get_dialog_sponsored_messages( auto local_id = current_sponsored_message_id_.get(); CHECK(!current_sponsored_message_id_.is_valid()); CHECK(!current_sponsored_message_id_.is_scheduled()); - SponsoredMessageInfo message_info; + SponsoredContentInfo message_info; message_info.random_id_ = sponsored_message->random_id_.as_slice().str(); auto is_inserted = messages->message_infos.emplace(local_id, std::move(message_info)).second; CHECK(is_inserted); @@ -439,7 +514,7 @@ void SponsoredMessageManager::view_sponsored_message(DialogId dialog_id, Message } random_id_it->second.is_viewed_ = true; - td_->create_handler()->send(dialog_id, random_id_it->second.random_id_); + td_->create_handler()->send(random_id_it->second.random_id_); } void SponsoredMessageManager::click_sponsored_message(DialogId dialog_id, MessageId sponsored_message_id, @@ -459,26 +534,157 @@ void SponsoredMessageManager::click_sponsored_message(DialogId dialog_id, Messag random_id_it->second.is_clicked_ = true; td_->create_handler(std::move(promise)) - ->send(dialog_id, random_id_it->second.random_id_, is_media_click, from_fullscreen); + ->send(random_id_it->second.random_id_, is_media_click, from_fullscreen); } void SponsoredMessageManager::report_sponsored_message( DialogId dialog_id, MessageId sponsored_message_id, const string &option_id, - Promise> &&promise) { + Promise> &&promise) { if (!dialog_id.is_valid() || !sponsored_message_id.is_valid_sponsored()) { return promise.set_error(Status::Error(400, "Invalid message specified")); } auto it = dialog_sponsored_messages_.find(dialog_id); if (it == dialog_sponsored_messages_.end()) { - return promise.set_value(td_api::make_object()); + return promise.set_value(td_api::make_object()); } auto random_id_it = it->second->message_infos.find(sponsored_message_id.get()); if (random_id_it == it->second->message_infos.end()) { - return promise.set_value(td_api::make_object()); + return promise.set_value(td_api::make_object()); } td_->create_handler(std::move(promise)) - ->send(dialog_id, random_id_it->second.random_id_, option_id); + ->send(random_id_it->second.random_id_, option_id); +} + +void SponsoredMessageManager::get_search_sponsored_dialogs( + const string &query, Promise> &&promise) { + if (query.size() < 4u) { + return promise.set_value(td_api::make_object()); + } + auto &dialogs = search_sponsored_dialogs_[query]; + if (dialogs != nullptr && dialogs->promises.empty()) { + if (dialogs->is_premium == td_->option_manager_->get_option_boolean("is_premium", false) && + dialogs->sponsored_enabled == td_->user_manager_->get_my_sponsored_enabled()) { + // use cached value + return promise.set_value(get_sponsored_chats_object(*dialogs)); + } else { + // drop cache + delete_cached_sponsored_dialogs_timeout_.cancel_timeout(dialogs->local_id); + local_id_to_search_query_.erase(dialogs->local_id); + for (auto &dialog : dialogs->dialogs) { + dialog_infos_.erase(dialog.local_id); + } + dialogs = nullptr; + } + } + + if (dialogs == nullptr) { + dialogs = make_unique(); + dialogs->local_id = ++current_local_id_; + local_id_to_search_query_[dialogs->local_id] = query; + } + dialogs->promises.push_back(std::move(promise)); + if (dialogs->promises.size() == 1) { + auto query_promise = + PromiseCreator::lambda([actor_id = actor_id(this), query]( + Result> &&result) { + send_closure(actor_id, &SponsoredMessageManager::on_get_search_sponsored_dialogs, query, std::move(result)); + }); + td_->create_handler(std::move(query_promise))->send(query); + } +} + +void SponsoredMessageManager::on_get_search_sponsored_dialogs( + const string &query, Result> &&result) { + G()->ignore_result_if_closing(result); + + auto &dialogs = search_sponsored_dialogs_[query]; + CHECK(dialogs != nullptr); + auto promises = std::move(dialogs->promises); + reset_to_empty(dialogs->promises); + CHECK(dialogs->dialogs.empty()); + + auto local_id = dialogs->local_id; + if (result.is_error()) { + search_sponsored_dialogs_.erase(query); + local_id_to_search_query_.erase(local_id); + fail_promises(promises, result.move_as_error()); + return; + } + + auto sponsored_dialogs_ptr = result.move_as_ok(); + switch (sponsored_dialogs_ptr->get_id()) { + case telegram_api::contacts_sponsoredPeers::ID: { + auto sponsored_dialogs = + telegram_api::move_object_as(sponsored_dialogs_ptr); + + td_->user_manager_->on_get_users(std::move(sponsored_dialogs->users_), "on_get_search_sponsored_dialogs"); + td_->chat_manager_->on_get_chats(std::move(sponsored_dialogs->chats_), "on_get_search_sponsored_dialogs"); + + for (auto &sponsored_dialog : sponsored_dialogs->peers_) { + auto dialog_id = DialogId(sponsored_dialog->peer_); + if (!dialog_id.is_valid() || !td_->dialog_manager_->have_dialog_info(dialog_id)) { + LOG(ERROR) << "Receive unknown " << dialog_id; + continue; + } + td_->dialog_manager_->force_create_dialog(dialog_id, "on_get_search_sponsored_dialogs"); + + auto dialog_local_id = ++current_local_id_; + + auto dialog_info = make_unique(); + dialog_info->random_id_ = sponsored_dialog->random_id_.as_slice().str(); + auto is_inserted = dialog_infos_.emplace(dialog_local_id, std::move(dialog_info)).second; + CHECK(is_inserted); + + dialogs->dialogs.emplace_back(dialog_local_id, dialog_id, std::move(sponsored_dialog->sponsor_info_), + std::move(sponsored_dialog->additional_info_)); + } + break; + } + case telegram_api::contacts_sponsoredPeersEmpty::ID: + break; + default: + UNREACHABLE(); + } + dialogs->is_premium = td_->option_manager_->get_option_boolean("is_premium", false); + dialogs->sponsored_enabled = td_->user_manager_->get_my_sponsored_enabled(); + + for (auto &promise : promises) { + promise.set_value(get_sponsored_chats_object(*dialogs)); + } + delete_cached_sponsored_dialogs_timeout_.set_timeout_in(local_id, 300.0); +} + +void SponsoredMessageManager::view_sponsored_dialog(int64 local_id, Promise &&promise) { + promise.set_value(Unit()); + + auto it = dialog_infos_.find(local_id); + if (it == dialog_infos_.end() || it->second->is_viewed_) { + return; + } + + it->second->is_viewed_ = true; + td_->create_handler()->send(it->second->random_id_); +} + +void SponsoredMessageManager::open_sponsored_dialog(int64 local_id, Promise &&promise) { + auto it = dialog_infos_.find(local_id); + if (it == dialog_infos_.end() || it->second->is_clicked_) { + return promise.set_value(Unit()); + } + + it->second->is_clicked_ = true; + td_->create_handler(std::move(promise))->send(it->second->random_id_, false, false); +} + +void SponsoredMessageManager::report_sponsored_dialog( + int64 local_id, const string &option_id, Promise> &&promise) { + auto it = dialog_infos_.find(local_id); + if (it == dialog_infos_.end()) { + return promise.set_value(td_api::make_object()); + } + + td_->create_handler(std::move(promise))->send(it->second->random_id_, option_id); } } // namespace td diff --git a/third-party/td/td/td/telegram/SponsoredMessageManager.h b/third-party/td/td/td/telegram/SponsoredMessageManager.h index 37c69a8c5e..cfee977aad 100644 --- a/third-party/td/td/td/telegram/SponsoredMessageManager.h +++ b/third-party/td/td/td/telegram/SponsoredMessageManager.h @@ -41,20 +41,35 @@ class SponsoredMessageManager final : public Actor { bool from_fullscreen, Promise &&promise); void report_sponsored_message(DialogId dialog_id, MessageId sponsored_message_id, const string &option_id, - Promise> &&promise); + Promise> &&promise); + + void get_search_sponsored_dialogs(const string &query, Promise> &&promise); + + void view_sponsored_dialog(int64 local_id, Promise &&promise); + + void open_sponsored_dialog(int64 local_id, Promise &&promise); + + void report_sponsored_dialog(int64 local_id, const string &option_id, + Promise> &&promise); private: + struct SponsoredContentInfo; struct SponsoredMessage; - struct SponsoredMessageInfo; struct DialogSponsoredMessages; + struct SponsoredDialog; + struct SponsoredDialogs; void tear_down() final; static void on_delete_cached_sponsored_messages_timeout_callback(void *sponsored_message_manager_ptr, int64 dialog_id_int); + static void on_delete_cached_sponsored_dialogs_timeout_callback(void *sponsored_message_manager_ptr, int64 local_id); + void delete_cached_sponsored_messages(DialogId dialog_id); + void delete_cached_sponsored_dialogs(int64 local_id); + td_api::object_ptr get_message_sponsor_object( const SponsoredMessage &sponsored_message) const; @@ -64,15 +79,31 @@ class SponsoredMessageManager final : public Actor { td_api::object_ptr get_sponsored_messages_object( DialogId dialog_id, const DialogSponsoredMessages &sponsored_messages) const; + td_api::object_ptr get_sponsored_chat_object(const SponsoredDialog &sponsored_dialog) const; + + td_api::object_ptr get_sponsored_chats_object( + const SponsoredDialogs &sponsored_dialogs) const; + void on_get_dialog_sponsored_messages( DialogId dialog_id, Result> &&result); + void on_get_search_sponsored_dialogs( + const string &query, Result> &&result); + FlatHashMap, DialogIdHash> dialog_sponsored_messages_; + FlatHashMap> search_sponsored_dialogs_; + FlatHashMap local_id_to_search_query_; + FlatHashMap> dialog_infos_; + MessageId current_sponsored_message_id_ = MessageId::max(); + int64 current_local_id_ = 0; + MultiTimeout delete_cached_sponsored_messages_timeout_{"DeleteCachedSponsoredMessagesTimeout"}; + MultiTimeout delete_cached_sponsored_dialogs_timeout_{"DeleteCachedSponsoredDialogsTimeout"}; + Td *td_; ActorShared<> parent_; }; diff --git a/third-party/td/td/td/telegram/StarGiftManager.cpp b/third-party/td/td/td/telegram/StarGiftManager.cpp index cc86d526a7..ccead5c564 100644 --- a/third-party/td/td/td/telegram/StarGiftManager.cpp +++ b/third-party/td/td/td/telegram/StarGiftManager.cpp @@ -8,6 +8,7 @@ #include "td/telegram/AccessRights.h" #include "td/telegram/AuthManager.h" +#include "td/telegram/BusinessConnectionManager.h" #include "td/telegram/ChatManager.h" #include "td/telegram/DialogId.h" #include "td/telegram/DialogManager.h" @@ -185,11 +186,13 @@ class ConvertStarGiftQuery final : public Td::ResultHandler { explicit ConvertStarGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(StarGiftId star_gift_id) { - dialog_id_ = star_gift_id.get_dialog_id(td_); + void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, DialogId dialog_id) { + dialog_id_ = dialog_id; auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); - send_query(G()->net_query_creator().create(telegram_api::payments_convertStarGift(std::move(input_gift)))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), telegram_api::payments_convertStarGift(std::move(input_gift)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -389,6 +392,14 @@ class GetUpgradeGiftPreviewQuery final : public Td::ResultHandler { static Promise get_gift_upgrade_promise(Td *td, const telegram_api::object_ptr &updates, Promise> &&promise) { + if (td->auth_manager_->is_bot()) { + return PromiseCreator::lambda([promise = std::move(promise)](Result result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + promise.set_value(td_api::make_object()); + }); + } vector> new_messages = UpdatesManager::get_new_messages(updates.get()); if (new_messages.size() != 1u || new_messages[0].second || new_messages[0].first->get_id() != telegram_api::messageService::ID) { @@ -423,15 +434,17 @@ class UpgradeStarGiftQuery final : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(StarGiftId star_gift_id, int64 star_count, bool keep_original_details) { + void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, bool keep_original_details) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); int32 flags = 0; if (keep_original_details) { flags |= telegram_api::payments_upgradeStarGift::KEEP_ORIGINAL_DETAILS_MASK; } - send_query(G()->net_query_creator().create( - telegram_api::payments_upgradeStarGift(flags, false /*ignored*/, std::move(input_gift)))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_upgradeStarGift(flags, false /*ignored*/, std::move(input_gift)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -461,11 +474,14 @@ class UpgradeGiftQuery final : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, + void send(BusinessConnectionId business_connection_id, + telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; - send_query(G()->net_query_creator().create( - telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -505,6 +521,7 @@ class UpgradeGiftQuery final : public Td::ResultHandler { class GetGiftUpgradePaymentFormQuery final : public Td::ResultHandler { Promise> promise_; + BusinessConnectionId business_connection_id_; int64 star_count_; telegram_api::object_ptr upgrade_input_invoice_; @@ -513,14 +530,18 @@ class GetGiftUpgradePaymentFormQuery final : public Td::ResultHandler { : promise_(std::move(promise)) { } - void send(telegram_api::object_ptr input_invoice, + void send(BusinessConnectionId business_connection_id, + telegram_api::object_ptr input_invoice, telegram_api::object_ptr upgrade_input_invoice, int64 star_count) { + business_connection_id_ = business_connection_id; upgrade_input_invoice_ = std::move(upgrade_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); - send_query( - G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -540,8 +561,13 @@ class GetGiftUpgradePaymentFormQuery final : public Td::ResultHandler { break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); + if (payment_form->invoice_->prices_.size() != 1u || + payment_form->invoice_->prices_[0]->amount_ != star_count_) { + td_->star_manager_->add_pending_owned_star_count(star_count_, false); + return promise_.set_error(Status::Error(400, "Wrong upgrade price specified")); + } td_->create_handler(std::move(promise_)) - ->send(std::move(upgrade_input_invoice_), payment_form->form_id_, star_count_); + ->send(business_connection_id_, std::move(upgrade_input_invoice_), payment_form->form_id_, star_count_); break; } default: @@ -562,11 +588,14 @@ class TransferStarGiftQuery final : public Td::ResultHandler { explicit TransferStarGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(StarGiftId star_gift_id, telegram_api::object_ptr receiver_input_peer) { + void send(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, + telegram_api::object_ptr receiver_input_peer) { auto input_gift = star_gift_id.get_input_saved_star_gift(td_); CHECK(input_gift != nullptr); - send_query(G()->net_query_creator().create( - telegram_api::payments_transferStarGift(std::move(input_gift), std::move(receiver_input_peer)))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_transferStarGift(std::move(input_gift), std::move(receiver_input_peer)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -594,11 +623,14 @@ class TransferGiftQuery final : public Td::ResultHandler { explicit TransferGiftQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(telegram_api::object_ptr input_invoice, int64 payment_form_id, + void send(BusinessConnectionId business_connection_id, + telegram_api::object_ptr input_invoice, int64 payment_form_id, int64 star_count) { star_count_ = star_count; - send_query(G()->net_query_creator().create( - telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_sendStarsForm(payment_form_id, std::move(input_invoice)), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -637,6 +669,7 @@ class TransferGiftQuery final : public Td::ResultHandler { class GetGiftTransferPaymentFormQuery final : public Td::ResultHandler { Promise promise_; + BusinessConnectionId business_connection_id_; int64 star_count_; telegram_api::object_ptr transfer_input_invoice_; @@ -644,14 +677,18 @@ class GetGiftTransferPaymentFormQuery final : public Td::ResultHandler { explicit GetGiftTransferPaymentFormQuery(Promise &&promise) : promise_(std::move(promise)) { } - void send(telegram_api::object_ptr input_invoice, + void send(BusinessConnectionId business_connection_id, + telegram_api::object_ptr input_invoice, telegram_api::object_ptr transfer_input_invoice, int64 star_count) { + business_connection_id_ = business_connection_id; transfer_input_invoice_ = std::move(transfer_input_invoice); star_count_ = star_count; td_->star_manager_->add_pending_owned_star_count(-star_count, false); - send_query( - G()->net_query_creator().create(telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr))); + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::payments_getPaymentForm(0, std::move(input_invoice), nullptr), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id))); } void on_result(BufferSlice packet) final { @@ -671,8 +708,13 @@ class GetGiftTransferPaymentFormQuery final : public Td::ResultHandler { break; case telegram_api::payments_paymentFormStarGift::ID: { auto payment_form = static_cast(payment_form_ptr.get()); + if (payment_form->invoice_->prices_.size() != 1u || + payment_form->invoice_->prices_[0]->amount_ != star_count_) { + td_->star_manager_->add_pending_owned_star_count(star_count_, false); + return promise_.set_error(Status::Error(400, "Wrong transfer price specified")); + } td_->create_handler(std::move(promise_)) - ->send(std::move(transfer_input_invoice_), payment_form->form_id_, star_count_); + ->send(business_connection_id_, std::move(transfer_input_invoice_), payment_form->form_id_, star_count_); break; } default: @@ -689,15 +731,21 @@ class GetGiftTransferPaymentFormQuery final : public Td::ResultHandler { class GetSavedStarGiftsQuery final : public Td::ResultHandler { Promise> promise_; DialogId dialog_id_; + BusinessConnectionId business_connection_id_; public: explicit GetSavedStarGiftsQuery(Promise> &&promise) : promise_(std::move(promise)) { } - void send(DialogId dialog_id, bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, bool exclude_limited, - bool exclude_unique, bool sort_by_value, const string &offset, int32 limit) { - dialog_id_ = dialog_id; + void send(BusinessConnectionId business_connection_id, DialogId dialog_id, bool exclude_unsaved, bool exclude_saved, + bool exclude_unlimited, bool exclude_limited, bool exclude_unique, bool sort_by_value, const string &offset, + int32 limit) { + business_connection_id_ = business_connection_id; + dialog_id_ = + business_connection_id.is_valid() + ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) + : dialog_id; auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); if (input_peer == nullptr) { return on_error(Status::Error(400, "Can't access the chat")); @@ -721,11 +769,12 @@ class GetSavedStarGiftsQuery final : public Td::ResultHandler { if (sort_by_value) { flags |= telegram_api::payments_getSavedStarGifts::SORT_BY_VALUE_MASK; } - send_query(G()->net_query_creator().create( + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), telegram_api::payments_getSavedStarGifts(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_peer), offset, limit), - {{dialog_id_}})); + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id), {{dialog_id_}})); } void on_result(BufferSlice packet) final { @@ -960,17 +1009,10 @@ void StarGiftManager::send_gift(int64 gift_id, DialogId dialog_id, td_api::objec get_formatted_text(td_, td_->dialog_manager_->get_my_dialog_id(), std::move(text), false, true, true, false)); MessageQuote::remove_unallowed_quote_entities(message); - int32 flags = 0; - if (is_private) { - flags |= telegram_api::inputInvoiceStarGift::HIDE_NAME_MASK; - } - if (pay_for_upgrade) { - flags |= telegram_api::inputInvoiceStarGift::INCLUDE_UPGRADE_MASK; - } auto input_invoice = telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), gift_id, nullptr); + 0, is_private, pay_for_upgrade, std::move(input_peer), gift_id, nullptr); auto send_input_invoice = telegram_api::make_object( - flags, false /*ignored*/, false /*ignored*/, std::move(send_input_peer), gift_id, nullptr); + 0, is_private, pay_for_upgrade, std::move(send_input_peer), gift_id, nullptr); if (!message.text.empty()) { input_invoice->flags_ |= telegram_api::inputInvoiceStarGift::MESSAGE_MASK; input_invoice->message_ = get_input_text_with_entities(td_->user_manager_.get(), message, "send_gift"); @@ -982,18 +1024,28 @@ void StarGiftManager::send_gift(int64 gift_id, DialogId dialog_id, td_api::objec ->send(std::move(input_invoice), std::move(send_input_invoice), star_count); } -void StarGiftManager::convert_gift(StarGiftId star_gift_id, Promise &&promise) { +void StarGiftManager::convert_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, + Promise &&promise) { + if (business_connection_id.is_valid()) { + TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); + } if (star_gift_id.get_input_saved_star_gift(td_) == nullptr) { return promise.set_error(Status::Error(400, "Invalid gift identifier specified")); } - auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id = star_gift_id.get_dialog_id(td_), - promise = std::move(promise)](Result &&result) mutable { - if (result.is_error()) { - return promise.set_error(result.move_as_error()); - } - send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, DialogId(), std::move(promise)); - }); - td_->create_handler(std::move(query_promise))->send(star_gift_id); + auto dialog_id = + business_connection_id.is_valid() + ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) + : star_gift_id.get_dialog_id(td_); + + auto query_promise = PromiseCreator::lambda( + [actor_id = actor_id(this), dialog_id, promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, DialogId(), std::move(promise)); + }); + td_->create_handler(std::move(query_promise)) + ->send(business_connection_id, star_gift_id, dialog_id); } void StarGiftManager::save_gift(StarGiftId star_gift_id, bool is_saved, Promise &&promise) { @@ -1032,8 +1084,13 @@ void StarGiftManager::get_gift_upgrade_preview(int64 gift_id, td_->create_handler(std::move(promise))->send(gift_id); } -void StarGiftManager::upgrade_gift(StarGiftId star_gift_id, bool keep_original_details, int64 star_count, +void StarGiftManager::upgrade_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, + bool keep_original_details, int64 star_count, Promise> &&promise) { + bool as_business = business_connection_id.is_valid(); + if (as_business) { + TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); + } auto input_saved_star_gift = star_gift_id.get_input_saved_star_gift(td_); if (input_saved_star_gift == nullptr) { return promise.set_error(Status::Error(400, "Invalid gift identifier specified")); @@ -1042,29 +1099,30 @@ void StarGiftManager::upgrade_gift(StarGiftId star_gift_id, bool keep_original_d return promise.set_error(Status::Error(400, "Invalid amount of Telegram Stars specified")); } if (star_count != 0) { - if (!td_->star_manager_->has_owned_star_count(star_count)) { + if (!as_business && !td_->star_manager_->has_owned_star_count(star_count)) { return promise.set_error(Status::Error(400, "Have not enough Telegram Stars")); } - int32 flags = 0; - if (keep_original_details) { - flags |= telegram_api::inputInvoiceStarGiftUpgrade::KEEP_ORIGINAL_DETAILS_MASK; - } auto input_invoice = telegram_api::make_object( - flags, false /*ignored*/, std::move(input_saved_star_gift)); + 0, keep_original_details, std::move(input_saved_star_gift)); auto upgrade_input_invoice = telegram_api::make_object( - flags, false /*ignored*/, star_gift_id.get_input_saved_star_gift(td_)); + 0, keep_original_details, star_gift_id.get_input_saved_star_gift(td_)); td_->create_handler(std::move(promise)) - ->send(std::move(input_invoice), std::move(upgrade_input_invoice), star_count); + ->send(business_connection_id, std::move(input_invoice), std::move(upgrade_input_invoice), star_count); } else { td_->create_handler(std::move(promise)) - ->send(star_gift_id, star_count, keep_original_details); + ->send(business_connection_id, star_gift_id, keep_original_details); } } -void StarGiftManager::transfer_gift(StarGiftId star_gift_id, DialogId receiver_dialog_id, int64 star_count, - Promise &&promise) { - auto input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, AccessRights::Read); - auto transfer_input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, AccessRights::Read); +void StarGiftManager::transfer_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, + DialogId receiver_dialog_id, int64 star_count, Promise &&promise) { + bool as_business = business_connection_id.is_valid(); + if (as_business) { + TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); + } + auto access_rights = as_business ? AccessRights::Know : AccessRights::Read; + auto input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, access_rights); + auto transfer_input_peer = td_->dialog_manager_->get_input_peer(receiver_dialog_id, access_rights); if (input_peer == nullptr || transfer_input_peer == nullptr) { return promise.set_error(Status::Error(400, "Have no access to the new gift owner")); } @@ -1075,17 +1133,19 @@ void StarGiftManager::transfer_gift(StarGiftId star_gift_id, DialogId receiver_d if (star_count < 0) { return promise.set_error(Status::Error(400, "Invalid amount of Telegram Stars specified")); } - auto query_promise = - PromiseCreator::lambda([actor_id = actor_id(this), dialog_id = star_gift_id.get_dialog_id(td_), - receiver_dialog_id, promise = std::move(promise)](Result &&result) mutable { - if (result.is_error()) { - return promise.set_error(result.move_as_error()); - } - send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, receiver_dialog_id, - std::move(promise)); - }); + auto dialog_id = + as_business ? DialogId(td_->business_connection_manager_->get_business_connection_user_id(business_connection_id)) + : star_gift_id.get_dialog_id(td_); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), dialog_id, receiver_dialog_id, + promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + return promise.set_error(result.move_as_error()); + } + send_closure(actor_id, &StarGiftManager::on_dialog_gift_transferred, dialog_id, receiver_dialog_id, + std::move(promise)); + }); if (star_count != 0) { - if (!td_->star_manager_->has_owned_star_count(star_count)) { + if (!as_business && !td_->star_manager_->has_owned_star_count(star_count)) { return query_promise.set_error(Status::Error(400, "Have not enough Telegram Stars")); } auto input_invoice = telegram_api::make_object( @@ -1093,9 +1153,10 @@ void StarGiftManager::transfer_gift(StarGiftId star_gift_id, DialogId receiver_d auto transfer_input_invoice = telegram_api::make_object( star_gift_id.get_input_saved_star_gift(td_), std::move(transfer_input_peer)); td_->create_handler(std::move(query_promise)) - ->send(std::move(input_invoice), std::move(transfer_input_invoice), star_count); + ->send(business_connection_id, std::move(input_invoice), std::move(transfer_input_invoice), star_count); } else { - td_->create_handler(std::move(query_promise))->send(star_gift_id, std::move(input_peer)); + td_->create_handler(std::move(query_promise)) + ->send(business_connection_id, star_gift_id, std::move(input_peer)); } } @@ -1116,16 +1177,20 @@ void StarGiftManager::on_dialog_gift_transferred(DialogId from_dialog_id, Dialog promise.set_value(Unit()); } -void StarGiftManager::get_saved_star_gifts(DialogId dialog_id, bool exclude_unsaved, bool exclude_saved, - bool exclude_unlimited, bool exclude_limited, bool exclude_unique, - bool sort_by_value, const string &offset, int32 limit, +void StarGiftManager::get_saved_star_gifts(BusinessConnectionId business_connection_id, DialogId dialog_id, + bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, + bool exclude_limited, bool exclude_unique, bool sort_by_value, + const string &offset, int32 limit, Promise> &&promise) { if (limit < 0) { return promise.set_error(Status::Error(400, "Limit must be non-negative")); } + if (business_connection_id.is_valid()) { + TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); + } td_->create_handler(std::move(promise)) - ->send(dialog_id, exclude_unsaved, exclude_saved, exclude_unlimited, exclude_limited, exclude_unique, - sort_by_value, offset, limit); + ->send(business_connection_id, dialog_id, exclude_unsaved, exclude_saved, exclude_unlimited, exclude_limited, + exclude_unique, sort_by_value, offset, limit); } void StarGiftManager::get_saved_star_gift(StarGiftId star_gift_id, diff --git a/third-party/td/td/td/telegram/StarGiftManager.h b/third-party/td/td/td/telegram/StarGiftManager.h index 8937afb655..2cd5464daa 100644 --- a/third-party/td/td/td/telegram/StarGiftManager.h +++ b/third-party/td/td/td/telegram/StarGiftManager.h @@ -6,6 +6,7 @@ // #pragma once +#include "td/telegram/BusinessConnectionId.h" #include "td/telegram/DialogId.h" #include "td/telegram/MessageFullId.h" #include "td/telegram/StarGiftId.h" @@ -44,7 +45,7 @@ class StarGiftManager final : public Actor { void send_gift(int64 gift_id, DialogId dialog_id, td_api::object_ptr text, bool is_private, bool pay_for_upgrade, Promise &&promise); - void convert_gift(StarGiftId star_gift_id, Promise &&promise); + void convert_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, Promise &&promise); void save_gift(StarGiftId star_gift_id, bool is_saved, Promise &&promise); @@ -54,14 +55,16 @@ class StarGiftManager final : public Actor { void get_gift_upgrade_preview(int64 gift_id, Promise> &&promise); - void upgrade_gift(StarGiftId star_gift_id, bool keep_original_details, int64 star_count, - Promise> &&promise); + void upgrade_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, bool keep_original_details, + int64 star_count, Promise> &&promise); - void transfer_gift(StarGiftId star_gift_id, DialogId receiver_dialog_id, int64 star_count, Promise &&promise); + void transfer_gift(BusinessConnectionId business_connection_id, StarGiftId star_gift_id, DialogId receiver_dialog_id, + int64 star_count, Promise &&promise); - void get_saved_star_gifts(DialogId dialog_id, bool exclude_unsaved, bool exclude_saved, bool exclude_unlimited, - bool exclude_limited, bool exclude_unique, bool sort_by_value, const string &offset, - int32 limit, Promise> &&promise); + void get_saved_star_gifts(BusinessConnectionId business_connection_id, DialogId dialog_id, bool exclude_unsaved, + bool exclude_saved, bool exclude_unlimited, bool exclude_limited, bool exclude_unique, + bool sort_by_value, const string &offset, int32 limit, + Promise> &&promise); void get_saved_star_gift(StarGiftId star_gift_id, Promise> &&promise); diff --git a/third-party/td/td/td/telegram/StarGiftSettings.cpp b/third-party/td/td/td/telegram/StarGiftSettings.cpp new file mode 100644 index 0000000000..b9e0ad66ae --- /dev/null +++ b/third-party/td/td/td/telegram/StarGiftSettings.cpp @@ -0,0 +1,39 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/StarGiftSettings.h" + +namespace td { + +StarGiftSettings::StarGiftSettings(bool display_gifts_button, + telegram_api::object_ptr &&settings) + : display_gifts_button_(display_gifts_button), disallowed_gifts_(std::move(settings)) { +} + +StarGiftSettings::StarGiftSettings(const td_api::object_ptr &settings) { + if (settings != nullptr) { + display_gifts_button_ = settings->show_gift_button_; + disallowed_gifts_ = DisallowedGiftsSettings(settings->accepted_gift_types_); + } +} + +td_api::object_ptr StarGiftSettings::get_gift_settings_object() const { + return td_api::make_object(display_gifts_button_, + disallowed_gifts_.get_accepted_gift_types_object()); +} + +bool operator==(const StarGiftSettings &lhs, const StarGiftSettings &rhs) { + return lhs.display_gifts_button_ == rhs.display_gifts_button_ && lhs.disallowed_gifts_ == rhs.disallowed_gifts_; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftSettings &settings) { + if (settings.display_gifts_button_) { + string_builder << "(show button)"; + } + return string_builder << settings.disallowed_gifts_; +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/StarGiftSettings.h b/third-party/td/td/td/telegram/StarGiftSettings.h new file mode 100644 index 0000000000..2a02b2425d --- /dev/null +++ b/third-party/td/td/td/telegram/StarGiftSettings.h @@ -0,0 +1,63 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/DisallowedGiftsSettings.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 StarGiftSettings { + bool display_gifts_button_ = false; + DisallowedGiftsSettings disallowed_gifts_; + + friend bool operator==(const StarGiftSettings &lhs, const StarGiftSettings &rhs); + + friend StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftSettings &settings); + + public: + StarGiftSettings() = default; + + StarGiftSettings(bool display_gifts_button, + telegram_api::object_ptr &&settings); + + explicit StarGiftSettings(const td_api::object_ptr &settings); + + td_api::object_ptr get_gift_settings_object() const; + + bool get_display_gifts_button() const { + return display_gifts_button_; + } + + const DisallowedGiftsSettings &get_disallowed_gifts() const { + return disallowed_gifts_; + } + + bool is_default() const { + return !display_gifts_button_ && disallowed_gifts_.is_default(); + } + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); +}; + +bool operator==(const StarGiftSettings &lhs, const StarGiftSettings &rhs); + +inline bool operator!=(const StarGiftSettings &lhs, const StarGiftSettings &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const StarGiftSettings &settings); + +} // namespace td diff --git a/third-party/td/td/td/telegram/StarGiftSettings.hpp b/third-party/td/td/td/telegram/StarGiftSettings.hpp new file mode 100644 index 0000000000..8369c4bb53 --- /dev/null +++ b/third-party/td/td/td/telegram/StarGiftSettings.hpp @@ -0,0 +1,41 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/DisallowedGiftsSettings.hpp" +#include "td/telegram/StarGiftSettings.h" + +#include "td/utils/common.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void StarGiftSettings::store(StorerT &storer) const { + bool has_default_disallowed_gifts = disallowed_gifts_.is_default(); + BEGIN_STORE_FLAGS(); + STORE_FLAG(display_gifts_button_); + STORE_FLAG(has_default_disallowed_gifts); + END_STORE_FLAGS(); + if (!has_default_disallowed_gifts) { + td::store(disallowed_gifts_, storer); + } +} + +template +void StarGiftSettings::parse(ParserT &parser) { + bool has_default_disallowed_gifts; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(display_gifts_button_); + PARSE_FLAG(has_default_disallowed_gifts); + END_PARSE_FLAGS(); + if (!has_default_disallowed_gifts) { + td::parse(disallowed_gifts_, parser); + } +} + +} // namespace td diff --git a/third-party/td/td/td/telegram/StarManager.cpp b/third-party/td/td/td/telegram/StarManager.cpp index d3a3cdcdfb..7af8462375 100644 --- a/third-party/td/td/td/telegram/StarManager.cpp +++ b/third-party/td/td/td/telegram/StarManager.cpp @@ -203,13 +203,9 @@ class GetStarsTransactionsQuery final : public Td::ResultHandler { if (input_peer == nullptr) { return on_error(Status::Error(400, "Have no access to the chat")); } - int32 flags = 0; - if (is_refund) { - flags |= telegram_api::inputStarsTransaction::REFUND_MASK; - } vector> transaction_ids; transaction_ids.push_back( - telegram_api::make_object(flags, false /*ignored*/, transaction_id)); + telegram_api::make_object(0, is_refund, transaction_id)); send_query(G()->net_query_creator().create( telegram_api::payments_getStarsTransactionsByID(std::move(input_peer), std::move(transaction_ids)))); } @@ -403,6 +399,21 @@ class GetStarsTransactionsQuery final : public Td::ResultHandler { if (dialog_id.get_type() == DialogType::User) { auto user_id = dialog_id.get_user_id(); auto user_id_object = td_->user_manager_->get_user_id_object(user_id, "starsTransactionPeer"); + if (transaction->business_transfer_) { + transaction->business_transfer_ = false; + if (is_purchase) { + if (for_user) { + product_info = nullptr; + return td_api::make_object(user_id_object); + } + } else { + if (for_bot) { + product_info = nullptr; + return td_api::make_object(user_id_object); + } + } + return nullptr; + } if (transaction->stargift_ != nullptr) { auto gift = StarGift(td_, std::move(transaction->stargift_), true); transaction->stargift_ = nullptr; @@ -439,6 +450,7 @@ class GetStarsTransactionsQuery final : public Td::ResultHandler { LOG(ERROR) << "Receive sale of an upgraded gift"; } else { if (for_user || for_channel) { + product_info = nullptr; return td_api::make_object(user_id_object, gift.get_gift_object(td_)); } @@ -502,22 +514,22 @@ class GetStarsTransactionsQuery final : public Td::ResultHandler { SCOPE_EXIT { bot_payload.clear(); }; + if (transaction->premium_gift_months_ > 0 && is_purchase) { + SCOPE_EXIT { + transaction->premium_gift_months_ = 0; + product_info = nullptr; + }; + if (for_user || for_bot) { + return td_api::make_object( + user_id_object, transaction->premium_gift_months_, + td_->stickers_manager_->get_premium_gift_sticker_object(transaction->premium_gift_months_, 0)); + } + } if (product_info != nullptr) { if (is_purchase) { if (for_user) { - if (transaction->premium_gift_months_ > 0) { - SCOPE_EXIT { - transaction->premium_gift_months_ = 0; - product_info = nullptr; - }; - return td_api::make_object( - user_id_object, transaction->premium_gift_months_, - td_->stickers_manager_->get_premium_gift_sticker_object(transaction->premium_gift_months_, - 0)); - } else { - return td_api::make_object( - user_id_object, std::move(product_info)); - } + return td_api::make_object(user_id_object, + std::move(product_info)); } } else { if (for_bot) { @@ -689,6 +701,9 @@ class GetStarsTransactionsQuery final : public Td::ResultHandler { if (transaction->premium_gift_months_) { LOG(ERROR) << "Receive Telegram Premium purchase with " << to_string(star_transaction); } + if (transaction->business_transfer_) { + LOG(ERROR) << "Receive business bot transfer with " << to_string(star_transaction); + } } if (!file_ids.empty()) { auto file_source_id = diff --git a/third-party/td/td/td/telegram/StatisticsManager.cpp b/third-party/td/td/td/telegram/StatisticsManager.cpp index cb072bef05..92453c63f8 100644 --- a/third-party/td/td/td/telegram/StatisticsManager.cpp +++ b/third-party/td/td/td/telegram/StatisticsManager.cpp @@ -189,12 +189,8 @@ class GetMegagroupStatsQuery final : public Td::ResultHandler { auto input_channel = td_->chat_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getMegagroupStats::DARK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::stats_getMegagroupStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); + telegram_api::stats_getMegagroupStats(0, is_dark, std::move(input_channel)), {}, dc_id)); } void on_result(BufferSlice packet) final { @@ -227,12 +223,8 @@ class GetBroadcastStatsQuery final : public Td::ResultHandler { auto input_channel = td_->chat_manager_->get_input_channel(channel_id); CHECK(input_channel != nullptr); - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getBroadcastStats::DARK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id)); + telegram_api::stats_getBroadcastStats(0, is_dark, std::move(input_channel)), {}, dc_id)); } void on_result(BufferSlice packet) final { @@ -306,12 +298,8 @@ class GetBroadcastRevenueStatsQuery final : public Td::ResultHandler { auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read); CHECK(input_peer != nullptr); - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getBroadcastRevenueStats::DARK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::stats_getBroadcastRevenueStats(flags, false /*ignored*/, std::move(input_peer)))); + telegram_api::stats_getBroadcastRevenueStats(0, is_dark, std::move(input_peer)))); } void on_result(BufferSlice packet) final { @@ -475,14 +463,10 @@ class GetMessageStatsQuery final : public Td::ResultHandler { return promise_.set_error(Status::Error(400, "Supergroup not found")); } - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getMessageStats::DARK_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::stats_getMessageStats(flags, false /*ignored*/, std::move(input_channel), - message_id.get_server_message_id().get()), - {}, dc_id)); + send_query( + G()->net_query_creator().create(telegram_api::stats_getMessageStats(0, is_dark, std::move(input_channel), + message_id.get_server_message_id().get()), + {}, dc_id)); } void on_result(BufferSlice packet) final { @@ -523,12 +507,8 @@ class GetStoryStatsQuery final : public Td::ResultHandler { return promise_.set_error(Status::Error(400, "Chat not found")); } - int32 flags = 0; - if (is_dark) { - flags |= telegram_api::stats_getStoryStats::DARK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::stats_getStoryStats(flags, false /*ignored*/, std::move(input_peer), story_id.get()), {}, dc_id)); + telegram_api::stats_getStoryStats(0, is_dark, std::move(input_peer), story_id.get()), {}, dc_id)); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/StickersManager.cpp b/third-party/td/td/td/telegram/StickersManager.cpp index d221b36c13..f8b2a0d124 100644 --- a/third-party/td/td/td/telegram/StickersManager.cpp +++ b/third-party/td/td/td/telegram/StickersManager.cpp @@ -1122,23 +1122,13 @@ class CreateNewStickerSetQuery final : public Td::ResultHandler { CHECK(input_user != nullptr); int32 flags = 0; - if (sticker_type == StickerType::Mask) { - flags |= telegram_api::stickers_createStickerSet::MASKS_MASK; - } - if (sticker_type == StickerType::CustomEmoji) { - flags |= telegram_api::stickers_createStickerSet::EMOJIS_MASK; - } - if (has_text_color) { - flags |= telegram_api::stickers_createStickerSet::TEXT_COLOR_MASK; - } if (!software.empty()) { flags |= telegram_api::stickers_createStickerSet::SOFTWARE_MASK; } - send_query(G()->net_query_creator().create( - telegram_api::stickers_createStickerSet(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_user), title, short_name, nullptr, - std::move(input_stickers), software), + telegram_api::stickers_createStickerSet( + flags, sticker_type == StickerType::Mask, sticker_type == StickerType::CustomEmoji, has_text_color, + std::move(input_user), title, short_name, nullptr, std::move(input_stickers), software), {{short_name}})); } @@ -2914,25 +2904,25 @@ std::pair StickersManager::on_get_sticker_document(tl_object_ptr< auto dc_id = DcId::internal(document->dc_id_); Dimensions dimensions; - tl_object_ptr sticker; - tl_object_ptr custom_emoji; + telegram_api::object_ptr sticker; + telegram_api::object_ptr custom_emoji; for (auto &attribute : document->attributes_) { switch (attribute->get_id()) { case telegram_api::documentAttributeVideo::ID: { - auto video = move_tl_object_as(attribute); + auto video = telegram_api::move_object_as(attribute); dimensions = get_dimensions(video->w_, video->h_, "sticker documentAttributeVideo"); break; } case telegram_api::documentAttributeImageSize::ID: { - auto image_size = move_tl_object_as(attribute); + auto image_size = telegram_api::move_object_as(attribute); dimensions = get_dimensions(image_size->w_, image_size->h_, "sticker documentAttributeImageSize"); break; } case telegram_api::documentAttributeSticker::ID: - sticker = move_tl_object_as(attribute); + sticker = telegram_api::move_object_as(attribute); break; case telegram_api::documentAttributeCustomEmoji::ID: - custom_emoji = move_tl_object_as(attribute); + custom_emoji = telegram_api::move_object_as(attribute); break; default: continue; @@ -3297,8 +3287,8 @@ void StickersManager::add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail) { void StickersManager::create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions, - tl_object_ptr sticker, - tl_object_ptr custom_emoji, + telegram_api::object_ptr sticker, + telegram_api::object_ptr custom_emoji, StickerFormat format, MultiPromiseActor *load_data_multipromise_ptr) { if (format == StickerFormat::Unknown && sticker == nullptr) { auto old_sticker = get_sticker(file_id); @@ -3335,7 +3325,7 @@ void StickersManager::create_sticker(FileId file_id, FileId premium_animation_fi s->set_id_ = on_get_input_sticker_set(file_id, std::move(sticker->stickerset_), load_data_multipromise_ptr); s->alt_ = std::move(sticker->alt_); - if ((sticker->flags_ & telegram_api::documentAttributeSticker::MASK_MASK) != 0) { + if (sticker->mask_) { s->type_ = StickerType::Mask; } s->mask_position_ = StickerMaskPosition(sticker->mask_coords_); @@ -3406,7 +3396,7 @@ SecretInputMedia StickersManager::get_secret_input_media( } } - vector> attributes; + vector> attributes; attributes.push_back( secret_api::make_object(sticker->alt_, std::move(input_sticker_set))); if (sticker->dimensions_.width != 0 && sticker->dimensions_.height != 0) { @@ -3436,12 +3426,12 @@ SecretInputMedia StickersManager::get_secret_input_media( LOG(ERROR) << "Have a sticker of size " << file_view.size() << " in " << sticker->set_id_; return {}; } - return SecretInputMedia{ - nullptr, make_tl_object( - full_remote_location->get_id(), full_remote_location->get_access_hash(), 0 /*date*/, - get_sticker_format_mime_type(sticker->format_), narrow_cast(file_view.size()), - make_tl_object("t"), full_remote_location->get_dc_id().get_raw_id(), - std::move(attributes))}; + return SecretInputMedia{nullptr, + secret_api::make_object( + full_remote_location->get_id(), full_remote_location->get_access_hash(), 0 /*date*/, + get_sticker_format_mime_type(sticker->format_), narrow_cast(file_view.size()), + secret_api::make_object("t"), + full_remote_location->get_dc_id().get_raw_id(), std::move(attributes))}; } } @@ -3470,14 +3460,13 @@ tl_object_ptr StickersManager::get_input_media( const Sticker *s = get_sticker(file_id); CHECK(s != nullptr); - vector> attributes; + vector> attributes; if (s->dimensions_.width != 0 && s->dimensions_.height != 0) { - attributes.push_back( - make_tl_object(s->dimensions_.width, s->dimensions_.height)); + attributes.push_back(telegram_api::make_object(s->dimensions_.width, + s->dimensions_.height)); } - attributes.push_back(make_tl_object( - 0, false /*ignored*/, emoji.empty() ? s->alt_ : emoji, make_tl_object(), - nullptr)); + attributes.push_back(telegram_api::make_object( + 0, false, emoji.empty() ? s->alt_ : emoji, make_tl_object(), nullptr)); int32 flags = 0; if (input_thumbnail != nullptr) { diff --git a/third-party/td/td/td/telegram/StickersManager.h b/third-party/td/td/td/telegram/StickersManager.h index 100c79ff43..f9314283f9 100644 --- a/third-party/td/td/td/telegram/StickersManager.h +++ b/third-party/td/td/td/telegram/StickersManager.h @@ -169,8 +169,8 @@ class StickersManager final : public Actor { Status on_animated_emoji_message_clicked(string &&emoji, MessageFullId message_full_id, string data); void create_sticker(FileId file_id, FileId premium_animation_file_id, string minithumbnail, PhotoSize thumbnail, - Dimensions dimensions, tl_object_ptr sticker, - tl_object_ptr custom_emoji, + Dimensions dimensions, telegram_api::object_ptr sticker, + telegram_api::object_ptr custom_emoji, StickerFormat sticker_format, MultiPromiseActor *load_data_multipromise_ptr); bool has_secret_input_media(FileId sticker_file_id) const; diff --git a/third-party/td/td/td/telegram/StoryManager.cpp b/third-party/td/td/td/telegram/StoryManager.cpp index cac6145833..eca4955ff5 100644 --- a/third-party/td/td/td/telegram/StoryManager.cpp +++ b/third-party/td/td/td/telegram/StoryManager.cpp @@ -76,14 +76,8 @@ class GetAllStoriesQuery final : public Td::ResultHandler { if (!state.empty()) { flags |= telegram_api::stories_getAllStories::STATE_MASK; } - if (is_next) { - flags |= telegram_api::stories_getAllStories::NEXT_MASK; - } - if (story_list_id == StoryListId::archive()) { - flags |= telegram_api::stories_getAllStories::HIDDEN_MASK; - } - send_query(G()->net_query_creator().create( - telegram_api::stories_getAllStories(flags, false /*ignored*/, false /*ignored*/, state))); + bool hidden = story_list_id == StoryListId::archive(); + send_query(G()->net_query_creator().create(telegram_api::stories_getAllStories(flags, is_next, hidden, state))); } void on_result(BufferSlice packet) final { @@ -273,15 +267,13 @@ class SendStoryReactionQuery final : public Td::ResultHandler { return on_error(Status::Error(400, "Can't access the chat")); } CHECK(!reaction_type.is_paid_reaction()); - - int32 flags = 0; - if (!reaction_type.is_empty() && add_to_recent) { - flags |= telegram_api::stories_sendReaction::ADD_TO_RECENT_MASK; + if (reaction_type.is_empty()) { + add_to_recent = false; } send_query(G()->net_query_creator().create( - telegram_api::stories_sendReaction(flags, false /*ignored*/, std::move(input_peer), - story_full_id.get_story_id().get(), reaction_type.get_input_reaction()), + telegram_api::stories_sendReaction(0, add_to_recent, std::move(input_peer), story_full_id.get_story_id().get(), + reaction_type.get_input_reaction()), {{story_full_id}, {"view_story"}})); } @@ -326,17 +318,8 @@ class GetStoryViewsListQuery final : public Td::ResultHandler { if (!query.empty()) { flags |= telegram_api::stories_getStoryViewsList::Q_MASK; } - if (only_contacts) { - flags |= telegram_api::stories_getStoryViewsList::JUST_CONTACTS_MASK; - } - if (prefer_forwards) { - flags |= telegram_api::stories_getStoryViewsList::FORWARDS_FIRST_MASK; - } - if (prefer_with_reaction) { - flags |= telegram_api::stories_getStoryViewsList::REACTIONS_FIRST_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::stories_getStoryViewsList(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, + telegram_api::stories_getStoryViewsList(flags, only_contacts, prefer_with_reaction, prefer_forwards, std::move(input_peer), query, story_id.get(), offset, limit))); } @@ -383,11 +366,8 @@ class GetStoryReactionsListQuery final : public Td::ResultHandler { if (!offset.empty()) { flags |= telegram_api::stories_getStoryReactionsList::OFFSET_MASK; } - if (prefer_forwards) { - flags |= telegram_api::stories_getStoryReactionsList::FORWARDS_FIRST_MASK; - } send_query(G()->net_query_creator().create(telegram_api::stories_getStoryReactionsList( - flags, false /*ignored*/, std::move(input_peer), story_full_id.get_story_id().get(), + flags, prefer_forwards, std::move(input_peer), story_full_id.get_story_id().get(), reaction_type.get_input_reaction(), offset, limit))); } @@ -995,11 +975,8 @@ class ActivateStealthModeQuery final : public Td::ResultHandler { } void send() { - int32 flags = - telegram_api::stories_activateStealthMode::PAST_MASK | telegram_api::stories_activateStealthMode::FUTURE_MASK; - - send_query(G()->net_query_creator().create( - telegram_api::stories_activateStealthMode(flags, false /*ignored*/, false /*ignored*/), {{"view_story"}})); + send_query( + G()->net_query_creator().create(telegram_api::stories_activateStealthMode(0, true, true), {{"view_story"}})); } void on_result(BufferSlice packet) final { @@ -1131,7 +1108,8 @@ class StoryManager::SendStoryQuery final : public Td::ResultHandler { const FormattedText &caption = story->caption_; auto entities = get_input_message_entities(td_->user_manager_.get(), &caption, "SendStoryQuery"); - if (!td_->option_manager_->get_option_boolean("can_use_text_entities_in_story_caption")) { + if (!td_->auth_manager_->is_bot() && + !td_->option_manager_->get_option_boolean("can_use_text_entities_in_story_caption")) { entities.clear(); } auto privacy_rules = story->privacy_rules_.get_input_privacy_rules(td_); @@ -1143,30 +1121,24 @@ class StoryManager::SendStoryQuery final : public Td::ResultHandler { if (!entities.empty()) { flags |= telegram_api::stories_sendStory::ENTITIES_MASK; } - if (pending_story_->story_->is_pinned_) { - flags |= telegram_api::stories_sendStory::PINNED_MASK; - } if (period != 86400) { flags |= telegram_api::stories_sendStory::PERIOD_MASK; } if (story->forward_info_ != nullptr) { - flags |= telegram_api::stories_sendStory::FWD_MODIFIED_MASK; flags |= telegram_api::stories_sendStory::FWD_FROM_ID_MASK; flags |= telegram_api::stories_sendStory::FWD_FROM_STORY_MASK; } - if (story->noforwards_) { - flags |= telegram_api::stories_sendStory::NOFORWARDS_MASK; - } auto input_media_areas = MediaArea::get_input_media_areas(td_, story->areas_); if (!input_media_areas.empty()) { flags |= telegram_api::stories_sendStory::MEDIA_AREAS_MASK; } send_query(G()->net_query_creator().create( - telegram_api::stories_sendStory(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, - std::move(input_peer), std::move(input_media), std::move(input_media_areas), - caption.text, std::move(entities), std::move(privacy_rules), - pending_story_->random_id_, period, std::move(fwd_input_peer), fwd_story_id), + telegram_api::stories_sendStory(flags, pending_story_->story_->is_pinned_, story->noforwards_, + story->forward_info_ != nullptr, std::move(input_peer), std::move(input_media), + std::move(input_media_areas), caption.text, std::move(entities), + std::move(privacy_rules), pending_story_->random_id_, period, + std::move(fwd_input_peer), fwd_story_id), {{pending_story_->dialog_id_}})); } @@ -1209,8 +1181,8 @@ class StoryManager::EditStoryQuery final : public Td::ResultHandler { unique_ptr pending_story_; public: - void send(const Story *story, unique_ptr pending_story, - telegram_api::object_ptr input_file, const BeingEditedStory *edited_story) { + void send(unique_ptr pending_story, telegram_api::object_ptr input_file, + const BeingEditedStory *edited_story) { pending_story_ = std::move(pending_story); CHECK(pending_story_ != nullptr); dialog_id_ = pending_story_->dialog_id_; @@ -1289,6 +1261,68 @@ class StoryManager::EditStoryQuery final : public Td::ResultHandler { } }; +class StoryManager::EditBusinessStoryQuery final : public Td::ResultHandler { + DialogId dialog_id_; + unique_ptr pending_story_; + + public: + void send(unique_ptr pending_story, telegram_api::object_ptr input_file, + const BeingEditedBusinessStory *edited_story) { + pending_story_ = std::move(pending_story); + CHECK(pending_story_ != nullptr); + dialog_id_ = pending_story_->dialog_id_; + + auto input_peer = td_->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read); + if (input_peer == nullptr) { + return on_error(Status::Error(400, "Can't access the chat")); + } + + int32 flags = telegram_api::stories_editStory::MEDIA_MASK | telegram_api::stories_editStory::MEDIA_AREAS_MASK | + telegram_api::stories_editStory::CAPTION_MASK | telegram_api::stories_editStory::ENTITIES_MASK | + telegram_api::stories_editStory::PRIVACY_RULES_MASK; + const StoryContent *content = edited_story->content_.get(); + CHECK(input_file != nullptr); + auto input_media = get_story_content_input_media(td_, content, std::move(input_file)); + CHECK(input_media != nullptr); + auto input_media_areas = MediaArea::get_input_media_areas(td_, edited_story->areas_); + auto entities = + get_input_message_entities(td_->user_manager_.get(), &edited_story->caption_, "EditBusinessStoryQuery"); + auto input_privacy_rules = edited_story->privacy_rules_.get_input_privacy_rules(td_); + send_query(G()->net_query_creator().create( + telegram_api::stories_editStory(flags, std::move(input_peer), pending_story_->story_id_.get(), + std::move(input_media), std::move(input_media_areas), + edited_story->caption_.text, std::move(entities), + std::move(input_privacy_rules)), + {{StoryFullId{pending_story_->dialog_id_, pending_story_->story_id_}}})); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(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 EditBusinessStoryQuery: " << to_string(ptr); + + td_->story_manager_->on_edit_business_story(std::move(pending_story_), std::move(ptr)); + } + + void on_error(Status status) final { + CHECK(td_->auth_manager_->is_bot()); + LOG(INFO) << "Receive error for EditBusinessStoryQuery: " << status; + + auto bad_parts = FileManager::get_missing_file_parts(status); + if (!bad_parts.empty()) { + td_->story_manager_->on_send_story_file_parts_missing(std::move(pending_story_), std::move(bad_parts)); + return; + } + + td_->dialog_manager_->on_get_dialog_error(dialog_id_, status, "EditBusinessStoryQuery"); + td_->story_manager_->delete_pending_story(std::move(pending_story_), std::move(status)); + } +}; + class StoryManager::UploadMediaCallback final : public FileManager::UploadCallback { public: void on_upload_ok(FileUploadId file_upload_id, telegram_api::object_ptr input_file) final { @@ -1818,9 +1852,10 @@ bool StoryManager::can_get_story_view_count(DialogId owner_dialog_id) { bool StoryManager::can_post_stories(DialogId owner_dialog_id) const { switch (owner_dialog_id.get_type()) { case DialogType::User: - return is_my_story(owner_dialog_id); + return is_my_story(owner_dialog_id) != td_->auth_manager_->is_bot(); case DialogType::Channel: - return td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_post_stories(); + return !td_->auth_manager_->is_bot() && + td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_post_stories(); case DialogType::Chat: case DialogType::SecretChat: case DialogType::None: @@ -1832,9 +1867,10 @@ bool StoryManager::can_post_stories(DialogId owner_dialog_id) const { bool StoryManager::can_edit_stories(DialogId owner_dialog_id) const { switch (owner_dialog_id.get_type()) { case DialogType::User: - return is_my_story(owner_dialog_id); + return is_my_story(owner_dialog_id) != td_->auth_manager_->is_bot(); case DialogType::Channel: - return td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_edit_stories(); + return !td_->auth_manager_->is_bot() && + td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_edit_stories(); case DialogType::Chat: case DialogType::SecretChat: case DialogType::None: @@ -1846,9 +1882,10 @@ bool StoryManager::can_edit_stories(DialogId owner_dialog_id) const { bool StoryManager::can_delete_stories(DialogId owner_dialog_id) const { switch (owner_dialog_id.get_type()) { case DialogType::User: - return is_my_story(owner_dialog_id); + return is_my_story(owner_dialog_id) != td_->auth_manager_->is_bot(); case DialogType::Channel: - return td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_delete_stories(); + return !td_->auth_manager_->is_bot() && + td_->chat_manager_->get_channel_status(owner_dialog_id.get_channel_id()).can_delete_stories(); case DialogType::Chat: case DialogType::SecretChat: case DialogType::None: @@ -1862,7 +1899,8 @@ bool StoryManager::can_edit_story(StoryFullId story_full_id, const Story *story) return false; } auto owner_dialog_id = story_full_id.get_dialog_id(); - return can_edit_stories(owner_dialog_id) || (story->is_outgoing_ && can_post_stories(owner_dialog_id)); + return can_edit_stories(owner_dialog_id) || + (story != nullptr && story->is_outgoing_ && can_post_stories(owner_dialog_id)); } bool StoryManager::can_toggle_story_is_pinned(StoryFullId story_full_id, const Story *story) const { @@ -2038,8 +2076,8 @@ StoryManager::ActiveStories *StoryManager::get_active_stories_force(DialogId own return active_stories; } - if (!G()->use_message_database() || failed_to_load_active_stories_.count(owner_dialog_id) > 0 || - !owner_dialog_id.is_valid()) { + if (td_->auth_manager_->is_bot() || !G()->use_message_database() || + failed_to_load_active_stories_.count(owner_dialog_id) > 0 || !owner_dialog_id.is_valid()) { return nullptr; } @@ -3646,9 +3684,6 @@ StoryId StoryManager::on_get_story(DialogId owner_dialog_id, LOG(ERROR) << "Receive a story in " << owner_dialog_id; return {}; } - if (td_->auth_manager_->is_bot()) { - return {}; - } CHECK(story_item_ptr != nullptr); switch (story_item_ptr->get_id()) { case telegram_api::storyItemDeleted::ID: @@ -3679,8 +3714,7 @@ StoryId StoryManager::on_get_new_story(DialogId owner_dialog_id, return StoryId(); } - td_->dialog_manager_->force_create_dialog(owner_dialog_id, "on_get_new_story"); - + bool is_bot = td_->auth_manager_->is_bot(); StoryId old_story_id; auto updates_story_ids_it = update_story_ids_.find(story_full_id); if (updates_story_ids_it != update_story_ids_.end()) { @@ -3699,7 +3733,8 @@ StoryId StoryManager::on_get_new_story(DialogId owner_dialog_id, } } - bool is_bot = td_->auth_manager_->is_bot(); + td_->dialog_manager_->force_create_dialog(owner_dialog_id, "on_get_new_story"); + auto caption = get_message_text(td_->user_manager_.get(), std::move(story_item->caption_), std::move(story_item->entities_), true, is_bot, story_item->date_, false, "on_get_new_story"); @@ -3712,6 +3747,9 @@ StoryId StoryManager::on_get_new_story(DialogId owner_dialog_id, bool is_changed = false; bool need_save_to_database = false; if (story == nullptr) { + if (is_bot && old_story_id == StoryId()) { + return StoryId(); + } auto s = make_unique(); story = s.get(); stories_.set(story_full_id, std::move(s)); @@ -3854,7 +3892,7 @@ StoryId StoryManager::on_get_new_story(DialogId owner_dialog_id, LOG(INFO) << "Receive " << story_full_id; - if (is_active_story(story)) { + if (!td_->auth_manager_->is_bot() && is_active_story(story)) { auto active_stories = get_active_stories_force(owner_dialog_id, "on_get_new_story"); if (active_stories == nullptr) { if (is_subscribed_to_dialog_stories(owner_dialog_id)) { @@ -3966,9 +4004,12 @@ void StoryManager::on_delete_story(StoryFullId story_full_id) { update_story_ids_.erase(story_full_id); - inaccessible_story_full_ids_.set(story_full_id, Time::now()); - send_closure_later(G()->messages_manager(), - &MessagesManager::update_story_max_reply_media_timestamp_in_replied_messages, story_full_id); + bool is_bot = td_->auth_manager_->is_bot(); + if (!is_bot) { + inaccessible_story_full_ids_.set(story_full_id, Time::now()); + send_closure_later(G()->messages_manager(), + &MessagesManager::update_story_max_reply_media_timestamp_in_replied_messages, story_full_id); + } const Story *story = get_story_force(story_full_id, "on_delete_story"); auto owner_dialog_id = story_full_id.get_dialog_id(); @@ -3997,12 +4038,14 @@ void StoryManager::on_delete_story(StoryFullId story_full_id) { LOG(INFO) << "Delete not found " << story_full_id; } - auto active_stories = get_active_stories_force(owner_dialog_id, "on_get_deleted_story"); - if (active_stories != nullptr && contains(active_stories->story_ids_, story_id)) { - auto story_ids = active_stories->story_ids_; - td::remove(story_ids, story_id); - on_update_active_stories(owner_dialog_id, active_stories->max_read_story_id_, std::move(story_ids), Promise(), - "on_delete_story"); + if (!is_bot) { + auto active_stories = get_active_stories_force(owner_dialog_id, "on_delete_story"); + if (active_stories != nullptr && contains(active_stories->story_ids_, story_id)) { + auto story_ids = active_stories->story_ids_; + td::remove(story_ids, story_id); + on_update_active_stories(owner_dialog_id, active_stories->max_read_story_id_, std::move(story_ids), + Promise(), "on_delete_story"); + } } delete_story_from_database(story_full_id); @@ -4478,6 +4521,9 @@ td_api::object_ptr StoryManager::get_update_cha void StoryManager::send_update_chat_active_stories(DialogId owner_dialog_id, const ActiveStories *active_stories, const char *source) { + if (td_->auth_manager_->is_bot()) { + return; + } if (updated_active_stories_.count(owner_dialog_id) == 0) { if (active_stories == nullptr || active_stories->public_order_ == 0) { LOG(INFO) << "Skip update about active stories in " << owner_dialog_id << " from " << source; @@ -4541,7 +4587,7 @@ void StoryManager::on_update_story_id(int64 random_id, StoryId new_story_id, con CHECK(is_deleted); if (!have_story_force(old_story_full_id)) { - LOG(INFO) << "Can't find sent story " << old_story_full_id; + LOG(INFO) << "Can't find sent " << old_story_full_id; // delete_sent_story_on_server(old_story_full_id, new_story_id); return; } @@ -5129,7 +5175,7 @@ void StoryManager::send_story(DialogId dialog_id, td_api::object_ptrdialog_manager_->get_my_dialog_id()) { + if (dialog_id != td_->dialog_manager_->get_my_dialog_id() && !is_bot) { settings = td_api::make_object(); } TRY_RESULT_PROMISE(promise, privacy_rules, @@ -5162,19 +5208,18 @@ void StoryManager::send_story(DialogId dialog_id, td_api::object_ptr areas; if (input_areas != nullptr) { + vector old_media_areas; for (auto &input_area : input_areas->areas_) { - MediaArea media_area(td_, std::move(input_area), Auto()); + MediaArea media_area(td_, std::move(input_area), old_media_areas); if (media_area.is_valid()) { areas.push_back(std::move(media_area)); } } } - if (!td_->option_manager_->get_option_boolean("can_use_text_entities_in_story_caption")) { + if (!is_bot && !td_->option_manager_->get_option_boolean("can_use_text_entities_in_story_caption")) { caption.entities.clear(); } - td_->dialog_manager_->force_create_dialog(dialog_id, "send_story"); - auto story = make_unique(); if (dialog_id.get_type() == DialogType::Channel && td_->chat_manager_->is_megagroup_channel(dialog_id.get_channel_id())) { @@ -5266,8 +5311,6 @@ void StoryManager::do_send_story(unique_ptr &&pending_story, vecto send_update_story(story_full_id, story.get()); stories_.set(story_full_id, std::move(story)); - auto active_stories = get_active_stories_force(pending_story->dialog_id_, "do_send_story"); - CHECK(pending_story->dialog_id_.is_valid()); CHECK(pending_story->random_id_ != 0); yet_unsent_stories_[pending_story->dialog_id_].insert(pending_story->send_story_num_); @@ -5275,9 +5318,13 @@ void StoryManager::do_send_story(unique_ptr &&pending_story, vecto being_sent_stories_[pending_story->random_id_] = story_full_id; being_sent_story_random_ids_[story_full_id] = pending_story->random_id_; - updated_active_stories_.insert(pending_story->dialog_id_); - send_update_chat_active_stories(pending_story->dialog_id_, active_stories, "do_send_story"); - update_story_list_sent_total_count(StoryListId::main(), "do_send_story"); + if (!td_->auth_manager_->is_bot()) { + auto active_stories = get_active_stories_force(pending_story->dialog_id_, "do_send_story"); + + updated_active_stories_.insert(pending_story->dialog_id_); + send_update_chat_active_stories(pending_story->dialog_id_, active_stories, "do_send_story"); + update_story_list_sent_total_count(StoryListId::main(), "do_send_story"); + } } auto file_upload_id = pending_story->file_upload_id_; @@ -5471,6 +5518,7 @@ void StoryManager::edit_story(DialogId owner_dialog_id, StoryId story_id, td_api::object_ptr &&input_story_content, td_api::object_ptr &&input_areas, td_api::object_ptr &&input_caption, Promise &&promise) { + CHECK(!td_->auth_manager_->is_bot()); StoryFullId story_full_id{owner_dialog_id, story_id}; const Story *story = get_story(story_full_id); if (story == nullptr || story->content_ == nullptr) { @@ -5578,8 +5626,96 @@ void StoryManager::edit_story(DialogId owner_dialog_id, StoryId story_id, do_send_story(std::move(pending_story), {}); } +void StoryManager::edit_business_story(DialogId owner_dialog_id, StoryId story_id, + td_api::object_ptr &&input_story_content, + td_api::object_ptr &&input_areas, + td_api::object_ptr &&input_caption, + td_api::object_ptr &&settings, + Promise> &&promise) { + CHECK(td_->auth_manager_->is_bot()); + StoryFullId story_full_id{owner_dialog_id, story_id}; + if (!can_edit_story(story_full_id, nullptr)) { + return promise.set_error(Status::Error(400, "Story can't be edited")); + } + + bool is_bot = true; + TRY_RESULT_PROMISE(promise, content, get_input_story_content(td_, std::move(input_story_content), owner_dialog_id)); + vector areas; + if (input_areas != nullptr) { + vector old_media_areas; + for (auto &input_area : input_areas->areas_) { + MediaArea media_area(td_, std::move(input_area), old_media_areas); + if (media_area.is_valid()) { + areas.push_back(std::move(media_area)); + } + } + } + TRY_RESULT_PROMISE(promise, caption, + get_formatted_text(td_, DialogId(), std::move(input_caption), is_bot, true, false, false)); + TRY_RESULT_PROMISE(promise, privacy_rules, + UserPrivacySettingRules::get_user_privacy_setting_rules(td_, std::move(settings))); + + auto edit_story_num = ++send_story_count_; + if (edit_story_num == 0) { + edit_story_num = ++send_story_count_; + } + + auto &edited_story = being_edited_business_stories_[edit_story_num]; + CHECK(edited_story == nullptr); + edited_story = make_unique(); + edited_story->content_ = std::move(content); + edited_story->areas_ = std::move(areas); + edited_story->caption_ = std::move(caption); + edited_story->privacy_rules_ = std::move(privacy_rules); + edited_story->promise_ = std::move(promise); + + auto new_story = make_unique(); + new_story->content_ = copy_story_content(edited_story->content_.get()); + + auto pending_story = + td::make_unique(owner_dialog_id, story_id, StoryFullId(), edit_story_num, 0, std::move(new_story)); + + do_send_story(std::move(pending_story), {}); +} + +void StoryManager::on_edit_business_story(unique_ptr &&pending_story, + telegram_api::object_ptr updates) { + CHECK(pending_story != nullptr); + if (pending_story->file_upload_id_.is_valid()) { + td_->file_manager_->delete_partial_remote_location(pending_story->file_upload_id_); + } + auto it = being_edited_business_stories_.find(pending_story->send_story_num_); + CHECK(it != being_edited_business_stories_.end()); + auto promise = std::move(it->second->promise_); + being_edited_business_stories_.erase(it); + + td_->updates_manager_->process_updates_users_and_chats(updates.get()); + + auto story = UpdatesManager::extract_story(updates.get(), pending_story->dialog_id_); + if (story == nullptr) { + LOG(ERROR) << "Receive unexpected edit story result: " << to_string(updates); + promise.set_error(Status::Error(400, "Failed to edit story")); + } else { + auto story_id = on_get_story(pending_story->dialog_id_, std::move(story)); + if (story_id != pending_story->story_id_) { + LOG(ERROR) << "Receive unexpected " << story_id << " instead of " << pending_story->story_id_; + promise.set_error(Status::Error(400, "Failed to edit story")); + } else { + promise.set_value(get_story_object({pending_story->dialog_id_, story_id})); + } + on_delete_story({pending_story->dialog_id_, story_id}); + } +} + void StoryManager::do_edit_story(unique_ptr &&pending_story, telegram_api::object_ptr input_file) { + if (td_->auth_manager_->is_bot()) { + CHECK(pending_story->send_story_num_ != 0); + auto edited_story = being_edited_business_stories_[pending_story->send_story_num_].get(); + CHECK(edited_story != nullptr); + td_->create_handler()->send(std::move(pending_story), std::move(input_file), edited_story); + return; + } StoryFullId story_full_id{pending_story->dialog_id_, pending_story->story_id_}; const Story *story = get_story(story_full_id); auto it = being_edited_stories_.find(story_full_id); @@ -5589,8 +5725,7 @@ void StoryManager::do_edit_story(unique_ptr &&pending_story, td_->file_manager_->cancel_upload(pending_story->file_upload_id_); return; } - CHECK(story->content_ != nullptr); - td_->create_handler()->send(story, std::move(pending_story), std::move(input_file), it->second.get()); + td_->create_handler()->send(std::move(pending_story), std::move(input_file), it->second.get()); } void StoryManager::edit_story_cover(DialogId owner_dialog_id, StoryId story_id, double main_frame_timestamp, @@ -5627,15 +5762,23 @@ void StoryManager::delete_pending_story(unique_ptr &&pending_story if (G()->close_flag() && G()->use_message_database()) { return; } + + CHECK(pending_story != nullptr); if (pending_story->file_upload_id_.is_valid()) { td_->file_manager_->delete_partial_remote_location(pending_story->file_upload_id_); } - - CHECK(pending_story != nullptr); StoryFullId story_full_id{pending_story->dialog_id_, pending_story->story_id_}; const Story *story = get_story(story_full_id); bool is_edit = pending_story->story_id_.is_server(); if (is_edit) { + if (td_->auth_manager_->is_bot()) { + CHECK(status.is_error()); + auto it = being_edited_business_stories_.find(pending_story->send_story_num_); + CHECK(it != being_edited_business_stories_.end()); + it->second->promise_.set_error(std::move(status)); + being_edited_business_stories_.erase(it); + return; + } auto it = being_edited_stories_.find(story_full_id); if (story == nullptr || it == being_edited_stories_.end() || edit_generations_[story_full_id] != pending_story->random_id_) { diff --git a/third-party/td/td/td/telegram/StoryManager.h b/third-party/td/td/td/telegram/StoryManager.h index 86cd1648ce..7f8a770e55 100644 --- a/third-party/td/td/td/telegram/StoryManager.h +++ b/third-party/td/td/td/telegram/StoryManager.h @@ -108,6 +108,14 @@ class StoryManager final : public Actor { int64 log_event_id_ = 0; }; + struct BeingEditedBusinessStory { + unique_ptr content_; + vector areas_; + FormattedText caption_; + UserPrivacySettingRules privacy_rules_; + Promise> promise_; + }; + struct PendingStory { DialogId dialog_id_; StoryId story_id_; @@ -228,6 +236,13 @@ class StoryManager final : public Actor { td_api::object_ptr &&input_areas, td_api::object_ptr &&input_caption, Promise &&promise); + void edit_business_story(DialogId owner_dialog_id, StoryId story_id, + td_api::object_ptr &&input_story_content, + td_api::object_ptr &&input_areas, + td_api::object_ptr &&input_caption, + td_api::object_ptr &&settings, + Promise> &&promise); + void edit_story_cover(DialogId owner_dialog_id, StoryId story_id, double main_frame_timestamp, Promise &&promise); @@ -378,6 +393,7 @@ class StoryManager final : public Actor { class SendStoryQuery; class EditStoryQuery; + class EditBusinessStoryQuery; class DeleteStoryOnServerLogEvent; class ReadStoriesOnServerLogEvent; @@ -577,6 +593,9 @@ class StoryManager final : public Actor { void do_edit_story(unique_ptr &&pending_story, telegram_api::object_ptr input_file); + void on_edit_business_story(unique_ptr &&pending_story, + telegram_api::object_ptr updates); + void on_toggle_story_is_pinned(StoryFullId story_full_id, bool is_pinned, Promise &&promise); void on_update_dialog_max_story_ids(DialogId owner_dialog_id, StoryId max_story_id, StoryId max_read_story_id); @@ -688,6 +707,8 @@ class StoryManager final : public Actor { FlatHashMap, StoryFullIdHash> being_edited_stories_; + FlatHashMap> being_edited_business_stories_; + FlatHashMap edit_generations_; FlatHashMap pending_story_views_; diff --git a/third-party/td/td/td/telegram/TopDialogManager.cpp b/third-party/td/td/td/telegram/TopDialogManager.cpp index 34070c3d84..f0d6feb7c8 100644 --- a/third-party/td/td/td/telegram/TopDialogManager.cpp +++ b/third-party/td/td/td/telegram/TopDialogManager.cpp @@ -48,16 +48,8 @@ class GetTopPeersQuery final : public Td::ResultHandler { } void send(int64 hash) { - int32 flags = - telegram_api::contacts_getTopPeers::CORRESPONDENTS_MASK | telegram_api::contacts_getTopPeers::BOTS_PM_MASK | - telegram_api::contacts_getTopPeers::BOTS_INLINE_MASK | telegram_api::contacts_getTopPeers::GROUPS_MASK | - telegram_api::contacts_getTopPeers::CHANNELS_MASK | telegram_api::contacts_getTopPeers::PHONE_CALLS_MASK | - telegram_api::contacts_getTopPeers::FORWARD_USERS_MASK | - telegram_api::contacts_getTopPeers::FORWARD_CHATS_MASK | telegram_api::contacts_getTopPeers::BOTS_APP_MASK; - send_query(G()->net_query_creator().create( - telegram_api::contacts_getTopPeers(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, - false /*ignored*/, false /*ignored*/, 0 /*offset*/, 100 /*limit*/, hash))); + send_query(G()->net_query_creator().create(telegram_api::contacts_getTopPeers( + 0, true, true, true, true, true, true, true, true, true, 0 /*offset*/, 100 /*limit*/, hash))); } void on_result(BufferSlice packet) final { diff --git a/third-party/td/td/td/telegram/UpdatesManager.cpp b/third-party/td/td/td/telegram/UpdatesManager.cpp index ce2465c69b..03e98646ae 100644 --- a/third-party/td/td/td/telegram/UpdatesManager.cpp +++ b/third-party/td/td/td/telegram/UpdatesManager.cpp @@ -991,6 +991,8 @@ bool UpdatesManager::is_acceptable_message(const telegram_api::Message *message_ case telegram_api::messageActionPrizeStars::ID: case telegram_api::messageActionStarGift::ID: case telegram_api::messageActionStarGiftUnique::ID: + case telegram_api::messageActionPaidMessagesRefunded::ID: + case telegram_api::messageActionPaidMessagesPrice::ID: break; case telegram_api::messageActionChatCreate::ID: { auto chat_create = static_cast(action); @@ -1508,6 +1510,25 @@ vector UpdatesManager::get_update_new_group_call_ids(const tel return input_group_call_ids; } +void UpdatesManager::process_updates_users_and_chats(telegram_api::Updates *updates_ptr) { + switch (updates_ptr->get_id()) { + case telegram_api::updatesCombined::ID: { + auto updates = static_cast(updates_ptr); + td_->user_manager_->on_get_users(std::move(updates->users_), "updatesCombined 2"); + td_->chat_manager_->on_get_chats(std::move(updates->chats_), "updatesCombined 2"); + break; + } + case telegram_api::updates::ID: { + auto updates = static_cast(updates_ptr); + td_->user_manager_->on_get_users(std::move(updates->users_), "updates 2"); + td_->chat_manager_->on_get_chats(std::move(updates->chats_), "updates 2"); + break; + } + default: + break; + } +} + string UpdatesManager::extract_join_group_call_presentation_params(telegram_api::Updates *updates_ptr) { auto updates = get_updates(updates_ptr); for (auto it = updates->begin(); it != updates->end(); ++it) { @@ -1522,6 +1543,24 @@ string UpdatesManager::extract_join_group_call_presentation_params(telegram_api: return string(); } +telegram_api::object_ptr UpdatesManager::extract_story(telegram_api::Updates *updates_ptr, + DialogId owner_dialog_id) { + auto updates = get_updates(updates_ptr); + if (updates->size() != 1u) { + return nullptr; + } + for (auto it = updates->begin(); it != updates->end(); ++it) { + auto *update_ptr = it->get(); + if (update_ptr->get_id() == telegram_api::updateStory::ID) { + auto update = static_cast(update_ptr); + if (DialogId(update->peer_) == owner_dialog_id) { + return std::move(update->story_); + } + } + } + return nullptr; +} + vector UpdatesManager::get_update_notify_settings_dialog_ids(const telegram_api::Updates *updates_ptr) { vector dialog_ids; auto updates = get_updates(updates_ptr); @@ -4008,8 +4047,9 @@ void UpdatesManager::on_update(tl_object_ptr upd void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { td_->user_manager_->on_update_user_name(UserId(update->user_id_), std::move(update->first_name_), - std::move(update->last_name_), - Usernames{string(), std::move(update->usernames_)}); + std::move(update->last_name_)); + td_->user_manager_->on_update_user_usernames(UserId(update->user_id_), + Usernames{string(), std::move(update->usernames_)}); promise.set_value(Unit()); } @@ -4622,4 +4662,8 @@ void UpdatesManager::on_update(tl_object_ptr update, Promise &&promise) { + promise.set_value(Unit()); +} + } // namespace td diff --git a/third-party/td/td/td/telegram/UpdatesManager.h b/third-party/td/td/td/telegram/UpdatesManager.h index 5120f53780..25cdb4db65 100644 --- a/third-party/td/td/td/telegram/UpdatesManager.h +++ b/third-party/td/td/td/telegram/UpdatesManager.h @@ -117,8 +117,13 @@ class UpdatesManager final : public Actor { static vector get_update_new_group_call_ids(const telegram_api::Updates *updates_ptr); + void process_updates_users_and_chats(telegram_api::Updates *updates_ptr); + static string extract_join_group_call_presentation_params(telegram_api::Updates *updates_ptr); + static telegram_api::object_ptr extract_story(telegram_api::Updates *updates_ptr, + DialogId owner_dialog_id); + static vector get_update_notify_settings_dialog_ids(const telegram_api::Updates *updates_ptr); static vector get_chat_dialog_ids(const telegram_api::Updates *updates_ptr); @@ -699,6 +704,8 @@ class UpdatesManager final : public Actor { // unsupported updates void on_update(tl_object_ptr update, Promise &&promise); + + void on_update(tl_object_ptr update, Promise &&promise); }; } // namespace td diff --git a/third-party/td/td/td/telegram/UserManager.cpp b/third-party/td/td/td/telegram/UserManager.cpp index b3d6fba544..9a1a5e3af4 100644 --- a/third-party/td/td/td/telegram/UserManager.cpp +++ b/third-party/td/td/td/telegram/UserManager.cpp @@ -15,6 +15,7 @@ #include "td/telegram/BotVerification.hpp" #include "td/telegram/BotVerifierSettings.hpp" #include "td/telegram/BusinessAwayMessage.h" +#include "td/telegram/BusinessConnectionManager.h" #include "td/telegram/BusinessGreetingMessage.h" #include "td/telegram/BusinessInfo.h" #include "td/telegram/BusinessInfo.hpp" @@ -61,6 +62,7 @@ #include "td/telegram/SecretChatLayer.h" #include "td/telegram/SecretChatsManager.h" #include "td/telegram/ServerMessageId.h" +#include "td/telegram/StarGiftSettings.hpp" #include "td/telegram/StarManager.h" #include "td/telegram/StickerPhotoSize.h" #include "td/telegram/StoryManager.h" @@ -94,6 +96,7 @@ #include #include +#include #include #include #include @@ -204,12 +207,8 @@ class AddContactQuery final : public Td::ResultHandler { void send(UserId user_id, telegram_api::object_ptr &&input_user, const Contact &contact, bool share_phone_number) { user_id_ = user_id; - int32 flags = 0; - if (share_phone_number) { - flags |= telegram_api::contacts_addContact::ADD_PHONE_PRIVACY_EXCEPTION_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::contacts_addContact(flags, false /*ignored*/, std::move(input_user), contact.get_first_name(), + telegram_api::contacts_addContact(0, share_phone_number, std::move(input_user), contact.get_first_name(), contact.get_last_name(), contact.get_phone_number()), {{DialogId(user_id)}})); } @@ -509,39 +508,31 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler { flags |= telegram_api::photos_uploadProfilePhoto::FILE_MASK; photo_input_file = std::move(input_file); } - if (td_->user_manager_->is_user_bot(user_id)) { + if (td_->user_manager_->is_user_bot(user_id) != td_->auth_manager_->is_bot()) { auto r_input_user = td_->user_manager_->get_input_user(user_id); if (r_input_user.is_error()) { return on_error(r_input_user.move_as_error()); } flags |= telegram_api::photos_uploadProfilePhoto::BOT_MASK; send_query(G()->net_query_creator().create( - telegram_api::photos_uploadProfilePhoto(flags, false /*ignored*/, r_input_user.move_as_ok(), + telegram_api::photos_uploadProfilePhoto(flags, is_fallback, r_input_user.move_as_ok(), std::move(photo_input_file), std::move(video_input_file), main_frame_timestamp, nullptr), {{user_id}})); } else if (user_id == td_->user_manager_->get_my_id()) { - if (is_fallback) { - flags |= telegram_api::photos_uploadProfilePhoto::FALLBACK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::photos_uploadProfilePhoto(flags, false /*ignored*/, nullptr, std::move(photo_input_file), + telegram_api::photos_uploadProfilePhoto(flags, is_fallback, nullptr, std::move(photo_input_file), std::move(video_input_file), main_frame_timestamp, nullptr), {{"me"}})); } else { - if (only_suggest) { - flags |= telegram_api::photos_uploadContactProfilePhoto::SUGGEST_MASK; - } else { - flags |= telegram_api::photos_uploadContactProfilePhoto::SAVE_MASK; - } auto r_input_user = td_->user_manager_->get_input_user(user_id); if (r_input_user.is_error()) { return on_error(r_input_user.move_as_error()); } send_query(G()->net_query_creator().create( - telegram_api::photos_uploadContactProfilePhoto(flags, false /*ignored*/, false /*ignored*/, - r_input_user.move_as_ok(), std::move(photo_input_file), - std::move(video_input_file), main_frame_timestamp, nullptr), + telegram_api::photos_uploadContactProfilePhoto(flags, only_suggest, !only_suggest, r_input_user.move_as_ok(), + std::move(photo_input_file), std::move(video_input_file), + main_frame_timestamp, nullptr), {{user_id}})); } } @@ -553,40 +544,32 @@ class UploadProfilePhotoQuery final : public Td::ResultHandler { is_fallback_ = is_fallback; only_suggest_ = only_suggest; - if (td_->user_manager_->is_user_bot(user_id)) { + if (td_->user_manager_->is_user_bot(user_id) != td_->auth_manager_->is_bot()) { auto r_input_user = td_->user_manager_->get_input_user(user_id); if (r_input_user.is_error()) { return on_error(r_input_user.move_as_error()); } - int32 flags = telegram_api::photos_uploadProfilePhoto::VIDEO_EMOJI_MARKUP_MASK; - flags |= telegram_api::photos_uploadProfilePhoto::BOT_MASK; + int32 flags = telegram_api::photos_uploadProfilePhoto::VIDEO_EMOJI_MARKUP_MASK | + telegram_api::photos_uploadProfilePhoto::BOT_MASK; send_query(G()->net_query_creator().create( - telegram_api::photos_uploadProfilePhoto(flags, false /*ignored*/, r_input_user.move_as_ok(), nullptr, nullptr, - 0.0, sticker_photo_size->get_input_video_size_object(td_)), + telegram_api::photos_uploadProfilePhoto(flags, is_fallback, r_input_user.move_as_ok(), nullptr, nullptr, 0.0, + sticker_photo_size->get_input_video_size_object(td_)), {{user_id}})); } else if (user_id == td_->user_manager_->get_my_id()) { int32 flags = telegram_api::photos_uploadProfilePhoto::VIDEO_EMOJI_MARKUP_MASK; - if (is_fallback) { - flags |= telegram_api::photos_uploadProfilePhoto::FALLBACK_MASK; - } send_query(G()->net_query_creator().create( - telegram_api::photos_uploadProfilePhoto(flags, false /*ignored*/, nullptr, nullptr, nullptr, 0.0, + telegram_api::photos_uploadProfilePhoto(flags, is_fallback, nullptr, nullptr, nullptr, 0.0, sticker_photo_size->get_input_video_size_object(td_)), {{"me"}})); } else { - int32 flags = telegram_api::photos_uploadContactProfilePhoto::VIDEO_EMOJI_MARKUP_MASK; - if (only_suggest) { - flags |= telegram_api::photos_uploadContactProfilePhoto::SUGGEST_MASK; - } else { - flags |= telegram_api::photos_uploadContactProfilePhoto::SAVE_MASK; - } auto r_input_user = td_->user_manager_->get_input_user(user_id); if (r_input_user.is_error()) { return on_error(r_input_user.move_as_error()); } + int32 flags = telegram_api::photos_uploadContactProfilePhoto::VIDEO_EMOJI_MARKUP_MASK; send_query(G()->net_query_creator().create( - telegram_api::photos_uploadContactProfilePhoto(flags, false /*ignored*/, false /*ignored*/, - r_input_user.move_as_ok(), nullptr, nullptr, 0.0, + telegram_api::photos_uploadContactProfilePhoto(flags, only_suggest, !only_suggest, r_input_user.move_as_ok(), + nullptr, nullptr, 0.0, sticker_photo_size->get_input_video_size_object(td_)), {{user_id}})); } @@ -640,24 +623,18 @@ class UpdateProfilePhotoQuery final : public Td::ResultHandler { old_photo_id_ = old_photo_id; is_fallback_ = is_fallback; file_reference_ = FileManager::extract_file_reference(input_photo); - int32 flags = 0; - if (is_fallback) { - flags |= telegram_api::photos_updateProfilePhoto::FALLBACK_MASK; - } - if (td_->user_manager_->is_user_bot(user_id)) { + if (user_id != td_->user_manager_->get_my_id()) { auto r_input_user = td_->user_manager_->get_input_user(user_id); if (r_input_user.is_error()) { return on_error(r_input_user.move_as_error()); } - flags |= telegram_api::photos_updateProfilePhoto::BOT_MASK; send_query(G()->net_query_creator().create( - telegram_api::photos_updateProfilePhoto(flags, false /*ignored*/, r_input_user.move_as_ok(), - std::move(input_photo)), + telegram_api::photos_updateProfilePhoto(telegram_api::photos_updateProfilePhoto::BOT_MASK, is_fallback, + r_input_user.move_as_ok(), std::move(input_photo)), {{user_id}})); } else { send_query(G()->net_query_creator().create( - telegram_api::photos_updateProfilePhoto(flags, false /*ignored*/, nullptr, std::move(input_photo)), - {{"me"}})); + telegram_api::photos_updateProfilePhoto(0, is_fallback, nullptr, std::move(input_photo)), {{"me"}})); } } @@ -697,6 +674,39 @@ class UpdateProfilePhotoQuery final : public Td::ResultHandler { } }; +class DeleteBusinessProfilePhotoQuery final : public Td::ResultHandler { + Promise promise_; + UserId user_id_; + bool is_fallback_; + + public: + explicit DeleteBusinessProfilePhotoQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(BusinessConnectionId business_connection_id, bool is_fallback) { + user_id_ = td_->business_connection_manager_->get_business_connection_user_id(business_connection_id); + is_fallback_ = is_fallback; + send_query(G()->net_query_creator().create_with_prefix( + business_connection_id.get_invoke_prefix(), + telegram_api::photos_updateProfilePhoto(0, is_fallback, nullptr, + telegram_api::make_object()), + td_->business_connection_manager_->get_business_connection_dc_id(business_connection_id), {{user_id_}})); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + td_->user_manager_->on_set_profile_photo(user_id_, result_ptr.move_as_ok(), is_fallback_, 0, std::move(promise_)); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class DeleteContactProfilePhotoQuery final : public Td::ResultHandler { Promise promise_; UserId user_id_; @@ -709,12 +719,9 @@ class DeleteContactProfilePhotoQuery final : public Td::ResultHandler { CHECK(input_user != nullptr); user_id_ = user_id; - int32 flags = 0; - flags |= telegram_api::photos_uploadContactProfilePhoto::SAVE_MASK; - send_query(G()->net_query_creator().create( - telegram_api::photos_uploadContactProfilePhoto(flags, false /*ignored*/, false /*ignored*/, - std::move(input_user), nullptr, nullptr, 0, nullptr), - {{user_id}})); + send_query(G()->net_query_creator().create(telegram_api::photos_uploadContactProfilePhoto( + 0, false, true, std::move(input_user), nullptr, nullptr, 0, nullptr), + {{user_id}})); } void on_result(BufferSlice packet) final { @@ -784,9 +791,6 @@ class UpdateColorQuery final : public Td::ResultHandler { accent_color_id_ = accent_color_id; background_custom_emoji_id_ = background_custom_emoji_id; int32 flags = 0; - if (for_profile) { - flags |= telegram_api::account_updateColor::FOR_PROFILE_MASK; - } if (accent_color_id.is_valid()) { flags |= telegram_api::account_updateColor::COLOR_MASK; } @@ -794,8 +798,7 @@ class UpdateColorQuery final : public Td::ResultHandler { flags |= telegram_api::account_updateColor::BACKGROUND_EMOJI_ID_MASK; } send_query(G()->net_query_creator().create( - telegram_api::account_updateColor(flags, false /*ignored*/, accent_color_id.get(), - background_custom_emoji_id.get()), + telegram_api::account_updateColor(flags, for_profile, accent_color_id.get(), background_custom_emoji_id.get()), {{"me"}})); } @@ -1792,6 +1795,7 @@ void UserManager::UserFull::store(StorerT &storer) const { bool has_bot_verification = bot_verification != nullptr; bool has_charge_paid_message_stars = charge_paid_message_stars != 0; bool has_send_paid_message_stars = send_paid_message_stars != 0; + bool has_gift_settings = !gift_settings.is_default(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_about); STORE_FLAG(is_blocked); @@ -1841,6 +1845,7 @@ void UserManager::UserFull::store(StorerT &storer) const { STORE_FLAG(has_bot_verification); STORE_FLAG(has_charge_paid_message_stars); STORE_FLAG(has_send_paid_message_stars); + STORE_FLAG(has_gift_settings); END_STORE_FLAGS(); } if (has_about) { @@ -1927,6 +1932,9 @@ void UserManager::UserFull::store(StorerT &storer) const { if (has_send_paid_message_stars) { store(send_paid_message_stars, storer); } + if (has_gift_settings) { + store(gift_settings, storer); + } } template @@ -1961,6 +1969,7 @@ void UserManager::UserFull::parse(ParserT &parser) { bool has_bot_verification = false; bool has_charge_paid_message_stars = false; bool has_send_paid_message_stars = false; + bool has_gift_settings = false; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_about); PARSE_FLAG(is_blocked); @@ -2010,6 +2019,7 @@ void UserManager::UserFull::parse(ParserT &parser) { PARSE_FLAG(has_bot_verification); PARSE_FLAG(has_charge_paid_message_stars); PARSE_FLAG(has_send_paid_message_stars); + PARSE_FLAG(has_gift_settings); END_PARSE_FLAGS(); } if (has_about) { @@ -2100,6 +2110,9 @@ void UserManager::UserFull::parse(ParserT &parser) { if (has_send_paid_message_stars) { parse(send_paid_message_stars, parser); } + if (has_gift_settings) { + parse(gift_settings, parser); + } } template @@ -2249,6 +2262,18 @@ UserManager::UserManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::m if (was_online_local_ >= unix_time && !td_->online_manager_->is_online()) { was_online_local_ = unix_time - 1; } + + auto log_event_string = G()->td_db()->get_binlog_pmc()->get("freeze_state"); + if (!log_event_string.empty()) { + string freeze_since_date; + string freeze_until_date; + std::tie(freeze_since_date, log_event_string) = split(log_event_string); + std::tie(freeze_until_date, freeze_appeal_url_) = split(log_event_string); + freeze_since_date_ = to_integer(freeze_since_date); + freeze_until_date_ = to_integer(freeze_until_date); + + send_closure(G()->td(), &Td::send_update, get_update_freeze_state_object()); + } } user_online_timeout_.set_callback(on_user_online_timeout_callback); @@ -2864,7 +2889,7 @@ void UserManager::on_binlog_secret_chat_event(BinlogEvent &&event) { update_secret_chat(c, secret_chat_id, true, false); } -void UserManager::on_update_user_name(UserId user_id, string &&first_name, string &&last_name, Usernames &&usernames) { +void UserManager::on_update_user_name(UserId user_id, string &&first_name, string &&last_name) { if (!user_id.is_valid()) { LOG(ERROR) << "Receive invalid " << user_id; return; @@ -2873,10 +2898,9 @@ void UserManager::on_update_user_name(UserId user_id, string &&first_name, strin User *u = get_user_force(user_id, "on_update_user_name"); if (u != nullptr) { on_update_user_name(u, user_id, std::move(first_name), std::move(last_name)); - on_update_user_usernames(u, user_id, std::move(usernames)); update_user(u, user_id); } else { - LOG(INFO) << "Ignore update user name about unknown " << user_id; + LOG(INFO) << "Ignore update about name of unknown " << user_id; } } @@ -2893,6 +2917,21 @@ void UserManager::on_update_user_name(User *u, UserId user_id, string &&first_na } } +void UserManager::on_update_user_usernames(UserId user_id, Usernames &&usernames) { + if (!user_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << user_id; + return; + } + + User *u = get_user_force(user_id, "on_update_user_usernames"); + if (u != nullptr) { + on_update_user_usernames(u, user_id, std::move(usernames)); + update_user(u, user_id); + } else { + LOG(INFO) << "Ignore update about usernames of unknown " << user_id; + } +} + void UserManager::on_update_user_usernames(User *u, UserId user_id, Usernames &&usernames) { if (u->usernames != usernames) { td_->dialog_manager_->on_dialog_usernames_updated(DialogId(user_id), u->usernames, usernames); @@ -3550,14 +3589,26 @@ void UserManager::on_update_user_full_gift_count(UserFull *user_full, UserId use } } +void UserManager::on_update_my_user_gift_settings(StarGiftSettings &&gift_settings, Promise &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + auto user_id = get_my_id(); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_gift_settings"); + if (user_full != nullptr && user_full->gift_settings != gift_settings) { + user_full->gift_settings = std::move(gift_settings); + user_full->is_changed = true; + update_user_full(user_full, user_id, "on_update_my_user_gift_settings"); + } + promise.set_value(Unit()); +} + void UserManager::on_update_my_user_location(DialogLocation &&location) { auto user_id = get_my_id(); - UserFull *user_full = get_user_full_force(user_id, "on_update_user_location"); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_location"); if (user_full == nullptr) { return; } on_update_user_full_location(user_full, user_id, std::move(location)); - update_user_full(user_full, user_id, "on_update_user_location"); + update_user_full(user_full, user_id, "on_update_my_user_location"); } void UserManager::on_update_user_full_location(UserFull *user_full, UserId user_id, DialogLocation &&location) { @@ -3569,12 +3620,12 @@ void UserManager::on_update_user_full_location(UserFull *user_full, UserId user_ void UserManager::on_update_my_user_work_hours(BusinessWorkHours &&work_hours) { auto user_id = get_my_id(); - UserFull *user_full = get_user_full_force(user_id, "on_update_user_work_hours"); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_work_hours"); if (user_full == nullptr) { return; } on_update_user_full_work_hours(user_full, user_id, std::move(work_hours)); - update_user_full(user_full, user_id, "on_update_user_work_hours"); + update_user_full(user_full, user_id, "on_update_my_user_work_hours"); } void UserManager::on_update_user_full_work_hours(UserFull *user_full, UserId user_id, BusinessWorkHours &&work_hours) { @@ -3586,12 +3637,12 @@ void UserManager::on_update_user_full_work_hours(UserFull *user_full, UserId use void UserManager::on_update_my_user_away_message(BusinessAwayMessage &&away_message) { auto user_id = get_my_id(); - UserFull *user_full = get_user_full_force(user_id, "on_update_user_away_message"); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_away_message"); if (user_full == nullptr) { return; } on_update_user_full_away_message(user_full, user_id, std::move(away_message)); - update_user_full(user_full, user_id, "on_update_user_away_message"); + update_user_full(user_full, user_id, "on_update_my_user_away_message"); } void UserManager::on_update_user_full_away_message(UserFull *user_full, UserId user_id, @@ -3608,12 +3659,12 @@ void UserManager::on_update_user_full_away_message(UserFull *user_full, UserId u void UserManager::on_update_my_user_greeting_message(BusinessGreetingMessage &&greeting_message) { auto user_id = get_my_id(); - UserFull *user_full = get_user_full_force(user_id, "on_update_user_greeting_message"); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_greeting_message"); if (user_full == nullptr) { return; } on_update_user_full_greeting_message(user_full, user_id, std::move(greeting_message)); - update_user_full(user_full, user_id, "on_update_user_greeting_message"); + update_user_full(user_full, user_id, "on_update_my_user_greeting_message"); } void UserManager::on_update_user_full_greeting_message(UserFull *user_full, UserId user_id, @@ -3630,12 +3681,12 @@ void UserManager::on_update_user_full_greeting_message(UserFull *user_full, User void UserManager::on_update_my_user_intro(BusinessIntro &&intro) { auto user_id = get_my_id(); - UserFull *user_full = get_user_full_force(user_id, "on_update_user_intro"); + UserFull *user_full = get_user_full_force(user_id, "on_update_my_user_intro"); if (user_full == nullptr) { return; } on_update_user_full_intro(user_full, user_id, std::move(intro)); - update_user_full(user_full, user_id, "on_update_user_intro"); + update_user_full(user_full, user_id, "on_update_my_user_intro"); } void UserManager::on_update_user_full_intro(UserFull *user_full, UserId user_id, BusinessIntro &&intro) { @@ -3965,6 +4016,29 @@ void UserManager::on_ignored_restriction_reasons_changed() { }); } +td_api::object_ptr UserManager::get_update_freeze_state_object() const { + return td_api::make_object(freeze_since_date_ > 0, freeze_since_date_, freeze_until_date_, + freeze_appeal_url_); +} + +void UserManager::on_update_freeze_state(int32 freeze_since_date, int32 freeze_until_date, string freeze_appeal_url) { + if (freeze_since_date == freeze_since_date_ && freeze_until_date == freeze_until_date_ && + freeze_appeal_url == freeze_appeal_url_) { + return; + } + freeze_since_date_ = freeze_since_date; + freeze_until_date_ = freeze_until_date; + freeze_appeal_url_ = std::move(freeze_appeal_url); + send_closure(G()->td(), &Td::send_update, get_update_freeze_state_object()); + + if (freeze_since_date_ > 0) { + G()->td_db()->get_binlog_pmc()->set( + "freeze_state", PSTRING() << freeze_since_date_ << ' ' << freeze_until_date_ << ' ' << freeze_appeal_url_); + } else { + G()->td_db()->get_binlog_pmc()->erase("freeze_state"); + } +} + void UserManager::invalidate_user_full(UserId user_id) { auto user_full = get_user_full_force(user_id, "invalidate_user_full"); if (user_full != nullptr) { @@ -5053,6 +5127,21 @@ void UserManager::set_bot_profile_photo(UserId bot_user_id, set_profile_photo_impl(bot_user_id, input_photo, false, false, std::move(promise)); } +void UserManager::set_business_profile_photo(BusinessConnectionId business_connection_id, + const td_api::object_ptr &input_photo, + bool is_fallback, Promise &&promise) { + TRY_STATUS_PROMISE(promise, td_->business_connection_manager_->check_business_connection(business_connection_id)); + if (input_photo == nullptr) { + td_->create_handler(std::move(promise))->send(business_connection_id, is_fallback); + return; + } + if (input_photo->get_id() == td_api::inputChatPhotoPrevious::ID) { + return promise.set_error(Status::Error(400, "Unsupported")); + } + auto user_id = td_->business_connection_manager_->get_business_connection_user_id(business_connection_id); + set_profile_photo_impl(user_id, input_photo, is_fallback, false, std::move(promise)); +} + void UserManager::set_profile_photo(const td_api::object_ptr &input_photo, bool is_fallback, Promise &&promise) { dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::UserpicSetup}, Promise()); @@ -7340,12 +7429,13 @@ void UserManager::on_get_user_full(telegram_api::object_ptrsponsored_enabled_; auto can_view_revenue = user->can_view_revenue_; auto bot_verification = BotVerification::get_bot_verification(std::move(user->bot_verification_)); + auto gift_settings = StarGiftSettings(user->display_gifts_button_, std::move(user->disallowed_gifts_)); if (user_full->can_be_called != can_be_called || user_full->supports_video_calls != supports_video_calls || user_full->has_private_calls != has_private_calls || user_full->voice_messages_forbidden != voice_messages_forbidden || user_full->can_pin_messages != can_pin_messages || user_full->has_pinned_stories != has_pinned_stories || user_full->sponsored_enabled != sponsored_enabled || user_full->can_view_revenue != can_view_revenue || - user_full->bot_verification != bot_verification) { + user_full->bot_verification != bot_verification || user_full->gift_settings != gift_settings) { user_full->can_be_called = can_be_called; user_full->supports_video_calls = supports_video_calls; user_full->has_private_calls = has_private_calls; @@ -7355,6 +7445,7 @@ void UserManager::on_get_user_full(telegram_api::object_ptrsponsored_enabled = sponsored_enabled; user_full->can_view_revenue = can_view_revenue; user_full->bot_verification = std::move(bot_verification); + user_full->gift_settings = std::move(gift_settings); user_full->is_changed = true; } @@ -7759,6 +7850,7 @@ void UserManager::drop_user_full(UserId user_id) { user_full->read_dates_private = false; user_full->contact_require_premium = false; user_full->birthdate = {}; + user_full->gift_settings = {}; user_full->sponsored_enabled = false; user_full->has_preview_medias = false; user_full->can_view_revenue = false; @@ -8581,7 +8673,8 @@ td_api::object_ptr UserManager::get_user_full_info_object( user_full->sponsored_enabled, user_full->need_phone_number_privacy_exception, user_full->wallpaper_overridden, std::move(bio_object), user_full->birthdate.get_birthdate_object(), personal_chat_id, user_full->gift_count, user_full->common_chat_count, user_full->charge_paid_message_stars, user_full->send_paid_message_stars, - std::move(bot_verification), std::move(business_info), std::move(bot_info)); + user_full->gift_settings.get_gift_settings_object(), std::move(bot_verification), std::move(business_info), + std::move(bot_info)); } td_api::object_ptr UserManager::get_update_contact_close_birthdays() const { @@ -8681,6 +8774,10 @@ void UserManager::get_current_state(vector> & if (!contact_birthdates_.users_.empty()) { updates.push_back(get_update_contact_close_birthdays()); } + + if (freeze_since_date_ > 0) { + updates.push_back(get_update_freeze_state_object()); + } } } // namespace td diff --git a/third-party/td/td/td/telegram/UserManager.h b/third-party/td/td/td/telegram/UserManager.h index 7cd025605e..4c771840aa 100644 --- a/third-party/td/td/td/telegram/UserManager.h +++ b/third-party/td/td/td/telegram/UserManager.h @@ -12,6 +12,7 @@ #include "td/telegram/BotCommand.h" #include "td/telegram/BotMenuButton.h" #include "td/telegram/BotVerifierSettings.h" +#include "td/telegram/BusinessConnectionId.h" #include "td/telegram/ChannelId.h" #include "td/telegram/Contact.h" #include "td/telegram/CustomEmojiId.h" @@ -29,6 +30,7 @@ #include "td/telegram/ReferralProgramInfo.h" #include "td/telegram/RestrictionReason.h" #include "td/telegram/SecretChatId.h" +#include "td/telegram/StarGiftSettings.h" #include "td/telegram/StoryId.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -120,7 +122,9 @@ class UserManager final : public Actor { void on_binlog_secret_chat_event(BinlogEvent &&event); - void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, Usernames &&usernames); + void on_update_user_name(UserId user_id, string &&first_name, string &&last_name); + + void on_update_user_usernames(UserId user_id, Usernames &&usernames); void on_update_user_phone_number(UserId user_id, string &&phone_number); @@ -149,6 +153,8 @@ class UserManager final : public Actor { void on_update_my_gift_count(int32 added_gift_count); + void on_update_my_user_gift_settings(StarGiftSettings &&gift_settings, Promise &&promise); + void on_update_my_user_location(DialogLocation &&location); void on_update_my_user_work_hours(BusinessWorkHours &&work_hours); @@ -189,6 +195,8 @@ class UserManager final : public Actor { void on_ignored_restriction_reasons_changed(); + void on_update_freeze_state(int32 freeze_since_date, int32 freeze_until_date, string freeze_appeal_url); + void invalidate_user_full(UserId user_id); bool have_user(UserId user_id) const; @@ -334,6 +342,10 @@ class UserManager final : public Actor { void set_bot_profile_photo(UserId bot_user_id, const td_api::object_ptr &input_photo, Promise &&promise); + void set_business_profile_photo(BusinessConnectionId business_connection_id, + const td_api::object_ptr &input_photo, bool is_fallback, + Promise &&promise); + void set_profile_photo(const td_api::object_ptr &input_photo, bool is_fallback, Promise &&promise); @@ -637,6 +649,7 @@ class UserManager final : public Actor { int32 gift_count = 0; int32 common_chat_count = 0; Birthdate birthdate; + StarGiftSettings gift_settings; ChannelId personal_channel_id; @@ -1055,6 +1068,8 @@ class UserManager final : public Actor { td_api::object_ptr get_secret_chat_object_const(SecretChatId secret_chat_id, const SecretChat *secret_chat) const; + td_api::object_ptr get_update_freeze_state_object() const; + Td *td_; ActorShared<> parent_; UserId my_id_; @@ -1152,6 +1167,10 @@ class UserManager final : public Actor { WaitFreeHashSet restricted_user_ids_; + int32 freeze_since_date_ = 0; + int32 freeze_until_date_ = 0; + string freeze_appeal_url_; + struct ContactBirthdates { vector> users_; double next_sync_time_ = 0.0; diff --git a/third-party/td/td/td/telegram/UserStarGift.cpp b/third-party/td/td/td/telegram/UserStarGift.cpp index 88a2bfd39b..77c4701e24 100644 --- a/third-party/td/td/td/telegram/UserStarGift.cpp +++ b/third-party/td/td/td/telegram/UserStarGift.cpp @@ -6,6 +6,7 @@ // #include "td/telegram/UserStarGift.h" +#include "td/telegram/AuthManager.h" #include "td/telegram/DialogId.h" #include "td/telegram/DialogManager.h" #include "td/telegram/MessageSender.h" @@ -54,7 +55,7 @@ UserStarGift::UserStarGift(Td *td, telegram_api::object_ptrauth_manager_->is_bot()) { LOG(ERROR) << "Receive non-saved gift for another user"; is_saved_ = true; } diff --git a/third-party/td/td/td/telegram/Version.h b/third-party/td/td/td/telegram/Version.h index c4aa2bac1d..a3df017eba 100644 --- a/third-party/td/td/td/telegram/Version.h +++ b/third-party/td/td/td/telegram/Version.h @@ -10,7 +10,7 @@ namespace td { -constexpr int32 MTPROTO_LAYER = 200; +constexpr int32 MTPROTO_LAYER = 201; enum class Version : int32 { Initial, // 0 diff --git a/third-party/td/td/td/telegram/VideoNotesManager.cpp b/third-party/td/td/td/telegram/VideoNotesManager.cpp index 634638b5c9..a6f74e2cb0 100644 --- a/third-party/td/td/td/telegram/VideoNotesManager.cpp +++ b/third-party/td/td/td/telegram/VideoNotesManager.cpp @@ -182,10 +182,9 @@ SecretInputMedia VideoNotesManager::get_secret_input_media( if (video_note->thumbnail.file_id.is_valid() && thumbnail.empty()) { return SecretInputMedia{}; } - vector> attributes; - attributes.push_back(make_tl_object( - secret_api::documentAttributeVideo::ROUND_MESSAGE_MASK, true, video_note->duration, video_note->dimensions.width, - video_note->dimensions.height)); + vector> attributes; + attributes.push_back(secret_api::make_object( + 0, true, video_note->duration, video_note->dimensions.width, video_note->dimensions.height)); return {std::move(input_file), std::move(thumbnail), @@ -226,12 +225,11 @@ tl_object_ptr VideoNotesManager::get_input_media( const VideoNote *video_note = get_video_note(file_id); CHECK(video_note != nullptr); - vector> attributes; + vector> attributes; auto suggested_video_note_length = narrow_cast(td_->option_manager_->get_option_integer("suggested_video_note_length", 384)); - attributes.push_back(make_tl_object( - telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK, false /*ignored*/, false /*ignored*/, - false /*ignored*/, video_note->duration, + attributes.push_back(telegram_api::make_object( + 0, true, false, false, video_note->duration, video_note->dimensions.width ? video_note->dimensions.width : suggested_video_note_length, video_note->dimensions.height ? video_note->dimensions.height : suggested_video_note_length, 0, 0.0, string())); int32 flags = 0; diff --git a/third-party/td/td/td/telegram/VideosManager.cpp b/third-party/td/td/td/telegram/VideosManager.cpp index 8de71b29bf..81036b3c7d 100644 --- a/third-party/td/td/td/telegram/VideosManager.cpp +++ b/third-party/td/td/td/telegram/VideosManager.cpp @@ -231,8 +231,8 @@ SecretInputMedia VideosManager::get_secret_input_media( if (video->thumbnail.file_id.is_valid() && thumbnail.empty()) { return {}; } - vector> attributes; - attributes.emplace_back(make_tl_object( + vector> attributes; + attributes.emplace_back(secret_api::make_object( 0, false, video->duration, video->dimensions.width, video->dimensions.height)); return {std::move(input_file), @@ -306,24 +306,18 @@ tl_object_ptr VideosManager::get_input_media( const Video *video = get_video(file_id); CHECK(video != nullptr); - vector> attributes; + vector> attributes; { int32 attribute_flags = 0; - if (video->supports_streaming) { - attribute_flags |= telegram_api::documentAttributeVideo::SUPPORTS_STREAMING_MASK; - } - if (video->is_animation) { - attribute_flags |= telegram_api::documentAttributeVideo::NOSOUND_MASK; - } if (video->start_ts > 0.0) { attribute_flags |= telegram_api::documentAttributeVideo::VIDEO_START_TS_MASK; } - attributes.push_back(make_tl_object( - attribute_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, video->precise_duration, + attributes.push_back(telegram_api::make_object( + attribute_flags, false, video->supports_streaming, video->is_animation, video->precise_duration, video->dimensions.width, video->dimensions.height, 0, video->start_ts, string())); } if (!video->file_name.empty()) { - attributes.push_back(make_tl_object(video->file_name)); + attributes.push_back(telegram_api::make_object(video->file_name)); } int32 flags = 0; vector> added_stickers; @@ -374,21 +368,15 @@ telegram_api::object_ptr VideosManager::get_story_docu vector> attributes; { int32 attribute_flags = 0; - if (video->supports_streaming) { - attribute_flags |= telegram_api::documentAttributeVideo::SUPPORTS_STREAMING_MASK; - } - if (video->is_animation) { - attribute_flags |= telegram_api::documentAttributeVideo::NOSOUND_MASK; - } if (main_frame_timestamp > 0.0) { attribute_flags |= telegram_api::documentAttributeVideo::VIDEO_START_TS_MASK; } attributes.push_back(telegram_api::make_object( - attribute_flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, video->precise_duration, + attribute_flags, false, video->supports_streaming, video->is_animation, video->precise_duration, video->dimensions.width, video->dimensions.height, 0, main_frame_timestamp, string())); } if (!video->file_name.empty()) { - attributes.push_back(make_tl_object(video->file_name)); + attributes.push_back(telegram_api::make_object(video->file_name)); } int32 flags = 0; vector> added_stickers; diff --git a/third-party/td/td/td/telegram/VoiceNotesManager.cpp b/third-party/td/td/td/telegram/VoiceNotesManager.cpp index d3cab66518..6a6a4d5310 100644 --- a/third-party/td/td/td/telegram/VoiceNotesManager.cpp +++ b/third-party/td/td/td/telegram/VoiceNotesManager.cpp @@ -156,10 +156,10 @@ SecretInputMedia VoiceNotesManager::get_secret_input_media( auto *voice_note = get_voice_note(voice_note_file_id); CHECK(voice_note != nullptr); - vector> attributes; - attributes.push_back(make_tl_object( - secret_api::documentAttributeAudio::VOICE_MASK | secret_api::documentAttributeAudio::WAVEFORM_MASK, - false /*ignored*/, voice_note->duration, "", "", BufferSlice(voice_note->waveform))); + vector> attributes; + attributes.push_back(secret_api::make_object( + secret_api::documentAttributeAudio::WAVEFORM_MASK, true, voice_note->duration, "", "", + BufferSlice(voice_note->waveform))); return {std::move(input_file), BufferSlice(), Dimensions(), voice_note->mime_type, file_view, std::move(attributes), caption, layer}; @@ -193,14 +193,14 @@ tl_object_ptr VoiceNotesManager::get_input_media( const VoiceNote *voice_note = get_voice_note(file_id); CHECK(voice_note != nullptr); - vector> attributes; + vector> attributes; { - int32 flags = telegram_api::documentAttributeAudio::VOICE_MASK; + int32 flags = 0; if (!voice_note->waveform.empty()) { flags |= telegram_api::documentAttributeAudio::WAVEFORM_MASK; } - attributes.push_back(make_tl_object( - flags, false /*ignored*/, voice_note->duration, "", "", BufferSlice(voice_note->waveform))); + attributes.push_back(telegram_api::make_object( + flags, true, voice_note->duration, "", "", BufferSlice(voice_note->waveform))); } string mime_type = voice_note->mime_type; if (mime_type != "audio/ogg" && mime_type != "audio/mpeg" && mime_type != "audio/mp4") { diff --git a/third-party/td/td/td/telegram/WebPageBlock.cpp b/third-party/td/td/td/telegram/WebPageBlock.cpp index fe3985ad07..dc8902af39 100644 --- a/third-party/td/td/td/telegram/WebPageBlock.cpp +++ b/third-party/td/td/td/telegram/WebPageBlock.cpp @@ -2111,15 +2111,9 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptrsecond; } - string url; - WebPageId web_page_id; - if ((page_block->flags_ & telegram_api::pageBlockPhoto::URL_MASK) != 0) { - url = std::move(page_block->url_); - web_page_id = WebPageId(page_block->webpage_id_); - } return td::make_unique(std::move(photo), get_page_block_caption(std::move(page_block->caption_), documents), - std::move(url), web_page_id); + std::move(page_block->url_), WebPageId(page_block->webpage_id_)); } case telegram_api::pageBlockVideo::ID: { auto page_block = move_tl_object_as(page_block_ptr); @@ -2154,11 +2148,9 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptrallow_scrolling_; bool has_dimensions = (page_block->flags_ & telegram_api::pageBlockEmbed::W_MASK) != 0; Photo poster_photo; - if ((page_block->flags_ & telegram_api::pageBlockEmbed::POSTER_PHOTO_ID_MASK) != 0) { - auto it = photos.find(page_block->poster_photo_id_); - if (it != photos.end()) { - poster_photo = *it->second; - } + auto it = photos.find(page_block->poster_photo_id_); + if (it != photos.end()) { + poster_photo = *it->second; } Dimensions dimensions; if (has_dimensions) { @@ -2266,12 +2258,8 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptrtext_ != nullptr) { cell.text = get_rich_text(std::move(table_cell->text_), documents); } - if ((table_cell->flags_ & telegram_api::pageTableCell::COLSPAN_MASK) != 0) { - cell.colspan = table_cell->colspan_; - } - if ((table_cell->flags_ & telegram_api::pageTableCell::ROWSPAN_MASK) != 0) { - cell.rowspan = table_cell->rowspan_; - } + cell.colspan = max(table_cell->colspan_, 1); + cell.rowspan = max(table_cell->rowspan_, 1); return cell; }); }); @@ -2288,25 +2276,23 @@ unique_ptr get_web_page_block(Td *td, tl_object_ptr(page_block_ptr); - auto articles = transform( - std::move(page_block->articles_), [&](tl_object_ptr &&related_article) { - RelatedArticle article; - article.url = std::move(related_article->url_); - article.web_page_id = WebPageId(related_article->webpage_id_); - article.title = std::move(related_article->title_); - article.description = std::move(related_article->description_); - if ((related_article->flags_ & telegram_api::pageRelatedArticle::PHOTO_ID_MASK) != 0) { - auto it = photos.find(related_article->photo_id_); - if (it != photos.end()) { - article.photo = *it->second; - } - } - article.author = std::move(related_article->author_); - if ((related_article->flags_ & telegram_api::pageRelatedArticle::PUBLISHED_DATE_MASK) != 0) { - article.published_date = related_article->published_date_; - } - return article; - }); + auto articles = transform(std::move(page_block->articles_), + [&](tl_object_ptr &&related_article) { + RelatedArticle article; + article.url = std::move(related_article->url_); + article.web_page_id = WebPageId(related_article->webpage_id_); + article.title = std::move(related_article->title_); + article.description = std::move(related_article->description_); + auto it = photos.find(related_article->photo_id_); + if (it != photos.end()) { + article.photo = *it->second; + } + article.author = std::move(related_article->author_); + if (related_article->published_date_ > 0) { + article.published_date = related_article->published_date_; + } + return article; + }); return td::make_unique(get_rich_text(std::move(page_block->title_), documents), std::move(articles)); } diff --git a/third-party/td/td/td/telegram/cli.cpp b/third-party/td/td/td/telegram/cli.cpp index 243edd599d..8be806dccf 100644 --- a/third-party/td/td/td/telegram/cli.cpp +++ b/third-party/td/td/td/telegram/cli.cpp @@ -2870,6 +2870,16 @@ class CliClient final : public Actor { send_request(td_api::make_object()); } else if (op == "dsc") { send_request(td_api::make_object()); + } else if (op == "sgss") { + bool show_button; + bool unlimited_gifts; + bool limited_gifts; + bool upgraded_gifts; + bool premium_subscription; + get_args(args, show_button, unlimited_gifts, limited_gifts, upgraded_gifts, premium_subscription); + send_request(td_api::make_object(td_api::make_object( + show_button, td_api::make_object(unlimited_gifts, limited_gifts, upgraded_gifts, + premium_subscription)))); } else if (op == "gag") { send_request(td_api::make_object()); } else if (op == "sendg" || op == "sendgp" || op == "sgift") { @@ -2883,7 +2893,7 @@ class CliClient final : public Actor { } else if (op == "sellg") { string star_gift_id; get_args(args, star_gift_id); - send_request(td_api::make_object(star_gift_id)); + send_request(td_api::make_object(business_connection_id_, star_gift_id)); } else if (op == "tgis") { string star_gift_id; bool is_saved; @@ -2909,14 +2919,15 @@ class CliClient final : public Actor { bool keep_original_details; int64 star_count; get_args(args, received_gift_id, keep_original_details, star_count); - send_request(td_api::make_object(received_gift_id, keep_original_details, star_count)); + send_request(td_api::make_object(business_connection_id_, received_gift_id, + keep_original_details, star_count)); } else if (op == "tg") { string received_gift_id; string new_owner_id; int64 star_count; get_args(args, received_gift_id, new_owner_id, star_count); - send_request( - td_api::make_object(received_gift_id, as_message_sender(new_owner_id), star_count)); + send_request(td_api::make_object(business_connection_id_, received_gift_id, + as_message_sender(new_owner_id), star_count)); } else if (op == "grgs" || op == "grgsp") { string owner_id; int32 limit; @@ -2928,9 +2939,9 @@ class CliClient final : public Actor { bool exclude_upgraded; get_args(args, owner_id, limit, offset, exclude_unsaved, exclude_saved, exclude_unlimited, exclude_limited, exclude_upgraded); - send_request(td_api::make_object(as_message_sender(owner_id), exclude_unsaved, - exclude_saved, exclude_unlimited, exclude_limited, - exclude_upgraded, op == "grgsp", offset, limit)); + send_request(td_api::make_object( + business_connection_id_, as_message_sender(owner_id), exclude_unsaved, exclude_saved, exclude_unlimited, + exclude_limited, exclude_upgraded, op == "grgsp", offset, limit)); } else if (op == "grg") { string received_gift_id; get_args(args, received_gift_id); @@ -3667,6 +3678,14 @@ class CliClient final : public Actor { send_request(td_api::make_object(args)); } else if (op == "apgc") { send_request(td_api::make_object(args)); + } else if (op == "gpws") { + UserId user_id; + int64 star_count; + int32 month_count; + string text; + get_args(args, user_id, star_count, month_count, text); + send_request( + td_api::make_object(user_id, star_count, month_count, as_formatted_text(text))); } else if (op == "lpg") { int64 giveaway_id; int32 user_count; @@ -3689,10 +3708,10 @@ class CliClient final : public Actor { send_request(td_api::make_object()); } else if (op == "gsta" || op == "gsti" || op == "gsto") { string owner_id; - string subscription_id; - string offset; string limit; - get_args(args, owner_id, subscription_id, offset, limit); + string offset; + string subscription_id; + get_args(args, owner_id, limit, offset, subscription_id); td_api::object_ptr direction; if (op == "gsti") { direction = td_api::make_object(); @@ -4231,6 +4250,17 @@ class CliClient final : public Actor { string option_id; get_args(args, chat_id, message_id, option_id); send_request(td_api::make_object(chat_id, message_id, option_id)); + } else if (op == "gssc") { + send_request(td_api::make_object(args)); + } else if (op == "vsc") { + send_request(td_api::make_object(to_integer(args))); + } else if (op == "osc") { + send_request(td_api::make_object(to_integer(args))); + } else if (op == "rsc") { + int64 unique_id; + string option_id; + get_args(args, unique_id, option_id); + send_request(td_api::make_object(unique_id, option_id)); } else if (op == "gmlink") { ChatId chat_id; MessageId message_id; @@ -6662,6 +6692,74 @@ class CliClient final : public Actor { MessageThreadId message_thread_id; get_args(args, chat_id, message_thread_id); send_request(td_api::make_object(chat_id, message_thread_id)); + } else if (op == "rbm") { + ChatId chat_id; + MessageId message_id; + get_args(args, chat_id, message_id); + send_request(td_api::make_object(business_connection_id_, chat_id, message_id)); + } else if (op == "dbm") { + string message_ids; + get_args(args, message_ids); + send_request( + td_api::make_object(business_connection_id_, as_message_ids(message_ids))); + } + if (op == "ebsp" || op == "ebsv") { + ChatId story_sender_chat_id; + StoryId story_id; + string story; + StoryPrivacySettings rules; + InputStoryAreas areas; + get_args(args, story_sender_chat_id, story_id, story, rules, areas); + td_api::object_ptr content; + if (op == "ebsp") { + content = + td_api::make_object(as_input_file(story), get_added_sticker_file_ids()); + } else { + content = td_api::make_object(as_input_file(story), + get_added_sticker_file_ids(), 10, 0.5, true); + } + send_request(td_api::make_object(story_sender_chat_id, story_id, std::move(content), + areas, get_caption(), rules)); + } else if (op == "dbs") { + StoryId story_id; + get_args(args, story_id); + send_request(td_api::make_object(business_connection_id_, story_id)); + } else if (op == "sban") { + string first_name; + string last_name; + get_args(args, first_name, last_name); + send_request(td_api::make_object(business_connection_id_, first_name, last_name)); + } else if (op == "sbab") { + string bio; + get_args(args, bio); + send_request(td_api::make_object(business_connection_id_, bio)); + } else if (op == "sbau") { + string username; + get_args(args, username); + send_request(td_api::make_object(business_connection_id_, username)); + } else if (op == "sbapp" || op == "sbappf") { + InputChatPhoto input_chat_photo; + get_args(args, input_chat_photo); + send_request(td_api::make_object(business_connection_id_, + input_chat_photo, op == "sbappf")); + } else if (op == "sbags") { + bool show_button; + bool unlimited_gifts; + bool limited_gifts; + bool upgraded_gifts; + bool premium_subscription; + get_args(args, show_button, unlimited_gifts, limited_gifts, upgraded_gifts, premium_subscription); + send_request(td_api::make_object( + business_connection_id_, + td_api::make_object( + show_button, td_api::make_object(unlimited_gifts, limited_gifts, + upgraded_gifts, premium_subscription)))); + } else if (op == "gbasa") { + send_request(td_api::make_object(business_connection_id_)); + } else if (op == "tbas") { + int64 star_count; + get_args(args, star_count); + send_request(td_api::make_object(business_connection_id_, star_count)); } else if (op == "grib") { send_request(td_api::make_object()); } else if (op == "gob") { @@ -6757,9 +6855,31 @@ class CliClient final : public Actor { UserId bot_user_id; string chat_ids; bool can_reply; - get_args(args, bot_user_id, chat_ids, can_reply); - send_request(td_api::make_object( - td_api::make_object(bot_user_id, as_business_recipients(chat_ids), can_reply))); + bool can_read_messages; + bool can_delete_outgoing_messages; + bool can_delete_incoming_messages; + bool can_edit_name; + bool can_edit_bio; + bool can_edit_profile_photo; + bool can_edit_username; + bool can_view_gifts; + bool can_sell_gifts; + bool can_change_gift_settings; + bool can_transfer_and_upgrade_gifts; + bool can_transfer_stars; + bool can_manage_stories; + get_args(args, bot_user_id, chat_ids, can_reply, can_read_messages, can_delete_outgoing_messages, + can_delete_incoming_messages, can_edit_name, can_edit_bio, can_edit_profile_photo, can_edit_username, + can_view_gifts, can_sell_gifts, can_change_gift_settings, can_transfer_and_upgrade_gifts, + can_transfer_stars, can_manage_stories); + send_request( + td_api::make_object(td_api::make_object( + bot_user_id, as_business_recipients(chat_ids), + td_api::make_object( + can_reply, can_read_messages, can_delete_outgoing_messages, can_delete_incoming_messages, + can_edit_name, can_edit_bio, can_edit_profile_photo, can_edit_username, can_view_gifts, + can_sell_gifts, can_change_gift_settings, can_transfer_and_upgrade_gifts, can_transfer_stars, + can_manage_stories)))); } else if (op == "tbcbcip") { ChatId chat_id; bool is_paused; diff --git a/third-party/td/td/td/telegram/files/FileDownloader.cpp b/third-party/td/td/td/telegram/files/FileDownloader.cpp index 70314c3bca..5ccf9e9102 100644 --- a/third-party/td/td/td/telegram/files/FileDownloader.cpp +++ b/third-party/td/td/td/telegram/files/FileDownloader.cpp @@ -148,27 +148,26 @@ Result FileDownloader::start_part(Part part, int32 part_count, int6 auto net_query_type = is_small_ ? NetQuery::Type::DownloadSmall : NetQuery::Type::Download; NetQueryPtr net_query; if (!use_cdn_) { - int32 flags = 0; + bool cdn_supported = false; #if !TD_EMSCRIPTEN // CDN is supported, unless we use domains instead of IPs from a browser if (streaming_offset == 0) { - flags |= telegram_api::upload_getFile::CDN_SUPPORTED_MASK; + cdn_supported = true; } #endif DcId dc_id = remote_.is_web() ? G()->get_webfile_dc_id() : remote_.get_dc_id(); auto unique_id = UniqueId::next(UniqueId::Type::Default, static_cast(QueryType::Default)); - net_query = - remote_.is_web() - ? G()->net_query_creator().create( - unique_id, nullptr, - telegram_api::upload_getWebFile(remote_.as_input_web_file_location(), narrow_cast(part.offset), - narrow_cast(size)), - {}, dc_id, net_query_type, NetQuery::AuthFlag::On) - : G()->net_query_creator().create( - unique_id, nullptr, - telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, - remote_.as_input_file_location(), part.offset, narrow_cast(size)), - {}, dc_id, net_query_type, NetQuery::AuthFlag::On); + net_query = remote_.is_web() + ? G()->net_query_creator().create( + unique_id, nullptr, + telegram_api::upload_getWebFile(remote_.as_input_web_file_location(), + narrow_cast(part.offset), narrow_cast(size)), + {}, dc_id, net_query_type, NetQuery::AuthFlag::On) + : G()->net_query_creator().create( + unique_id, nullptr, + telegram_api::upload_getFile(0, false, cdn_supported, remote_.as_input_file_location(), + part.offset, narrow_cast(size)), + {}, dc_id, net_query_type, NetQuery::AuthFlag::On); } else { if (remote_.is_web()) { return Status::Error("Can't download web file from CDN"); diff --git a/third-party/td/td/td/telegram/files/FileGenerateManager.cpp b/third-party/td/td/td/telegram/files/FileGenerateManager.cpp index 1494de5793..7ec423bbbc 100644 --- a/third-party/td/td/td/telegram/files/FileGenerateManager.cpp +++ b/third-party/td/td/td/telegram/files/FileGenerateManager.cpp @@ -150,7 +150,7 @@ class WebFileDownloadGenerateActor final : public FileGenerateActor { }; ActorOwn net_callback_; - Result> parse_conversion() { + Result> parse_conversion() { auto parts = full_split(Slice(conversion_), '#'); if (parts.size() <= 2 || !parts[0].empty() || !parts.back().empty()) { return Status::Error("Wrong conversion"); @@ -170,11 +170,8 @@ class WebFileDownloadGenerateActor final : public FileGenerateActor { << parts[2] << ".jpg"; int32 flags = telegram_api::inputWebFileAudioAlbumThumbLocation::TITLE_MASK; - if (is_small) { - flags |= telegram_api::inputWebFileAudioAlbumThumbLocation::SMALL_MASK; - } - return make_tl_object(flags, false /*ignored*/, nullptr, - parts[2].str(), parts[3].str()); + return telegram_api::make_object( + flags, is_small, nullptr, parts[2].str(), parts[3].str()); } if (parts.size() != 9 || parts[1] != "map") { @@ -212,9 +209,9 @@ class WebFileDownloadGenerateActor final : public FileGenerateActor { double latitude = 90 - 360 * std::atan(std::exp(((y + 0.1) / size - 0.5) * 2 * PI)) / PI; int64 access_hash = G()->get_location_access_hash(latitude, longitude); - return make_tl_object( - make_tl_object(0, latitude, longitude, 0), access_hash, width, height, zoom, - scale); + return telegram_api::make_object( + telegram_api::make_object(0, latitude, longitude, 0), access_hash, width, height, + zoom, scale); } void start_up() final { diff --git a/third-party/td/td/td/telegram/net/NetQueryCreator.cpp b/third-party/td/td/td/telegram/net/NetQueryCreator.cpp index 6faaf3750b..cda7c403d6 100644 --- a/third-party/td/td/td/telegram/net/NetQueryCreator.cpp +++ b/third-party/td/td/td/telegram/net/NetQueryCreator.cpp @@ -42,7 +42,7 @@ NetQueryPtr NetQueryCreator::create_with_prefix(const telegram_api::object_ptr &prefix, const telegram_api::Function &function, vector &&chain_ids, DcId dc_id, NetQuery::Type type, NetQuery::AuthFlag auth_flag) { - LOG(INFO) << "Create query " << to_string(function); + LOG(INFO) << "Create query " << (prefix != nullptr ? "with a prefix " : "") << to_string(function); string prefix_str; if (prefix != nullptr) { auto storer = DefaultStorer(*prefix); diff --git a/third-party/td/td/td/telegram/net/NetQueryDelayer.cpp b/third-party/td/td/td/telegram/net/NetQueryDelayer.cpp index 72ab66fad0..e3371b2097 100644 --- a/third-party/td/td/td/telegram/net/NetQueryDelayer.cpp +++ b/third-party/td/td/td/telegram/net/NetQueryDelayer.cpp @@ -57,8 +57,10 @@ void NetQueryDelayer::delay(NetQueryPtr query) { break; } } - if (timeout == 0 && begins_with(error_message, "FLOOD_SKIP_FAILED_WAIT")) { - timeout = 1; + if (timeout == 0) { + if (begins_with(error_message, "FLOOD_SKIP_FAILED_WAIT")) { + timeout = 1; + } } } else { G()->net_query_dispatcher().dispatch(std::move(query)); diff --git a/third-party/td/td/td/telegram/net/NetQueryDispatcher.cpp b/third-party/td/td/td/telegram/net/NetQueryDispatcher.cpp index 2e4ae3fb4a..e53d83c5dd 100644 --- a/third-party/td/td/td/telegram/net/NetQueryDispatcher.cpp +++ b/third-party/td/td/td/telegram/net/NetQueryDispatcher.cpp @@ -92,10 +92,13 @@ void NetQueryDispatcher::dispatch(NetQueryPtr net_query) { if (net_query->is_ready() && net_query->is_error()) { auto code = net_query->error().code(); + auto message = net_query->error().message(); if (code == 303) { try_fix_migrate(net_query); } else if (code == NetQuery::Resend) { net_query->resend(); + } else if (code == 420 && message == "FROZEN_METHOD_INVALID") { + net_query->set_error(Status::Error(406, message)); } else if (code < 0 || code == 500 || (code == 420 && !begins_with(net_query->error().message(), "STORY_SEND_FLOOD_") && !begins_with(net_query->error().message(), "PREMIUM_SUB_ACTIVE_UNTIL_"))) { diff --git a/third-party/td/td/tde2e/CMakeLists.txt b/third-party/td/td/tde2e/CMakeLists.txt index 37c751f43e..3e6e91a592 100644 --- a/third-party/td/td/tde2e/CMakeLists.txt +++ b/third-party/td/td/tde2e/CMakeLists.txt @@ -54,9 +54,10 @@ set(TDE2E_SOURCE set(TDE2E_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/td/e2e/TestBlockchain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/td/e2e/TestBlockchain.h - ${CMAKE_CURRENT_SOURCE_DIR}/test/e2e.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/blockchain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/e2e.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/encryption.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/EncryptionTestVectors.h ) set(TDE2E_TEST_SOURCE "${TDE2E_TEST_SOURCE}" PARENT_SCOPE) diff --git a/third-party/td/td/tde2e/td/e2e/Blockchain.cpp b/third-party/td/td/tde2e/td/e2e/Blockchain.cpp index 758a43d7a0..ffa2d250dd 100644 --- a/third-party/td/td/tde2e/td/e2e/Blockchain.cpp +++ b/third-party/td/td/tde2e/td/e2e/Blockchain.cpp @@ -11,16 +11,17 @@ #include "td/telegram/e2e_api.hpp" #include "td/utils/algorithm.h" +#include "td/utils/as.h" #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/format.h" #include "td/utils/misc.h" #include "td/utils/overloaded.h" #include "td/utils/SliceBuilder.h" -#include "td/utils/tl_helpers.h" #include "td/utils/tl_parsers.h" #include +#include #include #include #include @@ -37,6 +38,7 @@ e2e::object_ptr GroupParticipant::to_tl() const return e2e::make_object(user_id, public_key.to_u256(), flags, add_users(), remove_users(), version); } + td::int32 GroupState::version() const { if (participants.empty()) { return 0; @@ -47,6 +49,7 @@ td::int32 GroupState::version() const { } return std::clamp(version, 0, 255); } + td::Result GroupState::get_participant(td::int64 user_id) const { for (const auto &participant : participants) { if (participant.user_id == user_id) { @@ -63,7 +66,8 @@ td::Result GroupState::get_participant(const PublicKey &public } } return td::Status::Error("Participant not found"); -}; +} + Permissions GroupState::get_permissions(const PublicKey &public_key, td::int32 limit_permissions) const { limit_permissions &= GroupParticipantFlags::AllPermissions; auto r_participant = get_participant(public_key); @@ -81,46 +85,58 @@ GroupStateRef GroupState::from_tl(const td::e2e_api::e2e_chain_groupState &state return std::make_shared( GroupState{td::transform(state.participants_, participant_from_tl), state.external_permissions_}); } + e2e::object_ptr GroupState::to_tl() const { return e2e::make_object( td::transform(participants, [](const GroupParticipant &participant) { return participant.to_tl(); }), external_permissions); } + GroupStateRef GroupState::empty_state() { static GroupStateRef state = std::make_shared(); return state; } + GroupSharedKeyRef GroupSharedKey::from_tl(const td::e2e_api::e2e_chain_sharedKey &shared_key) { return std::make_shared(GroupSharedKey{PublicKey::from_u256(shared_key.ek_), shared_key.encrypted_shared_key_, shared_key.dest_user_id_, shared_key.dest_header_}); } + e2e::object_ptr GroupSharedKey::to_tl() const { return e2e::make_object(ek.to_u256(), encrypted_shared_key, std::vector(dest_user_id), std::vector(dest_header)); } + GroupSharedKeyRef GroupSharedKey::empty_shared_key() { static GroupSharedKeyRef shared_key = std::make_shared(); return shared_key; } + ChangeSetValue ChangeSetValue::from_tl(const td::e2e_api::e2e_chain_changeSetValue &change) { return ChangeSetValue{change.key_, change.value_}; } + e2e::object_ptr ChangeSetValue::to_tl() const { return e2e::make_object(key, value); } + ChangeSetGroupState ChangeSetGroupState::from_tl(const td::e2e_api::e2e_chain_changeSetGroupState &change) { return ChangeSetGroupState{GroupState::from_tl(*change.group_state_)}; } + e2e::object_ptr ChangeSetGroupState::to_tl() const { return e2e::make_object(group_state->to_tl()); } + ChangeSetSharedKey ChangeSetSharedKey::from_tl(const td::e2e_api::e2e_chain_changeSetSharedKey &change) { return ChangeSetSharedKey{GroupSharedKey::from_tl(*change.shared_key_)}; } + e2e::object_ptr ChangeSetSharedKey::to_tl() const { return e2e::make_object(shared_key->to_tl()); } + Change Change::from_tl(const td::e2e_api::e2e_chain_Change &change) { Change res; downcast_call( @@ -136,6 +152,7 @@ Change Change::from_tl(const td::e2e_api::e2e_chain_Change &change) { })); return res; } + e2e::object_ptr Change::to_tl() const { return std::visit( td::overloaded( @@ -149,6 +166,7 @@ e2e::object_ptr Change::to_tl() const { }), value); } + td::UInt256 Block::calc_hash() const { if (height_ == -1) { return {}; @@ -158,6 +176,7 @@ td::UInt256 Block::calc_hash() const { td::sha256(serialized_block, hash.as_mutable_slice()); return hash; } + Block Block::from_tl(const e2e::e2e_chain_block &block) { Block result; result.state_proof_ = StateProof::from_tl(*block.state_proof_); @@ -179,7 +198,7 @@ td::Result Block::from_tl_serialized(td::Slice new_block) { auto magic = parser.fetch_int(); if (magic != td::e2e_api::e2e_chain_block::ID) { return td::Status::Error(PSLICE() << "Expected magic " << td::format::as_hex(td::e2e_api::e2e_chain_block::ID) - << ", got " << td::format::as_hex(magic)); + << ", but received " << td::format::as_hex(magic)); } auto block_tl = td::e2e_api::e2e_chain_block::fetch(parser); parser.fetch_end(); @@ -200,9 +219,11 @@ e2e::object_ptr Block::to_tl() const { return e2e::make_object(signature_.to_u512(), flags, prev_block_hash_, std::move(changes), height_, std::move(state_proof), public_key); } + std::string Block::to_tl_serialized() const { return serialize_boxed(*to_tl()); } + td::StringBuilder &operator<<(td::StringBuilder &sb, const Block &block) { return sb << "Block(sign=" << block.signature_ << "..., prev_hash=" << hex_encode(block.prev_block_hash_.as_slice().substr(0, 8)) @@ -211,29 +232,35 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const Block &block) { << "\tchanges=" << block.changes_ << "\n" << "\tsignature_key=" << block.o_signature_public_key_ << ")"; } + td::Result key_to_bitstring(td::Slice key) { if (key.size() != 32) { return td::Status::Error("Invalid key size"); } return BitString(key); } + td::Result KeyValueState::get_value(td::Slice key) const { TRY_RESULT(bitstring, key_to_bitstring(key)); return get(node_, bitstring, snapshot_.value()); } + td::Result KeyValueState::gen_proof(td::Span keys) const { // TODO: validate keys.. TRY_RESULT(pruned_tree, generate_pruned_tree(node_, keys, snapshot_.value())); return TrieNode::serialize_for_network(pruned_tree); } + td::Result KeyValueState::create_from_hash(KeyValueHash hash) { auto node = std::make_shared(hash.hash); return KeyValueState{std::move(node), td::Slice()}; } + td::Result KeyValueState::create_from_snapshot(td::Slice snapshot) { TRY_RESULT(node, TrieNode::fetch_from_snapshot(snapshot)); return KeyValueState{std::move(node), snapshot}; } + td::Result KeyValueState::build_snapshot() const { return TrieNode::serialize_for_snapshot(node_, snapshot_.value()); } @@ -260,6 +287,7 @@ StateProof StateProof::from_tl(const td::e2e_api::e2e_chain_stateProof &proof) { } return res; } + e2e::object_ptr StateProof::to_tl() const { td::int32 flags{}; e2e::object_ptr o_group_state_tl; @@ -276,6 +304,7 @@ e2e::object_ptr StateProof::to_tl() const { return e2e::make_object(flags, kv_hash.hash, std::move(o_group_state_tl), std::move(o_shared_key_tl)); } + td::StringBuilder &operator<<(td::StringBuilder &sb, const StateProof &state) { sb << "StateProof{"; sb << "\n\tkv=" << td::format::as_hex_dump<0>(state.kv_hash.hash.as_slice().substr(0, 8)); @@ -396,7 +425,7 @@ td::Status State::validate_shared_key(const GroupSharedKeyRef &shared_key, const participants.insert(user_id); } if (participants.size() != shared_key->dest_user_id.size()) { - return td::Status::Error("Shared key has duplicated users"); + return td::Status::Error("Shared key has duplicate users"); } for (auto &p : group_state->participants) { if (!participants.count(p.user_id)) { @@ -688,11 +717,12 @@ td::Result Blockchain::create_from_block(Block block, td::optional Blockchain::from_any_to_local(std::string block) { } return block; } + td::Result Blockchain::from_server_to_local(std::string block) { if (block.size() < 4) { - return td::Status::Error("block is too short"); + return td::Status::Error("Block is too short"); } td::int32 server_magic = td::as(block.data()); if (is_good_magic(server_magic)) { @@ -721,9 +752,10 @@ td::Result Blockchain::from_server_to_local(std::string block) { td::as(block.data()) = real_magic; return block; } + td::Result Blockchain::from_local_to_server(std::string block) { if (block.size() < 4) { - return td::Status::Error("block is too short"); + return td::Status::Error("Block is too short"); } td::int32 magic = td::as(block.data()); td::as(block.data()) = magic + 1; diff --git a/third-party/td/td/tde2e/td/e2e/Blockchain.md b/third-party/td/td/tde2e/td/e2e/Blockchain.md index 14c95c1c3c..d8915cfe39 100644 --- a/third-party/td/td/tde2e/td/e2e/Blockchain.md +++ b/third-party/td/td/tde2e/td/e2e/Blockchain.md @@ -1,9 +1,5 @@ # Blockchain Implementation Documentation -#TODO -- version of supported encryption protocol in each participant (should use the lowest one) -- store hash of block in broadcast - ## Overview The blockchain implementation provides a distributed ledger system that maintains a consistent state across multiple participants. It supports key-value storage, participant management, and secure state transitions. The blockchain is designed with security in mind, ensuring only valid blocks with proper signatures and correct heights can be applied. @@ -22,15 +18,14 @@ A block consists of: - **Previous Block Hash**: Links to the previous block, creating a chain - **Changes**: A vector of operations to apply to the blockchain state - **Height**: Sequential block number, critical for validation -- **State Proof**: Contains hashes and states for validation. This is proof of the state of the blockchain after the block was applied. +- **State Proof**: Contains hashes and states for validation. This proof represents the state of the blockchain after the block was applied. - **Signature Public Key**: The key of the participant who created the block -# Start of Selection -### Signature generation +### Signature Generation The signature is generated for the TL-serialized block with the signature field zeroed. -### Block hash generation +### Block Hash Generation The hash of the block is the SHA256 of the TL-serialized block. @@ -113,26 +108,26 @@ The complete blockchain state consists of: A block is either applied completely or not at all. -1. The block’s height is checked. It must be exactly one more than the current blockchain height. +1. The block's height is checked. It must be exactly one more than the current blockchain height. - If the height is incorrect, the block is rejected with `HEIGHT_MISMATCH` -2. The hash of the previous block is checked. It must match the hash of the last applied block. +2. The hash of the previous block is checked. It must match the hash of the last applied block. - If the hash is incorrect, the block is rejected with `PREVIOUS_BLOCK_HASH_MISMATCH` -3. Determine the permissions of the participant who created the block, i.e., the one with `signer_public_key`. - - First, we look for the signer’s public key in the previous state. If found, use its permissions; otherwise, we use external_permissions -4. Verify that the block signature is valid. +3. The permissions of the participant who created the block (the one with `signer_public_key`) are determined. + - First, we look for the signer's public key in the previous state. If found, we use its permissions; otherwise, we use external_permissions +4. The block signature is verified. - If the signature is invalid, the block is rejected with `INVALID_SIGNATURE` -5. Next, apply the changes from the block one by one. - - Before applying a change, check that the participant has sufficient permissions to apply the change. - - Then, apply the change. - - After applying a block, the block creator’s permissions could be updated. It is important because any subsequent changes should be applied using the new permissions. The idea is that applying changes in the same block should yield the same result as applying them in separate blocks. +5. Next, changes from the block are applied one by one. + - Before applying a change, we check that the participant has sufficient permissions to apply it. + - Then, the change is applied. + - After applying a block, the block creator's permissions could be updated. This is important because any subsequent changes should be applied using the new permissions. The idea is that applying changes in the same block should yield the same result as applying them in separate blocks. - If any change is invalid, the block is rejected with the corresponding error. -6. After all changes are applied, the block’s state proof must be valid for the new state. +6. After all changes are applied, the block's state proof must be valid for the new state. - If the state proof is invalid, the block is rejected with `INVALID_STATE_PROOF` To apply the first block, an ephemeral block with height `-1` is used: - It has a hash of `UInt256(0)` - Its height is `-1` - - It has effective (but not explicitly stored) `self_join_permissions` with all permissions + - It has effective (but not explicitly stored, i.e., not reflected in its hash) `self_join_permissions` with all permissions There are also several optimizations for block serialization: - The `signer_public_key` can be omitted if it is the same as the public key of the first participant in the group state @@ -154,20 +149,25 @@ Currently, any participant can update any key with a new value. This change is a - Only participants with the `AddUsers` permission can add new participants - A participant may add users with permissions that are a non-strict subset of its own permissions +- As an exception, it is possible to give permissions to another user - Only participants with the `RemoveUsers` permission can remove existing participants -- It is okay to remove yourself from the group. In this case, you will not be able to add yourself back. -- Both the public key and user ID are unique in the group state. -- Any new state of the group is allowed otherwise. -- The shared key is automatically cleared by this change. +- Both the public key and user ID are unique in the group state +- Any new state of the group is allowed otherwise +- The shared key is automatically cleared by this change #### Shared Key Updates - The shared key cannot be overwritten by other participants. One must update the group state to clear the key first. - The shared key is automatically cleared by a `SetGroupState` change. - The shared key must contain all user_ids of all participants, and only them. -- How the shared key is encrypted is not the blockchain’s concern. +- How the shared key is encrypted is not the blockchain's concern. - Only participants may update the key. +Note: +- It is impossible to create a new key if the user is not in the group, even if it is an automatic removal of the key +- Only participants may create a new key +- It is impossible to remove yourself from the group (this could be allowed in the future, but would lead to an empty shared key) + ## Known Behaviors and Considerations ### Multiple Blocks at the Same Height @@ -175,61 +175,21 @@ Currently, any participant can update any key with a new value. This change is a If two blocks are built concurrently for the same height: - Only the first applied block will succeed - The second block will be rejected with `HEIGHT_MISMATCH` -- This is correct security behavior, not a bug +- This is intended behavior to avoid forks and force all changes to be applied **exactly** in the way the creator intended -### Outdated Proofs Handling +### Partial State Handling The client library does not store the entire key-value state. To create a block, the client must receive a proof of all changed keys from the server. ---- -1. It should be impossible to create a new key if the user is not in the group, even if the key is automatically removed after a state change. -2. It should be impossible to create a key if a participant has the Add or Remove permissions. -3. Statement (1) implies that it is impossible to remove yourself from the group. +### Security -TODO: -+ handle short keys in SetValue -+ dest_user_id and dest_header must be the same size -+ check proto version in call encryption -+ Do I need some permissions to raise others flags -- NO, just Add AND Remove -+ validate group state from received block -+ validate keys -+ verify that generated block will indeed apply -- It is checked but, not explicitly -+ verify creator of network packet (pass user_id into decrypt method. Or even user_id and public key?) -+ sort nonces -+ what if we change nothing in block -+ fail if height overflows -+ CHECK(blockhain.get_height() > height_); -+ turn off the log by default -+ return specific error in get_status -+ optimize hmac -+ remove user_id from packet? -+ encrypt key (simple version) -+ channel_id in encryption -+ user_id instead of public_key in GroupBroadcast ? -+ received_messages_ in CallVerificationChain are not needed for clients -+ turn off signature verification on server -+ fix up magic of block on server -+ remove user_id from payload (we already got it externally) +There are several aspects we should be particularly careful about: -- ! VERIFY block signature during creation blockchain from block -it is impossible to do currently using just current block, because we don't know who signe the block +1. Clients must apply only blocks received from the server. This is especially important for blocks created by the client itself. The server does extra work to ensure correctness of blocks and to prevent forks. Forks per se are not a security problem, but they would lead to broken calls. -later: --+ fix tl constructor? -- add magic in container's id -- ttl of chain on server +2. Blocks should be sent to the server until a response is received. This could be either success or error. In case of an error like INVALID_BLOCK__HASH_MISMATCH, a new block could be created, but one should be careful about how the group state has been changed. -------- - -# Security guidance - -There are several we should be really careful about. - -1. Client must apply only blocks received from server. It is especially important for blocks created by the client itself. Sever does extra work to ensure correctness of blocks and that the will be no forks. Forks per se are not a security problem, but they would lead to a broken call - -2. Blocks should be sent to the server till it some answer is received. Either it is success, or error. In case of error INVALID_BLOCK__HASH_MISMATCH, new block could be created, but one should be careful about how group state has been changed. - -3. Broadcast blocks should also be sent till accepted or declined. Probably it should be allowed to skip older packets. +3. Broadcast blocks should also be sent until they are accepted or declined. diff --git a/third-party/td/td/tde2e/td/e2e/Call.cpp b/third-party/td/td/tde2e/td/e2e/Call.cpp index 104a63067d..cd8987ca7e 100644 --- a/third-party/td/td/tde2e/td/e2e/Call.cpp +++ b/third-party/td/td/tde2e/td/e2e/Call.cpp @@ -22,7 +22,6 @@ #include "td/utils/SliceBuilder.h" #include "td/utils/tl_helpers.h" #include "td/utils/tl_parsers.h" -#include "td/utils/tl_storers.h" #include #include @@ -70,6 +69,14 @@ void CallVerificationChain::on_new_main_block(const Blockchain &blockhain) { } CHECK(participant_keys_.size() == group_state.participants.size()); + commit_at_ = td::Timestamp::now(); + reveal_at_ = {}; + done_at_ = {}; + users_ = {}; + for (auto &participant : group_state.participants) { + users_[participant.user_id]; + } + if (auto it = delayed_broadcasts_.find(height_); it != delayed_broadcasts_.end()) { for (auto &[message, broadcast] : it->second) { auto status = process_broadcast(std::move(message), std::move(broadcast)); @@ -89,7 +96,7 @@ td::Status CallVerificationChain::try_apply_block(td::Slice message) { downcast_call(*kv_broadcast, td::overloaded([&](auto &broadcast) { chain_height = broadcast.chain_height_; })); if (chain_height < height_) { - LOG(INFO) << "skip old broadcast " << to_short_string(kv_broadcast); + LOG(INFO) << "Skip old broadcast " << to_short_string(kv_broadcast); // broadcast is too old return td::Status::OK(); } @@ -100,7 +107,7 @@ td::Status CallVerificationChain::try_apply_block(td::Slice message) { << "broadcast_height=" << chain_height << " height=" << height_); } - LOG(INFO) << "delay broadcast " << to_short_string(kv_broadcast); + LOG(INFO) << "Delay broadcast " << to_short_string(kv_broadcast); delayed_broadcasts_[chain_height].emplace_back(message.str(), std::move(kv_broadcast)); return td::Status::OK(); } @@ -128,9 +135,9 @@ std::string CallVerificationChain::to_short_string(e2e::object_ptr broadcast) { td::Status status; - td::UInt256 got_chain_hash{}; - downcast_call(*broadcast, td::overloaded([&](auto &broadcast) { got_chain_hash = broadcast.chain_hash_; })); - if (got_chain_hash != last_block_hash_) { + td::UInt256 broadcast_chain_hash{}; + downcast_call(*broadcast, td::overloaded([&](auto &broadcast) { broadcast_chain_hash = broadcast.chain_hash_; })); + if (broadcast_chain_hash != last_block_hash_) { status = Error(E::InvalidBroadcast_InvalidBlockHash); } if (status.is_ok()) { @@ -174,9 +181,11 @@ td::Status CallVerificationChain::process_broadcast(e2e::e2e_chain_groupBroadcas } committed_[user_id] = nonce_commit.nonce_hash_.as_slice().str(); + users_[user_id].receive_commit_at_ = td::Timestamp::now(); if (committed_.size() == participant_keys_.size()) { state_ = Reveal; + reveal_at_ = td::Timestamp::now(); } return td::Status::OK(); @@ -209,6 +218,7 @@ td::Status CallVerificationChain::process_broadcast(e2e::e2e_chain_groupBroadcas } revealed_[user_id] = nonce_reveal.nonce_.as_slice().str(); + users_[user_id].receive_reveal_at_ = td::Timestamp::now(); CHECK(!verification_state_.emoji_hash); if (revealed_.size() == participant_keys_.size()) { @@ -223,6 +233,7 @@ td::Status CallVerificationChain::process_broadcast(e2e::e2e_chain_groupBroadcas verification_state_.emoji_hash = MessageEncryption::hmac_sha512(full_nonce, last_block_hash_.as_slice()).as_slice().str(); state_ = End; + done_at_ = td::Timestamp::now(); } return td::Status::OK(); } @@ -235,7 +246,7 @@ td::Status CallEncryption::add_shared_key(td::int32 epoch, td::SecureString key, TRY_RESULT(self, group_state->get_participant(private_key_.to_public_key())); if (self.user_id != user_id_) { // should not happen - return td::Status::Error("Wrong user id in state"); + return td::Status::Error("Wrong user identifier in state"); } LOG(INFO) << "Add key from epoch: " << epoch; @@ -263,7 +274,7 @@ td::Result CallEncryption::decrypt(td::int64 user_id, td::int32 cha return td::Status::Error("Unsupported protocol version"); } if (reserved != 0) { - return td::Status::Error("reserved part of head is not zero"); + return td::Status::Error("Reserved part of head is not zero"); } if (epochs_n > MAX_ACTIVE_EPOCHS) { @@ -273,18 +284,18 @@ td::Result CallEncryption::decrypt(td::int64 user_id, td::int32 cha using td::parse; std::vector epochs; - for (int i = 0; i < epochs_n; i++) { + for (td::int32 i = 0; i < epochs_n; i++) { epochs.push_back(parser.fetch_int()); } auto unencrypted_header = encrypted_data.substr(0, encrypted_data.size() - parser.get_left_len()); std::vector encrypted_headers; - for (int i = 0; i < epochs_n; i++) { - auto encrypted_header = parser.fetch_string_raw(32); + for (td::int32 i = 0; i < epochs_n; i++) { + auto encrypted_header = parser.template fetch_string_raw(32); encrypted_headers.emplace_back(encrypted_header); } - auto encrypted_packet = parser.fetch_string_raw(parser.get_left_len()); + auto encrypted_packet = parser.template fetch_string_raw(parser.get_left_len()); parser.fetch_end(); TRY_STATUS(parser.get_status()); @@ -404,7 +415,7 @@ td::Result CallEncryption::decrypt_packet_with_secret( if (channel_id != expected_channel_id) { // currently ignore expected_channel_id - // return td::Status::Error("Channel id mismatch"); + // return td::Status::Error("Channel identifier mismatch"); } TRY_STATUS(check_not_seen(participant.public_key, channel_id, seqno)); mark_as_seen(participant.public_key, channel_id, seqno); @@ -457,8 +468,9 @@ CallVerification CallVerification::create(td::int64 user_id, PrivateKey private_ CallVerification result; result.user_id_ = user_id; result.private_key_ = std::move(private_key); - result.on_new_main_block(blockchain); result.chain_.allow_delay(); + result.chain_.set_user_id(user_id); + result.on_new_main_block(blockchain); return result; } @@ -515,8 +527,8 @@ Call::Call(td::int64 user_id, PrivateKey pk, ClientBlockchain blockchain) , blockchain_(std::move(blockchain)) , call_encryption_(user_id, private_key_) { CHECK(private_key_); - LOG(INFO) << "Create call \n" << *this; call_verification_ = CallVerification::create(user_id_, private_key_, blockchain_.get_inner_chain()); + LOG(INFO) << "Create call \n" << *this; } td::Result Call::create_zero_block(const PrivateKey &private_key, GroupStateRef group_state) { @@ -663,18 +675,74 @@ td::StringBuilder &operator<<(td::StringBuilder &sb, const CallVerificationChain break; } sb << " commit_n=" << chain.committed_.size() << " reveal_n=" << chain.revealed_.size() << "}"; - switch (chain.state_) { - case CallVerificationChain::State::Commit: - sb << "\n\t\tcommit=" - << td::transform(chain.committed_, [&](auto &key) { return chain.participant_keys_.at(key.first); }); - break; - case CallVerificationChain::State::Reveal: - sb << "\n\t\treveal=" - << td::transform(chain.revealed_, [&](auto &key) { return chain.participant_keys_.at(key.first); }); - break; - case CallVerificationChain::State::End: - break; + auto now = td::Timestamp::now(); + sb << "\n\t\t"; + sb << "commit->"; + if (chain.state_ == CallVerificationChain::State::Commit) { + sb << (now.at() - chain.commit_at_.at()) << "s->..."; + } else { + sb << (chain.reveal_at_.at() - chain.commit_at_.at()) << "s->reveal->"; + if (chain.state_ == CallVerificationChain::State::Reveal) { + sb << (now.at() - chain.reveal_at_.at()) << "s->..."; + } else { + sb << (chain.done_at_.at() - chain.reveal_at_.at()) << "s->done"; + } } + auto it = chain.users_.find(chain.user_id_); + if (it != chain.users_.end()) { + const CallVerificationChain::UserState &self = it->second; + sb << "\n\t\tself:"; + if (self.receive_commit_at_) { + sb << " commit=" << self.receive_commit_at_.at() - chain.commit_at_.at() << "s"; + } else { + sb << " commit=" << now.at() - chain.commit_at_.at() << "s..."; + } + if (chain.state_ != CallVerificationChain::State::Commit) { + if (self.receive_reveal_at_) { + sb << " reveal=" << self.receive_reveal_at_.at() - chain.reveal_at_.at() << "s"; + } else { + sb << " reveal=" << now.at() - chain.reveal_at_.at() << "s..."; + } + } + } + + { + sb << "\n\t\t"; + sb << "commit ="; + auto users = td::transform(chain.users_, [&](auto &key) { + auto t = chain.users_.at(key.first).receive_commit_at_; + if (t) { + return std::make_tuple(-(t.at() - chain.commit_at_.at()), key.first, false); + } + return std::make_tuple(-(now.at() - chain.commit_at_.at()), key.first, true); + }); + std::sort(users.begin(), users.end()); + for (auto &user : users) { + sb << " " << std::get<1>(user) << ":" << -std::get<0>(user) << "s"; + if (std::get<2>(user)) { + sb << "..."; + } + } + } + if (chain.state_ != CallVerificationChain::State::Commit) { + sb << "\n\t\t"; + sb << "reveal ="; + auto users = td::transform(chain.users_, [&](auto &key) { + auto t = chain.users_.at(key.first).receive_reveal_at_; + if (t) { + return std::make_tuple(-(t.at() - chain.reveal_at_.at()), key.first, false); + } + return std::make_tuple(-(now.at() - chain.reveal_at_.at()), key.first, true); + }); + std::sort(users.begin(), users.end()); + for (auto &user : users) { + sb << " " << std::get<1>(user) << ":" << -std::get<0>(user) << "s"; + if (std::get<2>(user)) { + sb << "..."; + } + } + } + return sb; } diff --git a/third-party/td/td/tde2e/td/e2e/Call.h b/third-party/td/td/tde2e/td/e2e/Call.h index 154b19bf86..9ed7a98696 100644 --- a/third-party/td/td/tde2e/td/e2e/Call.h +++ b/third-party/td/td/tde2e/td/e2e/Call.h @@ -10,18 +10,17 @@ #include "td/e2e/Container.h" #include "td/e2e/e2e_api.h" -#include "td/utils/HashTableUtils.h" #include "td/utils/SharedSlice.h" #include "td/utils/Slice.h" -#include "td/utils/Span.h" +#include "td/utils/SliceBuilder.h" #include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" #include "td/utils/Time.h" #include "td/utils/UInt.h" #include "td/utils/VectorQueue.h" #include #include -#include #include namespace tde2e_core { @@ -43,6 +42,9 @@ struct CallVerificationChain { CallVerificationState get_verification_state() const; CallVerificationWords get_verification_words() const; + void set_user_id(td::int64 user_id) { + user_id_ = user_id; + } void allow_delay() { delay_allowed_ = true; } @@ -66,6 +68,17 @@ struct CallVerificationChain { std::map committed_; std::map revealed_; + td::int64 user_id_{}; + + td::Timestamp commit_at_{}; + td::Timestamp reveal_at_{}; + td::Timestamp done_at_{}; + struct UserState { + td::Timestamp receive_commit_at_{}; + td::Timestamp receive_reveal_at_{}; + }; + std::map users_; + bool delay_allowed_{false}; bool may_skip_signatures_validation_{false}; std::map>>> @@ -74,7 +87,7 @@ struct CallVerificationChain { class CallEncryption { public: - explicit CallEncryption(td::int64 user_id, PrivateKey private_key); + CallEncryption(td::int64 user_id, PrivateKey private_key); td::Status add_shared_key(td::int32 epoch, td::SecureString key, GroupStateRef group_state); void forget_shared_key(td::int32 epoch); @@ -83,7 +96,7 @@ class CallEncryption { private: static constexpr double FORGET_EPOCH_DELAY = 10; - static constexpr int MAX_ACTIVE_EPOCHS = 15; + static constexpr td::int32 MAX_ACTIVE_EPOCHS = 15; td::int64 user_id_{}; PrivateKey private_key_; @@ -194,7 +207,7 @@ struct Call { TRY_STATUS(call_verification_.receive_inbound_message(local_verification_message)); return get_verification_state(); } - friend td::StringBuilder &operator<<(td::StringBuilder &builder, const Call &call); + friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Call &call); private: td::Status status_{td::Status::OK()}; diff --git a/third-party/td/td/tde2e/td/e2e/EncryptedStorage.h b/third-party/td/td/tde2e/td/e2e/EncryptedStorage.h index 6a12379863..3c950f6045 100644 --- a/third-party/td/td/tde2e/td/e2e/EncryptedStorage.h +++ b/third-party/td/td/tde2e/td/e2e/EncryptedStorage.h @@ -324,8 +324,7 @@ struct EncryptedStorage { static td::Result create(td::Slice last_block, PrivateKey pk) { auto public_key = pk.to_public_key(); auto secret_for_key = MessageEncryption::hmac_sha512(pk.to_secure_string(), "EncryptedStorage::secret_for_key"); - auto secret_for_value = - MessageEncryption::hmac_sha512(pk.to_secure_string(), "EncryptedStorage::secret_for_value"); + auto secret_for_value = MessageEncryption::hmac_sha512(pk.to_secure_string(), "EncryptedStorage::secret_for_value"); ClientBlockchain blockchain; if (last_block.empty()) { TRY_RESULT_ASSIGN(blockchain, ClientBlockchain::create_empty()); diff --git a/third-party/td/td/tde2e/td/e2e/Encryption.md b/third-party/td/td/tde2e/td/e2e/Encryption.md index e457e67913..78217b9f8c 100644 --- a/third-party/td/td/tde2e/td/e2e/Encryption.md +++ b/third-party/td/td/tde2e/td/e2e/Encryption.md @@ -1,30 +1,64 @@ -# Encryption in secret group calls +# Encryption in Secret Group Calls -A group call consists of three parts: +A group call consists of three primary components: -1. Blockchain shared by all group members. It serves as a synchronization point for all changes in the group. It is also used to generate verification codes for MitM protection, as its hash depends on the whole history of call changes, including hash of shared key. Each block consists of changes to call participants and new shared keys encrypted for each participant. +1. **Blockchain** shared among all group members. This serves as a synchronization point for all group changes and generates verification codes for MitM protection. Its hash incorporates the entire history of call changes, including the shared key hash. Each block contains call participant changes and new shared keys individually encrypted for each participant. -2. Encryption protocol for network packets. It is currently designed to send a relatively small amount of packets as we encrypt at the video frame and audio frame level, not at the network level. We also sign each packet, so it is possible to verify authorship. Similar primitives are also used to encrypt a shared key for each participant. +2. **Encryption protocol** for network packets. Designed for efficiency, this protocol encrypts at the video and audio frame level rather than the network level. Each packet is signed to enable authorship verification. Similar encryption primitives secure the shared key for each participant. -3. Protocol for emoji generation. Naive generation of emoji straight from blockchain hash would not work, as it could be brute forced by the block's creator. To handle this we introduce a simple two-phase protocol. In the first phase each party commits to a value i.e. publishes its hash. In the second phase each party reveals a value. Hash of all values is used to mix unpredictable randomness into the blockchain's hash. +3. **Emoji generation protocol**. Direct generation of emojis from the blockchain hash is vulnerable to brute-force attacks by block creators. To mitigate this, we implement a two-phase protocol: first, each party commits to a value by publishing its hash; second, each party reveals their value. The combined hash of all values introduces unpredictable randomness into the blockchain hash. -Let's review each part in detail: +Let's examine each component in detail: ## Blockchain -The blockchain serves as a distributed ledger for group call state management. Each block contains a list of call participants and new shared keys encrypted for each of them. +The blockchain functions as a distributed ledger for managing group call state. Each block contains a participant list and new shared keys individually encrypted for all participants. -Hash of last block is used to generate **verification words** +The hash of the most recent block generates **verification words**. -Hash of last block with mixed unpredictable random is used to generate **verification emojis** +The hash of the most recent block, combined with unpredictable random values, generates **verification emojis**. + +For improved user experience, any person can currently join a call with server permission, without requiring explicit confirmation from existing participants. +While the blockchain supports an explicit confirmation mode, we currently use `external_permissions` in the blockchain state to allow self-addition to groups. + +Call security in this scheme depends on emoji verification by each participant. This approach could be enhanced in future versions with persistent user identities. + +### Typical Workflow + +#### Joining a Call + +To join a call, a user must: +- Request the latest blockchain block from the server +- Create a new block containing updated state that includes themselves and a new shared key encrypted for all participants (including themselves) +- Submit this block to the server + +The server validates that the block only adds the new user and attempts to apply it to the blockchain: + +- Upon success, all participants receive the new block, update their blockchain, and implement the new shared key +- If conflicts exist (such as another block already applied at the same height), the operation fails + +#### Removing a Participant from the Call + +When a participant becomes inactive, they must be removed from the call. This process follows a similar pattern to joining but is initiated by any active participant. + +For comprehensive details, refer to the [Blockchain documentation](Blockchain.md). + +#### Security +- Clients must only apply blocks as received from the server, which prevents blockchain forks when the server operates correctly +- The blockchain state must be explicitly displayed in the UI, even when the server withholds information about certain participants +- MitM protection relies on either verification words (currently not used in UI) or emojis; all participants must verify they see identical emojis +- If the server delivers different blocks to different participants, the resulting fork hashes will permanently differ +- The creator of a new key must be included as a participant in the block since they generate the shared key +- Notably, participants cannot remove themselves from a group, as this would require generating a new shared key for the remaining participants +- Active participants should remove inactive users from the group, particularly those blocking the emoji generation process +- In the current implementation without explicit confirmations, signatures provide limited security value since anyone with a key can join a call; however, they enhance protocol robustness -For details see [this document](Blockchain.md). ## Encryption -### Primitives used +### Core Primitives -We use several encryption primitives similar to MTProto 2.0. Here are the key functions: +Our encryption system utilizes several primitives similar to MTProto 2.0. The key functions include: #### encrypt_data(payload, secret) - encrypts payload with shared secret @@ -32,7 +66,7 @@ We use several encryption primitives similar to MTProto 2.0. Here are the key fu 2) padding = random_bytes(padding_size) with padding[0] = padding_size 3) padded_data = padding || payload 4) large_secret = KDF(secret, "tde2e_encrypt_data") -5) encrypt_secret = larges_secret[0:32] +5) encrypt_secret = large_secret[0:32] 6) hmac_secret = large_secret[32:64] 7) msg_id = HMAC-SHA512(hmac_secret, padded_data)[0:16] 8) (aes_key, aes_iv) = HMAC-SHA512(encrypt_secret, msg_id)[0:48] @@ -46,89 +80,99 @@ We use several encryption primitives similar to MTProto 2.0. Here are the key fu 3) (aes_key, aes_iv) = HMAC-SHA512(encrypt_secret, msg_id)[0:48] 4) encrypted_header = aes_cbc(aes_key, aes_iv, header) -KDF = HMAC-SHA512, also +Note: KDF refers to HMAC-SHA512 throughout this document -Decryption must verify that the message has a valid `msg_id` by computing HMAC over the decrypted data and comparing with the message's msg_id before accepting the payload. +#### Security +- Verification of `msg_id` during decryption is essential before accepting any payload +- Replay protection is implemented at a higher protocol level -### Packet encryption +### Packet Encryption -This is how we encrypt actual packets: +The encryption process for video and audio packets follows this sequence: -#### encrypt_packet(payload, active_epochs, user_id, channel_id, seq_num, private_key) - encrypts a packet +#### encrypt_packet(payload, active_epochs, user_id, channel_id, seqno, private_key) - encrypts a packet -First, we generate header_a describing epochs (blockchain heights) used +First, generate header_a describing the epochs (blockchain heights) in use: 1) epoch_id[i] = active_epochs[i].epoch (4 bytes) -2) header_a = active_epochs.size (4 bytes) || epoch_id[0] || epoch_id[1] || ... +2) header_a = active_epochs.size (4 bytes) || epoch_id[0] || epoch_id[1] || ... -Then, we encrypt payload with one_time_key. Signature in payload includes unencrypted header +Next, encrypt the payload using a one-time key. The signature in the payload incorporates the unencrypted header: 1) one_time_key = random(32) -2) packet_payload = channel_id (4 bytes) || seq_num (4 bytes) || payload +2) packet_payload = channel_id (4 bytes) || seqno (4 bytes) || payload 3) to_sign = HMAC-SHA512(header_a, packet_payload) 4) signature = sign(to_sign, private_key) // 64 bytes 5) signed_payload = packet_payload || signature 6) encrypted_payload = encrypt_data(signed_payload, one_time_key) -Finally, encrypt one_time_key with shared secret from each active epoch +Finally, encrypt the one-time key using the shared secret from each active epoch: 1) encrypted_key[i] = encrypt_header(one_time_key, encrypted_payload, active_epochs[i].shared_key) (32 bytes) 2) header_b = encrypted_key[0] || encrypted_key[1] || ... +The complete packet format is: header_a || header_b || encrypted_payload -result = header_a || header_b || encrypted_payload +#### Security +- The seqno value is unique for each (public key, channel_id) pair, providing protection against replay attacks; receivers should maintain records of recent seqno values and reject packets with known or outdated seqno values +- During decryption, the public key must be retrieved from the blockchain state using the externally provided user_id; this public key verifies the signature -- seqno is unique for each pair (public key; channel_id), so it is used as protection from replay attacks -- list of active epochs is also signed +### Shared Key Encryption -During decryption, we must take public key from blockchain's state. We also must verify user_id and channel_id. Expected user_id and channel_id are known externally +When modifying group state or shared key, the new shared key is encrypted for each participant using their respective public keys from the blockchain state: -### Encryption of shared key +1. Generate new cryptographic material: + - `group_shared_key = random(32 bytes)` - the new shared key for the call + - `one_time_secret = random(32 bytes)` - secret used for encryption + - `e_private_key, e_public_key = generate_private_key()` - key pair used to encrypt the one_time_secret -When changing group state or shared key, the following process is used: - -1. Generate new keys: - - `e_private_key = generate_private_key()` - - `group_shared_key = random(32 bytes)` - - `one_time_secret = random(32 bytes)` - -2. Encrypt group shared key: +2. Encrypt the group shared key: - `encrypted_group_shared_key = encrypt_data(group_shared_key, one_time_secret)` -3. For each participant: +3. For each participant in the group: - `shared_key = compute_shared_secret(e_private_key, participant.public_key)` - `encrypted_header = encrypt_header(one_time_secret, encrypted_group_shared_key, shared_key)` +The `e_public_key`, `encrypted_group_shared_key`, and `encrypted_header` for each participant are recorded in the blockchain state. -Key properties: -- Each participant may decrypt their version of header with their private key -- All participants will decrypt the same key (if they decrypt anything) +#### Security +- We cannot guarantee that every participant will successfully decrypt the key +- However, all participants who can decrypt will obtain the identical `shared_secret` +- Participants unable to decrypt the key must exit the call immediately, and specifically must not participate in the emoji generation process -## Emoji generation -The emoji hash generation uses a two-phase commit-reveal protocol to prevent brute-forcing by block creators. +## Emoji Generation -### Protocol Steps +The emoji hash generation employs a two-phase commit-reveal protocol to prevent block creators from performing brute-force attacks. + +#### Protocol Workflow 1. Initial Setup: - Each participant generates a random 32-byte nonce - `nonce_hash = SHA256(nonce)` 2. Commit Phase: - - Each participant broadcasts `nonce_hash` with their signature - - Must wait for all participants to commit - - State transitions to Reveal only after all commits received + - Each participant broadcasts their `nonce_hash` with a signature + - The system waits for all participants to submit commits + - Transition to the Reveal phase occurs only after receiving all commits 3. Reveal Phase: - Each participant broadcasts their original `nonce` with signature - - Verifies that `SHA256(revealed_nonce) == committed_hash` - - Must wait for all participants to reveal + - The system verifies that `SHA256(revealed_nonce) == committed_hash` + - The process waits for all participants to complete the reveal step 4. Final Hash Generation: - - Concatenate all revealed nonces in order - - `emoji_hash = SHA512(blockchain_hash || concatenated_sorted_nonces)` + - All revealed nonces are concatenated in order + - `emoji_hash = HMAC-SHA512(concatenated_sorted_nonces, blockchain_hash)` -Tl schema used for such broadcast is the following + +The TL schema for this broadcast mechanism is: ``` e2e.chain.groupBroadcastNonceCommit signature:int512 public_key:int256 chain_height:int32 chain_hash:int256 nonce_hash:int256 = e2e.chain.GroupBroadcast; e2e.chain.groupBroadcastNonceReveal signature:int512 public_key:int256 chain_height:int32 chain_hash:int256 nonce:int256 = e2e.chain.GroupBroadcast; ``` -The signature is for the TL serialization of the same object with zeroed signature \ No newline at end of file +The signature applies to the TL serialization of the same object with a zeroed signature field. + +#### Security +- The resulting `emoji_hash` remains completely unpredictable for all protocol participants +- For simplicity and protection against bugs, participants should only apply messages (including those they created themselves) when received from the server; this approach ensures that when any participant sees a packet, all participants see the packet +- Consequently, emojis won't be displayed before all clients with reasonable internet connections can also view them +- The two-phase commit-reveal protocol prevents any participant from biasing the emoji selection toward a specific pattern diff --git a/third-party/td/td/tde2e/td/e2e/Keys.cpp b/third-party/td/td/tde2e/td/e2e/Keys.cpp index 002b0fbe12..6d5f6186ef 100644 --- a/third-party/td/td/tde2e/td/e2e/Keys.cpp +++ b/third-party/td/td/tde2e/td/e2e/Keys.cpp @@ -4,9 +4,10 @@ // 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 "MessageEncryption.h" #include "td/e2e/Keys.h" +#include "td/e2e/MessageEncryption.h" + #include "td/utils/Ed25519.h" #include "td/utils/misc.h" #include "td/utils/SliceBuilder.h" diff --git a/third-party/td/td/tde2e/td/e2e/MessageEncryption.cpp b/third-party/td/td/tde2e/td/e2e/MessageEncryption.cpp index f741d79af3..a131988b36 100644 --- a/third-party/td/td/tde2e/td/e2e/MessageEncryption.cpp +++ b/third-party/td/td/tde2e/td/e2e/MessageEncryption.cpp @@ -29,7 +29,8 @@ td::AesCbcState MessageEncryption::calc_aes_cbc_state_from_hash(td::Slice hash) } td::SecureString MessageEncryption::gen_random_prefix(td::int64 data_size, td::int64 min_padding) { - td::SecureString buff(td::narrow_cast(((min_padding + 15 + data_size) & ~static_cast(15)) - data_size), '\0'); + td::SecureString buff( + td::narrow_cast(((min_padding + 15 + data_size) & ~static_cast(15)) - data_size), '\0'); td::Random::secure_bytes(buff.as_mutable_slice()); buff.as_mutable_slice().ubegin()[0] = td::narrow_cast(buff.size()); CHECK((buff.size() + data_size) % 16 == 0); @@ -37,13 +38,13 @@ td::SecureString MessageEncryption::gen_random_prefix(td::int64 data_size, td::i } td::SecureString MessageEncryption::gen_deterministic_prefix(td::int64 data_size, td::int64 min_padding) { - td::SecureString buff(td::narrow_cast(((min_padding + 15 + data_size) & ~static_cast(15)) - data_size), '\0'); + td::SecureString buff( + td::narrow_cast(((min_padding + 15 + data_size) & ~static_cast(15)) - data_size), '\0'); buff.as_mutable_slice().ubegin()[0] = td::narrow_cast(buff.size()); CHECK((buff.size() + data_size) % 16 == 0); return buff; } - td::SecureString MessageEncryption::kdf(td::Slice secret, td::Slice password, int iterations) { td::SecureString new_secret(64); pbkdf2_sha512(secret, password, iterations, new_secret.as_mutable_slice()); @@ -99,13 +100,13 @@ td::Result MessageEncryption::decrypt_data(td::Slice encrypted td::SecureString decrypted_data(encrypted_data.size(), '\0'); cbc_state.decrypt(encrypted_data, decrypted_data.as_mutable_slice()); - auto got_large_msg_id = hmac_sha512(hmac_secret, decrypted_data); - auto got_msg_id = got_large_msg_id.as_slice().substr(0, 16); + auto expected_large_msg_id = hmac_sha512(hmac_secret, decrypted_data); + auto expected_msg_id = expected_large_msg_id.as_slice().substr(0, 16); // check hash int is_mac_bad = 0; for (size_t i = 0; i < 16; i++) { - is_mac_bad |= got_msg_id[i] ^ msg_id[i]; + is_mac_bad |= expected_msg_id[i] ^ msg_id[i]; } if (is_mac_bad != 0) { return td::Status::Error("Failed to decrypt: msg_id mismatch"); diff --git a/third-party/td/td/tde2e/td/e2e/MessageEncryption.h b/third-party/td/td/tde2e/td/e2e/MessageEncryption.h index bfd9c3df6d..aac7090d51 100644 --- a/third-party/td/td/tde2e/td/e2e/MessageEncryption.h +++ b/third-party/td/td/tde2e/td/e2e/MessageEncryption.h @@ -19,7 +19,8 @@ class MessageEncryption { static td::Result decrypt_data(td::Slice encrypted_data, td::Slice secret); static td::SecureString hmac_sha512(td::Slice key, td::Slice message); static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations); - static td::Result encrypt_header(td::Slice decrypted_header, td::Slice encrypted_message, td::Slice secret); + static td::Result encrypt_header(td::Slice decrypted_header, td::Slice encrypted_message, + td::Slice secret); static td::Result decrypt_header(td::Slice encrypted_header, td::Slice encrypted_message, td::Slice secret); diff --git a/third-party/td/td/tde2e/td/e2e/TestBlockchain.cpp b/third-party/td/td/tde2e/td/e2e/TestBlockchain.cpp index 8ba8650fba..f9012d30a4 100644 --- a/third-party/td/td/tde2e/td/e2e/TestBlockchain.cpp +++ b/third-party/td/td/tde2e/td/e2e/TestBlockchain.cpp @@ -14,8 +14,6 @@ #include "td/utils/overloaded.h" #include "td/utils/simple_tests.h" #include "td/utils/SliceBuilder.h" -#include "td/utils/tl_helpers.h" -#include "td/utils/tl_parsers.h" #include #include diff --git a/third-party/td/td/tde2e/td/e2e/TestBlockchain.h b/third-party/td/td/tde2e/td/e2e/TestBlockchain.h index a2f96e0ea0..85c51e13c8 100644 --- a/third-party/td/td/tde2e/td/e2e/TestBlockchain.h +++ b/third-party/td/td/tde2e/td/e2e/TestBlockchain.h @@ -124,7 +124,7 @@ struct BlockBuilder { //!!! we should check it fails with invalid key length!!! BlockBuilder &set_value(td::Slice key, td::Slice value); - BlockBuilder &with_group_state(const std::vector &users, bool in_changes = true, + BlockBuilder &with_group_state(const std::vector &users, bool in_changes = true, bool in_proof = true, td::int32 external_permissions = 0); BlockBuilder &skip_group_state_proof(); BlockBuilder &with_shared_key(const std::vector &user_ids, bool in_changes = true, bool in_proof = true); diff --git a/third-party/td/td/tde2e/td/e2e/e2e_api.cpp b/third-party/td/td/tde2e/td/e2e/e2e_api.cpp index 1933557278..82f109c63a 100644 --- a/third-party/td/td/tde2e/td/e2e/e2e_api.cpp +++ b/third-party/td/td/tde2e/td/e2e/e2e_api.cpp @@ -20,19 +20,20 @@ #include "td/utils/base64.h" #include "td/utils/common.h" #include "td/utils/int_types.h" +#include "td/utils/logging.h" #include "td/utils/overloaded.h" #include "td/utils/Random.h" #include "td/utils/SharedSlice.h" +#include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" #include "td/utils/Span.h" #include "td/utils/Status.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/tl_parsers.h" #include "td/utils/UInt.h" #include -namespace tde2e_api { -} // namespace tde2e_api - namespace tde2e_core { namespace api = tde2e_api; @@ -469,7 +470,8 @@ class KeyChain { TRY_RESULT(call_ref, to_call_ref(call_id)); return call_ref->encrypt(channel_id, message); } - td::Result call_decrypt(api::CallId call_id, api::UserId user_id, api::CallChannelId channel_id, td::Slice message) { + td::Result call_decrypt(api::CallId call_id, api::UserId user_id, api::CallChannelId channel_id, + td::Slice message) { TRY_RESULT(call_ref, to_call_ref(call_id)); return call_ref->decrypt(user_id, channel_id, message); } @@ -766,7 +768,7 @@ Result call_describe_block(Slice block_slice) { td::TlParser parser(block_str); auto magic = parser.fetch_int(); if (magic != td::e2e_api::e2e_chain_block::ID) { - return td::Status::Error("wrong magic"); + return td::Status::Error("Wrong magic"); } auto block = td::e2e_api::e2e_chain_block::fetch(parser); parser.fetch_end(); diff --git a/third-party/td/td/tde2e/td/e2e/e2e_api.h b/third-party/td/td/tde2e/td/e2e/e2e_api.h index 06855a47f4..29b2a19b4a 100644 --- a/third-party/td/td/tde2e/td/e2e/e2e_api.h +++ b/third-party/td/td/tde2e/td/e2e/e2e_api.h @@ -174,7 +174,6 @@ Result login_finish_for_bob(LoginId bob_login_id, UserId alice_use Result login_destroy(LoginId login_id); Result login_destroy_all(); - // Personal info // TODO: UserId @@ -296,7 +295,8 @@ struct CallState { }; Result call_create_zero_block(PrivateKeyId private_key_id, const CallState &initial_state); -Result call_create_self_add_block(PrivateKeyId private_key_id, Slice previous_block, const CallParticipant &self); +Result call_create_self_add_block(PrivateKeyId private_key_id, Slice previous_block, + const CallParticipant &self); Result call_create(UserId user_id, PrivateKeyId private_key_id, Slice last_block); Result call_describe(CallId call); diff --git a/third-party/td/td/tde2e/td/e2e/encryption_test.py b/third-party/td/td/tde2e/td/e2e/encryption_test.py index 27e77e3221..f4eee9bf11 100644 --- a/third-party/td/td/tde2e/td/e2e/encryption_test.py +++ b/third-party/td/td/tde2e/td/e2e/encryption_test.py @@ -8,10 +8,10 @@ def generate_deterministic_padding(data_size, min_padding): # Calculate padding size to make total size multiple of 16 padding_size = ((min_padding + 15 + data_size) & -16) - data_size padding = bytearray(padding_size) - + # Only set the first byte to padding size, leave rest as zeros padding[0] = padding_size - + return padding @@ -30,7 +30,7 @@ def kdf(key, info): def encrypt_data_with_prefix(data, secret): # Ensure data is multiple of 16 bytes assert len(data) % 16 == 0 - + # Generate encryption and HMAC secrets large_secret = kdf(secret, "tde2e_encrypt_data") encrypt_secret = large_secret[:32] @@ -39,21 +39,21 @@ def encrypt_data_with_prefix(data, secret): # Generate message ID using HMAC large_msg_id = hmac_sha512(hmac_secret, data) msg_id = large_msg_id[:16] # Use first 16 bytes as message ID - + # Create result buffer result = bytearray(len(data) + 16) result[0:16] = msg_id - + # Generate key and IV for encryption encryption_secret = hmac_sha512(encrypt_secret, msg_id) key = encryption_secret[:32] iv = encryption_secret[32:48] - + # Encrypt data cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(data) result[16:] = encrypted - + return bytes(result) def encrypt_data_with_deterministic_padding(data, secret): @@ -72,21 +72,21 @@ def encrypt_header(header, encrypted_message, secret): # Verify inputs assert len(header) == 32 assert len(encrypted_message) >= 16 - + # Get msg_id from the beginning of encrypted message msg_id = encrypted_message[0:16] encryption_key = kdf(secret, "tde2e_encrypt_header")[:32] - + # Generate encryption key and IV from secret and message ID encryption_secret = kdf(encryption_key, msg_id) key = encryption_secret[:32] iv = encryption_secret[32:48] - + # Encrypt header with AES-CBC cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_header = cipher.encrypt(header) - + return encrypted_header def decrypt_data(encrypted_data, secret): @@ -95,36 +95,36 @@ def decrypt_data(encrypted_data, secret): raise ValueError("Failed to decrypt: data is too small") if len(encrypted_data) % 16 != 0: raise ValueError("Failed to decrypt: data size is not divisible by 16") - + # Extract msg_id and encrypted part msg_id = encrypted_data[0:16] encrypted_part = encrypted_data[16:] - + # Generate encryption and HMAC secrets large_secret = kdf(secret, "tde2e_encrypt_data") hmac_secret = large_secret[32:64] encrypt_secret = large_secret[:32] - + # Generate key and IV for decryption encryption_secret = hmac_sha512(encrypt_secret, msg_id) key = encryption_secret[:32] iv = encryption_secret[32:48] - + # Decrypt with AES-CBC cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(encrypted_part) - + # Verify msg_id large_msg_id = hmac_sha512(hmac_secret, decrypted_data) expected_msg_id = large_msg_id[:16] if msg_id != expected_msg_id: raise ValueError("Failed to decrypt: msg_id mismatch") - + # Extract actual data by removing padding prefix_size = decrypted_data[0] if prefix_size > len(decrypted_data) or prefix_size < 16: raise ValueError("Failed to decrypt: invalid prefix size") - + return decrypted_data[prefix_size:] def decrypt_header(encrypted_header, encrypted_message, secret): @@ -133,20 +133,20 @@ def decrypt_header(encrypted_header, encrypted_message, secret): raise ValueError("Failed to decrypt: invalid header size") if len(encrypted_message) < 16: raise ValueError("Failed to decrypt: invalid message size") - + # Get msg_id from the beginning of encrypted message msg_id = encrypted_message[0:16] encryption_key = kdf(secret, "tde2e_encrypt_header")[:32] - + # Generate encryption key and IV from secret and msg_id encryption_secret = kdf(encryption_key, msg_id) key = encryption_secret[:32] iv = encryption_secret[32:48] - + # Decrypt header with AES-CBC cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_header = cipher.decrypt(encrypted_header) - + return decrypted_header def generate_random_bytes(length): @@ -156,7 +156,7 @@ def generate_test_vectors(): # Generate random secrets and headers for each test secret = generate_random_bytes(32) header = generate_random_bytes(32) - + # Test vectors with different data patterns test_vectors = [ { @@ -202,29 +202,29 @@ def generate_test_vectors(): "header": binascii.hexlify(header).decode('ascii') }, ] - + # Generate encrypted data and headers for each test vector for vec in test_vectors: secret = binascii.unhexlify(vec["secret"]) data = binascii.unhexlify(vec["data"]) header = binascii.unhexlify(vec["header"]) - + encrypted = encrypt_data_with_deterministic_padding(data, secret) encrypted_header = encrypt_header(header, encrypted, secret) - + # Test decryption decrypted = decrypt_data(encrypted, secret) if decrypted != data: raise ValueError(f"Decryption failed for test vector: {vec['name']}") - + # Test header decryption decrypted_header = decrypt_header(encrypted_header, encrypted, secret) if decrypted_header != header: raise ValueError(f"Header decryption failed for test vector: {vec['name']}") - + vec["encrypted"] = binascii.hexlify(encrypted).decode('ascii') vec["encrypted_header"] = binascii.hexlify(encrypted_header).decode('ascii') - + return test_vectors def print_cpp_header(test_vectors): @@ -242,7 +242,7 @@ def print_cpp_header(test_vectors): print("};") print("\ninline std::vector get_test_vectors() {") print(" return {") - + for vec in test_vectors: print(" {") print(f' "{vec["name"]}",') @@ -252,14 +252,14 @@ def print_cpp_header(test_vectors): print(f' "{vec["encrypted"]}",') print(f' "{vec["encrypted_header"]}"') print(" },") - + print(" };") print("}") print("\n} // namespace tde2e_core") if __name__ == "__main__": test_vectors = generate_test_vectors() - + # Get the directory of the current script script_dir = os.path.dirname(os.path.abspath(__file__)) # Go up two levels to reach the tde2e directory @@ -267,7 +267,7 @@ if __name__ == "__main__": # Create the test directory if it doesn't exist test_dir = os.path.join(tde2e_dir, "test") os.makedirs(test_dir, exist_ok=True) - + # Write the header file header_path = os.path.join(test_dir, "EncryptionTestVectors.h") with open(header_path, "w") as f: @@ -276,4 +276,4 @@ if __name__ == "__main__": old_stdout = sys.stdout sys.stdout = f print_cpp_header(test_vectors) - sys.stdout = old_stdout \ No newline at end of file + sys.stdout = old_stdout diff --git a/third-party/td/td/tde2e/test/EncryptionTestVectors.h b/third-party/td/td/tde2e/test/EncryptionTestVectors.h index 4255948de5..8c233e8848 100644 --- a/third-party/td/td/tde2e/test/EncryptionTestVectors.h +++ b/third-party/td/td/tde2e/test/EncryptionTestVectors.h @@ -1,3 +1,9 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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 @@ -6,73 +12,70 @@ namespace tde2e_core { struct TestVector { - std::string name; - std::string secret; - std::string data; - std::string header; - std::string encrypted; - std::string encrypted_header; + std::string name; + std::string secret; + std::string data; + std::string header; + std::string encrypted; + std::string encrypted_header; }; inline std::vector get_test_vectors() { - return { - { - "empty_message", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "9e2476ad849d22a44d9135c5c3c5e8b52d4f88473ae8745f3a9cec4d54780caf", - "de3539a5e10b20a3a0cffc24dbbd76b3a7e0eeab402cb38396d64785a3ab7c25" - }, - { - "simple_message", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "48656c6c6f2c20576f726c6421", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "872b141f6d1e3554ead471dffd0fee5c04a0b04260eafcca9187158ce84c4487e9429df876706753913de61029402478", - "013037e02dc8dbf13598d96eb333a69a930efe043bac7dce0d6edfd1abc6bd2f" - }, - { - "long_message", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "0fd497802c755b9c6fb9d58d38b9fb5fbd7dc9ccf78af3eb003a22d46bfa38894135d976bbec0f3cadc2b61a4d8648120dada26b2c3153def20fb9e370def31c802e202846946b5cb1bc2c01a7b46605292d6043ffb8f4040aaf18914e1c93fe9e683f088e23ee5e1551f00068a23fa3ebb8d6b9dcbf7a9072b12323b1a64247ba9bc7d277b08cfcc37387a0d24afca170dc027d8f0212eb62bccd9555de98936047c9bfc6a03aa539073f458795bd94b9b43003fe2299805f90c1d30ca631c8054242687e41e890bf4d744b529d7e96ea48a5bcec700993b5e980173049cf9df6f93d62ccc06d933fd6d7063890fa2d", - "697558ba60e789622ee90dffc55f1f1148cfea568c573b257fcf2083cf8aad0a" - }, - { - "random_message", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "e61d2b05302c49faa9fac6a893957a846a2b30801dc171cf62ffec92297b10bfc4a82445839ecb4c5800ffb37c5356d4b95fc565ddb2e7f3e21f2936a952373c", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "515f0223f27302ca5e952ec978ec66bcdf04e7f72ae3a8e011e21457b355891d12e158c9b2dfb921520a0e5f531e6a20e95d42635b084a0b38a6e658f4a4181f85ea83756d316cea538cc34592491eef3e3530c34c63a693e3372cbdd0076628", - "b5bdfe6a3400c5d299d94756af4c18bbb8cca4a2635beeba3d89bbaa0025d9d3" - }, - { - "very_long_message", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "de6e0dbde23e04b6be3d4c46868a1171c6b879227b19e370765823390501b195783c356af2156e828e511473d5698c9bd1cb4a70b7e209bbb02f8dda044af02ab6869478b211e3a17d72fbe6289c1b2e6132cb141e89cf72cf70defd67a23fbfe8e718a09a9c6a345565ebdc73e1f59927744801eb4e6f0b30d2705ea181e02a4030252330ac73bc4a4d51d2ccafb2f3f62abe3e81163be325ac823571c8dfae739f70bff39164e3cec7f53b88f97735dc25ac0c0630b1b41a131a979ba5164ab92e103716e9096e2fb5a6434b31d2c3673fca7e54dcdcce3807bfd43ee7bc3422868094305a9847ce7666bf57e49fb3c2009cb30eb3ba955942b923ec2c2a4e0341d86b524b198974bdce9cf863ab3526e9e03e53399dad20fc218554567c440536a31e05573f4cb930ba6a", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "e296a98cd96154b27d4b94cc520a8f028c61bd5ea69d22f0c3c13658c4bd5db0bd6661d0d17fac791c5cc06b3a54a853916bd7bf8a00644acc53cca43b6f51c66bc6a6cb98cf8d9f23dcd639089d1d9d3dfbc8829a1a81638317bbd3edb070c1dae181d97605eb42a6111b8696a16cc3e42639e38e93f872111fd67e934740f73a57df0e6edc6726c9aff99682bfcb7ddd99a3bef30da70d3c21e590fd02defc23be9f7f243e45a56b13562b8ecea09a14ac5af3a0500cb52f73f1bffea4a6624644da0bc4d112e5ee684b13a2ae8dbf401a5a8e581295a9dc876eaeb8ae4d732fb78d50a92c302d15c0a2308e43fc6e147ec162b28a534d6c95a2020fa141f3ce7f7dd25ff000d35f732a145abf31b3ff4d0da015d39da3b0fc70e692b567a9507be59e8c91a63fe809c495d76e70ec857cc24978771fc9314251e2bc4b1b24e0448514f97a6d438255cba8c1854019", - "8dab56b50ce6a4315b6e0b4eb32c39dfe817cb462ee9e070e2a60bb843c16107" - }, - { - "message_with_special_chars", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "2014d1125081316a334896cbe5aed251d9fba6f3422afbc7e7bc9019e10e184241d18f71ef86603b5ce03e1c351e3dfff8adfb8f3498300a712ec134b367c533", - "6f5a5de410db3606ff94fa14d0e084a6ba5a51241a2abf45ac593e4748c477ce" - }, - { - "message_with_unicode", - "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", - "48656c6c6f2c20e4b896e7958c21", - "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", - "7ae70cb905f23109477b4d758d3907238ff4c37e2f351f086268ba3e85cef0257af58a70d8838c7b9c044f30382c2ccf", - "7dfb2d2e76df39b2fafdf01de009088a7e4d045b8630941986111ef2010d7c4a" - }, - }; + return { + {"empty_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", "", + "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "9e2476ad849d22a44d9135c5c3c5e8b52d4f88473ae8745f3a9cec4d54780caf", + "de3539a5e10b20a3a0cffc24dbbd76b3a7e0eeab402cb38396d64785a3ab7c25"}, + {"simple_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "48656c6c6f2c20576f726c6421", "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "872b141f6d1e3554ead471dffd0fee5c04a0b04260eafcca9187158ce84c4487e9429df876706753913de61029402478", + "013037e02dc8dbf13598d96eb333a69a930efe043bac7dce0d6edfd1abc6bd2f"}, + {"long_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787" + "878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878" + "787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787" + "8787878787878787878787878787878787878787878787878787878787878787878", + "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "0fd497802c755b9c6fb9d58d38b9fb5fbd7dc9ccf78af3eb003a22d46bfa38894135d976bbec0f3cadc2b61a4d8648120dada26b2c3153d" + "ef20fb9e370def31c802e202846946b5cb1bc2c01a7b46605292d6043ffb8f4040aaf18914e1c93fe9e683f088e23ee5e1551f00068a23f" + "a3ebb8d6b9dcbf7a9072b12323b1a64247ba9bc7d277b08cfcc37387a0d24afca170dc027d8f0212eb62bccd9555de98936047c9bfc6a03" + "aa539073f458795bd94b9b43003fe2299805f90c1d30ca631c8054242687e41e890bf4d744b529d7e96ea48a5bcec700993b5e980173049" + "cf9df6f93d62ccc06d933fd6d7063890fa2d", + "697558ba60e789622ee90dffc55f1f1148cfea568c573b257fcf2083cf8aad0a"}, + {"random_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "e61d2b05302c49faa9fac6a893957a846a2b30801dc171cf62ffec92297b10bfc4a82445839ecb4c5800ffb37c5356d4b95fc565ddb2e7f" + "3e21f2936a952373c", + "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "515f0223f27302ca5e952ec978ec66bcdf04e7f72ae3a8e011e21457b355891d12e158c9b2dfb921520a0e5f531e6a20e95d42635b084a0" + "b38a6e658f4a4181f85ea83756d316cea538cc34592491eef3e3530c34c63a693e3372cbdd0076628", + "b5bdfe6a3400c5d299d94756af4c18bbb8cca4a2635beeba3d89bbaa0025d9d3"}, + {"very_long_message", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "de6e0dbde23e04b6be3d4c46868a1171c6b879227b19e370765823390501b195783c356af2156e828e511473d5698c9bd1cb4a70b7e209b" + "bb02f8dda044af02ab6869478b211e3a17d72fbe6289c1b2e6132cb141e89cf72cf70defd67a23fbfe8e718a09a9c6a345565ebdc73e1f5" + "9927744801eb4e6f0b30d2705ea181e02a4030252330ac73bc4a4d51d2ccafb2f3f62abe3e81163be325ac823571c8dfae739f70bff3916" + "4e3cec7f53b88f97735dc25ac0c0630b1b41a131a979ba5164ab92e103716e9096e2fb5a6434b31d2c3673fca7e54dcdcce3807bfd43ee7" + "bc3422868094305a9847ce7666bf57e49fb3c2009cb30eb3ba955942b923ec2c2a4e0341d86b524b198974bdce9cf863ab3526e9e03e533" + "99dad20fc218554567c440536a31e05573f4cb930ba6a", + "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "e296a98cd96154b27d4b94cc520a8f028c61bd5ea69d22f0c3c13658c4bd5db0bd6661d0d17fac791c5cc06b3a54a853916bd7bf8a00644" + "acc53cca43b6f51c66bc6a6cb98cf8d9f23dcd639089d1d9d3dfbc8829a1a81638317bbd3edb070c1dae181d97605eb42a6111b8696a16c" + "c3e42639e38e93f872111fd67e934740f73a57df0e6edc6726c9aff99682bfcb7ddd99a3bef30da70d3c21e590fd02defc23be9f7f243e4" + "5a56b13562b8ecea09a14ac5af3a0500cb52f73f1bffea4a6624644da0bc4d112e5ee684b13a2ae8dbf401a5a8e581295a9dc876eaeb8ae" + "4d732fb78d50a92c302d15c0a2308e43fc6e147ec162b28a534d6c95a2020fa141f3ce7f7dd25ff000d35f732a145abf31b3ff4d0da015d" + "39da3b0fc70e692b567a9507be59e8c91a63fe809c495d76e70ec857cc24978771fc9314251e2bc4b1b24e0448514f97a6d438255cba8c1" + "854019", + "8dab56b50ce6a4315b6e0b4eb32c39dfe817cb462ee9e070e2a60bb843c16107"}, + {"message_with_special_chars", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "2014d1125081316a334896cbe5aed251d9fba6f3422afbc7e7bc9019e10e184241d18f71ef86603b5ce03e1c351e3dfff8adfb8f3498300" + "a712ec134b367c533", + "6f5a5de410db3606ff94fa14d0e084a6ba5a51241a2abf45ac593e4748c477ce"}, + {"message_with_unicode", "5a08a19b447df98136a4502e01b286011b2d148084a7ca17e3a93616d279eb2a", + "48656c6c6f2c20e4b896e7958c21", "a36c57ad5e8d6a30e80e010ab903b60da0206db1b4fd981cd61e059bbd8c0d4f", + "7ae70cb905f23109477b4d758d3907238ff4c37e2f351f086268ba3e85cef0257af58a70d8838c7b9c044f30382c2ccf", + "7dfb2d2e76df39b2fafdf01de009088a7e4d045b8630941986111ef2010d7c4a"}, + }; } -} // namespace tde2e_core +} // namespace tde2e_core diff --git a/third-party/td/td/tde2e/test/blockchain.cpp b/third-party/td/td/tde2e/test/blockchain.cpp index 6d13f11442..aca685de90 100644 --- a/third-party/td/td/tde2e/test/blockchain.cpp +++ b/third-party/td/td/tde2e/test/blockchain.cpp @@ -226,7 +226,7 @@ S_TEST(BlockchainValidation, GroupStateChanges) { TEST_TRY_STATUS(bt.expect_ok(block)); } { - TEST_DEBUG_VALUE(description, "Invalid: self join when there is no permissions"); + TEST_DEBUG_VALUE(description, "Invalid: self join when there is no permission"); BT bt; auto zero_block_without_external = BB().with_previous_block(minus_one_block) diff --git a/third-party/td/td/tde2e/test/e2e.cpp b/third-party/td/td/tde2e/test/e2e.cpp index 40fccc2c4b..cecf6be055 100644 --- a/third-party/td/td/tde2e/test/e2e.cpp +++ b/third-party/td/td/tde2e/test/e2e.cpp @@ -42,14 +42,17 @@ #include using namespace tde2e_core; + namespace api = tde2e_api; + template -td::Status expect_error(td::Result got) { - if (got.is_ok()) { - return td::Status::Error("Got Ok, instead of Error"); +static td::Status expect_error(td::Result result) { + if (result.is_ok()) { + return td::Status::Error("Receive Ok instead of Error"); } return td::Status::OK(); } + S_TEST(MessageEncryption, simple) { std::string secret = "secret"; { @@ -737,9 +740,9 @@ TEST(Call, Basic_API) { auto call2 = call_create(2, key2, block1).value(); ASSERT_EQ(call_get_verification_words(call2).value().words, call_get_verification_words(call1).value().words); - auto block2 = - F(call_create_change_state_block(call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) - .value(); + auto block2 = F(call_create_change_state_block( + call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) + .value(); call_describe_block(block2).value(); auto call3 = call_create(3, key3, block2).value(); @@ -749,9 +752,9 @@ TEST(Call, Basic_API) { // call2 and call3 verification ASSERT_EQ(call_get_verification_words(call2).value().words, call_get_verification_words(call3).value().words); - auto block31 = - F(call_create_change_state_block(call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) - .value(); + auto block31 = F(call_create_change_state_block( + call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) + .value(); call_apply_block(call2, block31).value(); auto commit2 = F(call_pull_outbound_messages(call2).value().at(0)).value(); @@ -788,9 +791,9 @@ TEST(Call, Basic_API) { ASSERT_EQ("hello", call_decrypt(call3, 2, 1, e).value()); ASSERT_TRUE(!call_decrypt(call3, 2, 1, e).is_ok()); - auto block3 = - F(call_create_change_state_block(call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) - .value(); + auto block3 = F(call_create_change_state_block( + call2, CallState{0, {CallParticipant{2, pkey2, 3}, CallParticipant{3, pkey3, 3}}})) + .value(); call_apply_block(call3, block3).value(); ASSERT_TRUE(!call_decrypt(call3, 2, 1, e).is_ok()); ASSERT_EQ("hello", call_decrypt(call3, 2, 1, e2).value()); diff --git a/third-party/td/td/tde2e/test/encryption.cpp b/third-party/td/td/tde2e/test/encryption.cpp index 5debe035f8..3ff96d5c63 100644 --- a/third-party/td/td/tde2e/test/encryption.cpp +++ b/third-party/td/td/tde2e/test/encryption.cpp @@ -1,12 +1,24 @@ -#include "EncryptionTestVectors.h" +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025 +// +// 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/e2e/MessageEncryption.h" + +#include "EncryptionTestVectors.h" + +#include "td/utils/logging.h" #include "td/utils/misc.h" +#include "td/utils/SharedSlice.h" #include "td/utils/simple_tests.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" #include "td/utils/tests.h" namespace tde2e_core { class EncryptionTest { -public: + public: static td::SecureString encrypt_data_with_deterministic_padding(td::Slice data, td::Slice secret) { auto prefix = MessageEncryption::gen_deterministic_prefix(data.size(), 16); td::SecureString combined(prefix.size() + data.size()); @@ -15,7 +27,7 @@ public: return MessageEncryption::encrypt_data_with_prefix(combined.as_slice(), secret); } }; -} +} // namespace tde2e_core using namespace tde2e_core; @@ -23,17 +35,17 @@ S_TEST(EncryptionTest, test_vectors) { auto test_vectors = get_test_vectors(); for (const auto &vec : test_vectors) { LOG(INFO) << "Testing vector: " << vec.name; - + // Convert hex strings to binary auto secret = td::hex_decode(vec.secret).move_as_ok(); auto data = td::hex_decode(vec.data).move_as_ok(); auto header = td::hex_decode(vec.header).move_as_ok(); auto expected_encrypted = td::hex_decode(vec.encrypted).move_as_ok(); auto expected_encrypted_header = td::hex_decode(vec.encrypted_header).move_as_ok(); - + // Test encrypt_data with deterministic padding auto encrypted = EncryptionTest::encrypt_data_with_deterministic_padding(data, secret); - + // Test encrypt_header auto encrypted_header_result = MessageEncryption::encrypt_header(header, encrypted, secret); ASSERT_TRUE(encrypted_header_result.is_ok()); @@ -42,10 +54,11 @@ S_TEST(EncryptionTest, test_vectors) { auto decrypted_result = MessageEncryption::decrypt_data(expected_encrypted, secret); ASSERT_TRUE(decrypted_result.is_ok()); ASSERT_EQ(td::hex_encode(decrypted_result.ok()), vec.data); - - auto decrypted_header_result = MessageEncryption::decrypt_header(expected_encrypted_header, expected_encrypted, secret); + + auto decrypted_header_result = + MessageEncryption::decrypt_header(expected_encrypted_header, expected_encrypted, secret); ASSERT_TRUE(decrypted_header_result.is_ok()); ASSERT_EQ(td::hex_encode(decrypted_header_result.ok()), vec.header); } return td::Status::OK(); -} \ No newline at end of file +} diff --git a/third-party/td/td/tdutils/td/utils/crypto.cpp b/third-party/td/td/tdutils/td/utils/crypto.cpp index 7169dd7b6a..bbaef2466c 100644 --- a/third-party/td/td/tdutils/td/utils/crypto.cpp +++ b/third-party/td/td/tdutils/td/utils/crypto.cpp @@ -752,6 +752,7 @@ static void init_thread_local_evp_md_ctx(const EVP_MD_CTX *&evp_md_ctx, const ch evp_md_ctx = nullptr; })); } + static void init_thread_local_evp_mac_ctx(EVP_MAC_CTX *&evp_mac_ctx, const char *digest) { EVP_MAC *hmac = EVP_MAC_fetch(nullptr, "HMAC", nullptr); LOG_IF(FATAL, hmac == nullptr); @@ -990,6 +991,7 @@ static void hmac_impl_finish(EVP_MAC_CTX *ctx, Slice key, Slice message, Mutable res = EVP_MAC_final(ctx, dest.ubegin(), nullptr, dest.size()); LOG_IF(FATAL, res != 1); } + static void hmac_impl_sha256(Slice key, Slice message, MutableSlice dest) { static TD_THREAD_LOCAL EVP_MAC_CTX *ctx = nullptr; if (ctx == nullptr) { @@ -997,6 +999,7 @@ static void hmac_impl_sha256(Slice key, Slice message, MutableSlice dest) { } hmac_impl_finish(ctx, key, message, dest); } + static void hmac_impl_sha512(Slice key, Slice message, MutableSlice dest) { static TD_THREAD_LOCAL EVP_MAC_CTX *ctx = nullptr; if (ctx == nullptr) {