Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2022-11-06 12:19:22 +01:00
commit f42cce37a4
246 changed files with 11214 additions and 5045 deletions

View File

@ -136,12 +136,9 @@ if (IOS_DEPLOYMENT_TARGET)
endif()
set (CMAKE_SHARED_LINKER_FLAGS_INIT "-fapplication-extension")
if (NOT SIMULATOR_FLAG)
set (BITCODE "-fembed-bitcode")
endif()
set (CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE}")
set (CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS}")
# Hidden visibilty is required for cxx on iOS
set (CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE} -fvisibility-inlines-hidden")
set (CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fvisibility-inlines-hidden")
set (CMAKE_C_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
set (CMAKE_CXX_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")

View File

@ -6,7 +6,7 @@ if (POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
project(TDLib VERSION 1.8.6 LANGUAGES CXX C)
project(TDLib VERSION 1.8.8 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
@ -324,6 +324,7 @@ set(TDLIB_SOURCE
td/telegram/DialogId.cpp
td/telegram/DialogInviteLink.cpp
td/telegram/DialogLocation.cpp
td/telegram/DialogNotificationSettings.cpp
td/telegram/DialogParticipant.cpp
td/telegram/DialogParticipantFilter.cpp
td/telegram/DialogSource.cpp
@ -355,6 +356,11 @@ set(TDLIB_SOURCE
td/telegram/files/FileUploader.cpp
td/telegram/files/PartsManager.cpp
td/telegram/files/ResourceManager.cpp
td/telegram/ForumTopic.cpp
td/telegram/ForumTopicEditedData.cpp
td/telegram/ForumTopicIcon.cpp
td/telegram/ForumTopicInfo.cpp
td/telegram/ForumTopicManager.cpp
td/telegram/Game.cpp
td/telegram/GameManager.cpp
td/telegram/Global.cpp
@ -366,6 +372,7 @@ set(TDLIB_SOURCE
td/telegram/InlineQueriesManager.cpp
td/telegram/InputDialogId.cpp
td/telegram/InputGroupCallId.cpp
td/telegram/InputInvoice.cpp
td/telegram/InputMessageText.cpp
td/telegram/JsonValue.cpp
td/telegram/LanguagePackManager.cpp
@ -376,8 +383,10 @@ set(TDLIB_SOURCE
td/telegram/MessageContent.cpp
td/telegram/MessageContentType.cpp
td/telegram/MessageEntity.cpp
td/telegram/MessageExtendedMedia.cpp
td/telegram/MessageId.cpp
td/telegram/MessageReaction.cpp
td/telegram/MessageReplyHeader.cpp
td/telegram/MessageReplyInfo.cpp
td/telegram/MessagesDb.cpp
td/telegram/MessageSearchFilter.cpp
@ -405,11 +414,12 @@ set(TDLIB_SOURCE
td/telegram/net/SessionMultiProxy.cpp
td/telegram/NewPasswordState.cpp
td/telegram/NotificationManager.cpp
td/telegram/NotificationSettings.cpp
td/telegram/NotificationSettingsScope.cpp
td/telegram/NotificationSettingsManager.cpp
td/telegram/NotificationSound.cpp
td/telegram/NotificationType.cpp
td/telegram/OptionManager.cpp
td/telegram/OrderInfo.cpp
td/telegram/Payments.cpp
td/telegram/PasswordManager.cpp
td/telegram/PhoneNumberManager.cpp
@ -425,6 +435,7 @@ set(TDLIB_SOURCE
td/telegram/ReplyMarkup.cpp
td/telegram/ReportReason.cpp
td/telegram/RestrictionReason.cpp
td/telegram/ScopeNotificationSettings.cpp
td/telegram/SecretChatActor.cpp
td/telegram/SecretChatDb.cpp
td/telegram/SecretChatsManager.cpp
@ -451,7 +462,9 @@ set(TDLIB_SOURCE
td/telegram/ThemeManager.cpp
td/telegram/TopDialogCategory.cpp
td/telegram/TopDialogManager.cpp
td/telegram/TranscriptionInfo.cpp
td/telegram/UpdatesManager.cpp
td/telegram/Usernames.cpp
td/telegram/Venue.cpp
td/telegram/VideoNotesManager.cpp
td/telegram/VideosManager.cpp
@ -519,6 +532,7 @@ set(TDLIB_SOURCE
td/telegram/Contact.h
td/telegram/ContactsManager.h
td/telegram/CountryInfoManager.h
td/telegram/CustomEmojiId.h
td/telegram/DelayDispatcher.h
td/telegram/Dependencies.h
td/telegram/DeviceTokenManager.h
@ -536,6 +550,7 @@ set(TDLIB_SOURCE
td/telegram/DialogInviteLink.h
td/telegram/DialogListId.h
td/telegram/DialogLocation.h
td/telegram/DialogNotificationSettings.h
td/telegram/DialogParticipant.h
td/telegram/DialogParticipantFilter.h
td/telegram/DialogSource.h
@ -576,6 +591,11 @@ set(TDLIB_SOURCE
td/telegram/files/ResourceManager.h
td/telegram/files/ResourceState.h
td/telegram/FolderId.h
td/telegram/ForumTopic.h
td/telegram/ForumTopicEditedData.h
td/telegram/ForumTopicIcon.h
td/telegram/ForumTopicInfo.h
td/telegram/ForumTopicManager.h
td/telegram/FullMessageId.h
td/telegram/Game.h
td/telegram/GameManager.h
@ -590,8 +610,10 @@ set(TDLIB_SOURCE
td/telegram/InlineQueriesManager.h
td/telegram/InputDialogId.h
td/telegram/InputGroupCallId.h
td/telegram/InputInvoice.h
td/telegram/InputMessageText.h
td/telegram/JsonValue.h
td/telegram/LabeledPricePart.h
td/telegram/LanguagePackManager.h
td/telegram/LinkManager.h
td/telegram/Location.h
@ -603,9 +625,11 @@ set(TDLIB_SOURCE
td/telegram/MessageContentType.h
td/telegram/MessageCopyOptions.h
td/telegram/MessageEntity.h
td/telegram/MessageExtendedMedia.h
td/telegram/MessageId.h
td/telegram/MessageLinkInfo.h
td/telegram/MessageReaction.h
td/telegram/MessageReplyHeader.h
td/telegram/MessageReplyInfo.h
td/telegram/MessageThreadInfo.h
td/telegram/MessagesDb.h
@ -645,12 +669,13 @@ set(TDLIB_SOURCE
td/telegram/NotificationGroupType.h
td/telegram/NotificationId.h
td/telegram/NotificationManager.h
td/telegram/NotificationSettings.h
td/telegram/NotificationSettingsScope.h
td/telegram/NotificationSettingsManager.h
td/telegram/NotificationSound.h
td/telegram/NotificationSoundType.h
td/telegram/NotificationType.h
td/telegram/OptionManager.h
td/telegram/OrderInfo.h
td/telegram/PasswordManager.h
td/telegram/Payments.h
td/telegram/PhoneNumberManager.h
@ -672,6 +697,7 @@ set(TDLIB_SOURCE
td/telegram/RequestActor.h
td/telegram/RestrictionReason.h
td/telegram/ScheduledServerMessageId.h
td/telegram/ScopeNotificationSettings.h
td/telegram/SecretChatActor.h
td/telegram/SecretChatId.h
td/telegram/SecretChatDb.h
@ -705,9 +731,11 @@ set(TDLIB_SOURCE
td/telegram/ThemeManager.h
td/telegram/TopDialogCategory.h
td/telegram/TopDialogManager.h
td/telegram/TranscriptionInfo.h
td/telegram/UniqueId.h
td/telegram/UpdatesManager.h
td/telegram/UserId.h
td/telegram/Usernames.h
td/telegram/Venue.h
td/telegram/Version.h
td/telegram/VideoNotesManager.h
@ -721,6 +749,7 @@ set(TDLIB_SOURCE
td/telegram/AudiosManager.hpp
td/telegram/AuthManager.hpp
td/telegram/BackgroundType.hpp
td/telegram/DialogNotificationSettings.hpp
td/telegram/DialogFilter.hpp
td/telegram/Dimensions.hpp
td/telegram/Document.hpp
@ -732,14 +761,17 @@ set(TDLIB_SOURCE
td/telegram/files/FileLocation.hpp
td/telegram/files/FileManager.hpp
td/telegram/files/FileSourceId.hpp
td/telegram/ForumTopicEditedData.hpp
td/telegram/ForumTopicIcon.hpp
td/telegram/Game.hpp
td/telegram/InputInvoice.hpp
td/telegram/InputMessageText.hpp
td/telegram/MessageEntity.hpp
td/telegram/MessageExtendedMedia.hpp
td/telegram/MessageReaction.hpp
td/telegram/MessageReplyInfo.hpp
td/telegram/MinChannel.hpp
td/telegram/NotificationSettings.hpp
td/telegram/Payments.hpp
td/telegram/OrderInfo.hpp
td/telegram/Photo.hpp
td/telegram/PhotoSize.hpp
td/telegram/PhotoSizeSource.hpp
@ -747,10 +779,12 @@ set(TDLIB_SOURCE
td/telegram/PollManager.hpp
td/telegram/PremiumGiftOption.hpp
td/telegram/ReplyMarkup.hpp
td/telegram/ScopeNotificationSettings.hpp
td/telegram/SecureValue.hpp
td/telegram/SendCodeHelper.hpp
td/telegram/StickerSetId.hpp
td/telegram/StickersManager.hpp
td/telegram/TranscriptionInfo.hpp
td/telegram/VideoNotesManager.hpp
td/telegram/VideosManager.hpp
td/telegram/VoiceNotesManager.hpp

View File

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

View File

@ -66,7 +66,7 @@ function split_file($file, $chunks, $undo) {
}
if (!file_exists($cpp_name)) {
echo "ERROR: skip unexisting file $cpp_name".PHP_EOL;
echo "ERROR: skip nonexistent file $cpp_name".PHP_EOL;
return;
}
@ -286,12 +286,13 @@ function split_file($file, $chunks, $undo) {
'documents_manager[_(-][^.]|DocumentsManager' => "DocumentsManager",
'file_reference_manager[_(-][^.]|FileReferenceManager|file_references[)]' => 'FileReferenceManager',
'file_manager[_(-][^.]|FileManager([^ ;.]| [^*])|update_file[)]' => 'files/FileManager',
'forum_topic_manager[_(-][^.]|ForumTopicManager' => 'ForumTopicManager',
'G[(][)]|Global[^A-Za-z]' => 'Global',
'game_manager[_(-][^.]|GameManager' => 'GameManager',
'group_call_manager[_(-][^.]|GroupCallManager' => 'GroupCallManager',
'HashtagHints' => 'HashtagHints',
'inline_queries_manager[_(-][^.]|InlineQueriesManager' => 'InlineQueriesManager',
'language_pack_manager[_(-][^.]|LanguagePackManager' => 'LanguagePackManager',
'language_pack_manager[_(-]|LanguagePackManager' => 'LanguagePackManager',
'link_manager[_(-][^.]|LinkManager' => 'LinkManager',
'LogeventIdWithGeneration|add_log_event|delete_log_event|get_erase_log_event_promise|parse_time|store_time' => 'logevent/LogEventHelper',
'MessageCopyOptions' => 'MessageCopyOptions',
@ -303,7 +304,7 @@ function split_file($file, $chunks, $undo) {
'poll_manager[_(-][^.]|PollManager' => "PollManager",
'PublicDialogType|get_public_dialog_type' => 'PublicDialogType',
'SecretChatActor' => 'SecretChatActor',
'secret_chats_manager[_(-][^.]|SecretChatsManager' => 'SecretChatsManager',
'secret_chats_manager[_(-]|SecretChatsManager' => 'SecretChatsManager',
'sponsored_message_manager[_(-][^.]|SponsoredMessageManager' => 'SponsoredMessageManager',
'stickers_manager[_(-][^.]|StickersManager' => 'StickersManager',
'[>](td_db[(][)]|get_td_db_impl[(])|TdDb[^A-Za-z]' => 'TdDb',

View File

@ -138,7 +138,7 @@ static void measure(td::StringBuilder &sb, td::Slice name, td::Slice key_name, t
ht.emplace(key_generator.next(), value_generator.next());
update();
if ((i + 1) % p == 0) {
stat.emplace_back(Stat{pi, min_ratio, max_ratio});
stat.push_back(Stat{pi, min_ratio, max_ratio});
reset();
pi++;
p *= 10;

View File

@ -134,7 +134,7 @@ See [example/cpp](https://github.com/tdlight-team/tdlight/tree/master/example/cp
See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbour-fernschreiber) and [Depecher](https://github.com/blacksailer/depecher) Telegram apps for Sailfish OS,
[TELEports](https://gitlab.com/ubports/development/apps/teleports) a Qt-client for Ubuntu Touch, [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin,
or [MeeGram](https://github.com/qtinsider/meegram2) - a Telegram client for Nokia N9,
[TDLib Native Sciter Extension](https://github.com/RadRussianRus/TDLibNSE) - a Sciter native extension for TDLib's JSON interface, all of which are based on TDLib.
[TDLib Native Sciter Extension](https://github.com/EricKotato/TDLibNSE) - a Sciter native extension for TDLib's JSON interface, all of which are based on TDLib.
<a name="swift"></a>
## Using TDLib in Swift projects
@ -182,7 +182,7 @@ See also [f-Telegram](https://github.com/evgfilim1/ftg) - Flutter Telegram clien
TDLib can be used from the Rust programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
See [rust-tdlib](https://github.com/aCLr/rust-tdlib), [tdlib](https://github.com/melix99/tdlib-rs), or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
See [rust-tdlib](https://github.com/aCLr/rust-tdlib), or [tdlib](https://github.com/melix99/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures),
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), [tdjson-rs](https://github.com/mersinvald/tdjson-rs), [rust-tdlib](https://github.com/vhaoran/rust-tdlib), or [tdlib-json-sys](https://github.com/aykxt/tdlib-json-sys) for examples of TDLib Rust bindings.

View File

@ -0,0 +1,26 @@
FROM --platform=linux/amd64 ubuntu:22.04 as build
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq default-jdk g++ git gperf make perl php-cli unzip wget && rm -rf /var/lib/apt/lists/*
WORKDIR /home
ARG ANDROID_NDK_VERSION=23.2.8568313
COPY ./check-environment.sh ./fetch-sdk.sh ./
RUN ./fetch-sdk.sh SDK "$ANDROID_NDK_VERSION"
ARG OPENSSL_VERSION=OpenSSL_1_1_1q
COPY ./build-openssl.sh ./
RUN ./build-openssl.sh SDK "$ANDROID_NDK_VERSION" openssl "$OPENSSL_VERSION"
ADD "https://api.github.com/repos/tdlib/td/git/refs/heads/master" version.json
ARG COMMIT_HASH=master
RUN git clone https://github.com/tdlib/td.git && cd td && git checkout "$COMMIT_HASH"
RUN cd td && git merge-base --is-ancestor bcd89728c3b93b67448b93b4863dc5bd4e122a4c "$COMMIT_HASH"
ARG ANDROID_STL=c++_static
RUN td/example/android/build-tdlib.sh SDK "$ANDROID_NDK_VERSION" openssl "$ANDROID_STL" && rm -rf td/example/android/build-*
FROM scratch
COPY --from=build /home/td/example/android/tdlib/tdlib* /

View File

@ -17,6 +17,8 @@ If you already have prebuilt OpenSSL, you can skip the third step and specify pa
If you want to update TDLib to a newer version, you need to run only the script `./build-tdlib.sh`.
You can specify different OpenSSL version as the fourth parameter to the script `./build-openssl.sh`. By default OpenSSL 1.1.1 is used because of much smaller binary footprint than newer OpenSSL versions.
You can specify different OpenSSL version as the fourth parameter to the script `./build-openssl.sh`. By default OpenSSL 1.1.1 is used because of much smaller binary footprint and better performance than newer OpenSSL versions.
You can build TDLib against shared standard C++ library by specifying "c++_shared" as the fourth parameter to the script `./build-tdlib.sh`. This can reduce total application size if you have a lot of other C++ code and want it to use the same shared library.
Alternatively, you can use Docker to build TDLib for Android. Use `docker build --output tdlib .` to build the latest TDLib commit from Github, or `docker build --build-arg COMMIT_HASH=<commit-hash> --output tdlib .` to build specific commit. The output archives will be placed in the tdlib directory as specified.

View File

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

View File

@ -1,10 +1,8 @@
diff --git a/Makefile b/Makefile
index 695be54..4efe5e5 100644
index 695be54..eda7b0d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,10 +5,13 @@
# - iOS - build everything for iOS
# - tvOS - build everything for tvOS
@@ -7,8 +7,11 @@
# - watchOS - build everything for watchOS
# - OpenSSL-macOS - build OpenSSL for macOS
# - OpenSSL-iOS - build OpenSSL for iOS
@ -16,7 +14,14 @@ index 695be54..4efe5e5 100644
# - BZip2-macOS - build BZip2 for macOS
# - BZip2-iOS - build BZip2 for iOS
# - BZip2-tvOS - build BZip2 for tvOS
@@ -36,31 +39,45 @@ OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION)
@@ -30,37 +33,51 @@ PYTHON_VERSION=2.7.14
PYTHON_VER=$(basename $(PYTHON_VERSION))
OPENSSL_VERSION_NUMBER=1.0.2
-OPENSSL_REVISION=n
+OPENSSL_REVISION=u
OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION)
BZIP2_VERSION=1.0.6
# Supported OS
@ -33,19 +38,23 @@ index 695be54..4efe5e5 100644
-TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.i386 iphoneos.armv7 iphoneos.armv7s iphoneos.arm64
+TARGETS-iOS=iphoneos.armv7 iphoneos.armv7s iphoneos.arm64
CFLAGS-iOS=-mios-version-min=7.0
CFLAGS-iphoneos.armv7=-fembed-bitcode
CFLAGS-iphoneos.armv7s=-fembed-bitcode
CFLAGS-iphoneos.arm64=-fembed-bitcode
-CFLAGS-iphoneos.armv7=-fembed-bitcode
-CFLAGS-iphoneos.armv7s=-fembed-bitcode
-CFLAGS-iphoneos.arm64=-fembed-bitcode
+CFLAGS-iphoneos.armv7=
+CFLAGS-iphoneos.armv7s=
+CFLAGS-iphoneos.arm64=
+
+# iOS-simulator targets
+TARGETS-iOS-simulator=iphonesimulator.x86_64 iphonesimulator.i386 iphonesimulator.arm64
+CFLAGS-iOS-simulator=-mios-simulator-version-min=7.0
+
# tvOS targets
-TARGETS-tvOS=appletvsimulator.x86_64 appletvos.arm64
+TARGETS-tvOS=appletvos.arm64
CFLAGS-tvOS=-mtvos-version-min=9.0
CFLAGS-appletvos.arm64=-fembed-bitcode
-CFLAGS-appletvos.arm64=-fembed-bitcode
+CFLAGS-appletvos.arm64=
PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no
+# tvOS-simulator targets
@ -56,8 +65,9 @@ index 695be54..4efe5e5 100644
-TARGETS-watchOS=watchsimulator.i386 watchos.armv7k
+TARGETS-watchOS=watchos.armv7k watchos.arm64_32
CFLAGS-watchOS=-mwatchos-version-min=4.0
CFLAGS-watchos.armv7k=-fembed-bitcode
+CFLAGS-watchos.arm64_32=-fembed-bitcode
-CFLAGS-watchos.armv7k=-fembed-bitcode
+CFLAGS-watchos.armv7k=
+CFLAGS-watchos.arm64_32=
PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no
+# watchOS-simulator targets
@ -80,3 +90,15 @@ index 695be54..4efe5e5 100644
else
cd $$(OPENSSL_DIR-$1) && \
CC="$$(CC-$1)" \
@@ -216,7 +235,10 @@ $$(OPENSSL_DIR-$1)/libssl.a $$(OPENSSL_DIR-$1)/libcrypto.a: $$(OPENSSL_DIR-$1)/M
CC="$$(CC-$1)" \
CROSS_TOP="$$(dir $$(SDK_ROOT-$1)).." \
CROSS_SDK="$$(notdir $$(SDK_ROOT-$1))" \
- make all && make install
+ make build_libs && \
+ mkdir -p "$(PROJECT_DIR)/build/$2/openssl/lib" && \
+ cp libcrypto.a libssl.a "$(PROJECT_DIR)/build/$2/openssl/lib"
+ -cd $$(OPENSSL_DIR-$1) && make install_sw 2> /dev/null
# Unpack BZip2
$$(BZIP2_DIR-$1)/Makefile: downloads/bzip2-$(BZIP2_VERSION).tgz

View File

@ -26,12 +26,12 @@ do
echo $platform
cd Python-Apple-support
#NB: -j will fail
make OpenSSL-$platform
make OpenSSL-$platform || exit 1
cd ..
rm -rf third_party/openssl/$platform
mkdir -p third_party/openssl/$platform/lib
cp ./Python-Apple-support/build/$platform/libcrypto.a third_party/openssl/$platform/lib/
cp ./Python-Apple-support/build/$platform/libssl.a third_party/openssl/$platform/lib/
cp -r ./Python-Apple-support/build/$platform/Support/OpenSSL/Headers/ third_party/openssl/$platform/include
rm -rf third_party/openssl/$platform || exit 1
mkdir -p third_party/openssl/$platform/lib || exit 1
cp ./Python-Apple-support/build/$platform/libcrypto.a third_party/openssl/$platform/lib/ || exit 1
cp ./Python-Apple-support/build/$platform/libssl.a third_party/openssl/$platform/lib/ || exit 1
cp -r ./Python-Apple-support/build/$platform/openssl/include/ third_party/openssl/$platform/include || exit 1
done
done

View File

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

View File

@ -60743,17 +60743,24 @@ act_like_temp_file:
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
int level = SQLITE_DEFAULT_SYNCHRONOUS + 1;
pPager->noSync = pPager->tempFile || level==PAGER_SYNCHRONOUS_OFF;
if( pPager->noSync ){
assert( pPager->fullSync==0 );
assert( pPager->extraSync==0 );
assert( pPager->syncFlags==0 );
assert( pPager->walSyncFlags==0 );
}else{
pPager->fullSync = 1;
pPager->extraSync = 0;
pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
pPager->walSyncFlags = (pPager->syncFlags<<2);
if( pPager->fullSync ){
pPager->walSyncFlags |= pPager->syncFlags;
}
#if SQLITE_DEFAULT_CKPTFULLFSYNC
pPager->walSyncFlags |= (SQLITE_SYNC_FULL<<2);
#endif
}
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */

View File

@ -212,14 +212,14 @@ abstract class TlDocumentationGenerator
$known_fields[$field_name] = $field_type;
continue;
}
$this->printError("Have no info about field `$field_name`");
$this->printError("Have no documentation for field `$field_name`");
}
foreach ($info as $name => $value) {
if (!$value) {
$this->printError("info[$name] for $class_name is empty");
$this->printError("Documentation for field $name of $class_name is empty");
} elseif (($value[0] < 'A' || $value[0] > 'Z') && ($value[0] < '0' || $value[0] > '9')) {
$this->printError("info[$name] for $class_name doesn't begins with capital letter");
$this->printError("Documentation for field $name of $class_name doesn't begin with a capital letter");
}
}
@ -235,7 +235,7 @@ abstract class TlDocumentationGenerator
}
foreach (array_diff_key($info, $known_fields) as $field_name => $field_info) {
$this->printError("Have info about unexisted field `$field_name`");
$this->printError("Have info about nonexistent field `$field_name`");
}
if (array_keys($info) !== array_keys($known_fields)) {

View File

@ -15,12 +15,12 @@ vector {t:Type} # [ t ] = Vector t;
decryptedMessage8#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService8#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
decryptedMessageMediaPhoto23#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaPhoto8#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaVideo8#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
decryptedMessageMediaDocument23#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaDocument8#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio8#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
@ -59,7 +59,7 @@ decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker23#fb0a5727 = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeVideo23#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio23#51448e5 duration:int = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
photoSizeEmpty#e17e23c type:string = PhotoSize;
@ -105,7 +105,7 @@ decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia;
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#bb718624 = SendMessageAction;
documentAttributeVideo66#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
// layer 73

View File

@ -66,7 +66,7 @@ textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
textEntities entities:vector<textEntity> = TextEntities;
//@description A text with some entities @text The text @entities Entities contained in the text. Entities can be nested, but must not mutually intersect with each other.
//-Pre, Code and PreCode entities can't contain other entities. Bold, Italic, Underline, Strikethrough, and Spoiler entities can contain and to be contained in all other entities. All other entities can't contain each other
//-Pre, Code and PreCode entities can't contain other entities. Bold, Italic, Underline, Strikethrough, and Spoiler entities can contain and can be part of any other entities. All other entities can't contain each other
formattedText text:string entities:vector<textEntity> = FormattedText;
@ -163,9 +163,6 @@ remoteFile id:string unique_id:string is_uploading_active:Bool is_uploading_comp
//@remote Information about the remote copy of the file
file id:int32 size:int53 expected_size:int53 local:localFile remote:remoteFile = File;
//@description Represents a list of files @files List of files
files files:vector<file> = Files;
//@class InputFile @description Points to a file
@ -323,9 +320,9 @@ sticker set_id:int64 width:int32 height:int32 emoji:string format:StickerFormat
video duration:int32 width:int32 height:int32 file_name:string mime_type:string has_stickers:Bool supports_streaming:Bool minithumbnail:minithumbnail thumbnail:thumbnail video:file = Video;
//@description Describes a video note. The video must be equal in width and height, cropped to a circle, and stored in MPEG4 format @duration Duration of the video, in seconds; as defined by the sender
//@length Video width and height; as defined by the sender @minithumbnail Video minithumbnail; may be null
//@thumbnail Video thumbnail in JPEG format; as defined by the sender; may be null @video File containing the video
videoNote duration:int32 length:int32 minithumbnail:minithumbnail thumbnail:thumbnail video:file = VideoNote;
//@waveform A waveform representation of the video note's audio in 5-bit format; may be empty if unknown @length Video width and height; as defined by the sender @minithumbnail Video minithumbnail; may be null
//@thumbnail Video thumbnail in JPEG format; as defined by the sender; may be null @speech_recognition_result Result of speech recognition in the video note; may be null @video File containing the video
videoNote duration:int32 waveform:bytes length:int32 minithumbnail:minithumbnail thumbnail:thumbnail speech_recognition_result:SpeechRecognitionResult video:file = VideoNote;
//@description Describes a voice note. The voice note must be encoded with the Opus codec, and stored inside an OGG container. Voice notes can have only a single audio channel
//@duration Duration of the voice note, in seconds; as defined by the sender @waveform A waveform representation of the voice note in 5-bit format
@ -334,9 +331,11 @@ voiceNote duration:int32 waveform:bytes mime_type:string speech_recognition_resu
//@description Describes an animated or custom representation of an emoji
//@sticker Sticker for the emoji; may be null if yet unknown for a custom emoji. If the sticker is a custom emoji, it can have arbitrary format different from stickerFormatTgs
//@sticker_width Expected width of the sticker, which can be used if the sticker is null
//@sticker_height Expected height of the sticker, which can be used if the sticker is null
//@fitzpatrick_type Emoji modifier fitzpatrick type; 0-6; 0 if none
//@sound File containing the sound to be played when the sticker is clicked; may be null. The sound is encoded with the Opus codec, and stored inside an OGG container
animatedEmoji sticker:sticker fitzpatrick_type:int32 sound:file = AnimatedEmoji;
animatedEmoji sticker:sticker sticker_width:int32 sticker_height:int32 fitzpatrick_type:int32 sound:file = AnimatedEmoji;
//@description Describes a user contact @phone_number Phone number of the user @first_name First name of the user; 1-255 characters in length @last_name Last name of the user @vcard Additional data about the user in a form of vCard; 0-2048 bytes in length @user_id Identifier of the user, if known; otherwise 0
contact phone_number:string first_name:string last_name:string vcard:string user_id:int53 = Contact;
@ -467,7 +466,8 @@ inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputC
//@can_change_info True, if the user can change the chat title, photo, and other settings
//@can_invite_users True, if the user can invite new users to the chat
//@can_pin_messages True, if the user can pin messages
chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_polls:Bool can_send_other_messages:Bool can_add_web_page_previews:Bool can_change_info:Bool can_invite_users:Bool can_pin_messages:Bool = ChatPermissions;
//@can_manage_topics True, if the user can manage topics
chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_polls:Bool can_send_other_messages:Bool can_add_web_page_previews:Bool can_change_info:Bool can_invite_users:Bool can_pin_messages:Bool can_manage_topics:Bool = ChatPermissions;
//@description Describes rights of the administrator
//@can_manage_chat True, if the administrator can get chat event log, get chat statistics, get message statistics in channels, get channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other privilege; applicable to supergroups and channels only
@ -478,10 +478,11 @@ chatPermissions can_send_messages:Bool can_send_media_messages:Bool can_send_pol
//@can_invite_users True, if the administrator can invite new users to the chat
//@can_restrict_members True, if the administrator can restrict, ban, or unban chat members; always true for channels
//@can_pin_messages True, if the administrator can pin messages; applicable to basic groups and supergroups only
//@can_manage_topics True, if the administrator can manage topics; applicable to forum supergroups only
//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them
//@can_manage_video_chats True, if the administrator can manage video chats
//@is_anonymous True, if the administrator isn't shown in the chat member list and sends messages anonymously; applicable to supergroups only
chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatAdministratorRights;
chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_manage_topics:Bool can_promote_members:Bool can_manage_video_chats:Bool is_anonymous:Bool = ChatAdministratorRights;
//@description Describes an option for buying Telegram Premium to a user
@ -493,6 +494,7 @@ chatAdministratorRights can_manage_chat:Bool can_change_info:Bool can_post_messa
//@payment_link An internal link to be opened for buying Telegram Premium to the user if store payment isn't possible; may be null if direct payment isn't available
premiumPaymentOption currency:string amount:int53 discount_percentage:int32 month_count:int32 store_product_id:string payment_link:InternalLinkType = PremiumPaymentOption;
//@description Describes a custom emoji to be shown instead of the Telegram Premium badge @custom_emoji_id Identifier of the custom emoji in stickerFormatTgs format. If the custom emoji belongs to the sticker set GetOption("themed_emoji_statuses_sticker_set_id"), then it's color must be changed to the color of the Telegram Premium badge
emojiStatus custom_emoji_id:int64 = EmojiStatus;
@ -500,11 +502,18 @@ emojiStatus custom_emoji_id:int64 = EmojiStatus;
emojiStatuses emoji_statuses:vector<emojiStatus> = EmojiStatuses;
//@description Describes usernames assigned to a user, a supergroup, or a channel
//@active_usernames List of active usernames; the first one must be shown as the primary username. The order of active usernames can be changed with reorderActiveUsernames or reorderSupergroupActiveUsernames
//@disabled_usernames List of currently disabled usernames; the username can be activated with toggleUsernameIsActive/toggleSupergroupUsernameIsActive
//@editable_username The active username, which can be changed with setUsername/setSupergroupUsername
usernames active_usernames:vector<string> disabled_usernames:vector<string> editable_username:string = Usernames;
//@description Represents a user
//@id User identifier
//@first_name First name of the user
//@last_name Last name of the user
//@username Username of the user
//@usernames Usernames of the user; may be null
//@phone_number Phone number of the user
//@status Current online status of the user
//@profile_photo Profile photo of the user; may be null
@ -521,7 +530,7 @@ emojiStatuses emoji_statuses:vector<emojiStatus> = EmojiStatuses;
//@type Type of the user
//@language_code IETF language tag of the user's language; only available to bots
//@added_to_attachment_menu True, if the user added the current bot to attachment menu; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto emoji_status:emojiStatus is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
user id:int53 first_name:string last_name:string usernames:usernames phone_number:string status:UserStatus profile_photo:profilePhoto emoji_status:emojiStatus is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_premium:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string added_to_attachment_menu:Bool = User;
//@description Contains information about a bot
@ -536,7 +545,7 @@ user id:int53 first_name:string last_name:string username:string phone_number:st
botInfo share_text:string description:string photo:photo animation:animation menu_button:botMenuButton commands:vector<botCommand> default_group_administrator_rights:chatAdministratorRights default_channel_administrator_rights:chatAdministratorRights = BotInfo;
//@description Contains full information about a user
//@photo User profile photo; may be null
//@photo User profile photo; may be null if empty or unknown. If non-null, then it is the same photo as in user.profile_photo and chat.photo
//@is_blocked True, if the user is blocked by the current user
//@can_be_called True, if the user can be called
//@supports_video_calls True, if a video call can be created with the user
@ -719,7 +728,7 @@ chatJoinRequestsInfo total_count:int32 user_ids:vector<int53> = ChatJoinRequests
basicGroup id:int53 member_count:int32 status:ChatMemberStatus is_active:Bool upgraded_to_supergroup_id:int53 = BasicGroup;
//@description Contains full information about a basic group
//@photo Chat photo; may be null
//@photo Chat photo; may be null if empty or unknown. If non-null, then it is the same photo as in chat.photo
//@param_description Group description. Updated only after the basic group is opened
//@creator_user_id User identifier of the creator of the group; 0 if unknown
//@members Group members
@ -730,9 +739,9 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb
//@description Represents a supergroup or channel with zero or more members (subscribers in the case of channels). From the point of view of the system, a channel is a special kind of a supergroup: only administrators can post and see the list of members, and posts from all administrators use the name and photo of the channel instead of individual names and profile photos. Unlike supergroups, channels can have an unlimited number of subscribers
//@id Supergroup or channel identifier
//@username Username of the supergroup or channel; empty for private supergroups or channels
//@usernames Usernames of the supergroup or channel; may be null
//@date Point in time (Unix timestamp) when the current user joined, or the point in time when the supergroup or channel was created, in case the user is not a member
//@status Status of the current user in the supergroup or channel; custom title will be always empty
//@status Status of the current user in the supergroup or channel; custom title will always be empty
//@member_count Number of members in the supergroup or channel; 0 if unknown. Currently, it is guaranteed to be known only if the supergroup or channel was received through searchPublicChats, searchChatsNearby, getInactiveSupergroupChats, getSuitableDiscussionChats, getGroupsInCommon, or getUserPrivacySettingRules
//@has_linked_chat True, if the channel has a discussion group, or the supergroup is the designated discussion group for a channel
//@has_location True, if the supergroup is connected to a location, i.e. the supergroup is a location-based supergroup
@ -742,14 +751,15 @@ basicGroupFullInfo photo:chatPhoto description:string creator_user_id:int53 memb
//@is_slow_mode_enabled True, if the slow mode is enabled in the supergroup
//@is_channel True, if the supergroup is a channel
//@is_broadcast_group True, if the supergroup is a broadcast group, i.e. only administrators can send messages and there is no limit on the number of members
//@is_forum True, if the supergroup must be shown as a forum by default
//@is_verified True, if the supergroup or channel is verified
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this supergroup or channel must be restricted
//@is_scam True, if many users reported this supergroup or channel as a scam
//@is_fake True, if many users reported this supergroup or channel as a fake account
supergroup id:int53 username:string date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool join_to_send_messages:Bool join_by_request:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
supergroup id:int53 usernames:usernames date:int32 status:ChatMemberStatus member_count:int32 has_linked_chat:Bool has_location:Bool sign_messages:Bool join_to_send_messages:Bool join_by_request:Bool is_slow_mode_enabled:Bool is_channel:Bool is_broadcast_group:Bool is_forum:Bool is_verified:Bool restriction_reason:string is_scam:Bool is_fake:Bool = Supergroup;
//@description Contains full information about a supergroup or channel
//@photo Chat photo; may be null
//@photo Chat photo; may be null if empty or unknown. If non-null, then it is the same photo as in chat.photo
//@param_description Supergroup or channel description
//@member_count Number of members in the supergroup or channel; 0 if unknown
//@administrator_count Number of privileged users in the supergroup or channel; 0 if unknown
@ -763,7 +773,7 @@ supergroup id:int53 username:string date:int32 status:ChatMemberStatus member_co
//@can_set_sticker_set True, if the supergroup sticker set can be changed
//@can_set_location True, if the supergroup location can be changed
//@can_get_statistics True, if the supergroup or channel statistics are available
//@is_all_history_available True, if new chat members will have access to old messages. In public or discussion groups and both public and private channels, old messages are always available, so this option affects only private supergroups without a linked chat. The value of this field is only available for chat administrators
//@is_all_history_available True, if new chat members will have access to old messages. In public, discussion, of forum groups and all channels, old messages are always available, so this option affects only private non-forum supergroups without a linked chat. The value of this field is only available for chat administrators
//@sticker_set_id Identifier of the supergroup sticker set; 0 if none
//@location Location to which the supergroup is connected; may be null
//@invite_link Primary invite link for the chat; may be null. For chat administrators with can_invite_users right only
@ -809,6 +819,13 @@ messageSenderChat chat_id:int53 = MessageSender;
messageSenders total_count:int32 senders:vector<MessageSender> = MessageSenders;
//@description Represents a message sender, which can be used to send messages in a chat @sender Available message senders @needs_premium True, if Telegram Premium is needed to use the message sender
chatMessageSender sender:MessageSender needs_premium:Bool = ChatMessageSender;
//@description Represents a list of message senders, which can be used to send messages in a chat @senders List of available message senders
chatMessageSenders senders:vector<chatMessageSender> = ChatMessageSenders;
//@class MessageForwardOrigin @description Contains information about the origin of a forwarded message
//@description The message was originally sent by a known user @sender_user_id Identifier of the user that originally sent the message
@ -911,6 +928,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@can_report_reactions True, if reactions on the message can be reported through reportMessageReactions
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
//@is_topic_message True, if the message is a forum topic message
//@contains_unread_mention True, if the message contains an unread mention for the current user
//@date Point in time (Unix timestamp) when the message was sent
//@edit_date Point in time (Unix timestamp) when the message was last edited
@ -928,7 +946,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
//@content Content of the message
//@reply_markup Reply markup for the message; may be null
message id:int53 sender_id:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_saved:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_added_reactions:Bool can_get_statistics:Bool can_get_message_thread:Bool can_get_viewers:Bool can_get_media_timestamp_links:Bool can_report_reactions:Bool has_timestamped_media:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo unread_reactions:vector<unreadReaction> reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int53 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
message id:int53 sender_id:MessageSender chat_id:int53 sending_state:MessageSendingState scheduling_state:MessageSchedulingState is_outgoing:Bool is_pinned:Bool can_be_edited:Bool can_be_forwarded:Bool can_be_saved:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_added_reactions:Bool can_get_statistics:Bool can_get_message_thread:Bool can_get_viewers:Bool can_get_media_timestamp_links:Bool can_report_reactions:Bool has_timestamped_media:Bool is_channel_post:Bool is_topic_message:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo unread_reactions:vector<unreadReaction> reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int53 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
//@description Contains a list of messages @total_count Approximate total number of messages found @messages List of messages; messages may be null
messages total_count:int32 messages:vector<message> = Messages;
@ -954,9 +972,13 @@ messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalen
//@is_recommended True, if the message needs to be labeled as "recommended" instead of "sponsored"
//@sponsor_chat_id Sponsor chat identifier; 0 if the sponsor chat is accessible through an invite link
//@sponsor_chat_info Information about the sponsor chat; may be null unless sponsor_chat_id == 0
//@show_chat_photo True, if the sponsor's chat photo must be shown
//@link An internal link to be opened when the sponsored message is clicked; may be null if the sponsor chat needs to be opened instead
//@content Content of the message. Currently, can be only of the type messageText
sponsoredMessage message_id:int53 is_recommended:Bool sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
sponsoredMessage message_id:int53 is_recommended:Bool sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo show_chat_photo:Bool link:InternalLinkType content:MessageContent = SponsoredMessage;
//@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<sponsoredMessage> messages_between:int32 = SponsoredMessages;
//@description Describes a file added to file download list
@ -1156,7 +1178,7 @@ chatsNearby users_nearby:vector<chatNearby> supergroups_nearby:vector<chatNearby
//@class PublicChatType @description Describes a type of public chats
//@description The chat is public, because it has username
//@description The chat is public, because it has an active username
publicChatTypeHasUsername = PublicChatType;
//@description The chat is public, because it is a location-based supergroup
@ -1165,7 +1187,7 @@ publicChatTypeIsLocationBased = PublicChatType;
//@class ChatActionBar @description Describes actions which must be possible to do through a chat action bar
//@description The chat can be reported as spam using the method reportChat with the reason chatReportReasonSpam
//@description The chat can be reported as spam using the method reportChat with the reason chatReportReasonSpam. If the chat is a private chat with a user with an emoji status, then a notice about emoji status usage must be shown
//@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings
chatActionBarReportSpam can_unarchive:Bool = ChatActionBar;
@ -1175,7 +1197,7 @@ chatActionBarReportUnrelatedLocation = ChatActionBar;
//@description The chat is a recently created group chat to which new members can be invited
chatActionBarInviteMembers = ChatActionBar;
//@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be blocked using the method toggleMessageSenderIsBlocked, or the other user can be added to the contact list using the method addContact
//@description The chat is a private or secret chat, which can be reported using the method reportChat, or the other user can be blocked using the method toggleMessageSenderIsBlocked, or the other user can be added to the contact list using the method addContact. If the chat is a private chat with a user with an emoji status, then a notice about emoji status usage must be shown
//@can_unarchive If true, the chat was automatically archived and can be moved back to the main chat list using addChatToList simultaneously with setting chat notification settings to default using setChatNotificationSettings
//@distance If non-negative, the current user was found by the peer through searchChatsNearby and this is the distance between the users
chatActionBarReportAddBlock can_unarchive:Bool distance:int32 = ChatActionBar;
@ -1290,13 +1312,40 @@ webAppInfo launch_id:int64 url:string = WebAppInfo;
//@description Contains information about a message thread
//@chat_id Identifier of the chat to which the message thread belongs
//@message_thread_id Message thread identifier, unique within the chat
//@reply_info Information about the message thread
//@reply_info Information about the message thread; may be null for forum topic threads
//@unread_message_count Approximate number of unread messages in the message thread
//@messages The messages from which the thread starts. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id)
//@draft_message A draft of a message in the message thread; may be null
messageThreadInfo chat_id:int53 message_thread_id:int53 reply_info:messageReplyInfo unread_message_count:int32 messages:vector<message> draft_message:draftMessage = MessageThreadInfo;
//@description Describes a forum topic icon @color Color of the topic icon in RGB format @custom_emoji_id Unique identifier of the custom emoji shown on the topic icon; 0 if none
forumTopicIcon color:int32 custom_emoji_id:int64 = ForumTopicIcon;
//@description Contains basic information about a forum topic
//@message_thread_id Message thread identifier of the topic
//@name Name of the topic
//@icon Icon of the topic
//@creation_date Date the topic was created
//@creator_id Identifier of the creator of the topic
//@is_outgoing True, if the topic was created by the current user
//@is_closed True, if the topic is closed
forumTopicInfo message_thread_id:int53 name:string icon:forumTopicIcon creation_date:int32 creator_id:MessageSender is_outgoing:Bool is_closed:Bool = ForumTopicInfo;
//@description Describes a forum topic
//@info Basic information about the topic
//@last_message Last message in the topic; may be null
//@is_pinned True, if the topic is pinned in the topic list
//@unread_count Number of unread messages in the topic
//@last_read_inbox_message_id Identifier of the last read incoming message
//@last_read_outbox_message_id Identifier of the last read outgoing message
//@unread_mention_count Number of unread messages with a mention/reply in the topic
//@unread_reaction_count Number of messages with unread reactions in the topic
//@notification_settings Notification settings for the topic
//@draft_message A draft of a message in the topic; may be null
forumTopic info:forumTopicInfo last_message:message is_pinned:Bool unread_count:int32 last_read_inbox_message_id:int53 last_read_outbox_message_id:int53 unread_mention_count:int32 unread_reaction_count:int32 notification_settings:chatNotificationSettings draft_message:draftMessage = ForumTopic;
//@class RichText @description Describes a text object inside an instant-view web page
//@description A plain text @text Text
@ -1648,7 +1697,7 @@ paymentResult success:Bool verification_url:string = PaymentResult;
paymentReceipt title:string description:formattedText photo:photo date:int32 seller_bot_user_id:int53 payment_provider_user_id:int53 invoice:invoice order_info:orderInfo shipping_option:shippingOption credentials_title:string tip_amount:int53 = PaymentReceipt;
//@class InputInvoice @description Describe an invoice to process
//@class InputInvoice @description Describes an invoice to process
//@description An invoice from a message of the type messageInvoice @chat_id Chat identifier of the message @message_id Message identifier
inputInvoiceMessage chat_id:int53 message_id:int53 = InputInvoice;
@ -1657,6 +1706,27 @@ inputInvoiceMessage chat_id:int53 message_id:int53 = InputInvoice;
inputInvoiceName name:string = InputInvoice;
//@class MessageExtendedMedia @description Describes a media, which is attached to an invoice
//@description The media is hidden until the invoice is paid
//@width Media width; 0 if unknown
//@height Media height; 0 if unknown
//@duration Media duration; 0 if unknown
//@minithumbnail Media minithumbnail; may be null
//@caption Media caption
messageExtendedMediaPreview width:int32 height:int32 duration:int32 minithumbnail:minithumbnail caption:formattedText = MessageExtendedMedia;
//@description The media is a photo @photo The photo @caption Photo caption
messageExtendedMediaPhoto photo:photo caption:formattedText = MessageExtendedMedia;
//@description The media is a video @video The video @caption Photo caption
messageExtendedMediaVideo video:video caption:formattedText = MessageExtendedMedia;
//@description The media is unuspported @caption Media caption
messageExtendedMediaUnsupported caption:formattedText = MessageExtendedMedia;
//@description File with the date it was uploaded @file The file @date Point in time (Unix timestamp) when the file was uploaded
datedFile file:file date:int32 = DatedFile;
@ -1974,7 +2044,8 @@ messagePoll poll:poll = MessageContent;
//@description A message with an invoice from a bot @title Product title @param_description Product description @photo Product photo; may be null @currency Currency for the product price @total_amount Product total price in the smallest units of the currency
//@start_parameter Unique invoice bot start_parameter. To share an invoice use the URL https://t.me/{bot_username}?start={start_parameter} @is_test True, if the invoice is a test invoice
//@need_shipping_address True, if the shipping address must be specified @receipt_message_id The identifier of the message with the receipt, after the product has been purchased
messageInvoice title:string description:formattedText photo:photo currency:string total_amount:int53 start_parameter:string is_test:Bool need_shipping_address:Bool receipt_message_id:int53 = MessageContent;
//@extended_media Extended media attached to the invoice; may be null
messageInvoice title:string description:formattedText photo:photo currency:string total_amount:int53 start_parameter:string is_test:Bool need_shipping_address:Bool receipt_message_id:int53 extended_media:MessageExtendedMedia = MessageContent;
//@description A message with information about an ended call @is_video True, if the call was a video call @discard_reason Reason why the call was discarded @duration Call duration, in seconds
messageCall is_video:Bool discard_reason:CallDiscardReason duration:int32 = MessageContent;
@ -2036,13 +2107,22 @@ messageChatSetTheme theme_name:string = MessageContent;
//@description The TTL (Time To Live) setting for messages in the chat has been changed @ttl New message TTL
messageChatSetTtl ttl:int32 = MessageContent;
//@description A forum topic has been created @name Name of the topic @icon Icon of the topic
messageForumTopicCreated name:string icon:forumTopicIcon = MessageContent;
//@description A forum topic has been edited @name If non-empty, the new name of the topic @edit_icon_custom_emoji_id True, if icon's custom_emoji_id is changed @icon_custom_emoji_id New unique identifier of the custom emoji shown on the topic icon; 0 if none. Must be ignored if edit_icon_custom_emoji_id is false
messageForumTopicEdited name:string edit_icon_custom_emoji_id:Bool icon_custom_emoji_id:int64 = MessageContent;
//@description A forum topic has been closed or opened @is_closed True if the topic was closed or reopened
messageForumTopicIsClosedToggled is_closed:Bool = MessageContent;
//@description A non-standard action has happened in the chat @text Message text to be shown in the chat
messageCustomServiceAction text:string = MessageContent;
//@description A new high score was achieved in a game @game_message_id Identifier of the message with the game, can be an identifier of a deleted message @game_id Identifier of the game; may be different from the games presented in the message with the game @score New score
messageGameScore game_message_id:int53 game_id:int64 score:int32 = MessageContent;
//@description A payment has been completed @invoice_chat_id Identifier of the chat, containing the corresponding invoice message; 0 if unknown @invoice_message_id Identifier of the message with the corresponding invoice; can be 0 or an identifier of a deleted message
//@description A payment has been completed @invoice_chat_id Identifier of the chat, containing the corresponding invoice message @invoice_message_id Identifier of the message with the corresponding invoice; can be 0 or an identifier of a deleted message
//@currency Currency for the price of the product @total_amount Total price for the product, in the smallest units of the currency
//@is_recurring True, if this is a recurring payment @is_first_recurring True, if this is the first recurring payment @invoice_name Name of the invoice; may be empty if unknown
messagePaymentSuccessful invoice_chat_id:int53 invoice_message_id:int53 currency:string total_amount:int53 is_recurring:Bool is_first_recurring:Bool invoice_name:string = MessageContent;
@ -2084,7 +2164,7 @@ messageUnsupported = MessageContent;
//@class TextEntityType @description Represents a part of the text which must be formatted differently
//@description A mention of a user by their username
//@description A mention of a user, a supergroup, or a channel by their username
textEntityTypeMention = TextEntityType;
//@description A hashtag text, beginning with "#"
@ -2190,7 +2270,7 @@ inputMessageAnimation animation:InputFile thumbnail:inputThumbnail added_sticker
//@performer Performer of the audio; 0-64 characters, may be replaced by the server @caption Audio caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
inputMessageAudio audio:InputFile album_cover_thumbnail:inputThumbnail duration:int32 title:string performer:string caption:formattedText = InputMessageContent;
//@description A document message (general file) @document Document to be sent @thumbnail Document thumbnail; pass null to skip thumbnail uploading @disable_content_type_detection If true, automatic file type detection will be disabled and the document will be always sent as file. Always true for files sent to secret chats @caption Document caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
//@description A document message (general file) @document Document to be sent @thumbnail Document thumbnail; pass null to skip thumbnail uploading @disable_content_type_detection If true, automatic file type detection will be disabled and the document will always be sent as file. Always true for files sent to secret chats @caption Document caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
inputMessageDocument document:InputFile thumbnail:inputThumbnail disable_content_type_detection:Bool caption:formattedText = InputMessageContent;
//@description A photo message @photo Photo to send. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20 @thumbnail Photo thumbnail to be sent; pass null to skip thumbnail uploading. The thumbnail is sent to the other party only in secret chats @added_sticker_file_ids File identifiers of the stickers added to the photo, if applicable @width Photo width @height Photo height @caption Photo caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
@ -2208,7 +2288,7 @@ inputMessageVideo video:InputFile thumbnail:inputThumbnail added_sticker_file_id
//@description A video note message @video_note Video note to be sent @thumbnail Video thumbnail; pass null to skip thumbnail uploading @duration Duration of the video, in seconds @length Video width and height; must be positive and not greater than 640
inputMessageVideoNote video_note:InputFile thumbnail:inputThumbnail duration:int32 length:int32 = InputMessageContent;
//@description A voice note message @voice_note Voice note to be sent @duration Duration of the voice note, in seconds @waveform Waveform representation of the voice note, in 5-bit format @caption Voice note caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
//@description A voice note message @voice_note Voice note to be sent @duration Duration of the voice note, in seconds @waveform Waveform representation of the voice note in 5-bit format @caption Voice note caption; pass null to use an empty caption; 0-GetOption("message_caption_length_max") characters
inputMessageVoiceNote voice_note:InputFile duration:int32 waveform:bytes caption:formattedText = InputMessageContent;
//@description A message with a location @location Location to be sent @live_period Period for which the location can be updated, in seconds; must be between 60 and 86400 for a live location and 0 otherwise
@ -2232,7 +2312,8 @@ inputMessageGame bot_user_id:int53 game_short_name:string = InputMessageContent;
//@photo_url Product photo URL; optional @photo_size Product photo size @photo_width Product photo width @photo_height Product photo height
//@payload The invoice payload @provider_token Payment provider token @provider_data JSON-encoded data about the invoice, which will be shared with the payment provider
//@start_parameter Unique invoice bot deep link parameter for the generation of this invoice. If empty, it would be possible to pay directly from forwards of the invoice message
inputMessageInvoice invoice:invoice title:string description:string photo_url:string photo_size:int32 photo_width:int32 photo_height:int32 payload:bytes provider_token:string provider_data:string start_parameter:string = InputMessageContent;
//@extended_media_content The content of extended media attached to the invoice. The content of the message to be sent. Must be one of the following types: inputMessagePhoto, inputMessageVideo
inputMessageInvoice invoice:invoice title:string description:string photo_url:string photo_size:int32 photo_width:int32 photo_height:int32 payload:bytes provider_token:string provider_data:string start_parameter:string extended_media_content:InputMessageContent = InputMessageContent;
//@description A message with a poll. Polls can't be sent to secret chats. Polls can be sent only to a private chat with a bot @question Poll question; 1-255 characters (up to 300 characters for bots) @options List of poll answer options, 2-10 strings 1-100 characters each
//@is_anonymous True, if the poll voters are anonymous. Non-anonymous polls can't be sent or forwarded to channels @type Type of the poll
@ -2901,9 +2982,12 @@ chatEventStickerSetChanged old_sticker_set_id:int64 new_sticker_set_id:int64 = C
//@description The chat title was changed @old_title Previous chat title @new_title New chat title
chatEventTitleChanged old_title:string new_title:string = ChatEventAction;
//@description The chat username was changed @old_username Previous chat username @new_username New chat username
//@description The chat editable username was changed @old_username Previous chat username @new_username New chat username
chatEventUsernameChanged old_username:string new_username:string = ChatEventAction;
//@description The chat active usernames were changed @old_usernames Previous list of active usernames @new_usernames New list of active usernames
chatEventActiveUsernamesChanged old_usernames:vector<string> new_usernames:vector<string> = ChatEventAction;
//@description The has_protected_content setting of a channel was toggled @has_protected_content New value of has_protected_content
chatEventHasProtectedContentToggled has_protected_content:Bool = ChatEventAction;
@ -2940,6 +3024,24 @@ chatEventVideoChatParticipantIsMutedToggled participant_id:MessageSender is_mute
//@description A video chat participant volume level was changed @participant_id Identifier of the affected group call participant @volume_level New value of volume_level; 1-20000 in hundreds of percents
chatEventVideoChatParticipantVolumeLevelChanged participant_id:MessageSender volume_level:int32 = ChatEventAction;
//@description The is_forum setting of a channel was toggled @is_forum New value of is_forum
chatEventIsForumToggled is_forum:Bool = ChatEventAction;
//@description A new forum topic was created @topic_info Information about the topic
chatEventForumTopicCreated topic_info:forumTopicInfo = ChatEventAction;
//@description A forum topic was edited @old_topic_info Old information about the topic @new_topic_info New information about the topic
chatEventForumTopicEdited old_topic_info:forumTopicInfo new_topic_info:forumTopicInfo = ChatEventAction;
//@description A forum topic was closed or reopened @topic_info New information about the topic
chatEventForumTopicToggleIsClosed topic_info:forumTopicInfo = ChatEventAction;
//@description A forum topic was deleted @topic_info Information about the topic
chatEventForumTopicDeleted topic_info:forumTopicInfo = ChatEventAction;
//@description A pinned forum topic was changed @old_topic_info Information about the old pinned topic; may be null @new_topic_info Information about the new pinned topic; may be null
chatEventForumTopicPinned old_topic_info:forumTopicInfo new_topic_info:forumTopicInfo = ChatEventAction;
//@description Represents a chat event @id Chat event identifier @date Point in time (Unix timestamp) when the event happened @member_id Identifier of the user or chat who performed the action @action The action
chatEvent id:int64 date:int32 member_id:MessageSender action:ChatEventAction = ChatEvent;
@ -2959,7 +3061,8 @@ chatEvents events:vector<chatEvent> = ChatEvents;
//@setting_changes True, if changes in chat settings need to be returned
//@invite_link_changes True, if changes to invite links need to be returned
//@video_chat_changes True, if video chat actions need to be returned
chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool invite_link_changes:Bool video_chat_changes:Bool = ChatEventLogFilters;
//@forum_changes True, if forum-related actions need to be returned
chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool member_joins:Bool member_leaves:Bool member_invites:Bool member_promotions:Bool member_restrictions:Bool info_changes:Bool setting_changes:Bool invite_link_changes:Bool video_chat_changes:Bool forum_changes:Bool = ChatEventLogFilters;
//@class LanguagePackStringValue @description Represents the value of a string in a language pack
@ -3067,6 +3170,9 @@ premiumFeatureEmojiStatus = PremiumFeature;
//@description Profile photo animation on message and chat screens
premiumFeatureAnimatedProfilePhoto = PremiumFeature;
//@description The ability to set a custom emoji as a forum topic icon
premiumFeatureForumTopicIcon = PremiumFeature;
//@description Allowed to set a premium appllication icons
premiumFeatureAppIcons = PremiumFeature;
@ -3160,7 +3266,7 @@ pushReceiverId id:int64 = PushReceiverId;
backgroundFillSolid color:int32 = BackgroundFill;
//@description Describes a gradient fill of a background @top_color A top color of the background in the RGB24 format @bottom_color A bottom color of the background in the RGB24 format
//@rotation_angle Clockwise rotation angle of the gradient, in degrees; 0-359. Must be always divisible by 45
//@rotation_angle Clockwise rotation angle of the gradient, in degrees; 0-359. Must always be divisible by 45
backgroundFillGradient top_color:int32 bottom_color:int32 rotation_angle:int32 = BackgroundFill;
//@description Describes a freeform gradient fill of a background @colors A list of 3 or 4 colors of the freeform gradients in the RGB24 format
@ -3739,8 +3845,8 @@ internalLinkTypeFilterSettings = InternalLinkType;
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
//@description The link must be opened in an Instant View. Call getWebPageInstantView with the given URL to process the link @url URL to be passed to getWebPageInstantView
internalLinkTypeInstantView url:string = InternalLinkType;
//@description The link must be opened in an Instant View. Call getWebPageInstantView with the given URL to process the link @url URL to be passed to getWebPageInstantView @fallback_url An URL to open if getWebPageInstantView fails
internalLinkTypeInstantView url:string fallback_url:string = InternalLinkType;
//@description The link is a link to an invoice. Call getPaymentForm with the given invoice name to process the link @invoice_name Name of the invoice
internalLinkTypeInvoice invoice_name:string = InternalLinkType;
@ -3820,11 +3926,11 @@ messageLink link:string is_public:Bool = MessageLink;
//@description Contains information about a link to a message in a chat
//@is_public True, if the link is a public link for a message in a chat
//@chat_id If found, identifier of the chat to which the message belongs, 0 otherwise
//@message_thread_id If found, identifier of the message thread in which to open the message, or which to open in case of a missing message
//@message If found, the linked message; may be null
//@media_timestamp Timestamp from which the video/audio/video note/voice note playing must start, in seconds; 0 if not specified. The media can be in the message content or in its web page preview
//@for_album True, if the whole media album to which the message belongs is linked
//@for_comment True, if the message is linked as a channel post comment or from a message thread
messageLinkInfo is_public:Bool chat_id:int53 message:message media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLinkInfo;
messageLinkInfo is_public:Bool chat_id:int53 message_thread_id:int53 message:message media_timestamp:int32 for_album:Bool = MessageLinkInfo;
//@description Contains a part of a file @data File bytes
@ -4352,6 +4458,9 @@ updateChatFilters chat_filters:vector<chatFilterInfo> main_chat_list_position:in
//@description The number of online group members has changed. This update with non-zero number of online group members is sent only for currently opened chats. There is no guarantee that it will be sent just after the number of online users has changed @chat_id Identifier of the chat @online_member_count New number of online members in the chat, or 0 if unknown
updateChatOnlineMemberCount chat_id:int53 online_member_count:int32 = Update;
//@description Basic information about a topic in a forum chat was changed @chat_id Chat identifier @info New information about the topic
updateForumTopicInfo chat_id:int53 info:forumTopicInfo = Update;
//@description Notification settings for some type of chats were updated @scope Types of chats for which notification settings were updated @notification_settings The new notification settings
updateScopeNotificationSettings scope:NotificationSettingsScope notification_settings:scopeNotificationSettings = Update;
@ -4917,11 +5026,12 @@ getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 off
//@chat_id Chat identifier @remove_from_chat_list Pass true to remove the chat from all chat lists @revoke Pass true to delete chat history for all users
deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
//@description Deletes a chat along with all messages in the corresponding chat for all chat members. For group chats this will release the username and remove all members. Use the field chat.can_be_deleted_for_all_users to find whether the method can be applied to the chat @chat_id Chat identifier
//@description Deletes a chat along with all messages in the corresponding chat for all chat members. For group chats this will release the usernames and remove all members. Use the field chat.can_be_deleted_for_all_users to find whether the method can be applied to the chat @chat_id Chat identifier
deleteChat chat_id:int53 = Ok;
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
//-(searchSecretMessages must be used instead), or without an enabled message database. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
//-(searchSecretMessages must be used instead), or without an enabled message database. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit.
//-A combination of query, sender_id, filter and message_thread_id search criteria is expected to be supported, only if it is required for Telegram official application implementation
//@chat_id Identifier of the chat in which to search messages
//@query Query to search for
//@sender_id Identifier of the sender of messages to search for; pass null to search for messages from any sender. Not supported in secret chats
@ -4993,6 +5103,12 @@ getChatMessageCalendar chat_id:int53 filter:SearchMessagesFilter from_message_id
//@description Returns approximate number of messages of the specified type in the chat @chat_id Identifier of the chat in which to count messages @filter Filter for message content; searchMessagesFilterEmpty is unsupported in this function @return_local Pass true to get the number of messages without sending network requests, or -1 if the number of messages is unknown locally
getChatMessageCount chat_id:int53 filter:SearchMessagesFilter return_local:Bool = Count;
//@description Returns approximate 1-based position of a message among messages, which can be found by the specified filter in the chat. Cannot be used in secret chats
//@chat_id Identifier of the chat in which to find message position @message_id Message identifier
//@filter Filter for message content; searchMessagesFilterEmpty, searchMessagesFilterUnreadMention, searchMessagesFilterUnreadReaction, and searchMessagesFilterFailedToSend are unsupported in this function
//@message_thread_id If not 0, only messages in the specified thread will be considered; supergroups only
getChatMessagePosition chat_id:int53 message_id:int53 filter:SearchMessagesFilter message_thread_id:int53 = Count;
//@description Returns all scheduled messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id) @chat_id Chat identifier
getChatScheduledMessages chat_id:int53 = Messages;
@ -5003,8 +5119,8 @@ getChatScheduledMessages chat_id:int53 = Messages;
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
getMessagePublicForwards chat_id:int53 message_id:int53 offset:string limit:int32 = FoundMessages;
//@description Returns sponsored message to be shown in a chat; for channel chats only. Returns a 404 error if there is no sponsored message in the chat @chat_id Identifier of the chat
getChatSponsoredMessage chat_id:int53 = SponsoredMessage;
//@description Returns sponsored messages to be shown in a chat; for channel chats only @chat_id Identifier of the chat
getChatSponsoredMessages chat_id:int53 = SponsoredMessages;
//@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
@ -5019,8 +5135,8 @@ removeNotificationGroup notification_group_id:int32 max_notification_id:int32 =
//@message_id Identifier of the message
//@media_timestamp If not 0, timestamp from which the video/audio/video note/voice note playing must start, in seconds. The media can be in the message content or in its web page preview
//@for_album Pass true to create a link for the whole media album
//@for_comment Pass true to create a link to the message as a channel post comment, or from a message thread
getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLink;
//@in_message_thread Pass true to create a link to the message as a channel post comment, in a message thread, or a forum topic
getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bool in_message_thread:Bool = MessageLink;
//@description Returns an HTML code for embedding the message. Available only for messages in supergroups and channels with a username
//@chat_id Identifier of the chat to which the message belongs
@ -5038,17 +5154,17 @@ getMessageLinkInfo url:string = MessageLinkInfo;
//@to_language_code A two-letter ISO 639-1 language code of the language to which the message is translated
translateText text:string from_language_code:string to_language_code:string = Text;
//@description Recognizes speech in a voice note message. The message must be successfully sent and must not be scheduled. May return an error with a message "MSG_VOICE_TOO_LONG" if the voice note is too long to be recognized
//@description Recognizes speech in a video note or a voice note message. The message must be successfully sent and must not be scheduled. May return an error with a message "MSG_VOICE_TOO_LONG" if media duration is too big to be recognized
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
recognizeSpeech chat_id:int53 message_id:int53 = Ok;
//@description Rates recognized speech in a voice note message @chat_id Identifier of the chat to which the message belongs @message_id Identifier of the message @is_good Pass true if the speech recognition is good
//@description Rates recognized speech in a video note or a voice note message @chat_id Identifier of the chat to which the message belongs @message_id Identifier of the message @is_good Pass true if the speech recognition is good
rateSpeechRecognition chat_id:int53 message_id:int53 is_good:Bool = Ok;
//@description Returns list of message sender identifiers, which can be used to send messages in a chat @chat_id Chat identifier
getChatAvailableMessageSenders chat_id:int53 = MessageSenders;
getChatAvailableMessageSenders chat_id:int53 = ChatMessageSenders;
//@description Selects a message sender to send messages in a chat @chat_id Chat identifier @message_sender_id New message sender for the chat
setChatMessageSender chat_id:int53 message_sender_id:MessageSender = Ok;
@ -5087,13 +5203,14 @@ sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to_mess
//@description Forwards previously sent messages. Returns the forwarded messages in the same order as the message identifiers passed in message_ids. If a message can't be forwarded, null will be returned instead of the message
//@chat_id Identifier of the chat to which to forward messages
//@message_thread_id If not 0, a message thread identifier in which the message will be sent; for forum threads only
//@from_chat_id Identifier of the chat from which to forward messages
//@message_ids Identifiers of the messages to forward. Message identifiers must be in a strictly increasing order. At most 100 messages can be forwarded simultaneously
//@options Options to be used to send the messages; pass null to use default options
//@send_copy Pass true to copy content of the messages without reference to the original sender. Always true if the messages are forwarded to a secret chat or are local
//@remove_caption Pass true to remove media captions of message copies. Ignored if send_copy is false
//@only_preview Pass true to get fake messages instead of actually forwarding them
forwardMessages chat_id:int53 from_chat_id:int53 message_ids:vector<int53> options:messageSendOptions send_copy:Bool remove_caption:Bool only_preview:Bool = Messages;
forwardMessages chat_id:int53 message_thread_id:int53 from_chat_id:int53 message_ids:vector<int53> options:messageSendOptions send_copy:Bool remove_caption:Bool only_preview:Bool = Messages;
//@description Resends messages which failed to send. Can be called only for messages for which messageSendingStateFailed.can_retry is true and after specified in messageSendingStateFailed.retry_after time passed.
//-If a message is re-sent, the corresponding failed to send message is deleted. Returns the sent messages in the same order as the message identifiers passed in message_ids. If a message can't be re-sent, null will be returned instead of the message
@ -5197,11 +5314,39 @@ editInlineMessageReplyMarkup inline_message_id:string reply_markup:ReplyMarkup =
editMessageSchedulingState chat_id:int53 message_id:int53 scheduling_state:MessageSchedulingState = Ok;
//@description Returns list of custom emojis, which can be used as forum topic icon by all users
getForumTopicDefaultIcons = Stickers;
//@description Creates a topic in a forum supergroup chat; requires can_manage_topics rights in the supergroup
//@chat_id Identifier of the chat
//@name Name of the topic; 1-128 characters
//@icon Icon of the topic. Icon color must be one of 0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F. Telegram Premium users can use any custom emoji as topic icon, other users can use only a custom emoji returned by getForumTopicDefaultIcons
createForumTopic chat_id:int53 name:string icon:forumTopicIcon = ForumTopicInfo;
//@description Edits title and icon of a topic in a forum supergroup chat; requires can_manage_topics administrator rights in the supergroup unless the user is creator of the topic
//@chat_id Identifier of the chat
//@message_thread_id Message thread identifier of the forum topic
//@name New name of the topic; 1-128 characters
//@icon_custom_emoji_id Identifier of the new custom emoji for topic icon. Telegram Premium users can use any custom emoji, other users can use only a custom emoji returned by getForumTopicDefaultIcons
editForumTopic chat_id:int53 message_thread_id:int53 name:string icon_custom_emoji_id:int64 = Ok;
//@description Toggles whether a topic is closed in a forum supergroup chat; requires can_manage_topics administrator rights in the supergroup unless the user is creator of the topic
//@chat_id Identifier of the chat
//@message_thread_id Message thread identifier of the forum topic
//@is_closed Pass true to close the topic; pass false to reopen it
toggleForumTopicIsClosed chat_id:int53 message_thread_id:int53 is_closed:Bool = Ok;
//@description Deletes all messages in a forum topic; requires can_delete_messages administrator rights in the supergroup unless the user is creator of the topic, the topic has no messages from other users and has at most 11 messages
//@chat_id Identifier of the chat
//@message_thread_id Message thread identifier of the forum topic
deleteForumTopic chat_id:int53 message_thread_id:int53 = Ok;
//@description Returns information about a emoji reaction. Returns a 404 error if the reaction is not found @emoji Text representation of the reaction
getEmojiReaction emoji:string = EmojiReaction;
//@description Returns TGS files with generic animations for custom emoji reactions
getCustomEmojiReactionAnimations = Files;
//@description Returns TGS stickers with generic animations for custom emoji reactions
getCustomEmojiReactionAnimations = Stickers;
//@description Returns reactions, which can be added to a message. The list can change after updateActiveEmojiReactions, updateChatAvailableReactions for the chat, or updateMessageInteractionInfo for the message
//@chat_id Identifier of the chat to which the message belongs
@ -5238,10 +5383,10 @@ getMessageAddedReactions chat_id:int53 message_id:int53 reaction_type:ReactionTy
setDefaultReactionType reaction_type:ReactionType = Ok;
//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) contained in the text. Can be called synchronously @text The text in which to look for entites
//@description Returns all entities (mentions, hashtags, cashtags, bot commands, bank card numbers, URLs, and email addresses) found in the text. Can be called synchronously @text The text in which to look for entites
getTextEntities text:string = TextEntities;
//@description Parses Bold, Italic, Underline, Strikethrough, Spoiler, CustomEmoji, Code, Pre, PreCode, TextUrl and MentionName entities contained in the text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
//@description Parses Bold, Italic, Underline, Strikethrough, Spoiler, CustomEmoji, Code, Pre, PreCode, TextUrl and MentionName entities from a marked-up text. Can be called synchronously @text The text to parse @parse_mode Text parse mode
parseTextEntities text:string parse_mode:TextParseMode = FormattedText;
//@description Parses Markdown entities in a human-friendly format, ignoring markup errors. Can be called synchronously
@ -5347,8 +5492,9 @@ sendWebAppData bot_user_id:int53 button_text:string data:string = Ok;
//@url The URL from an inlineKeyboardButtonTypeWebApp button, a botMenuButton button, or an internalLinkTypeAttachmentMenuBot link, or an empty string otherwise
//@theme Preferred Web App theme; pass null to use the default theme
//@application_name Short name of the application; 0-64 English letters, digits, and underscores
//@message_thread_id If not 0, a message thread identifier in which the message will be sent
//@reply_to_message_id Identifier of the replied message for the message sent by the Web App; 0 if none
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters application_name:string reply_to_message_id:int53 = WebAppInfo;
openWebApp chat_id:int53 bot_user_id:int53 url:string theme:themeParameters application_name:string message_thread_id:int53 reply_to_message_id:int53 = WebAppInfo;
//@description Informs TDLib that a previously opened Web App was closed @web_app_launch_id Identifier of Web App launch, received from openWebApp
closeWebApp web_app_launch_id:int64 = Ok;
@ -5432,9 +5578,15 @@ getExternalLink link:string allow_write_access:Bool = HttpUrl;
//@description Marks all mentions in a chat as read @chat_id Chat identifier
readAllChatMentions chat_id:int53 = Ok;
//@description Marks all reactions in a chat as read @chat_id Chat identifier
//@description Marks all mentions in a forum topic as read @chat_id Chat identifier @message_thread_id Message thread identifier in which mentions are marked as read
readAllMessageThreadMentions chat_id:int53 message_thread_id:int53 = Ok;
//@description Marks all reactions in a chat or a forum topic as read @chat_id Chat identifier
readAllChatReactions chat_id:int53 = Ok;
//@description Marks all reactions in a forum topic as read @chat_id Chat identifier @message_thread_id Message thread identifier in which reactions are marked as read
readAllMessageThreadReactions chat_id:int53 message_thread_id:int53 = Ok;
//@description Returns an existing chat corresponding to a given user @user_id User identifier @force Pass true to create the chat without a network request. In this case all information about the chat except its type, title and photo can be incorrect
createPrivateChat user_id:int53 force:Bool = Chat;
@ -5564,6 +5716,10 @@ unpinChatMessage chat_id:int53 message_id:int53 = Ok;
//@description Removes all pinned messages from a chat; requires can_pin_messages rights in the group or can_edit_messages rights in the channel @chat_id Identifier of the chat
unpinAllChatMessages chat_id:int53 = Ok;
//@description Removes all pinned messages from a forum topic; requires can_pin_messages rights in the supergroup @chat_id Identifier of the chat
//@message_thread_id Message thread identifier in which messages will be unpinned
unpinAllMessageThreadMessages chat_id:int53 message_thread_id:int53 = Ok;
//@description Adds the current user as a new member to a chat. Private and secret chats can't be joined using this method. May return an error with a message "INVITE_REQUEST_SENT" if only a join request was created @chat_id Chat identifier
joinChat chat_id:int53 = Ok;
@ -5600,7 +5756,7 @@ transferChatOwnership chat_id:int53 user_id:int53 password:string = Ok;
//@description Returns information about a single member of a chat @chat_id Chat identifier @member_id Member identifier
getChatMember chat_id:int53 member_id:MessageSender = ChatMember;
//@description Searches for a specified query in the first name, last name and username of the members of a specified chat. Requires administrator rights in channels
//@description Searches for a specified query in the first name, last name and usernames of the members of a specified chat. Requires administrator rights in channels
//@chat_id Chat identifier
//@query Query to search for
//@limit The maximum number of users to be returned; up to 200
@ -6045,12 +6201,12 @@ sharePhoneNumber user_id:int53 = Ok;
getUserProfilePhotos user_id:int53 offset:int32 limit:int32 = ChatPhotos;
//@description Returns stickers from the installed sticker sets that correspond to a given emoji. If the emoji is non-empty, then favorite, recently used or trending stickers may also be returned
//@sticker_type Type of the sticker sets to return
//@emoji String representation of emoji. If empty, returns all known installed stickers
//@description Returns stickers from the installed sticker sets that correspond to a given emoji or can be found by sticker-specific keywords. If the query is non-empty, then favorite, recently used or trending stickers may also be returned
//@sticker_type Type of the stickers to return
//@query Search query; an emoji or a keyword prefix. If empty, returns all known installed stickers
//@limit The maximum number of stickers to be returned
//@chat_id Chat identifier for which to return stickers. Available custom emoji may be different for different chats
getStickers sticker_type:StickerType emoji:string limit:int32 chat_id:int53 = Stickers;
//@chat_id Chat identifier for which to return stickers. Available custom emoji stickers may be different for different chats
getStickers sticker_type:StickerType query:string limit:int32 chat_id:int53 = Stickers;
//@description Searches for stickers from public sticker sets that correspond to a given emoji @emoji String representation of emoji; must be non-empty @limit The maximum number of stickers to be returned; 0-100
searchStickers emoji:string limit:int32 = Stickers;
@ -6174,9 +6330,15 @@ setName first_name:string last_name:string = Ok;
//@description Changes the bio of the current user @bio The new value of the user bio; 0-GetOption("bio_length_max") characters without line feeds
setBio bio:string = Ok;
//@description Changes the username of the current user @username The new value of the username. Use an empty string to remove the username
//@description Changes the editable username of the current user @username The new value of the username. Use an empty string to remove the username. The username can't be completely removed if there is another active or disabled username
setUsername username:string = Ok;
//@description Changes active state for a username of the current user. The editable username can't be disabled. May return an error with a message "USERNAMES_ACTIVE_TOO_MUCH" if the maximum number of active usernames has been reached @username The username to change @is_active Pass true to activate the username; pass false to disable it
toggleUsernameIsActive username:string is_active:Bool = Ok;
//@description Changes order of active usernames of the current user @usernames The new order of active usernames. All currently active usernames must be specified
reorderActiveUsernames usernames:vector<string> = Ok;
//@description Changes the emoji status of the current user; for Telegram Premium users only
//@emoji_status New emoji status; pass null to switch to the default badge
//@duration Duration of the status, in seconds; pass 0 to keep the status active until it will be changed manually
@ -6256,9 +6418,18 @@ disconnectWebsite website_id:int64 = Ok;
disconnectAllWebsites = Ok;
//@description Changes the username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username
//@description Changes the editable username of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @username New value of the username. Use an empty string to remove the username. The username can't be completely removed if there is another active or disabled username
setSupergroupUsername supergroup_id:int53 username:string = Ok;
//@description Changes active state for a username of a supergroup or channel, requires owner privileges in the supergroup or channel. The editable username can't be disabled. May return an error with a message "USERNAMES_ACTIVE_TOO_MUCH" if the maximum number of active usernames has been reached @supergroup_id Identifier of the supergroup or channel @username The username to change @is_active Pass true to activate the username; pass false to disable it
toggleSupergroupUsernameIsActive supergroup_id:int53 username:string is_active:Bool = Ok;
//@description Disables all active non-editable usernames of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel
disableAllSupergroupUsernames supergroup_id:int53 = Ok;
//@description Changes order of active usernames of a supergroup or channel, requires owner privileges in the supergroup or channel @supergroup_id Identifier of the supergroup or channel @usernames The new order of active usernames. All currently active usernames must be specified
reorderSupergroupActiveUsernames supergroup_id:int53 usernames:vector<string> = Ok;
//@description Changes the sticker set of a supergroup; requires can_change_info administrator right @supergroup_id Identifier of the supergroup @sticker_set_id New value of the supergroup sticker set identifier. Use 0 to remove the supergroup sticker set
setSupergroupStickerSet supergroup_id:int53 sticker_set_id:int64 = Ok;
@ -6274,6 +6445,9 @@ toggleSupergroupJoinByRequest supergroup_id:int53 join_by_request:Bool = Ok;
//@description Toggles whether the message history of a supergroup is available to new members; requires can_change_info administrator right @supergroup_id The identifier of the supergroup @is_all_history_available The new value of is_all_history_available
toggleSupergroupIsAllHistoryAvailable supergroup_id:int53 is_all_history_available:Bool = Ok;
//@description Toggles whether the supergroup is a forum; requires owner privileges in the supergroup @supergroup_id Identifier of the supergroup @is_forum New value of is_forum. A supergroup can be converted to a forum, only if it has at least GetOption("forum_member_count_min") members
toggleSupergroupIsForum supergroup_id:int53 is_forum:Bool = Ok;
//@description Upgrades supergroup to a broadcast group; requires owner privileges in the supergroup @supergroup_id Identifier of the supergroup
toggleSupergroupIsBroadcastGroup supergroup_id:int53 = Ok;

View File

@ -59,7 +59,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#d9799874 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string = InputMedia;
inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
@ -101,7 +101,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#5d99adee flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus = User;
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -116,7 +116,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
@ -145,7 +145,7 @@ messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Do
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string extended_media:flags.4?MessageExtendedMedia = MessageMedia;
messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
@ -183,6 +183,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
messageActionTopicEdit#b18a431c flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = MessageAction;
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -211,6 +213,7 @@ inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
inputNotifyUsers#193b4417 = InputNotifyPeer;
inputNotifyChats#4a95e84e = InputNotifyPeer;
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer;
inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings;
@ -289,7 +292,7 @@ updateUserTyping#c01e857f user_id:long action:SendMessageAction = Update;
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;
updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
updateUserName#c3f202e0 user_id:long first_name:string last_name:string username:string = Update;
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
@ -324,7 +327,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update;
updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update;
@ -340,7 +343,7 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
updateLangPackTooLong#46560264 lang_code:string = Update;
updateLangPack#56022f4d difference:LangPackDifference = Update;
updateFavedStickers#e511996d = Update;
updateChannelReadMessagesContents#44bdd535 channel_id:long messages:Vector<int> = Update;
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update;
updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
@ -377,7 +380,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update;
updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
@ -388,6 +391,8 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
updateChannelPinnedTopic#f694b0ae flags:# channel_id:long topic_id:flags.0?int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -460,6 +465,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
notifyUsers#b4c83b4c = NotifyPeer;
notifyChats#c007cec3 = NotifyPeer;
notifyBroadcasts#d612e8ef = NotifyPeer;
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
sendMessageTypingAction#16bf744e = SendMessageAction;
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
@ -578,10 +584,11 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
botCommand#c27ac8c7 command:string description:string = BotCommand;
@ -760,7 +767,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = StickerSetCovered;
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
@ -942,12 +949,18 @@ channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatI
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
popularContact#5ce14175 client_id:long importers:int = PopularContact;
@ -1105,9 +1118,9 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
statsURL#47a971e0 url:string = StatsURL;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true until_date:int = ChatBannedRights;
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
@ -1238,7 +1251,7 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
@ -1306,9 +1319,10 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
@ -1318,7 +1332,7 @@ searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosi
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;
channels.sendAsPeers#8356cda9 peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
channels.sendAsPeers#f496b0c6 peers:Vector<SendAsPeer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;
@ -1433,6 +1447,20 @@ account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = accou
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword;
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
forumTopicDeleted#23f109b id:int = ForumTopic;
forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1523,7 +1551,7 @@ account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?I
account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool;
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
account.getTheme#3a5869ec format:string theme:InputTheme = Theme;
account.getThemes#7206e458 format:string hash:long = account.Themes;
account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool;
account.getContentSettings#8b9b4dae = account.ContentSettings;
@ -1543,6 +1571,8 @@ account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool;
account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses;
account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
account.clearRecentEmojiStatuses#18201aae = Bool;
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@ -1579,9 +1609,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMessage#1cc20387 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#7547c966 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#c661bbc4 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
@ -1624,14 +1654,14 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendInlineBotResult#d3fbdccb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
@ -1657,10 +1687,10 @@ messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates;
messages.getFavedStickers#4f1aaa9 hash:long = messages.FavedStickers;
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMultiMedia#b6f11a1c flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@ -1677,7 +1707,7 @@ messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.getSearchCounters#ae7cc1 flags:# peer:InputPeer top_msg_id:flags.0?int filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
@ -1695,7 +1725,7 @@ messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messag
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages;
messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed;
@ -1726,14 +1756,14 @@ messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:C
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#fc87a53c flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
@ -1746,6 +1776,7 @@ messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer =
messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
messages.clearRecentReactions#9dfeefb4 = Bool;
messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1830,6 +1861,16 @@ channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates;
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics;
channels.editForumTopic#6c883e2d flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = Updates;
channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinned:Bool = Updates;
channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;

View File

@ -202,6 +202,10 @@ std::string TD_TL_writer_java::gen_output_begin() const {
" } catch (UnsatisfiedLinkError e) {\n"
" e.printStackTrace();\n" +
" }\n"
" }\n\n"
" private " +
tl_name +
"() {\n"
" }\n\n";
}

View File

@ -1042,7 +1042,7 @@ Status SessionConnection::do_flush() {
auto start_time = Time::now();
auto result = raw_connection_->flush(auth_data_->get_auth_key(), *this);
auto elapsed_time = Time::now() - start_time;
if (elapsed_time >= 0.01) {
if (elapsed_time >= 0.1) {
LOG(ERROR) << "RawConnection::flush took " << elapsed_time << " seconds, written " << last_write_size_
<< " bytes, read " << last_read_size_ << " bytes and returned " << result;
}

View File

@ -179,7 +179,7 @@ FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation,
CHECK(a->file_id == file_id);
if (a->mime_type != new_animation->mime_type) {
LOG(DEBUG) << "Animation " << file_id << " info has changed";
a->mime_type = new_animation->mime_type;
a->mime_type = std::move(new_animation->mime_type);
}
if (a->file_name != new_animation->file_name) {
LOG(DEBUG) << "Animation " << file_id << " file name has changed";
@ -205,7 +205,7 @@ FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation,
LOG(INFO) << "Animation " << file_id << " thumbnail has changed from " << a->thumbnail << " to "
<< new_animation->thumbnail;
}
a->thumbnail = new_animation->thumbnail;
a->thumbnail = std::move(new_animation->thumbnail);
}
if (a->animated_thumbnail != new_animation->animated_thumbnail) {
if (!a->animated_thumbnail.file_id.is_valid()) {
@ -214,7 +214,7 @@ FileId AnimationsManager::on_get_animation(unique_ptr<Animation> new_animation,
LOG(INFO) << "Animation " << file_id << " animated thumbnail has changed from " << a->animated_thumbnail
<< " to " << new_animation->animated_thumbnail;
}
a->animated_thumbnail = new_animation->animated_thumbnail;
a->animated_thumbnail = std::move(new_animation->animated_thumbnail);
}
if (a->has_stickers != new_animation->has_stickers && new_animation->has_stickers) {
a->has_stickers = new_animation->has_stickers;
@ -389,7 +389,7 @@ SecretInputMedia AnimationsManager::get_secret_input_media(FileId animation_file
attributes.push_back(make_tl_object<secret_api::documentAttributeFilename>(animation->file_name));
}
if (animation->duration != 0 && animation->mime_type == "video/mp4") {
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo66>(
attributes.push_back(make_tl_object<secret_api::documentAttributeVideo>(
0, false, animation->duration, animation->dimensions.width, animation->dimensions.height));
}
if (animation->dimensions.width != 0 && animation->dimensions.height != 0) {

View File

@ -39,6 +39,7 @@ class RequestWebViewQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::webAppInfo>> promise_;
DialogId dialog_id_;
UserId bot_user_id_;
MessageId top_thread_message_id_;
MessageId reply_to_message_id_;
DialogId as_dialog_id_;
bool from_attach_menu_ = false;
@ -49,8 +50,8 @@ class RequestWebViewQuery final : public Td::ResultHandler {
}
void send(DialogId dialog_id, UserId bot_user_id, tl_object_ptr<telegram_api::InputUser> &&input_user, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform, MessageId reply_to_message_id,
bool silent, DialogId as_dialog_id) {
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform, MessageId top_thread_message_id,
MessageId reply_to_message_id, bool silent, DialogId as_dialog_id) {
dialog_id_ = dialog_id;
bot_user_id_ = bot_user_id;
reply_to_message_id_ = reply_to_message_id;
@ -86,6 +87,10 @@ class RequestWebViewQuery final : public Td::ResultHandler {
flags |= telegram_api::messages_requestWebView::THEME_PARAMS_MASK;
}
if (top_thread_message_id.is_valid()) {
flags |= telegram_api::messages_requestWebView::TOP_MSG_ID_MASK;
}
if (reply_to_message_id.is_valid()) {
flags |= telegram_api::messages_requestWebView::REPLY_TO_MSG_ID_MASK;
}
@ -105,7 +110,7 @@ class RequestWebViewQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(telegram_api::messages_requestWebView(
flags, false /*ignored*/, false /*ignored*/, std::move(input_peer), std::move(input_user), url, start_parameter,
std::move(theme_parameters), platform, reply_to_message_id.get_server_message_id().get(),
std::move(as_input_peer))));
top_thread_message_id.get_server_message_id().get(), std::move(as_input_peer))));
}
void on_result(BufferSlice packet) final {
@ -115,8 +120,8 @@ class RequestWebViewQuery final : public Td::ResultHandler {
}
auto ptr = result_ptr.move_as_ok();
td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, reply_to_message_id_,
as_dialog_id_);
td_->attach_menu_manager_->open_web_view(ptr->query_id_, dialog_id_, bot_user_id_, top_thread_message_id_,
reply_to_message_id_, as_dialog_id_);
promise_.set_value(td_api::make_object<td_api::webAppInfo>(ptr->query_id_, ptr->url_));
}
@ -134,8 +139,8 @@ class ProlongWebViewQuery final : public Td::ResultHandler {
DialogId dialog_id_;
public:
void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId reply_to_message_id, bool silent,
DialogId as_dialog_id) {
void send(DialogId dialog_id, UserId bot_user_id, int64 query_id, MessageId top_thread_message_id,
MessageId reply_to_message_id, bool silent, DialogId as_dialog_id) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Write);
@ -149,6 +154,10 @@ class ProlongWebViewQuery final : public Td::ResultHandler {
flags |= telegram_api::messages_prolongWebView::REPLY_TO_MSG_ID_MASK;
}
if (top_thread_message_id.is_valid()) {
flags |= telegram_api::messages_prolongWebView::TOP_MSG_ID_MASK;
}
if (silent) {
flags |= telegram_api::messages_prolongWebView::SILENT_MASK;
}
@ -163,7 +172,8 @@ class ProlongWebViewQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(telegram_api::messages_prolongWebView(
flags, false /*ignored*/, std::move(input_peer), r_input_user.move_as_ok(), query_id,
reply_to_message_id.get_server_message_id().get(), std::move(as_input_peer))));
reply_to_message_id.get_server_message_id().get(), top_thread_message_id.get_server_message_id().get(),
std::move(as_input_peer))));
}
void on_result(BufferSlice packet) final {
@ -585,9 +595,9 @@ void AttachMenuManager::ping_web_view() {
for (const auto &it : opened_web_views_) {
const auto &opened_web_view = it.second;
bool silent = td_->messages_manager_->get_dialog_silent_send_message(opened_web_view.dialog_id_);
td_->create_handler<ProlongWebViewQuery>()->send(opened_web_view.dialog_id_, opened_web_view.bot_user_id_, it.first,
opened_web_view.reply_to_message_id_, silent,
opened_web_view.as_dialog_id_);
td_->create_handler<ProlongWebViewQuery>()->send(
opened_web_view.dialog_id_, opened_web_view.bot_user_id_, it.first, opened_web_view.top_thread_message_id_,
opened_web_view.reply_to_message_id_, silent, opened_web_view.as_dialog_id_);
}
schedule_ping_web_view();
@ -599,9 +609,10 @@ void AttachMenuManager::schedule_ping_web_view() {
ping_web_view_timeout_.set_timeout_in(PING_WEB_VIEW_TIMEOUT);
}
void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,
string &&url, td_api::object_ptr<td_api::themeParameters> &&theme,
string &&platform, Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise) {
void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id,
MessageId reply_to_message_id, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise) {
TRY_STATUS_PROMISE(promise, td_->contacts_manager_->get_bot_data(bot_user_id));
TRY_RESULT_PROMISE(promise, input_user, td_->contacts_manager_->get_input_user(bot_user_id));
@ -630,17 +641,23 @@ void AttachMenuManager::request_web_view(DialogId dialog_id, UserId bot_user_id,
!td_->messages_manager_->have_message_force({dialog_id, reply_to_message_id}, "request_web_view")) {
reply_to_message_id = MessageId();
}
if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server() ||
dialog_id.get_type() != DialogType::Channel ||
!td_->contacts_manager_->is_megagroup_channel(dialog_id.get_channel_id())) {
top_thread_message_id = MessageId();
}
bool silent = td_->messages_manager_->get_dialog_silent_send_message(dialog_id);
DialogId as_dialog_id = td_->messages_manager_->get_dialog_default_send_message_as_dialog_id(dialog_id);
td_->create_handler<RequestWebViewQuery>(std::move(promise))
->send(dialog_id, bot_user_id, std::move(input_user), std::move(url), std::move(theme), std::move(platform),
reply_to_message_id, silent, as_dialog_id);
top_thread_message_id, reply_to_message_id, silent, as_dialog_id);
}
void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id,
MessageId reply_to_message_id, DialogId as_dialog_id) {
MessageId top_thread_message_id, MessageId reply_to_message_id,
DialogId as_dialog_id) {
if (query_id == 0) {
LOG(ERROR) << "Receive Web App query identifier == 0";
return;
@ -652,6 +669,7 @@ void AttachMenuManager::open_web_view(int64 query_id, DialogId dialog_id, UserId
OpenedWebView opened_web_view;
opened_web_view.dialog_id_ = dialog_id;
opened_web_view.bot_user_id_ = bot_user_id;
opened_web_view.top_thread_message_id_ = top_thread_message_id;
opened_web_view.reply_to_message_id_ = reply_to_message_id;
opened_web_view.as_dialog_id_ = as_dialog_id;
opened_web_views_.emplace(query_id, std::move(opened_web_view));

View File

@ -32,12 +32,13 @@ class AttachMenuManager final : public Actor {
void init();
void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id, string &&url,
void request_web_view(DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id,
MessageId reply_to_message_id, string &&url,
td_api::object_ptr<td_api::themeParameters> &&theme, string &&platform,
Promise<td_api::object_ptr<td_api::webAppInfo>> &&promise);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId reply_to_message_id,
DialogId as_dialog_id);
void open_web_view(int64 query_id, DialogId dialog_id, UserId bot_user_id, MessageId top_thread_message_id,
MessageId reply_to_message_id, DialogId as_dialog_id);
void close_web_view(int64 query_id, Promise<Unit> &&promise);
@ -154,6 +155,7 @@ class AttachMenuManager final : public Actor {
struct OpenedWebView {
DialogId dialog_id_;
UserId bot_user_id_;
MessageId top_thread_message_id_;
MessageId reply_to_message_id_;
DialogId as_dialog_id_;
};

View File

@ -101,13 +101,13 @@ FileId AudiosManager::on_get_audio(unique_ptr<Audio> new_audio, bool replace) {
CHECK(a->file_id == new_audio->file_id);
if (a->mime_type != new_audio->mime_type) {
LOG(DEBUG) << "Audio " << file_id << " info has changed";
a->mime_type = new_audio->mime_type;
a->mime_type = std::move(new_audio->mime_type);
}
if (a->duration != new_audio->duration || a->title != new_audio->title || a->performer != new_audio->performer) {
LOG(DEBUG) << "Audio " << file_id << " info has changed";
a->duration = new_audio->duration;
a->title = new_audio->title;
a->performer = new_audio->performer;
a->title = std::move(new_audio->title);
a->performer = std::move(new_audio->performer);
}
if (a->file_name != new_audio->file_name) {
LOG(DEBUG) << "Audio " << file_id << " file name has changed";
@ -126,7 +126,7 @@ FileId AudiosManager::on_get_audio(unique_ptr<Audio> new_audio, bool replace) {
LOG(INFO) << "Audio " << file_id << " thumbnail has changed from " << a->thumbnail << " to "
<< new_audio->thumbnail;
}
a->thumbnail = new_audio->thumbnail;
a->thumbnail = std::move(new_audio->thumbnail);
}
}

View File

@ -845,7 +845,7 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
} else {
status = std::move(result->error());
}
LOG_IF(ERROR, status.is_error() && status.error().code() != 401) << "Receive error for auth.logOut: " << status;
LOG_IF(ERROR, status.is_error() && status.code() != 401) << "Receive error for auth.logOut: " << status;
// state_ will stay LoggingOut, so no queries will work.
destroy_auth_keys();
if (query_id_ != 0) {
@ -897,7 +897,7 @@ void AuthManager::on_delete_account_result(NetQueryPtr &result) {
} else {
status = std::move(result->error());
}
if (status.is_error() && status.error().message() != "USER_DEACTIVATED") {
if (status.is_error() && status.message() != "USER_DEACTIVATED") {
LOG(WARNING) << "Request account.deleteAccount failed: " << status;
// TODO handle some errors
if (query_id_ != 0) {

View File

@ -449,23 +449,6 @@ void BackgroundManager::get_backgrounds(bool for_dark_theme,
}
}
Result<string> BackgroundManager::get_background_url(const string &name,
td_api::object_ptr<td_api::BackgroundType> background_type) {
TRY_RESULT(type, BackgroundType::get_background_type(background_type.get()));
auto url = PSTRING() << G()->get_option_string("t_me_url", "https://t.me/") << "bg/";
auto link = type.get_link();
if (type.has_file()) {
url += name;
if (!link.empty()) {
url += '?';
url += link;
}
} else {
url += link;
}
return url;
}
void BackgroundManager::reload_background_from_server(
BackgroundId background_id, const string &background_name,
telegram_api::object_ptr<telegram_api::InputWallPaper> &&input_wallpaper, Promise<Unit> &&promise) const {

View File

@ -37,9 +37,6 @@ class BackgroundManager final : public Actor {
void get_backgrounds(bool for_dark_theme, Promise<td_api::object_ptr<td_api::backgrounds>> &&promise);
static Result<string> get_background_url(const string &name,
td_api::object_ptr<td_api::BackgroundType> background_type);
void reload_background(BackgroundId background_id, int64 access_hash, Promise<Unit> &&promise);
std::pair<BackgroundId, BackgroundType> search_background(const string &name, Promise<Unit> &&promise);

View File

@ -82,6 +82,8 @@ BackgroundFill::BackgroundFill(const telegram_api::wallPaperSettings *settings)
LOG(ERROR) << "Receive " << to_string(*settings);
rotation_angle_ = 0;
}
} else {
bottom_color_ = top_color_;
}
}

View File

@ -953,6 +953,8 @@ void CallActor::loop() {
break;
}
LOG(INFO) << "Close " << local_call_id_;
container_.for_each(
[](auto id, Promise<NetQueryPtr> &promise) { promise.set_error(Global::request_aborted_error()); });
stop();
break;
}

View File

@ -9,7 +9,6 @@
#include "td/telegram/telegram_api.hpp"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/SliceBuilder.h"
@ -174,25 +173,24 @@ ActorId<CallActor> CallManager::get_call_actor(CallId call_id) {
void CallManager::hangup() {
close_flag_ = true;
for (auto &it : id_to_actor_) {
LOG(INFO) << "Ask close CallActor " << it.first;
LOG(INFO) << "Ask to close CallActor " << it.first.get();
it.second.reset();
}
if (id_to_actor_.empty()) {
stop();
}
}
void CallManager::hangup_shared() {
auto token = narrow_cast<int32>(get_link_token());
auto it = id_to_actor_.find(CallId(token));
if (it != id_to_actor_.end()) {
LOG(INFO) << "Close CallActor " << tag("id", it->first);
it->second.release();
id_to_actor_.erase(it);
} else {
LOG(FATAL) << "Unknown CallActor hangup " << tag("id", static_cast<int32>(token));
}
CHECK(it != id_to_actor_.end());
LOG(INFO) << "Closed CallActor " << it->first.get();
it->second.release();
id_to_actor_.erase(it);
if (close_flag_ && id_to_actor_.empty()) {
stop();
}
}
} // namespace td

View File

@ -10,6 +10,7 @@
#include "td/telegram/CallId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"

View File

@ -176,7 +176,7 @@ void CallbackQueriesManager::on_new_query(int32 flags, int64 callback_query_id,
LOG(ERROR) << "Receive new callback query from invalid " << sender_user_id << " in " << dialog_id;
return;
}
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Have no info about " << sender_user_id;
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Receive unknown " << sender_user_id;
if (!td_->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive new callback query";
return;
@ -208,7 +208,7 @@ void CallbackQueriesManager::on_new_inline_query(
LOG(ERROR) << "Receive new callback query from invalid " << sender_user_id;
return;
}
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Have no info about " << sender_user_id;
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Receive unknown " << sender_user_id;
if (!td_->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive new callback query";
return;

View File

@ -6,6 +6,8 @@
//
#pragma once
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChatId.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/FullMessageId.h"
@ -22,6 +24,12 @@ class ChainId {
uint64 id = 0;
public:
ChainId(ChannelId channel_id) : ChainId(DialogId(channel_id)) {
}
ChainId(ChatId chat_id) : ChainId(DialogId(chat_id)) {
}
ChainId(DialogId dialog_id, MessageContentType message_content_type)
: id((static_cast<uint64>(dialog_id.get()) << 10) + get_message_content_chain_id(message_content_type)) {
}

View File

@ -12,10 +12,14 @@
namespace td {
ClientActor::ClientActor(unique_ptr<TdCallback> callback, Options options) {
ClientActor::ClientActor(unique_ptr<TdCallback> callback, Options options)
: callback_(std::move(callback)), options_(std::move(options)) {
}
void ClientActor::start_up() {
Td::Options td_options;
td_options.net_query_stats = std::move(options.net_query_stats);
td_ = create_actor<Td>("Td", std::move(callback), std::move(td_options));
td_options.net_query_stats = std::move(options_.net_query_stats);
td_ = create_actor<Td>("Td", std::move(callback_), std::move(td_options));
}
void ClientActor::request(uint64 id, td_api::object_ptr<td_api::Function> request) {

View File

@ -81,7 +81,11 @@ class ClientActor final : public Actor {
ClientActor &operator=(const ClientActor &other) = delete;
private:
void start_up() final;
ActorOwn<Td> td_;
unique_ptr<TdCallback> callback_;
Options options_;
};
/**

View File

@ -48,8 +48,7 @@ static std::pair<td_api::object_ptr<td_api::Function>, string> to_request(Slice
td_api::object_ptr<td_api::Function> func;
auto status = from_json(func, std::move(json_value));
if (status.is_error()) {
return {get_return_error_function(PSLICE()
<< "Failed to parse JSON object as TDLib request: " << status.error().message()),
return {get_return_error_function(PSLICE() << "Failed to parse JSON object as TDLib request: " << status.message()),
std::move(extra)};
}
return std::make_pair(std::move(func), std::move(extra));

View File

@ -1467,9 +1467,11 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
string autologin_token;
vector<string> autologin_domains;
vector<string> url_auth_domains;
vector<string> whitelisted_domains;
vector<tl_object_ptr<telegram_api::jsonObjectValue>> new_values;
string ignored_restriction_reasons;
string restriction_add_platforms;
vector<string> dice_emojis;
FlatHashMap<string, size_t> dice_emoji_index;
FlatHashMap<string, string> dice_emoji_success_value;
@ -1489,6 +1491,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
bool is_premium_available = false;
int32 stickers_premium_by_emoji_num = 0;
int32 stickers_normal_by_emoji_per_premium_num = 2;
int32 forum_upgrade_participants_min = 200;
if (config->get_id() == telegram_api::jsonObject::ID) {
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
Slice key = key_value->key_;
@ -1518,6 +1521,25 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
}
continue;
}
if (key == "restriction_add_platforms") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto platforms = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &platform : platforms) {
auto platform_name = get_json_value_string(std::move(platform), key);
if (!platform_name.empty() && platform_name.find(',') == string::npos) {
if (!restriction_add_platforms.empty()) {
restriction_add_platforms += ',';
}
restriction_add_platforms += platform_name;
} else {
LOG(ERROR) << "Receive unexpected restriction platform " << platform_name;
}
}
} else {
LOG(ERROR) << "Receive unexpected restriction_add_platforms " << to_string(*value);
}
continue;
}
if (key == "emojies_animated_zoom") {
animated_emoji_zoom = get_json_value_double(std::move(key_value->value_), key);
continue;
@ -1680,13 +1702,24 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (value->get_id() == telegram_api::jsonArray::ID) {
auto domains = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &domain : domains) {
autologin_domains.push_back(get_json_value_string(std::move(domain), key));
url_auth_domains.push_back(get_json_value_string(std::move(domain), key));
}
} else {
LOG(ERROR) << "Receive unexpected url_auth_domains " << to_string(*value);
}
continue;
}
if (key == "whitelisted_domains") {
if (value->get_id() == telegram_api::jsonArray::ID) {
auto domains = std::move(static_cast<telegram_api::jsonArray *>(value)->value_);
for (auto &domain : domains) {
whitelisted_domains.push_back(get_json_value_string(std::move(domain), key));
}
} else {
LOG(ERROR) << "Receive unexpected whitelisted_domains " << to_string(*value);
}
continue;
}
if (key == "round_video_encoding") {
if (value->get_id() == telegram_api::jsonObject::ID) {
auto video_note_settings = std::move(static_cast<telegram_api::jsonObject *>(value)->value_);
@ -1811,6 +1844,10 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
G()->set_option_integer(key, setting_value);
continue;
}
if (key == "forum_upgrade_participants_min") {
forum_upgrade_participants_min = get_json_value_int(std::move(key_value->value_), key);
continue;
}
new_values.push_back(std::move(key_value));
}
@ -1820,7 +1857,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
config = make_tl_object<telegram_api::jsonObject>(std::move(new_values));
send_closure(G()->link_manager(), &LinkManager::update_autologin_domains, std::move(autologin_token),
std::move(autologin_domains), std::move(url_auth_domains));
std::move(autologin_domains), std::move(url_auth_domains), std::move(whitelisted_domains));
Global &options = *G();
@ -1839,6 +1876,11 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
get_content_settings(Auto());
}
}
if (restriction_add_platforms.empty()) {
options.set_option_empty("restriction_add_platforms");
} else {
options.set_option_string("restriction_add_platforms", restriction_add_platforms);
}
if (!dice_emojis.empty()) {
vector<string> dice_success_values(dice_emojis.size());
@ -1892,6 +1934,11 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
} else {
options.set_option_integer("reactions_uniq_max", reactions_uniq_max);
}
if (forum_upgrade_participants_min < 0) {
options.set_option_empty("forum_member_count_min");
} else {
options.set_option_integer("forum_member_count_min", forum_upgrade_participants_min);
}
bool is_premium = options.get_option_boolean("is_premium");

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#include "td/telegram/ChannelType.h"
#include "td/telegram/ChatId.h"
#include "td/telegram/Contact.h"
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/DialogAdministrator.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogInviteLink.h"
@ -38,6 +39,7 @@
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Usernames.h"
#include "td/actor/actor.h"
#include "td/actor/MultiPromise.h"
@ -128,13 +130,12 @@ class ContactsManager final : public Actor {
string get_dialog_about(DialogId dialog_id);
bool is_update_about_username_change_received(UserId user_id) const;
string get_dialog_search_text(DialogId dialog_id) const;
void for_each_secret_chat_with_user(UserId user_id, const std::function<void(SecretChatId)> &f);
string get_user_username(UserId user_id) const;
string get_channel_username(ChannelId channel_id) const;
string get_secret_chat_username(SecretChatId secret_chat_id) const;
string get_user_first_username(UserId user_id) const;
string get_channel_first_username(ChannelId channel_id) const;
int32 get_secret_chat_date(SecretChatId secret_chat_id) const;
int32 get_secret_chat_ttl(SecretChatId secret_chat_id) const;
@ -178,7 +179,7 @@ class ContactsManager final : public Actor {
void on_update_profile_success(int32 flags, const string &first_name, const string &last_name, const string &about);
void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_name(UserId user_id, string &&first_name, string &&last_name, Usernames &&usernames);
void on_update_user_phone_number(UserId user_id, string &&phone_number);
void on_update_user_photo(UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo_ptr);
void on_update_user_emoji_status(UserId user_id, tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status);
@ -201,7 +202,8 @@ class ContactsManager final : public Actor {
void on_update_chat_default_permissions(ChatId chat_id, RestrictedRights default_permissions, int32 version);
void on_update_chat_pinned_message(ChatId chat_id, MessageId pinned_message_id, int32 version);
void on_update_channel_username(ChannelId channel_id, string &&username);
void on_update_channel_editable_username(ChannelId channel_id, string &&username);
void on_update_channel_usernames(ChannelId channel_id, Usernames &&usernames);
void on_update_channel_description(ChannelId channel_id, string &&description);
void on_update_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id);
void on_update_channel_linked_channel_id(ChannelId channel_id, ChannelId group_channel_id);
@ -285,6 +287,18 @@ class ContactsManager final : public Actor {
UserId add_channel_bot_user();
void on_update_username_is_active(string &&username, bool is_active, Promise<Unit> &&promise);
void on_update_active_usernames_order(vector<string> &&usernames, Promise<Unit> &&promise);
void on_update_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active,
Promise<Unit> &&promise);
void on_deactivate_channel_usernames(ChannelId channel_id, Promise<Unit> &&promise);
void on_update_channel_active_usernames_order(ChannelId channel_id, vector<string> &&usernames,
Promise<Unit> &&promise);
void on_update_online_status_privacy();
void on_update_phone_number_privacy();
@ -347,12 +361,23 @@ class ContactsManager final : public Actor {
void set_username(const string &username, Promise<Unit> &&promise);
void toggle_username_is_active(string &&username, bool is_active, Promise<Unit> &&promise);
void reorder_usernames(vector<string> &&usernames, Promise<Unit> &&promise);
void set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void set_chat_description(ChatId chat_id, const string &description, Promise<Unit> &&promise);
void set_channel_username(ChannelId channel_id, const string &username, Promise<Unit> &&promise);
void toggle_channel_username_is_active(ChannelId channel_id, string &&username, bool is_active,
Promise<Unit> &&promise);
void disable_all_channel_usernames(ChannelId channel_id, Promise<Unit> &&promise);
void reorder_channel_usernames(ChannelId channel_id, vector<string> &&usernames, Promise<Unit> &&promise);
void set_channel_sticker_set(ChannelId channel_id, StickerSetId sticker_set_id, Promise<Unit> &&promise);
void toggle_channel_sign_messages(ChannelId channel_id, bool sign_messages, Promise<Unit> &&promise);
@ -364,6 +389,8 @@ class ContactsManager final : public Actor {
void toggle_channel_is_all_history_available(ChannelId channel_id, bool is_all_history_available,
Promise<Unit> &&promise);
void toggle_channel_is_forum(ChannelId channel_id, bool is_forum, Promise<Unit> &&promise);
void convert_channel_to_gigagroup(ChannelId channel_id, Promise<Unit> &&promise);
void set_channel_description(ChannelId channel_id, const string &description, Promise<Unit> &&promise);
@ -545,9 +572,11 @@ class ContactsManager final : public Actor {
ChannelType get_channel_type(ChannelId channel_id) const;
bool is_broadcast_channel(ChannelId channel_id) const;
bool is_megagroup_channel(ChannelId channel_id) const;
bool is_forum_channel(ChannelId channel_id) const;
int32 get_channel_date(ChannelId channel_id) const;
DialogParticipantStatus get_channel_status(ChannelId channel_id) const;
DialogParticipantStatus get_channel_permissions(ChannelId channel_id) const;
bool get_channel_is_verified(ChannelId channel_id) const;
int32 get_channel_participant_count(ChannelId channel_id) const;
bool get_channel_sign_messages(ChannelId channel_id) const;
bool get_channel_has_linked_channel(ChannelId channel_id) const;
@ -644,11 +673,11 @@ class ContactsManager final : public Actor {
struct User {
string first_name;
string last_name;
string username;
Usernames usernames;
string phone_number;
int64 access_hash = -1;
EmojiStatus emoji_status;
int64 last_sent_emoji_status = 0;
CustomEmojiId last_sent_emoji_status;
ProfilePhoto photo;
@ -778,7 +807,8 @@ class ContactsManager final : public Actor {
ChannelId migrated_to_channel_id;
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false};
RestrictedRights default_permissions{false, false, false, false, false, false,
false, false, false, false, false, false};
static constexpr uint32 CACHE_VERSION = 4;
uint32 cache_version = 0;
@ -846,14 +876,15 @@ class ContactsManager final : public Actor {
int64 access_hash = 0;
string title;
DialogPhoto photo;
string username;
Usernames usernames;
vector<RestrictionReason> restriction_reasons;
DialogParticipantStatus status = DialogParticipantStatus::Banned(0);
RestrictedRights default_permissions{false, false, false, false, false, false, false, false, false, false, false};
RestrictedRights default_permissions{false, false, false, false, false, false,
false, false, false, false, false, false};
int32 date = 0;
int32 participant_count = 0;
static constexpr uint32 CACHE_VERSION = 9;
static constexpr uint32 CACHE_VERSION = 10;
uint32 cache_version = 0;
bool has_linked_channel = false;
@ -867,6 +898,7 @@ class ContactsManager final : public Actor {
bool is_megagroup = false;
bool is_gigagroup = false;
bool is_forum = false;
bool is_verified = false;
bool is_scam = false;
bool is_fake = false;
@ -1079,6 +1111,7 @@ class ContactsManager final : public Actor {
static constexpr int32 USER_FLAG_IS_PREMIUM = 1 << 28;
static constexpr int32 USER_FLAG_ATTACH_MENU_ENABLED = 1 << 29;
static constexpr int32 USER_FLAG_HAS_EMOJI_STATUS = 1 << 30;
static constexpr int32 USER_FLAG_HAS_USERNAMES = 1 << 0;
static constexpr int32 USER_FULL_FLAG_IS_BLOCKED = 1 << 0;
static constexpr int32 USER_FULL_FLAG_HAS_ABOUT = 1 << 1;
@ -1115,7 +1148,7 @@ class ContactsManager final : public Actor {
static constexpr int32 CHANNEL_FLAG_USER_IS_CREATOR = 1 << 0;
static constexpr int32 CHANNEL_FLAG_USER_HAS_LEFT = 1 << 2;
static constexpr int32 CHANNEL_FLAG_IS_BROADCAST = 1 << 5;
static constexpr int32 CHANNEL_FLAG_IS_PUBLIC = 1 << 6;
static constexpr int32 CHANNEL_FLAG_HAS_USERNAME = 1 << 6;
static constexpr int32 CHANNEL_FLAG_IS_VERIFIED = 1 << 7;
static constexpr int32 CHANNEL_FLAG_IS_MEGAGROUP = 1 << 8;
static constexpr int32 CHANNEL_FLAG_IS_RESTRICTED = 1 << 9;
@ -1138,6 +1171,8 @@ class ContactsManager final : public Actor {
static constexpr int32 CHANNEL_FLAG_NOFORWARDS = 1 << 27;
static constexpr int32 CHANNEL_FLAG_JOIN_TO_SEND = 1 << 28;
static constexpr int32 CHANNEL_FLAG_JOIN_REQUEST = 1 << 29;
static constexpr int32 CHANNEL_FLAG_IS_FORUM = 1 << 30;
static constexpr int32 CHANNEL_FLAG_HAS_USERNAMES = 1 << 0;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_PARTICIPANT_COUNT = 1 << 0;
static constexpr int32 CHANNEL_FULL_FLAG_HAS_ADMINISTRATOR_COUNT = 1 << 1;
@ -1239,6 +1274,9 @@ class ContactsManager final : public Actor {
SecretChat *add_secret_chat(SecretChatId secret_chat_id);
string get_user_search_text(UserId user_id) const;
static string get_user_search_text(const User *u);
static DialogParticipantStatus get_chat_status(const Chat *c);
DialogParticipantStatus get_chat_permissions(const Chat *c) const;
@ -1251,13 +1289,17 @@ class ContactsManager final : public Actor {
static bool get_channel_join_to_send(const Channel *c);
static bool get_channel_join_request(const Channel *c);
string get_channel_search_text(ChannelId channel_id) const;
static string get_channel_search_text(const Channel *c);
void set_my_id(UserId my_id);
static bool is_valid_username(const string &username);
static bool is_allowed_username(const string &username);
void on_set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, string &&username);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name);
void on_update_user_usernames(User *u, UserId user_id, Usernames &&usernames);
void on_update_user_phone_number(User *u, UserId user_id, string &&phone_number);
void on_update_user_photo(User *u, UserId user_id, tl_object_ptr<telegram_api::UserProfilePhoto> &&photo,
const char *source);
@ -1317,7 +1359,7 @@ class ContactsManager final : public Actor {
tl_object_ptr<telegram_api::ChatPhoto> &&chat_photo_ptr);
void on_update_channel_photo(Channel *c, ChannelId channel_id, DialogPhoto &&photo, bool invalidate_photo_cache);
static void on_update_channel_title(Channel *c, ChannelId channel_id, string &&title);
void on_update_channel_username(Channel *c, ChannelId channel_id, string &&username);
void on_update_channel_usernames(Channel *c, ChannelId channel_id, Usernames &&usernames);
void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status);
static void on_update_channel_default_permissions(Channel *c, ChannelId channel_id,
RestrictedRights default_permissions);
@ -1339,10 +1381,14 @@ class ContactsManager final : public Actor {
static void on_update_channel_full_bot_user_ids(ChannelFull *channel_full, ChannelId channel_id,
vector<UserId> &&bot_user_ids);
void toggle_username_is_active_impl(string &&username, bool is_active, Promise<Unit> &&promise);
void reorder_usernames_impl(vector<string> &&usernames, Promise<Unit> &&promise);
void on_channel_status_changed(Channel *c, ChannelId channel_id, const DialogParticipantStatus &old_status,
const DialogParticipantStatus &new_status);
void on_channel_username_changed(const Channel *c, ChannelId channel_id, const string &old_username,
const string &new_username);
void on_channel_usernames_changed(const Channel *c, ChannelId channel_id, const Usernames &old_usernames,
const Usernames &new_usernames);
void remove_linked_channel_id(ChannelId channel_id);
ChannelId get_linked_channel_id(ChannelId channel_id) const;
@ -1836,7 +1882,7 @@ class ContactsManager final : public Actor {
bool are_contacts_loaded_ = false;
int32 next_contacts_sync_date_ = 0;
Hints contacts_hints_; // search contacts by first name, last name and username
Hints contacts_hints_; // search contacts by first name, last name and usernames
vector<Promise<Unit>> load_contacts_queries_;
MultiPromiseActor load_contact_users_multipromise_{"LoadContactUsersMultiPromiseActor"};
int32 saved_contact_count_ = -1;

View File

@ -136,7 +136,7 @@ void CountryInfoManager::tear_down() {
}
string CountryInfoManager::get_main_language_code() {
return to_lower(td_->language_pack_manager_->get_actor_unsafe()->get_main_language_code());
return to_lower(td_->language_pack_manager_.get_actor_unsafe()->get_main_language_code());
}
void CountryInfoManager::get_countries(Promise<td_api::object_ptr<td_api::countries>> &&promise) {

View File

@ -0,0 +1,65 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
#include <functional>
#include <type_traits>
namespace td {
class CustomEmojiId {
int64 id = 0;
public:
CustomEmojiId() = default;
explicit CustomEmojiId(int64 custom_emoji_id) : id(custom_emoji_id) {
}
template <class T, typename = std::enable_if_t<std::is_convertible<T, int64>::value>>
CustomEmojiId(T custom_emoji_id) = delete;
bool is_valid() const {
return id != 0;
}
int64 get() const {
return id;
}
bool operator==(const CustomEmojiId &other) const {
return id == other.id;
}
bool operator!=(const CustomEmojiId &other) const {
return id != other.id;
}
template <class StorerT>
void store(StorerT &storer) const {
storer.store_long(id);
}
template <class ParserT>
void parse(ParserT &parser) {
id = parser.fetch_long();
}
};
struct CustomEmojiIdHash {
std::size_t operator()(CustomEmojiId custom_emoji_id) const {
return std::hash<int64>()(custom_emoji_id.get());
}
};
inline StringBuilder &operator<<(StringBuilder &string_builder, CustomEmojiId custom_emoji_id) {
return string_builder << "custom emoji " << custom_emoji_id.get();
}
} // namespace td

View File

@ -10,6 +10,7 @@
#include "td/telegram/TdDb.h"
#include "td/utils/common.h"
#include "td/utils/misc.h"
namespace td {
@ -20,6 +21,18 @@ static string good_prime_key(Slice prime_str) {
}
int DhCache::is_good_prime(Slice prime_str) const {
static string built_in_good_prime =
hex_decode(
"c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac9"
"25139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4a"
"c8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17"
"ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959"
"d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b")
.move_as_ok();
if (prime_str == built_in_good_prime) {
return 1;
}
string value = G()->td_db()->get_binlog_pmc()->get(good_prime_key(prime_str));
if (value == "good") {
return 1;

View File

@ -403,6 +403,8 @@ bool DialogAction::is_canceled_by_message_of_type(MessageContentType message_con
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return false;
default:
UNREACHABLE();

View File

@ -12,6 +12,7 @@
#include "td/telegram/DialogInviteLink.h"
#include "td/telegram/DialogLocation.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/ForumTopicInfo.h"
#include "td/telegram/Global.h"
#include "td/telegram/GroupCallManager.h"
#include "td/telegram/GroupCallParticipant.h"
@ -127,6 +128,11 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
return td_api::make_object<td_api::chatEventUsernameChanged>(std::move(action->prev_value_),
std::move(action->new_value_));
}
case telegram_api::channelAdminLogEventActionChangeUsernames::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionChangeUsernames>(action_ptr);
return td_api::make_object<td_api::chatEventActiveUsernamesChanged>(std::move(action->prev_value_),
std::move(action->new_value_));
}
case telegram_api::channelAdminLogEventActionChangePhoto::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionChangePhoto>(action_ptr);
auto file_manager = td->file_manager_.get();
@ -352,6 +358,59 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
old_available_reactions.get_chat_available_reactions_object(),
new_available_reactions.get_chat_available_reactions_object());
}
case telegram_api::channelAdminLogEventActionToggleForum::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionToggleForum>(action_ptr);
return td_api::make_object<td_api::chatEventIsForumToggled>(action->new_value_);
}
case telegram_api::channelAdminLogEventActionCreateTopic::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionCreateTopic>(action_ptr);
auto topic_info = ForumTopicInfo(action->topic_);
if (topic_info.is_empty()) {
return nullptr;
}
actor_dialog_id = topic_info.get_creator_dialog_id();
return td_api::make_object<td_api::chatEventForumTopicCreated>(topic_info.get_forum_topic_info_object(td));
}
case telegram_api::channelAdminLogEventActionEditTopic::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionEditTopic>(action_ptr);
auto old_topic_info = ForumTopicInfo(action->prev_topic_);
auto new_topic_info = ForumTopicInfo(action->new_topic_);
if (old_topic_info.is_empty() || new_topic_info.is_empty() ||
old_topic_info.get_top_thread_message_id() != new_topic_info.get_top_thread_message_id()) {
LOG(ERROR) << "Receive " << to_string(action);
return nullptr;
}
if (old_topic_info.is_closed() != new_topic_info.is_closed()) {
return td_api::make_object<td_api::chatEventForumTopicToggleIsClosed>(
new_topic_info.get_forum_topic_info_object(td));
}
return td_api::make_object<td_api::chatEventForumTopicEdited>(old_topic_info.get_forum_topic_info_object(td),
new_topic_info.get_forum_topic_info_object(td));
}
case telegram_api::channelAdminLogEventActionDeleteTopic::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionDeleteTopic>(action_ptr);
auto topic_info = ForumTopicInfo(action->topic_);
if (topic_info.is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::chatEventForumTopicDeleted>(topic_info.get_forum_topic_info_object(td));
}
case telegram_api::channelAdminLogEventActionPinTopic::ID: {
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionPinTopic>(action_ptr);
ForumTopicInfo old_topic_info;
ForumTopicInfo new_topic_info;
if (action->prev_topic_ != nullptr) {
old_topic_info = ForumTopicInfo(action->prev_topic_);
}
if (action->new_topic_ != nullptr) {
new_topic_info = ForumTopicInfo(action->new_topic_);
}
if (old_topic_info.is_empty() && new_topic_info.is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::chatEventForumTopicPinned>(old_topic_info.get_forum_topic_info_object(td),
new_topic_info.get_forum_topic_info_object(td));
}
default:
UNREACHABLE();
return nullptr;
@ -411,7 +470,7 @@ class GetChannelAdminLogQuery final : public Td::ResultHandler {
LOG(ERROR) << "Receive invalid " << user_id;
continue;
}
LOG_IF(ERROR, !td_->contacts_manager_->have_user(user_id)) << "Have no info about " << user_id;
LOG_IF(ERROR, !td_->contacts_manager_->have_user(user_id)) << "Receive unknown " << user_id;
DialogId actor_dialog_id;
auto action = get_chat_event_action_object(td_, channel_id_, std::move(event->action_), actor_dialog_id);
@ -485,11 +544,15 @@ static telegram_api::object_ptr<telegram_api::channelAdminLogEventsFilter> get_i
if (filters->video_chat_changes_) {
flags |= telegram_api::channelAdminLogEventsFilter::GROUP_CALL_MASK;
}
if (filters->forum_changes_) {
flags |= telegram_api::channelAdminLogEventsFilter::FORUMS_MASK;
}
return telegram_api::make_object<telegram_api::channelAdminLogEventsFilter>(
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*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/);
}
void get_dialog_event_log(Td *td, DialogId dialog_id, const string &query, int64 from_event_id, int32 limit,

View File

@ -0,0 +1,102 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/DialogNotificationSettings.h"
#include "td/telegram/Global.h"
#include <limits>
namespace td {
StringBuilder &operator<<(StringBuilder &string_builder, const DialogNotificationSettings &notification_settings) {
return string_builder << "[" << notification_settings.mute_until << ", " << notification_settings.sound << ", "
<< notification_settings.show_preview << ", " << notification_settings.silent_send_message
<< ", " << notification_settings.disable_pinned_message_notifications << ", "
<< notification_settings.disable_mention_notifications << ", "
<< notification_settings.use_default_mute_until << ", "
<< notification_settings.use_default_show_preview << ", "
<< notification_settings.use_default_disable_pinned_message_notifications << ", "
<< notification_settings.use_default_disable_mention_notifications << ", "
<< notification_settings.is_synchronized << "]";
}
td_api::object_ptr<td_api::chatNotificationSettings> get_chat_notification_settings_object(
const DialogNotificationSettings *notification_settings) {
CHECK(notification_settings != nullptr);
return td_api::make_object<td_api::chatNotificationSettings>(
notification_settings->use_default_mute_until, max(0, notification_settings->mute_until - G()->unix_time()),
is_notification_sound_default(notification_settings->sound),
get_notification_sound_ringtone_id(notification_settings->sound), notification_settings->use_default_show_preview,
notification_settings->show_preview, notification_settings->use_default_disable_pinned_message_notifications,
notification_settings->disable_pinned_message_notifications,
notification_settings->use_default_disable_mention_notifications,
notification_settings->disable_mention_notifications);
}
static int32 get_mute_until(int32 mute_for) {
if (mute_for <= 0) {
return 0;
}
const int32 MAX_PRECISE_MUTE_FOR = 366 * 86400;
int32 current_time = G()->unix_time();
if (mute_for > MAX_PRECISE_MUTE_FOR || mute_for >= std::numeric_limits<int32>::max() - current_time) {
return std::numeric_limits<int32>::max();
}
return mute_for + current_time;
}
Result<DialogNotificationSettings> get_dialog_notification_settings(
td_api::object_ptr<td_api::chatNotificationSettings> &&notification_settings, bool old_silent_send_message) {
if (notification_settings == nullptr) {
return Status::Error(400, "New notification settings must be non-empty");
}
int32 mute_until =
notification_settings->use_default_mute_for_ ? 0 : get_mute_until(notification_settings->mute_for_);
return DialogNotificationSettings(
notification_settings->use_default_mute_for_, mute_until,
get_notification_sound(notification_settings->use_default_sound_, notification_settings->sound_id_),
notification_settings->use_default_show_preview_, notification_settings->show_preview_, old_silent_send_message,
notification_settings->use_default_disable_pinned_message_notifications_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->use_default_disable_mention_notifications_,
notification_settings->disable_mention_notifications_);
}
DialogNotificationSettings get_dialog_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_use_default_disable_pinned_message_notifications,
bool old_disable_pinned_message_notifications,
bool old_use_default_disable_mention_notifications,
bool old_disable_mention_notifications) {
if (settings == nullptr) {
return DialogNotificationSettings();
}
bool use_default_mute_until = (settings->flags_ & telegram_api::peerNotifySettings::MUTE_UNTIL_MASK) == 0;
bool use_default_show_preview = (settings->flags_ & telegram_api::peerNotifySettings::SHOW_PREVIEWS_MASK) == 0;
auto mute_until = use_default_mute_until || settings->mute_until_ <= G()->unix_time() ? 0 : settings->mute_until_;
bool silent_send_message =
(settings->flags_ & telegram_api::peerNotifySettings::SILENT_MASK) == 0 ? false : settings->silent_;
return {use_default_mute_until,
mute_until,
get_notification_sound(settings.get()),
use_default_show_preview,
settings->show_previews_,
silent_send_message,
old_use_default_disable_pinned_message_notifications,
old_disable_pinned_message_notifications,
old_use_default_disable_mention_notifications,
old_disable_mention_notifications};
}
bool are_default_dialog_notification_settings(const DialogNotificationSettings &settings, bool compare_sound) {
return settings.use_default_mute_until && (!compare_sound || is_notification_sound_default(settings.sound)) &&
settings.use_default_show_preview && settings.use_default_disable_pinned_message_notifications &&
settings.use_default_disable_mention_notifications;
}
} // namespace td

View File

@ -55,68 +55,20 @@ class DialogNotificationSettings {
}
};
enum class NotificationSettingsScope : int32 { Private, Group, Channel };
class ScopeNotificationSettings {
public:
int32 mute_until = 0;
unique_ptr<NotificationSound> sound;
bool show_preview = true;
bool is_synchronized = false;
// local settings
bool disable_pinned_message_notifications = false;
bool disable_mention_notifications = false;
ScopeNotificationSettings() = default;
ScopeNotificationSettings(int32 mute_until, unique_ptr<NotificationSound> &&sound, bool show_preview,
bool disable_pinned_message_notifications, bool disable_mention_notifications)
: mute_until(mute_until)
, sound(std::move(sound))
, show_preview(show_preview)
, is_synchronized(true)
, disable_pinned_message_notifications(disable_pinned_message_notifications)
, disable_mention_notifications(disable_mention_notifications) {
}
};
StringBuilder &operator<<(StringBuilder &string_builder, const DialogNotificationSettings &notification_settings);
StringBuilder &operator<<(StringBuilder &string_builder, NotificationSettingsScope scope);
StringBuilder &operator<<(StringBuilder &string_builder, const ScopeNotificationSettings &notification_settings);
td_api::object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope_object(
NotificationSettingsScope scope);
td_api::object_ptr<td_api::chatNotificationSettings> get_chat_notification_settings_object(
const DialogNotificationSettings *notification_settings);
td_api::object_ptr<td_api::scopeNotificationSettings> get_scope_notification_settings_object(
const ScopeNotificationSettings *notification_settings);
telegram_api::object_ptr<telegram_api::InputNotifyPeer> get_input_notify_peer(NotificationSettingsScope scope);
NotificationSettingsScope get_notification_settings_scope(
const td_api::object_ptr<td_api::NotificationSettingsScope> &scope);
Result<DialogNotificationSettings> get_dialog_notification_settings(
td_api::object_ptr<td_api::chatNotificationSettings> &&notification_settings, bool old_silent_send_message);
Result<ScopeNotificationSettings> get_scope_notification_settings(
td_api::object_ptr<td_api::scopeNotificationSettings> &&notification_settings);
DialogNotificationSettings get_dialog_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_use_default_disable_pinned_message_notifications,
bool old_disable_pinned_message_notifications,
bool old_use_default_disable_mention_notifications,
bool old_disable_mention_notifications);
ScopeNotificationSettings get_scope_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_disable_pinned_message_notifications,
bool old_disable_mention_notifications);
bool are_default_dialog_notification_settings(const DialogNotificationSettings &settings, bool compare_sound);
} // namespace td

View File

@ -6,8 +6,8 @@
//
#pragma once
#include "td/telegram/DialogNotificationSettings.h"
#include "td/telegram/Global.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/NotificationSound.h"
#include "td/utils/tl_helpers.h"
@ -86,58 +86,4 @@ void parse(DialogNotificationSettings &notification_settings, ParserT &parser) {
}
}
template <class StorerT>
void store(const ScopeNotificationSettings &notification_settings, StorerT &storer) {
bool is_muted = notification_settings.mute_until != 0 && notification_settings.mute_until > G()->unix_time();
bool has_sound = notification_settings.sound != nullptr;
bool has_ringtone_support = true;
BEGIN_STORE_FLAGS();
STORE_FLAG(is_muted);
STORE_FLAG(has_sound);
STORE_FLAG(notification_settings.show_preview);
STORE_FLAG(false);
STORE_FLAG(notification_settings.is_synchronized);
STORE_FLAG(notification_settings.disable_pinned_message_notifications);
STORE_FLAG(notification_settings.disable_mention_notifications);
STORE_FLAG(has_ringtone_support);
END_STORE_FLAGS();
if (is_muted) {
store(notification_settings.mute_until, storer);
}
if (has_sound) {
store(notification_settings.sound, storer);
}
}
template <class ParserT>
void parse(ScopeNotificationSettings &notification_settings, ParserT &parser) {
bool is_muted;
bool has_sound;
bool silent_send_message_ignored;
bool has_ringtone_support;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_muted);
PARSE_FLAG(has_sound);
PARSE_FLAG(notification_settings.show_preview);
PARSE_FLAG(silent_send_message_ignored);
PARSE_FLAG(notification_settings.is_synchronized);
PARSE_FLAG(notification_settings.disable_pinned_message_notifications);
PARSE_FLAG(notification_settings.disable_mention_notifications);
PARSE_FLAG(has_ringtone_support);
END_PARSE_FLAGS();
(void)silent_send_message_ignored;
if (is_muted) {
parse(notification_settings.mute_until, parser);
}
if (has_sound) {
if (has_ringtone_support) {
parse_notification_sound(notification_settings.sound, parser);
} else {
string sound;
parse(sound, parser);
notification_settings.sound = get_legacy_notification_sound(sound);
}
}
}
} // namespace td

View File

@ -28,10 +28,10 @@ AdministratorRights::AdministratorRights(const tl_object_ptr<telegram_api::chatA
if (!rights->other_) {
LOG(ERROR) << "Receive wrong other flag in " << to_string(rights);
}
*this =
AdministratorRights(rights->anonymous_, rights->other_, rights->change_info_, rights->post_messages_,
rights->edit_messages_, rights->delete_messages_, rights->invite_users_, rights->ban_users_,
rights->pin_messages_, rights->add_admins_, rights->manage_call_, channel_type);
*this = AdministratorRights(rights->anonymous_, rights->other_, rights->change_info_, rights->post_messages_,
rights->edit_messages_, rights->delete_messages_, rights->invite_users_,
rights->ban_users_, rights->pin_messages_, rights->manage_topics_, rights->add_admins_,
rights->manage_call_, channel_type);
}
AdministratorRights::AdministratorRights(const td_api::object_ptr<td_api::chatAdministratorRights> &rights,
@ -43,16 +43,19 @@ AdministratorRights::AdministratorRights(const td_api::object_ptr<td_api::chatAd
*this = AdministratorRights(rights->is_anonymous_, rights->can_manage_chat_, rights->can_change_info_,
rights->can_post_messages_, rights->can_edit_messages_, rights->can_delete_messages_,
rights->can_invite_users_, rights->can_restrict_members_, rights->can_pin_messages_,
rights->can_promote_members_, rights->can_manage_video_chats_, channel_type);
rights->can_manage_topics_, rights->can_promote_members_, rights->can_manage_video_chats_,
channel_type);
}
AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dialog, bool can_change_info,
bool can_post_messages, bool can_edit_messages, bool can_delete_messages,
bool can_invite_users, bool can_restrict_members, bool can_pin_messages,
bool can_promote_members, bool can_manage_calls, ChannelType channel_type) {
bool can_manage_topics, bool can_promote_members, bool can_manage_calls,
ChannelType channel_type) {
switch (channel_type) {
case ChannelType::Broadcast:
can_pin_messages = false;
can_manage_topics = false;
is_anonymous = false;
break;
case ChannelType::Megagroup:
@ -70,6 +73,7 @@ AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dial
(static_cast<uint32>(can_invite_users) * CAN_INVITE_USERS) |
(static_cast<uint32>(can_restrict_members) * CAN_RESTRICT_MEMBERS) |
(static_cast<uint32>(can_pin_messages) * CAN_PIN_MESSAGES) |
(static_cast<uint32>(can_manage_topics) * CAN_MANAGE_TOPICS) |
(static_cast<uint32>(can_promote_members) * CAN_PROMOTE_MEMBERS) |
(static_cast<uint32>(can_manage_calls) * CAN_MANAGE_CALLS) |
(static_cast<uint32>(is_anonymous) * IS_ANONYMOUS);
@ -83,7 +87,7 @@ AdministratorRights::AdministratorRights(bool is_anonymous, bool can_manage_dial
telegram_api::object_ptr<telegram_api::chatAdminRights> AdministratorRights::get_chat_admin_rights() const {
int32 flags = 0;
if ((flags_ & CAN_CHANGE_INFO_AND_SETTINGS) != 0) {
if (can_change_info_and_settings()) {
flags |= telegram_api::chatAdminRights::CHANGE_INFO_MASK;
}
if (can_post_messages()) {
@ -95,15 +99,18 @@ telegram_api::object_ptr<telegram_api::chatAdminRights> AdministratorRights::get
if (can_delete_messages()) {
flags |= telegram_api::chatAdminRights::DELETE_MESSAGES_MASK;
}
if ((flags_ & CAN_INVITE_USERS) != 0) {
if (can_invite_users()) {
flags |= telegram_api::chatAdminRights::INVITE_USERS_MASK;
}
if (can_restrict_members()) {
flags |= telegram_api::chatAdminRights::BAN_USERS_MASK;
}
if ((flags_ & CAN_PIN_MESSAGES) != 0) {
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;
}
@ -119,14 +126,15 @@ telegram_api::object_ptr<telegram_api::chatAdminRights> AdministratorRights::get
return telegram_api::make_object<telegram_api::chatAdminRights>(
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*/);
}
td_api::object_ptr<td_api::chatAdministratorRights> AdministratorRights::get_chat_administrator_rights_object() const {
return td_api::make_object<td_api::chatAdministratorRights>(
can_manage_dialog(), can_change_info_and_settings(), can_post_messages(), can_edit_messages(),
can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), can_promote_members(),
can_manage_calls(), is_anonymous());
can_delete_messages(), can_invite_users(), can_restrict_members(), can_pin_messages(), can_manage_topics(),
can_promote_members(), can_manage_calls(), is_anonymous());
}
bool operator==(const AdministratorRights &lhs, const AdministratorRights &rhs) {
@ -163,6 +171,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const AdministratorRigh
if (status.can_pin_messages()) {
string_builder << "(pin)";
}
if (status.can_manage_topics()) {
string_builder << "(manage_topics)";
}
if (status.can_promote_members()) {
string_builder << "(promote)";
}
@ -186,9 +197,10 @@ RestrictedRights::RestrictedRights(const tl_object_ptr<telegram_api::chatBannedR
LOG_IF(ERROR, rights->until_date_ != std::numeric_limits<int32>::max())
<< "Have until date " << rights->until_date_ << " in restricted rights";
*this = RestrictedRights(!rights->send_messages_, !rights->send_media_, !rights->send_stickers_, !rights->send_gifs_,
!rights->send_games_, !rights->send_inline_, !rights->embed_links_, !rights->send_polls_,
!rights->change_info_, !rights->invite_users_, !rights->pin_messages_);
*this =
RestrictedRights(!rights->send_messages_, !rights->send_media_, !rights->send_stickers_, !rights->send_gifs_,
!rights->send_games_, !rights->send_inline_, !rights->embed_links_, !rights->send_polls_,
!rights->change_info_, !rights->invite_users_, !rights->pin_messages_, !rights->manage_topics_);
}
RestrictedRights::RestrictedRights(const td_api::object_ptr<td_api::chatPermissions> &rights) {
@ -204,13 +216,15 @@ RestrictedRights::RestrictedRights(const td_api::object_ptr<td_api::chatPermissi
*this = RestrictedRights(can_send_messages, can_send_media, rights->can_send_other_messages_,
rights->can_send_other_messages_, rights->can_send_other_messages_,
rights->can_send_other_messages_, rights->can_add_web_page_previews_, can_send_polls,
rights->can_change_info_, rights->can_invite_users_, rights->can_pin_messages_);
rights->can_change_info_, rights->can_invite_users_, rights->can_pin_messages_,
rights->can_manage_topics_);
}
RestrictedRights::RestrictedRights(bool can_send_messages, bool can_send_media, bool can_send_stickers,
bool can_send_animations, bool can_send_games, bool can_use_inline_bots,
bool can_add_web_page_previews, bool can_send_polls,
bool can_change_info_and_settings, bool can_invite_users, bool can_pin_messages) {
bool can_change_info_and_settings, bool can_invite_users, bool can_pin_messages,
bool can_manage_topics) {
flags_ = (static_cast<uint32>(can_send_messages) * CAN_SEND_MESSAGES) |
(static_cast<uint32>(can_send_media) * CAN_SEND_MEDIA) |
(static_cast<uint32>(can_send_stickers) * CAN_SEND_STICKERS) |
@ -221,14 +235,16 @@ RestrictedRights::RestrictedRights(bool can_send_messages, bool can_send_media,
(static_cast<uint32>(can_send_polls) * CAN_SEND_POLLS) |
(static_cast<uint32>(can_change_info_and_settings) * CAN_CHANGE_INFO_AND_SETTINGS) |
(static_cast<uint32>(can_invite_users) * CAN_INVITE_USERS) |
(static_cast<uint32>(can_pin_messages) * CAN_PIN_MESSAGES);
(static_cast<uint32>(can_pin_messages) * CAN_PIN_MESSAGES) |
(static_cast<uint32>(can_manage_topics) * CAN_MANAGE_TOPICS);
}
td_api::object_ptr<td_api::chatPermissions> RestrictedRights::get_chat_permissions_object() const {
return td_api::make_object<td_api::chatPermissions>(
can_send_messages(), can_send_media(), can_send_polls(),
can_send_stickers() || can_send_animations() || can_send_games() || can_use_inline_bots(),
can_add_web_page_previews(), can_change_info_and_settings(), can_invite_users(), can_pin_messages());
can_add_web_page_previews(), can_change_info_and_settings(), can_invite_users(), can_pin_messages(),
can_manage_topics());
}
tl_object_ptr<telegram_api::chatBannedRights> RestrictedRights::get_chat_banned_rights() const {
@ -266,12 +282,15 @@ tl_object_ptr<telegram_api::chatBannedRights> RestrictedRights::get_chat_banned_
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<telegram_api::chatBannedRights>(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*/, 0);
return make_tl_object<telegram_api::chatBannedRights>(
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*/, 0);
}
bool operator==(const RestrictedRights &lhs, const RestrictedRights &rhs) {
@ -317,6 +336,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const RestrictedRights
if (!status.can_pin_messages()) {
string_builder << "(pin)";
}
if (!status.can_manage_topics()) {
string_builder << "(topics)";
}
return string_builder;
}
@ -375,16 +397,16 @@ DialogParticipantStatus DialogParticipantStatus::Banned(int32 banned_until_date)
}
DialogParticipantStatus DialogParticipantStatus::GroupAdministrator(bool is_creator) {
return Administrator(
AdministratorRights(false, true, true, false, false, true, true, true, true, false, true, ChannelType::Unknown),
string(), is_creator);
return Administrator(AdministratorRights(false, true, true, false, false, true, true, true, true, false, false, true,
ChannelType::Unknown),
string(), is_creator);
}
DialogParticipantStatus DialogParticipantStatus::ChannelAdministrator(bool is_creator, bool is_megagroup) {
auto rights = is_megagroup ? AdministratorRights(false, true, true, false, false, true, true, true, true, false,
auto rights = is_megagroup ? AdministratorRights(false, true, true, false, false, true, true, true, true, true, false,
false, ChannelType::Megagroup)
: AdministratorRights(false, true, false, true, true, true, false, true, false, false,
false, ChannelType::Broadcast);
false, false, ChannelType::Broadcast);
return Administrator(rights, string(), is_creator);
}
@ -417,7 +439,7 @@ DialogParticipantStatus::DialogParticipantStatus(bool is_member,
RestrictedRights DialogParticipantStatus::get_effective_restricted_rights() const {
return RestrictedRights(can_send_messages(), can_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_change_info_and_settings(), can_invite_users(), can_pin_messages(), can_create_topics());
}
tl_object_ptr<td_api::ChatMemberStatus> DialogParticipantStatus::get_chat_member_status_object() const {

View File

@ -33,11 +33,13 @@ class AdministratorRights {
static constexpr uint32 CAN_PROMOTE_MEMBERS = 1 << 8;
static constexpr uint32 CAN_MANAGE_CALLS = 1 << 9;
static constexpr uint32 CAN_MANAGE_DIALOG = 1 << 10;
static constexpr uint32 CAN_MANAGE_TOPICS = 1 << 11;
static constexpr uint32 IS_ANONYMOUS = 1 << 13;
static constexpr uint32 ALL_ADMINISTRATOR_RIGHTS =
CAN_CHANGE_INFO_AND_SETTINGS | CAN_POST_MESSAGES | CAN_EDIT_MESSAGES | CAN_DELETE_MESSAGES | CAN_INVITE_USERS |
CAN_RESTRICT_MEMBERS | CAN_PIN_MESSAGES | CAN_PROMOTE_MEMBERS | CAN_MANAGE_CALLS | CAN_MANAGE_DIALOG;
static constexpr uint32 ALL_ADMINISTRATOR_RIGHTS = CAN_CHANGE_INFO_AND_SETTINGS | CAN_POST_MESSAGES |
CAN_EDIT_MESSAGES | CAN_DELETE_MESSAGES | CAN_INVITE_USERS |
CAN_RESTRICT_MEMBERS | CAN_PIN_MESSAGES | CAN_MANAGE_TOPICS |
CAN_PROMOTE_MEMBERS | CAN_MANAGE_CALLS | CAN_MANAGE_DIALOG;
uint32 flags_;
@ -57,8 +59,8 @@ class AdministratorRights {
AdministratorRights(bool is_anonymous, bool can_manage_dialog, bool can_change_info, bool can_post_messages,
bool can_edit_messages, bool can_delete_messages, bool can_invite_users,
bool can_restrict_members, bool can_pin_messages, bool can_promote_members, bool can_manage_calls,
ChannelType channel_type);
bool can_restrict_members, bool can_pin_messages, bool can_manage_topics,
bool can_promote_members, bool can_manage_calls, ChannelType channel_type);
telegram_api::object_ptr<telegram_api::chatAdminRights> get_chat_admin_rights() const;
@ -88,11 +90,6 @@ class AdministratorRights {
return (flags_ & CAN_INVITE_USERS) != 0;
}
bool can_manage_invite_links() const {
// invite links can be managed, only if administrator was explicitly granted the right
return (flags_ & CAN_INVITE_USERS) != 0;
}
bool can_restrict_members() const {
return (flags_ & CAN_RESTRICT_MEMBERS) != 0;
}
@ -101,6 +98,10 @@ class AdministratorRights {
return (flags_ & CAN_PIN_MESSAGES) != 0;
}
bool can_manage_topics() const {
return (flags_ & CAN_MANAGE_TOPICS) != 0;
}
bool can_promote_members() const {
return (flags_ & CAN_PROMOTE_MEMBERS) != 0;
}
@ -146,9 +147,10 @@ class RestrictedRights {
static constexpr uint32 CAN_CHANGE_INFO_AND_SETTINGS = 1 << 24;
static constexpr uint32 CAN_INVITE_USERS = 1 << 25;
static constexpr uint32 CAN_PIN_MESSAGES = 1 << 26;
static constexpr uint32 CAN_MANAGE_TOPICS = 1 << 12;
static constexpr uint32 ALL_ADMIN_PERMISSION_RIGHTS =
CAN_CHANGE_INFO_AND_SETTINGS | CAN_INVITE_USERS | CAN_PIN_MESSAGES;
CAN_CHANGE_INFO_AND_SETTINGS | CAN_INVITE_USERS | CAN_PIN_MESSAGES | CAN_MANAGE_TOPICS;
static constexpr uint32 ALL_RESTRICTED_RIGHTS =
CAN_SEND_MESSAGES | CAN_SEND_MEDIA | CAN_SEND_STICKERS | CAN_SEND_ANIMATIONS | CAN_SEND_GAMES |
@ -168,7 +170,8 @@ class RestrictedRights {
RestrictedRights(bool can_send_messages, bool can_send_media, bool can_send_stickers, bool can_send_animations,
bool can_send_games, bool can_use_inline_bots, bool can_add_web_page_previews, bool can_send_polls,
bool can_change_info_and_settings, bool can_invite_users, bool can_pin_messages);
bool can_change_info_and_settings, bool can_invite_users, bool can_pin_messages,
bool can_manage_topics);
td_api::object_ptr<td_api::chatPermissions> get_chat_permissions_object() const;
@ -186,6 +189,10 @@ class RestrictedRights {
return (flags_ & CAN_PIN_MESSAGES) != 0;
}
bool can_manage_topics() const {
return (flags_ & CAN_MANAGE_TOPICS) != 0;
}
bool can_send_messages() const {
return (flags_ & CAN_SEND_MESSAGES) != 0;
}
@ -240,7 +247,7 @@ bool operator!=(const RestrictedRights &lhs, const RestrictedRights &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const RestrictedRights &status);
class DialogParticipantStatus {
// only flags 11 and 12 are unused
// all flags are used
static constexpr uint32 HAS_RANK = 1 << 14;
static constexpr uint32 CAN_BE_EDITED = 1 << 15;
@ -348,6 +355,15 @@ class DialogParticipantStatus {
return get_administrator_rights().can_pin_messages() || get_restricted_rights().can_pin_messages();
}
bool can_edit_topics() const {
// topics can be edited, only if administrator was explicitly granted the right
return get_administrator_rights().can_manage_topics();
}
bool can_create_topics() const {
return get_administrator_rights().can_manage_topics() || get_restricted_rights().can_manage_topics();
}
bool can_promote_members() const {
return get_administrator_rights().can_promote_members();
}

View File

@ -122,6 +122,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
}
}
int32 video_duration = 0;
string video_waveform;
if (video != nullptr) {
video_duration = video->duration_;
auto video_dimensions = get_dimensions(video->w_, video->h_, "documentAttributeVideo");
@ -131,6 +132,11 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
}
dimensions = video_dimensions;
}
if (audio != nullptr) {
video_waveform = audio->waveform_.as_slice().str();
type_attributes--;
audio = nullptr;
}
if (animated != nullptr) {
type_attributes--;
@ -514,7 +520,7 @@ Document DocumentsManager::on_get_document(RemoteDocument remote_document, Dialo
break;
case Document::Type::VideoNote:
td_->video_notes_manager_->create_video_note(file_id, std::move(minithumbnail), std::move(thumbnail),
video_duration, dimensions, !is_web);
video_duration, dimensions, std::move(video_waveform), !is_web);
break;
case Document::Type::VoiceNote: {
int32 duration = 0;
@ -545,11 +551,11 @@ FileId DocumentsManager::on_get_document(unique_ptr<GeneralDocument> new_documen
CHECK(d->file_id == new_document->file_id);
if (d->mime_type != new_document->mime_type) {
LOG(DEBUG) << "Document " << file_id << " mime_type has changed";
d->mime_type = new_document->mime_type;
d->mime_type = std::move(new_document->mime_type);
}
if (d->file_name != new_document->file_name) {
LOG(DEBUG) << "Document " << file_id << " file_name has changed";
d->file_name = new_document->file_name;
d->file_name = std::move(new_document->file_name);
}
if (d->minithumbnail != new_document->minithumbnail) {
d->minithumbnail = std::move(new_document->minithumbnail);
@ -561,7 +567,7 @@ FileId DocumentsManager::on_get_document(unique_ptr<GeneralDocument> new_documen
LOG(INFO) << "Document " << file_id << " thumbnail has changed from " << d->thumbnail << " to "
<< new_document->thumbnail;
}
d->thumbnail = new_document->thumbnail;
d->thumbnail = std::move(new_document->thumbnail);
}
}

View File

@ -24,6 +24,7 @@
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/tl_helpers.h"
#include <algorithm>

View File

@ -47,12 +47,14 @@ void DownloadManagerCallback::update_file_removed(FileId file_id, DownloadManage
void DownloadManagerCallback::start_file(FileId file_id, int8 priority, ActorShared<DownloadManager> download_manager) {
send_closure_later(td_->file_manager_actor_, &FileManager::download, file_id,
make_download_file_callback(td_, std::move(download_manager)), priority,
FileManager::KEEP_DOWNLOAD_OFFSET, FileManager::IGNORE_DOWNLOAD_LIMIT);
FileManager::KEEP_DOWNLOAD_OFFSET, FileManager::IGNORE_DOWNLOAD_LIMIT,
Promise<td_api::object_ptr<td_api::file>>());
}
void DownloadManagerCallback::pause_file(FileId file_id) {
send_closure_later(td_->file_manager_actor_, &FileManager::download, file_id, nullptr, 0,
FileManager::KEEP_DOWNLOAD_OFFSET, FileManager::KEEP_DOWNLOAD_LIMIT);
FileManager::KEEP_DOWNLOAD_OFFSET, FileManager::KEEP_DOWNLOAD_LIMIT,
Promise<td_api::object_ptr<td_api::file>>());
}
void DownloadManagerCallback::delete_file(FileId file_id) {
@ -75,7 +77,7 @@ FileView DownloadManagerCallback::get_file_view(FileId file_id) {
}
FileView DownloadManagerCallback::get_sync_file_view(FileId file_id) {
td_->file_manager_->check_local_location(file_id);
td_->file_manager_->check_local_location(file_id, true);
return get_file_view(file_id);
}

View File

@ -14,8 +14,11 @@
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <limits>
namespace td {
struct EmojiStatuses {
@ -203,7 +206,7 @@ EmojiStatus::EmojiStatus(const td_api::object_ptr<td_api::emojiStatus> &emoji_st
return;
}
custom_emoji_id_ = emoji_status->custom_emoji_id_;
custom_emoji_id_ = CustomEmojiId(emoji_status->custom_emoji_id_);
if (duration != 0) {
int32 current_time = G()->unix_time();
if (duration >= std::numeric_limits<int32>::max() - current_time) {
@ -223,12 +226,12 @@ EmojiStatus::EmojiStatus(tl_object_ptr<telegram_api::EmojiStatus> &&emoji_status
break;
case telegram_api::emojiStatus::ID: {
auto status = static_cast<const telegram_api::emojiStatus *>(emoji_status.get());
custom_emoji_id_ = status->document_id_;
custom_emoji_id_ = CustomEmojiId(status->document_id_);
break;
}
case telegram_api::emojiStatusUntil::ID: {
auto status = static_cast<const telegram_api::emojiStatusUntil *>(emoji_status.get());
custom_emoji_id_ = status->document_id_;
custom_emoji_id_ = CustomEmojiId(status->document_id_);
until_date_ = status->until_;
break;
}
@ -242,24 +245,24 @@ tl_object_ptr<telegram_api::EmojiStatus> EmojiStatus::get_input_emoji_status() c
return make_tl_object<telegram_api::emojiStatusEmpty>();
}
if (until_date_ != 0) {
return make_tl_object<telegram_api::emojiStatusUntil>(custom_emoji_id_, until_date_);
return make_tl_object<telegram_api::emojiStatusUntil>(custom_emoji_id_.get(), until_date_);
}
return make_tl_object<telegram_api::emojiStatus>(custom_emoji_id_);
return make_tl_object<telegram_api::emojiStatus>(custom_emoji_id_.get());
}
td_api::object_ptr<td_api::emojiStatus> EmojiStatus::get_emoji_status_object() const {
if (is_empty()) {
return nullptr;
}
return td_api::make_object<td_api::emojiStatus>(custom_emoji_id_);
return td_api::make_object<td_api::emojiStatus>(custom_emoji_id_.get());
}
int64 EmojiStatus::get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const {
CustomEmojiId EmojiStatus::get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const {
if (!is_premium) {
return 0;
return CustomEmojiId();
}
if (until_date_ != 0 && until_date_ <= unix_time) {
return 0;
return CustomEmojiId();
}
return custom_emoji_id_;
}
@ -268,7 +271,7 @@ StringBuilder &operator<<(StringBuilder &string_builder, const EmojiStatus &emoj
if (emoji_status.is_empty()) {
return string_builder << "DefaultProfileBadge";
}
string_builder << "CustomEmoji " << emoji_status.custom_emoji_id_;
string_builder << emoji_status.custom_emoji_id_;
if (emoji_status.until_date_ != 0) {
string_builder << " until " << emoji_status.until_date_;
}

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -19,7 +20,7 @@ namespace td {
class Td;
class EmojiStatus {
int64 custom_emoji_id_ = 0;
CustomEmojiId custom_emoji_id_;
int32 until_date_ = 0;
friend bool operator==(const EmojiStatus &lhs, const EmojiStatus &rhs);
@ -37,13 +38,13 @@ class EmojiStatus {
td_api::object_ptr<td_api::emojiStatus> get_emoji_status_object() const;
int64 get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const;
CustomEmojiId get_effective_custom_emoji_id(bool is_premium, int32 unix_time) const;
bool is_empty() const {
return custom_emoji_id_ == 0;
return !custom_emoji_id_.is_valid();
}
int64 get_custom_emoji_id() const {
CustomEmojiId get_custom_emoji_id() const {
return custom_emoji_id_;
}
@ -57,7 +58,7 @@ class EmojiStatus {
template <class StorerT>
void store(StorerT &storer) const {
bool has_custom_emoji_id = custom_emoji_id_ != 0;
bool has_custom_emoji_id = custom_emoji_id_.is_valid();
bool has_until_date = until_date_ != 0;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_custom_emoji_id);

View File

@ -0,0 +1,52 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ForumTopic.h"
#include "td/telegram/DraftMessage.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/Td.h"
#include "td/utils/logging.h"
namespace td {
ForumTopic::ForumTopic(Td *td, tl_object_ptr<telegram_api::ForumTopic> &&forum_topic_ptr) {
CHECK(forum_topic_ptr != nullptr);
if (forum_topic_ptr->get_id() != telegram_api::forumTopic::ID) {
LOG(INFO) << "Receive " << to_string(forum_topic_ptr);
return;
}
info_ = ForumTopicInfo(forum_topic_ptr);
auto *forum_topic = static_cast<telegram_api::forumTopic *>(forum_topic_ptr.get());
last_message_id_ = MessageId(ServerMessageId(forum_topic->top_message_));
is_pinned_ = forum_topic->pinned_;
unread_count_ = forum_topic->unread_count_;
last_read_inbox_message_id_ = MessageId(ServerMessageId(forum_topic->read_inbox_max_id_));
last_read_outbox_message_id_ = MessageId(ServerMessageId(forum_topic->read_outbox_max_id_));
unread_mention_count_ = forum_topic->unread_mentions_count_;
unread_reaction_count_ = forum_topic->unread_reactions_count_;
notification_settings_ =
get_dialog_notification_settings(std::move(forum_topic->notify_settings_), false, false, false, false);
draft_message_ = get_draft_message(td->contacts_manager_.get(), std::move(forum_topic->draft_));
}
td_api::object_ptr<td_api::forumTopic> ForumTopic::get_forum_topic_object(Td *td) const {
if (is_empty()) {
return nullptr;
}
// TODO draft_message = can_send_message(dialog_id, info_.get_top_thread_message_id()).is_ok() ? ... : nullptr;
// TODO last_message
auto draft_message = get_draft_message_object(draft_message_);
return td_api::make_object<td_api::forumTopic>(
info_.get_forum_topic_info_object(td), nullptr, is_pinned_, unread_count_, last_read_inbox_message_id_.get(),
last_read_outbox_message_id_.get(), unread_mention_count_, unread_reaction_count_,
get_chat_notification_settings_object(&notification_settings_), std::move(draft_message));
}
} // namespace td

50
td/telegram/ForumTopic.h Normal file
View File

@ -0,0 +1,50 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogNotificationSettings.h"
#include "td/telegram/ForumTopicInfo.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
namespace td {
class DraftMessage;
class Td;
class ForumTopic {
ForumTopicInfo info_;
MessageId last_message_id_;
bool is_pinned_ = false;
int32 unread_count_ = 0;
MessageId last_read_inbox_message_id_;
MessageId last_read_outbox_message_id_;
int32 unread_mention_count_ = 0;
int32 unread_reaction_count_ = 0;
DialogNotificationSettings notification_settings_;
unique_ptr<DraftMessage> draft_message_;
public:
ForumTopic() = default;
ForumTopic(Td *td, tl_object_ptr<telegram_api::ForumTopic> &&forum_topic_ptr);
bool is_empty() const {
return info_.is_empty();
}
MessageId get_top_thread_message_id() const {
return info_.get_top_thread_message_id();
}
td_api::object_ptr<td_api::forumTopic> get_forum_topic_object(Td *td) const;
};
} // namespace td

View File

@ -0,0 +1,42 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ForumTopicEditedData.h"
namespace td {
td_api::object_ptr<td_api::MessageContent> ForumTopicEditedData::get_message_content_object() const {
if (edit_is_closed_) {
return td_api::make_object<td_api::messageForumTopicIsClosedToggled>(is_closed_);
}
return td_api::make_object<td_api::messageForumTopicEdited>(title_, edit_icon_custom_emoji_id_,
icon_custom_emoji_id_.get());
}
bool operator==(const ForumTopicEditedData &lhs, const ForumTopicEditedData &rhs) {
return lhs.title_ == rhs.title_ && lhs.icon_custom_emoji_id_ == rhs.icon_custom_emoji_id_ &&
lhs.edit_icon_custom_emoji_id_ == rhs.edit_icon_custom_emoji_id_ &&
lhs.edit_is_closed_ == rhs.edit_is_closed_ && lhs.is_closed_ == rhs.is_closed_;
}
bool operator!=(const ForumTopicEditedData &lhs, const ForumTopicEditedData &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicEditedData &topic_edited_data) {
if (!topic_edited_data.title_.empty()) {
string_builder << "set title to \"" << topic_edited_data.title_ << '"';
}
if (topic_edited_data.edit_icon_custom_emoji_id_) {
string_builder << "set icon to " << topic_edited_data.icon_custom_emoji_id_;
}
if (topic_edited_data.edit_is_closed_) {
string_builder << "set is_closed to " << topic_edited_data.is_closed_;
}
return string_builder;
}
} // namespace td

View File

@ -0,0 +1,60 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
namespace td {
class ForumTopicEditedData {
string title_;
CustomEmojiId icon_custom_emoji_id_;
bool edit_icon_custom_emoji_id_ = false;
bool edit_is_closed_ = false;
bool is_closed_ = false;
friend bool operator==(const ForumTopicEditedData &lhs, const ForumTopicEditedData &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicEditedData &topic_edited_data);
friend class ForumTopicInfo;
public:
ForumTopicEditedData() = default;
bool is_empty() const {
return title_.empty() && !edit_icon_custom_emoji_id_ && !edit_is_closed_;
}
ForumTopicEditedData(string &&title, bool edit_icon_custom_emoji_id, int64 icon_custom_emoji_id, bool edit_is_closed,
bool is_closed)
: title_(std::move(title))
, icon_custom_emoji_id_(icon_custom_emoji_id)
, edit_icon_custom_emoji_id_(edit_icon_custom_emoji_id)
, edit_is_closed_(edit_is_closed)
, is_closed_(is_closed) {
}
td_api::object_ptr<td_api::MessageContent> get_message_content_object() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
bool operator==(const ForumTopicEditedData &lhs, const ForumTopicEditedData &rhs);
bool operator!=(const ForumTopicEditedData &lhs, const ForumTopicEditedData &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicEditedData &topic_edited_data);
} // namespace td

View File

@ -0,0 +1,54 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/ForumTopicEditedData.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void ForumTopicEditedData::store(StorerT &storer) const {
bool has_title = !title_.empty();
bool has_icon_custom_emoji_id = icon_custom_emoji_id_.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(edit_icon_custom_emoji_id_);
STORE_FLAG(edit_is_closed_);
STORE_FLAG(is_closed_);
STORE_FLAG(has_title);
STORE_FLAG(has_icon_custom_emoji_id);
END_STORE_FLAGS();
if (has_title) {
td::store(title_, storer);
}
if (has_icon_custom_emoji_id) {
td::store(icon_custom_emoji_id_, storer);
}
}
template <class ParserT>
void ForumTopicEditedData::parse(ParserT &parser) {
bool has_title;
bool has_icon_custom_emoji_id;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(edit_icon_custom_emoji_id_);
PARSE_FLAG(edit_is_closed_);
PARSE_FLAG(is_closed_);
PARSE_FLAG(has_title);
PARSE_FLAG(has_icon_custom_emoji_id);
END_PARSE_FLAGS();
if (has_title) {
td::parse(title_, parser);
}
if (has_icon_custom_emoji_id) {
td::parse(icon_custom_emoji_id_, parser);
}
}
} // namespace td

View File

@ -0,0 +1,43 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ForumTopicIcon.h"
namespace td {
ForumTopicIcon::ForumTopicIcon(int32 color, int64 custom_emoji_id)
: color_(color & 0xFFFFFF), custom_emoji_id_(custom_emoji_id) {
}
bool ForumTopicIcon::edit_custom_emoji_id(CustomEmojiId custom_emoji_id) {
if (custom_emoji_id_ != custom_emoji_id) {
custom_emoji_id_ = custom_emoji_id;
return true;
}
return false;
}
td_api::object_ptr<td_api::forumTopicIcon> ForumTopicIcon::get_forum_topic_icon_object() const {
return td_api::make_object<td_api::forumTopicIcon>(color_, custom_emoji_id_.get());
}
bool operator==(const ForumTopicIcon &lhs, const ForumTopicIcon &rhs) {
return lhs.color_ == rhs.color_ && lhs.custom_emoji_id_ == rhs.custom_emoji_id_;
}
bool operator!=(const ForumTopicIcon &lhs, const ForumTopicIcon &rhs) {
return !(lhs == rhs);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicIcon &topic_icon) {
string_builder << "icon color " << topic_icon.color_;
if (topic_icon.custom_emoji_id_.is_valid()) {
string_builder << " and " << topic_icon.custom_emoji_id_;
}
return string_builder;
}
} // namespace td

View File

@ -0,0 +1,45 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
namespace td {
class ForumTopicIcon {
int32 color_ = 0x6FB9F0;
CustomEmojiId custom_emoji_id_;
friend bool operator==(const ForumTopicIcon &lhs, const ForumTopicIcon &rhs);
friend StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicIcon &topic_icon);
public:
ForumTopicIcon() = default;
ForumTopicIcon(int32 color, int64 custom_emoji_id);
bool edit_custom_emoji_id(CustomEmojiId custom_emoji_id);
td_api::object_ptr<td_api::forumTopicIcon> get_forum_topic_icon_object() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
bool operator==(const ForumTopicIcon &lhs, const ForumTopicIcon &rhs);
bool operator!=(const ForumTopicIcon &lhs, const ForumTopicIcon &rhs);
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicIcon &topic_icon);
} // namespace td

View File

@ -0,0 +1,40 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/ForumTopicIcon.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void ForumTopicIcon::store(StorerT &storer) const {
bool has_custom_emoji_id = custom_emoji_id_.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_custom_emoji_id);
END_STORE_FLAGS();
td::store(color_, storer);
if (has_custom_emoji_id) {
td::store(custom_emoji_id_, storer);
}
}
template <class ParserT>
void ForumTopicIcon::parse(ParserT &parser) {
bool has_custom_emoji_id;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_custom_emoji_id);
END_PARSE_FLAGS();
td::parse(color_, parser);
if (has_custom_emoji_id) {
td::parse(custom_emoji_id_, parser);
}
}
} // namespace td

View File

@ -0,0 +1,70 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ForumTopicInfo.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/ServerMessageId.h"
#include "td/utils/logging.h"
namespace td {
ForumTopicInfo::ForumTopicInfo(const tl_object_ptr<telegram_api::ForumTopic> &forum_topic_ptr) {
CHECK(forum_topic_ptr != nullptr);
if (forum_topic_ptr->get_id() != telegram_api::forumTopic::ID) {
LOG(ERROR) << "Receive " << to_string(forum_topic_ptr);
return;
}
const auto *forum_topic = static_cast<const telegram_api::forumTopic *>(forum_topic_ptr.get());
top_thread_message_id_ = MessageId(ServerMessageId(forum_topic->id_));
title_ = forum_topic->title_;
icon_ = ForumTopicIcon(forum_topic->icon_color_, forum_topic->icon_emoji_id_);
creation_date_ = forum_topic->date_;
creator_dialog_id_ = DialogId(forum_topic->from_id_);
is_outgoing_ = forum_topic->my_;
is_closed_ = forum_topic->closed_;
if (creation_date_ <= 0 || !top_thread_message_id_.is_valid() || !creator_dialog_id_.is_valid()) {
LOG(ERROR) << "Receive " << to_string(forum_topic_ptr);
*this = ForumTopicInfo();
}
}
bool ForumTopicInfo::apply_edited_data(const ForumTopicEditedData &edited_data) {
bool is_changed = false;
if (!edited_data.title_.empty() && edited_data.title_ != title_) {
title_ = edited_data.title_;
is_changed = true;
}
if (edited_data.edit_icon_custom_emoji_id_ && icon_.edit_custom_emoji_id(edited_data.icon_custom_emoji_id_)) {
is_changed = true;
}
if (edited_data.edit_is_closed_ && edited_data.is_closed_ != is_closed_) {
is_closed_ = edited_data.is_closed_;
is_changed = true;
}
return is_changed;
}
td_api::object_ptr<td_api::forumTopicInfo> ForumTopicInfo::get_forum_topic_info_object(Td *td) const {
if (is_empty()) {
return nullptr;
}
auto creator_id = get_message_sender_object_const(td, creator_dialog_id_, "get_forum_topic_info_object");
return td_api::make_object<td_api::forumTopicInfo>(top_thread_message_id_.get(), title_,
icon_.get_forum_topic_icon_object(), creation_date_,
std::move(creator_id), is_outgoing_, is_closed_);
}
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicInfo &topic_info) {
return string_builder << "Forum topic " << topic_info.top_thread_message_id_.get() << '/' << topic_info.title_
<< " by " << topic_info.creator_dialog_id_ << " with " << topic_info.icon_;
}
} // namespace td

View File

@ -0,0 +1,77 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/ForumTopicEditedData.h"
#include "td/telegram/ForumTopicIcon.h"
#include "td/telegram/MessageId.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 Td;
class ForumTopicInfo {
MessageId top_thread_message_id_;
string title_;
ForumTopicIcon icon_;
int32 creation_date_ = 0;
DialogId creator_dialog_id_;
bool is_outgoing_ = false;
bool is_closed_ = false;
friend StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicInfo &topic_info);
public:
ForumTopicInfo() = default;
explicit ForumTopicInfo(const tl_object_ptr<telegram_api::ForumTopic> &forum_topic_ptr);
ForumTopicInfo(MessageId top_thread_message_id, string title, ForumTopicIcon icon, int32 creation_date,
DialogId creator_dialog_id, bool is_outgoing, bool is_closed)
: top_thread_message_id_(top_thread_message_id)
, title_(std::move(title))
, icon_(std::move(icon))
, creation_date_(creation_date)
, creator_dialog_id_(creator_dialog_id)
, is_outgoing_(is_outgoing)
, is_closed_(is_closed) {
}
bool is_empty() const {
return !top_thread_message_id_.is_valid();
}
MessageId get_top_thread_message_id() const {
return top_thread_message_id_;
}
DialogId get_creator_dialog_id() const {
return creator_dialog_id_;
}
bool is_outgoing() const {
return is_outgoing_;
}
bool is_closed() const {
return is_closed_;
}
bool apply_edited_data(const ForumTopicEditedData &edited_data);
td_api::object_ptr<td_api::forumTopicInfo> get_forum_topic_info_object(Td *td) const;
};
StringBuilder &operator<<(StringBuilder &string_builder, const ForumTopicInfo &topic_info);
} // namespace td

View File

@ -0,0 +1,358 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/ForumTopicManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/ForumTopicIcon.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UpdatesManager.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Random.h"
namespace td {
class CreateForumTopicQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::forumTopicInfo>> promise_;
ChannelId channel_id_;
DialogId creator_dialog_id_;
int64 random_id_;
public:
explicit CreateForumTopicQuery(Promise<td_api::object_ptr<td_api::forumTopicInfo>> &&promise)
: promise_(std::move(promise)) {
}
void send(ChannelId channel_id, const string &title, int32 icon_color, CustomEmojiId icon_custom_emoji_id,
DialogId as_dialog_id) {
channel_id_ = channel_id;
creator_dialog_id_ = DialogId(td_->contacts_manager_->get_my_id());
int32 flags = 0;
if (icon_color != -1) {
flags |= telegram_api::channels_createForumTopic::ICON_COLOR_MASK;
}
if (icon_custom_emoji_id.is_valid()) {
flags |= telegram_api::channels_createForumTopic::ICON_EMOJI_ID_MASK;
}
tl_object_ptr<telegram_api::InputPeer> as_input_peer;
if (as_dialog_id.is_valid()) {
as_input_peer = td_->messages_manager_->get_input_peer(as_dialog_id, AccessRights::Write);
if (as_input_peer != nullptr) {
flags |= telegram_api::channels_createForumTopic::SEND_AS_MASK;
creator_dialog_id_ = as_dialog_id;
}
}
do {
random_id_ = Random::secure_int64();
} while (random_id_ == 0);
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
send_query(G()->net_query_creator().create(
telegram_api::channels_createForumTopic(flags, std::move(input_channel), title, icon_color,
icon_custom_emoji_id.get(), random_id_, std::move(as_input_peer)),
{{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_createForumTopic>(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 CreateForumTopicQuery: " << to_string(ptr);
auto message = UpdatesManager::get_message_by_random_id(ptr.get(), DialogId(channel_id_), random_id_);
if (message == nullptr || message->get_id() != telegram_api::messageService::ID) {
LOG(ERROR) << "Receive invalid result for CreateForumTopicQuery: " << to_string(ptr);
return promise_.set_error(Status::Error(400, "Invalid result received"));
}
auto service_message = static_cast<const telegram_api::messageService *>(message);
if (service_message->action_->get_id() != telegram_api::messageActionTopicCreate::ID) {
LOG(ERROR) << "Receive invalid result for CreateForumTopicQuery: " << to_string(ptr);
return promise_.set_error(Status::Error(400, "Invalid result received"));
}
auto action = static_cast<const telegram_api::messageActionTopicCreate *>(service_message->action_.get());
auto forum_topic_info =
td::make_unique<ForumTopicInfo>(MessageId(ServerMessageId(service_message->id_)), action->title_,
ForumTopicIcon(action->icon_color_, action->icon_emoji_id_),
service_message->date_, creator_dialog_id_, true, false);
td_->updates_manager_->on_get_updates(
std::move(ptr),
PromiseCreator::lambda([dialog_id = DialogId(channel_id_), forum_topic_info = std::move(forum_topic_info),
promise = std::move(promise_)](Unit result) mutable {
send_closure(G()->forum_topic_manager(), &ForumTopicManager::on_forum_topic_created, dialog_id,
std::move(forum_topic_info), std::move(promise));
}));
}
void on_error(Status status) final {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "CreateForumTopicQuery");
promise_.set_error(std::move(status));
}
};
class EditForumTopicQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
MessageId top_thread_message_id_;
public:
explicit EditForumTopicQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, MessageId top_thread_message_id, const string &title,
CustomEmojiId icon_custom_emoji_id) {
channel_id_ = channel_id;
top_thread_message_id_ = top_thread_message_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
int32 flags =
telegram_api::channels_editForumTopic::TITLE_MASK | telegram_api::channels_editForumTopic::ICON_EMOJI_ID_MASK;
send_query(G()->net_query_creator().create(
telegram_api::channels_editForumTopic(flags, std::move(input_channel),
top_thread_message_id.get_server_message_id().get(), title,
icon_custom_emoji_id.get(), false),
{{channel_id}}));
}
void send(ChannelId channel_id, MessageId top_thread_message_id, bool is_closed) {
channel_id_ = channel_id;
top_thread_message_id_ = top_thread_message_id;
auto input_channel = td_->contacts_manager_->get_input_channel(channel_id);
CHECK(input_channel != nullptr);
int32 flags = telegram_api::channels_editForumTopic::CLOSED_MASK;
send_query(G()->net_query_creator().create(
telegram_api::channels_editForumTopic(flags, std::move(input_channel),
top_thread_message_id.get_server_message_id().get(), string(), 0,
is_closed),
{{channel_id}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_editForumTopic>(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 EditForumTopicQuery: " << to_string(ptr);
td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
}
void on_error(Status status) final {
if (status.message() == "TOPIC_NOT_MODIFIED" && !td_->auth_manager_->is_bot()) {
return promise_.set_value(Unit());
}
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "EditForumTopicQuery");
promise_.set_error(std::move(status));
}
};
ForumTopicManager::ForumTopicManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
ForumTopicManager::~ForumTopicManager() {
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_topics_);
}
void ForumTopicManager::tear_down() {
parent_.reset();
}
void ForumTopicManager::create_forum_topic(DialogId dialog_id, string &&title,
td_api::object_ptr<td_api::forumTopicIcon> &&icon,
Promise<td_api::object_ptr<td_api::forumTopicInfo>> &&promise) {
TRY_STATUS_PROMISE(promise, is_forum(dialog_id));
auto channel_id = dialog_id.get_channel_id();
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_create_topics()) {
return promise.set_error(Status::Error(400, "Not enough rights to create a topic"));
}
auto new_title = clean_name(std::move(title), MAX_FORUM_TOPIC_TITLE_LENGTH);
if (new_title.empty()) {
return promise.set_error(Status::Error(400, "Title must be non-empty"));
}
int32 icon_color = -1;
CustomEmojiId icon_custom_emoji_id;
if (icon != nullptr) {
icon_color = icon->color_;
if (icon_color < 0 || icon_color > 0xFFFFFF) {
return promise.set_error(Status::Error(400, "Invalid icon color specified"));
}
icon_custom_emoji_id = CustomEmojiId(icon->custom_emoji_id_);
}
DialogId as_dialog_id = td_->messages_manager_->get_dialog_default_send_message_as_dialog_id(dialog_id);
td_->create_handler<CreateForumTopicQuery>(std::move(promise))
->send(channel_id, new_title, icon_color, icon_custom_emoji_id, as_dialog_id);
}
void ForumTopicManager::on_forum_topic_created(DialogId dialog_id, unique_ptr<ForumTopicInfo> &&forum_topic_info,
Promise<td_api::object_ptr<td_api::forumTopicInfo>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
auto topic_info = add_topic_info(dialog_id, std::move(forum_topic_info));
CHECK(topic_info != nullptr);
promise.set_value(topic_info->get_forum_topic_info_object(td_));
}
void ForumTopicManager::edit_forum_topic(DialogId dialog_id, MessageId top_thread_message_id, string &&title,
CustomEmojiId icon_custom_emoji_id, Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, is_forum(dialog_id));
auto channel_id = dialog_id.get_channel_id();
if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server()) {
return promise.set_error(Status::Error(400, "Invalid message thread identifier specified"));
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_edit_topics()) {
auto topic_info = get_topic_info(dialog_id, top_thread_message_id);
if (topic_info != nullptr && !topic_info->is_outgoing()) {
return promise.set_error(Status::Error(400, "Not enough rights to edit the topic"));
}
}
auto new_title = clean_name(std::move(title), MAX_FORUM_TOPIC_TITLE_LENGTH);
if (new_title.empty()) {
return promise.set_error(Status::Error(400, "Title must be non-empty"));
}
td_->create_handler<EditForumTopicQuery>(std::move(promise))
->send(channel_id, top_thread_message_id, new_title, icon_custom_emoji_id);
}
void ForumTopicManager::toggle_forum_topic_is_closed(DialogId dialog_id, MessageId top_thread_message_id,
bool is_closed, Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, is_forum(dialog_id));
auto channel_id = dialog_id.get_channel_id();
if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server()) {
return promise.set_error(Status::Error(400, "Invalid message thread identifier specified"));
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_edit_topics()) {
auto topic_info = get_topic_info(dialog_id, top_thread_message_id);
if (topic_info != nullptr && !topic_info->is_outgoing()) {
return promise.set_error(Status::Error(400, "Not enough rights to close or open the topic"));
}
}
td_->create_handler<EditForumTopicQuery>(std::move(promise))->send(channel_id, top_thread_message_id, is_closed);
}
void ForumTopicManager::delete_forum_topic(DialogId dialog_id, MessageId top_thread_message_id,
Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, is_forum(dialog_id));
auto channel_id = dialog_id.get_channel_id();
if (!top_thread_message_id.is_valid() || !top_thread_message_id.is_server()) {
return promise.set_error(Status::Error(400, "Invalid message thread identifier specified"));
}
if (!td_->contacts_manager_->get_channel_permissions(channel_id).can_delete_messages()) {
auto topic_info = get_topic_info(dialog_id, top_thread_message_id);
if (topic_info != nullptr && !topic_info->is_outgoing()) {
return promise.set_error(Status::Error(400, "Not enough rights to delete the topic"));
}
}
td_->messages_manager_->delete_topic_history(dialog_id, top_thread_message_id, std::move(promise));
}
void ForumTopicManager::on_forum_topic_edited(DialogId dialog_id, MessageId top_thread_message_id,
const ForumTopicEditedData &edited_data) {
auto topic_info = get_topic_info(dialog_id, top_thread_message_id);
if (topic_info == nullptr) {
return;
}
if (topic_info->apply_edited_data(edited_data)) {
send_update_forum_topic_info(dialog_id, topic_info);
}
}
Status ForumTopicManager::is_forum(DialogId dialog_id) {
if (!td_->messages_manager_->have_dialog_force(dialog_id, "ForumTopicManager::is_forum")) {
return Status::Error(400, "Chat not found");
}
if (dialog_id.get_type() != DialogType::Channel ||
!td_->contacts_manager_->is_forum_channel(dialog_id.get_channel_id())) {
return Status::Error(400, "The chat is not a forum");
}
return Status::OK();
}
ForumTopicInfo *ForumTopicManager::add_topic_info(DialogId dialog_id, unique_ptr<ForumTopicInfo> &&forum_topic_info) {
CHECK(forum_topic_info != nullptr);
auto *dialog_info = dialog_topics_.get_pointer(dialog_id);
if (dialog_info == nullptr) {
dialog_topics_.set(dialog_id, make_unique<DialogTopics>());
dialog_info = dialog_topics_.get_pointer(dialog_id);
CHECK(dialog_info != nullptr);
}
MessageId top_thread_message_id = forum_topic_info->get_top_thread_message_id();
auto topic_info = dialog_info->topic_infos_.get_pointer(top_thread_message_id);
if (topic_info == nullptr) {
dialog_info->topic_infos_.set(top_thread_message_id, std::move(forum_topic_info));
topic_info = get_topic_info(dialog_id, top_thread_message_id);
CHECK(topic_info != nullptr);
send_update_forum_topic_info(dialog_id, topic_info);
}
return topic_info;
}
ForumTopicInfo *ForumTopicManager::get_topic_info(DialogId dialog_id, MessageId top_thread_message_id) {
auto *dialog_info = dialog_topics_.get_pointer(dialog_id);
if (dialog_info == nullptr) {
return nullptr;
}
return dialog_info->topic_infos_.get_pointer(top_thread_message_id);
}
const ForumTopicInfo *ForumTopicManager::get_topic_info(DialogId dialog_id, MessageId top_thread_message_id) const {
auto *dialog_info = dialog_topics_.get_pointer(dialog_id);
if (dialog_info == nullptr) {
return nullptr;
}
return dialog_info->topic_infos_.get_pointer(top_thread_message_id);
}
td_api::object_ptr<td_api::updateForumTopicInfo> ForumTopicManager::get_update_forum_topic_info(
DialogId dialog_id, const ForumTopicInfo *topic_info) const {
return td_api::make_object<td_api::updateForumTopicInfo>(dialog_id.get(),
topic_info->get_forum_topic_info_object(td_));
}
void ForumTopicManager::send_update_forum_topic_info(DialogId dialog_id, const ForumTopicInfo *topic_info) const {
if (td_->auth_manager_->is_bot()) {
return;
}
send_closure(G()->td(), &Td::send_update, get_update_forum_topic_info(dialog_id, topic_info));
}
} // namespace td

View File

@ -0,0 +1,81 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/ForumTopicEditedData.h"
#include "td/telegram/ForumTopicInfo.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/td_api.h"
#include "td/actor/actor.h"
#include "td/utils/common.h"
#include "td/utils/Promise.h"
#include "td/utils/Status.h"
#include "td/utils/WaitFreeHashMap.h"
namespace td {
class Td;
class ForumTopicManager final : public Actor {
public:
ForumTopicManager(Td *td, ActorShared<> parent);
ForumTopicManager(const ForumTopicManager &) = delete;
ForumTopicManager &operator=(const ForumTopicManager &) = delete;
ForumTopicManager(ForumTopicManager &&) = delete;
ForumTopicManager &operator=(ForumTopicManager &&) = delete;
~ForumTopicManager() final;
void create_forum_topic(DialogId dialog_id, string &&title, td_api::object_ptr<td_api::forumTopicIcon> &&icon,
Promise<td_api::object_ptr<td_api::forumTopicInfo>> &&promise);
void on_forum_topic_created(DialogId dialog_id, unique_ptr<ForumTopicInfo> &&forum_topic_info,
Promise<td_api::object_ptr<td_api::forumTopicInfo>> &&promise);
void edit_forum_topic(DialogId dialog_id, MessageId top_thread_message_id, string &&title,
CustomEmojiId icon_custom_emoji_id, Promise<Unit> &&promise);
void toggle_forum_topic_is_closed(DialogId dialog_id, MessageId top_thread_message_id, bool is_closed,
Promise<Unit> &&promise);
void delete_forum_topic(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
void on_forum_topic_edited(DialogId dialog_id, MessageId top_thread_message_id,
const ForumTopicEditedData &edited_data);
private:
static constexpr size_t MAX_FORUM_TOPIC_TITLE_LENGTH = 128; // server side limit for forum topic title
struct DialogTopics {
WaitFreeHashMap<MessageId, unique_ptr<ForumTopicInfo>, MessageIdHash> topic_infos_;
};
void tear_down() final;
Status is_forum(DialogId dialog_id);
ForumTopicInfo *add_topic_info(DialogId dialog_id, unique_ptr<ForumTopicInfo> &&forum_topic_info);
ForumTopicInfo *get_topic_info(DialogId dialog_id, MessageId top_thread_message_id);
const ForumTopicInfo *get_topic_info(DialogId dialog_id, MessageId top_thread_message_id) const;
td_api::object_ptr<td_api::updateForumTopicInfo> get_update_forum_topic_info(DialogId dialog_id,
const ForumTopicInfo *topic_info) const;
void send_update_forum_topic_info(DialogId dialog_id, const ForumTopicInfo *topic_info) const;
Td *td_;
ActorShared<> parent_;
WaitFreeHashMap<DialogId, unique_ptr<DialogTopics>, DialogIdHash> dialog_topics_;
};
} // namespace td

View File

@ -42,6 +42,7 @@ class ContactsManager;
class DownloadManager;
class FileManager;
class FileReferenceManager;
class ForumTopicManager;
class GameManager;
class GroupCallManager;
class LanguagePackManager;
@ -303,6 +304,13 @@ class Global final : public ActorContext {
file_reference_manager_ = std::move(file_reference_manager);
}
ActorId<ForumTopicManager> forum_topic_manager() const {
return forum_topic_manager_;
}
void set_forum_topic_manager(ActorId<ForumTopicManager> forum_topic_manager) {
forum_topic_manager_ = forum_topic_manager;
}
ActorId<GameManager> game_manager() const {
return game_manager_;
}
@ -532,6 +540,7 @@ class Global final : public ActorContext {
ActorId<DownloadManager> download_manager_;
ActorId<FileManager> file_manager_;
ActorId<FileReferenceManager> file_reference_manager_;
ActorId<ForumTopicManager> forum_topic_manager_;
ActorId<GameManager> game_manager_;
ActorId<GroupCallManager> group_call_manager_;
ActorId<LanguagePackManager> language_pack_manager_;

View File

@ -19,6 +19,7 @@
#include "td/telegram/files/FileType.h"
#include "td/telegram/Game.h"
#include "td/telegram/Global.h"
#include "td/telegram/InputInvoice.h"
#include "td/telegram/InputMessageText.h"
#include "td/telegram/Location.h"
#include "td/telegram/MessageContent.h"
@ -27,7 +28,6 @@
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/Payments.h"
#include "td/telegram/Photo.h"
#include "td/telegram/PhotoFormat.h"
#include "td/telegram/PhotoSize.h"
@ -377,8 +377,9 @@ Result<tl_object_ptr<telegram_api::InputBotInlineMessage>> InlineQueriesManager:
return contact.get_input_bot_inline_message_media_contact(std::move(input_reply_markup));
}
if (constructor_id == td_api::inputMessageInvoice::ID) {
TRY_RESULT(input_invoice, process_input_message_invoice(std::move(input_message_content), td_));
return get_input_bot_inline_message_media_invoice(input_invoice, std::move(input_reply_markup), td_);
TRY_RESULT(input_invoice,
InputInvoice::process_input_message_invoice(std::move(input_message_content), td_, DialogId(), false));
return input_invoice.get_input_bot_inline_message_media_invoice(std::move(input_reply_markup), td_);
}
if (constructor_id == td_api::inputMessageLocation::ID) {
TRY_RESULT(location, process_input_message_location(std::move(input_message_content)));
@ -1948,7 +1949,7 @@ void InlineQueriesManager::save_recently_used_bots() {
value += ',';
value_ids += ',';
}
value += td_->contacts_manager_->get_user_username(bot_user_id);
value += td_->contacts_manager_->get_user_first_username(bot_user_id);
value_ids += to_string(bot_user_id.get());
}
G()->td_db()->get_binlog_pmc()->set("recently_used_inline_bot_usernames", value);
@ -2028,7 +2029,7 @@ void InlineQueriesManager::on_new_query(int64 query_id, UserId sender_user_id, L
LOG(ERROR) << "Receive new inline query from invalid " << sender_user_id;
return;
}
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Have no info about " << sender_user_id;
LOG_IF(ERROR, !td_->contacts_manager_->have_user(sender_user_id)) << "Receive unknown " << sender_user_id;
if (!td_->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive new inline query";
return;
@ -2067,7 +2068,7 @@ void InlineQueriesManager::on_chosen_result(
LOG(ERROR) << "Receive chosen inline query result from invalid " << user_id;
return;
}
LOG_IF(ERROR, !td_->contacts_manager_->have_user(user_id)) << "Have no info about " << user_id;
LOG_IF(ERROR, !td_->contacts_manager_->have_user(user_id)) << "Receive unknown " << user_id;
if (!td_->auth_manager_->is_bot()) {
LOG(ERROR) << "Receive chosen inline query result";
return;

View File

@ -0,0 +1,413 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/InputInvoice.h"
#include "td/telegram/Dimensions.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/misc.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/Td.h"
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/HttpUrl.h"
#include "td/utils/logging.h"
#include "td/utils/MimeType.h"
#include "td/utils/PathView.h"
namespace td {
bool operator==(const InputInvoice &lhs, const InputInvoice &rhs) {
auto are_invoice_equal = [](const InputInvoice::Invoice &lhs, const InputInvoice::Invoice &rhs) {
return lhs.is_test_ == rhs.is_test_ && lhs.need_name_ == rhs.need_name_ &&
lhs.need_phone_number_ == rhs.need_phone_number_ && lhs.need_email_address_ == rhs.need_email_address_ &&
lhs.need_shipping_address_ == rhs.need_shipping_address_ &&
lhs.send_phone_number_to_provider_ == rhs.send_phone_number_to_provider_ &&
lhs.send_email_address_to_provider_ == rhs.send_email_address_to_provider_ &&
lhs.is_flexible_ == rhs.is_flexible_ && lhs.currency_ == rhs.currency_ &&
lhs.price_parts_ == rhs.price_parts_ && lhs.max_tip_amount_ == rhs.max_tip_amount_ &&
lhs.suggested_tip_amounts_ == rhs.suggested_tip_amounts_ &&
lhs.recurring_payment_terms_of_service_url_ == rhs.recurring_payment_terms_of_service_url_;
};
return lhs.title_ == rhs.title_ && lhs.description_ == rhs.description_ && lhs.photo_ == rhs.photo_ &&
lhs.start_parameter_ == rhs.start_parameter_ && are_invoice_equal(lhs.invoice_, rhs.invoice_) &&
lhs.payload_ == rhs.payload_ && lhs.provider_token_ == rhs.provider_token_ &&
lhs.provider_data_ == rhs.provider_data_ && lhs.extended_media_ == rhs.extended_media_ &&
lhs.total_amount_ == rhs.total_amount_ && lhs.receipt_message_id_ == rhs.receipt_message_id_;
}
bool operator!=(const InputInvoice &lhs, const InputInvoice &rhs) {
return !(lhs == rhs);
}
InputInvoice::InputInvoice(tl_object_ptr<telegram_api::messageMediaInvoice> &&message_invoice, Td *td,
DialogId owner_dialog_id, FormattedText &&message) {
title_ = std::move(message_invoice->title_);
description_ = std::move(message_invoice->description_);
photo_ = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id);
start_parameter_ = std::move(message_invoice->start_param_);
invoice_.currency_ = std::move(message_invoice->currency_);
invoice_.is_test_ = message_invoice->test_;
invoice_.need_shipping_address_ = message_invoice->shipping_address_requested_;
// payload_ = string();
// provider_token_ = string();
// provider_data_ = string();
extended_media_ =
MessageExtendedMedia(td, std::move(message_invoice->extended_media_), std::move(message), owner_dialog_id);
if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_;
message_invoice->total_amount_ = 0;
}
total_amount_ = message_invoice->total_amount_;
if ((message_invoice->flags_ & telegram_api::messageMediaInvoice::RECEIPT_MSG_ID_MASK) != 0) {
receipt_message_id_ = MessageId(ServerMessageId(message_invoice->receipt_msg_id_));
if (!receipt_message_id_.is_valid()) {
LOG(ERROR) << "Receive as receipt message " << receipt_message_id_ << " in " << owner_dialog_id;
receipt_message_id_ = MessageId();
}
}
}
InputInvoice::InputInvoice(tl_object_ptr<telegram_api::botInlineMessageMediaInvoice> &&message_invoice, Td *td,
DialogId owner_dialog_id) {
title_ = std::move(message_invoice->title_);
description_ = std::move(message_invoice->description_);
photo_ = get_web_document_photo(td->file_manager_.get(), std::move(message_invoice->photo_), owner_dialog_id);
// start_parameter_ = string();
invoice_.currency_ = std::move(message_invoice->currency_);
invoice_.is_test_ = message_invoice->test_;
invoice_.need_shipping_address_ = message_invoice->shipping_address_requested_;
// payload_ = string();
// provider_token_ = string();
// provider_data_ = string();
// extended_media_ = MessageExtendedMedia();
if (message_invoice->total_amount_ <= 0 || !check_currency_amount(message_invoice->total_amount_)) {
LOG(ERROR) << "Receive invalid total amount " << message_invoice->total_amount_;
message_invoice->total_amount_ = 0;
}
total_amount_ = message_invoice->total_amount_;
// receipt_message_id_ = MessageId();
}
Result<InputInvoice> InputInvoice::process_input_message_invoice(
td_api::object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td, DialogId owner_dialog_id,
bool is_premium) {
CHECK(input_message_content != nullptr);
CHECK(input_message_content->get_id() == td_api::inputMessageInvoice::ID);
auto input_invoice = move_tl_object_as<td_api::inputMessageInvoice>(input_message_content);
if (input_invoice->invoice_ == nullptr) {
return Status::Error(400, "Invoice must be non-empty");
}
if (!clean_input_string(input_invoice->title_)) {
return Status::Error(400, "Invoice title must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->description_)) {
return Status::Error(400, "Invoice description must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->photo_url_)) {
return Status::Error(400, "Invoice photo URL must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->start_parameter_)) {
return Status::Error(400, "Invoice bot start parameter must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->provider_token_)) {
return Status::Error(400, "Invoice provider token must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->provider_data_)) {
return Status::Error(400, "Invoice provider data must be encoded in UTF-8");
}
if (!clean_input_string(input_invoice->invoice_->currency_)) {
return Status::Error(400, "Invoice currency must be encoded in UTF-8");
}
InputInvoice result;
result.title_ = std::move(input_invoice->title_);
result.description_ = std::move(input_invoice->description_);
auto r_http_url = parse_url(input_invoice->photo_url_);
if (r_http_url.is_error()) {
if (!input_invoice->photo_url_.empty()) {
LOG(INFO) << "Can't register url " << input_invoice->photo_url_;
}
} else {
auto url = r_http_url.ok().get_url();
auto r_invoice_file_id = td->file_manager_->from_persistent_id(url, FileType::Temp);
if (r_invoice_file_id.is_error()) {
LOG(INFO) << "Can't register url " << url;
} else {
auto invoice_file_id = r_invoice_file_id.move_as_ok();
PhotoSize s;
s.type = 'n';
s.dimensions = get_dimensions(input_invoice->photo_width_, input_invoice->photo_height_, nullptr);
s.size = input_invoice->photo_size_; // TODO use invoice_file_id size
s.file_id = invoice_file_id;
result.photo_.id = 0;
result.photo_.photos.push_back(s);
}
}
result.start_parameter_ = std::move(input_invoice->start_parameter_);
result.invoice_.currency_ = std::move(input_invoice->invoice_->currency_);
result.invoice_.price_parts_.reserve(input_invoice->invoice_->price_parts_.size());
int64 total_amount = 0;
for (auto &price : input_invoice->invoice_->price_parts_) {
if (!clean_input_string(price->label_)) {
return Status::Error(400, "Invoice price label must be encoded in UTF-8");
}
if (!check_currency_amount(price->amount_)) {
return Status::Error(400, "Too big amount of the currency specified");
}
result.invoice_.price_parts_.emplace_back(std::move(price->label_), price->amount_);
total_amount += price->amount_;
}
if (total_amount <= 0) {
return Status::Error(400, "Total price must be positive");
}
if (!check_currency_amount(total_amount)) {
return Status::Error(400, "Total price is too big");
}
result.total_amount_ = total_amount;
if (input_invoice->invoice_->max_tip_amount_ < 0 ||
!check_currency_amount(input_invoice->invoice_->max_tip_amount_)) {
return Status::Error(400, "Invalid max_tip_amount of the currency specified");
}
for (auto tip_amount : input_invoice->invoice_->suggested_tip_amounts_) {
if (tip_amount <= 0) {
return Status::Error(400, "Suggested tip amount must be positive");
}
if (tip_amount > input_invoice->invoice_->max_tip_amount_) {
return Status::Error(400, "Suggested tip amount can't be bigger than max_tip_amount");
}
}
if (input_invoice->invoice_->suggested_tip_amounts_.size() > 4) {
return Status::Error(400, "There can be at most 4 suggested tip amounts");
}
result.invoice_.max_tip_amount_ = input_invoice->invoice_->max_tip_amount_;
result.invoice_.suggested_tip_amounts_ = std::move(input_invoice->invoice_->suggested_tip_amounts_);
result.invoice_.recurring_payment_terms_of_service_url_ =
std::move(input_invoice->invoice_->recurring_payment_terms_of_service_url_);
result.invoice_.is_test_ = input_invoice->invoice_->is_test_;
result.invoice_.need_name_ = input_invoice->invoice_->need_name_;
result.invoice_.need_phone_number_ = input_invoice->invoice_->need_phone_number_;
result.invoice_.need_email_address_ = input_invoice->invoice_->need_email_address_;
result.invoice_.need_shipping_address_ = input_invoice->invoice_->need_shipping_address_;
result.invoice_.send_phone_number_to_provider_ = input_invoice->invoice_->send_phone_number_to_provider_;
result.invoice_.send_email_address_to_provider_ = input_invoice->invoice_->send_email_address_to_provider_;
result.invoice_.is_flexible_ = input_invoice->invoice_->is_flexible_;
if (result.invoice_.send_phone_number_to_provider_) {
result.invoice_.need_phone_number_ = true;
}
if (result.invoice_.send_email_address_to_provider_) {
result.invoice_.need_email_address_ = true;
}
if (result.invoice_.is_flexible_) {
result.invoice_.need_shipping_address_ = true;
}
result.payload_ = std::move(input_invoice->payload_);
result.provider_token_ = std::move(input_invoice->provider_token_);
result.provider_data_ = std::move(input_invoice->provider_data_);
TRY_RESULT(extended_media, MessageExtendedMedia::get_message_extended_media(
td, std::move(input_invoice->extended_media_content_), owner_dialog_id, is_premium));
result.extended_media_ = std::move(extended_media);
return result;
}
tl_object_ptr<td_api::messageInvoice> InputInvoice::get_message_invoice_object(Td *td, bool skip_bot_commands,
int32 max_media_timestamp) const {
return make_tl_object<td_api::messageInvoice>(
title_, get_product_description_object(description_), get_photo_object(td->file_manager_.get(), photo_),
invoice_.currency_, total_amount_, start_parameter_, invoice_.is_test_, invoice_.need_shipping_address_,
receipt_message_id_.get(),
extended_media_.get_message_extended_media_object(td, skip_bot_commands, max_media_timestamp));
}
tl_object_ptr<telegram_api::invoice> 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 (!recurring_payment_terms_of_service_url_.empty()) {
flags |= telegram_api::invoice::RECURRING_TERMS_URL_MASK;
}
auto prices = transform(price_parts_, [](const LabeledPricePart &price) {
return telegram_api::make_object<telegram_api::labeledPrice>(price.label, price.amount);
});
return make_tl_object<telegram_api::invoice>(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, currency_, std::move(prices),
max_tip_amount_, vector<int64>(suggested_tip_amounts_), recurring_payment_terms_of_service_url_);
}
static tl_object_ptr<telegram_api::inputWebDocument> get_input_web_document(const FileManager *file_manager,
const Photo &photo) {
if (photo.is_empty()) {
return nullptr;
}
CHECK(photo.photos.size() == 1);
const PhotoSize &size = photo.photos[0];
CHECK(size.file_id.is_valid());
vector<tl_object_ptr<telegram_api::DocumentAttribute>> attributes;
if (size.dimensions.width != 0 && size.dimensions.height != 0) {
attributes.push_back(
make_tl_object<telegram_api::documentAttributeImageSize>(size.dimensions.width, size.dimensions.height));
}
auto file_view = file_manager->get_file_view(size.file_id);
CHECK(file_view.has_url());
auto file_name = get_url_file_name(file_view.url());
return make_tl_object<telegram_api::inputWebDocument>(
file_view.url(), size.size, MimeType::from_extension(PathView(file_name).extension(), "image/jpeg"),
std::move(attributes));
}
tl_object_ptr<telegram_api::inputMediaInvoice> InputInvoice::get_input_media_invoice(
Td *td, tl_object_ptr<telegram_api::InputFile> input_file,
tl_object_ptr<telegram_api::InputFile> input_thumbnail) const {
int32 flags = 0;
if (!start_parameter_.empty()) {
flags |= telegram_api::inputMediaInvoice::START_PARAM_MASK;
}
auto input_web_document = get_input_web_document(td->file_manager_.get(), photo_);
if (input_web_document != nullptr) {
flags |= telegram_api::inputMediaInvoice::PHOTO_MASK;
}
telegram_api::object_ptr<telegram_api::InputMedia> extended_media;
if (!extended_media_.is_empty()) {
flags |= telegram_api::inputMediaInvoice::EXTENDED_MEDIA_MASK;
extended_media = extended_media_.get_input_media(td, std::move(input_file), std::move(input_thumbnail));
if (extended_media == nullptr) {
return nullptr;
}
}
return make_tl_object<telegram_api::inputMediaInvoice>(
flags, title_, description_, std::move(input_web_document), invoice_.get_input_invoice(), BufferSlice(payload_),
provider_token_,
telegram_api::make_object<telegram_api::dataJSON>(provider_data_.empty() ? "null" : provider_data_),
start_parameter_, std::move(extended_media));
}
tl_object_ptr<telegram_api::inputBotInlineMessageMediaInvoice> InputInvoice::get_input_bot_inline_message_media_invoice(
tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup, Td *td) const {
int32 flags = 0;
if (reply_markup != nullptr) {
flags |= telegram_api::inputBotInlineMessageMediaInvoice::REPLY_MARKUP_MASK;
}
auto input_web_document = get_input_web_document(td->file_manager_.get(), photo_);
if (input_web_document != nullptr) {
flags |= telegram_api::inputBotInlineMessageMediaInvoice::PHOTO_MASK;
}
return make_tl_object<telegram_api::inputBotInlineMessageMediaInvoice>(
flags, title_, description_, std::move(input_web_document), invoice_.get_input_invoice(), BufferSlice(payload_),
provider_token_,
telegram_api::make_object<telegram_api::dataJSON>(provider_data_.empty() ? "null" : provider_data_),
std::move(reply_markup));
}
vector<FileId> InputInvoice::get_file_ids(const Td *td) const {
auto file_ids = photo_get_file_ids(photo_);
extended_media_.append_file_ids(td, file_ids);
return file_ids;
}
void InputInvoice::delete_thumbnail(Td *td) {
extended_media_.delete_thumbnail(td);
}
bool InputInvoice::need_reget() const {
return extended_media_.need_reget();
}
bool InputInvoice::has_media_timestamp() const {
return extended_media_.has_media_timestamp();
}
bool InputInvoice::is_equal_but_different(const InputInvoice &other) const {
return extended_media_.is_equal_but_different(other.extended_media_);
}
const FormattedText *InputInvoice::get_caption() const {
return extended_media_.get_caption();
}
int32 InputInvoice::get_duration(const Td *td) const {
return extended_media_.get_duration(td);
}
FileId InputInvoice::get_upload_file_id() const {
return extended_media_.get_upload_file_id();
}
FileId InputInvoice::get_any_file_id() const {
return extended_media_.get_any_file_id();
}
FileId InputInvoice::get_thumbnail_file_id(const Td *td) const {
return extended_media_.get_thumbnail_file_id(td);
}
void InputInvoice::update_from(const InputInvoice &old_input_invoice) {
extended_media_.update_from(old_input_invoice.extended_media_);
}
bool InputInvoice::update_extended_media(telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media,
DialogId owner_dialog_id, Td *td) {
return extended_media_.update_to(td, std::move(extended_media), owner_dialog_id);
}
bool InputInvoice::need_poll_extended_media() const {
return extended_media_.need_poll();
}
tl_object_ptr<td_api::formattedText> get_product_description_object(const string &description) {
FormattedText result;
result.text = description;
result.entities = find_entities(result.text, true, true);
return get_formatted_text_object(result, true, 0);
}
} // namespace td

133
td/telegram/InputInvoice.h Normal file
View File

@ -0,0 +1,133 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/LabeledPricePart.h"
#include "td/telegram/MessageExtendedMedia.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/Photo.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
namespace td {
class Td;
class InputInvoice {
struct Invoice {
string currency_;
vector<LabeledPricePart> price_parts_;
int64 max_tip_amount_ = 0;
vector<int64> suggested_tip_amounts_;
string recurring_payment_terms_of_service_url_;
bool is_test_ = false;
bool need_name_ = false;
bool need_phone_number_ = false;
bool need_email_address_ = false;
bool need_shipping_address_ = false;
bool send_phone_number_to_provider_ = false;
bool send_email_address_to_provider_ = false;
bool is_flexible_ = false;
Invoice() = default;
Invoice(string &&currency, bool is_test, bool need_shipping_address)
: currency_(std::move(currency)), is_test_(is_test), need_shipping_address_(need_shipping_address) {
}
tl_object_ptr<telegram_api::invoice> get_input_invoice() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
string title_;
string description_;
Photo photo_;
string start_parameter_;
Invoice invoice_;
string payload_;
string provider_token_;
string provider_data_;
MessageExtendedMedia extended_media_;
int64 total_amount_ = 0;
MessageId receipt_message_id_;
friend bool operator==(const InputInvoice &lhs, const InputInvoice &rhs);
public:
InputInvoice() = default;
InputInvoice(tl_object_ptr<telegram_api::messageMediaInvoice> &&message_invoice, Td *td, DialogId owner_dialog_id,
FormattedText &&message);
InputInvoice(tl_object_ptr<telegram_api::botInlineMessageMediaInvoice> &&message_invoice, Td *td,
DialogId owner_dialog_id);
static Result<InputInvoice> process_input_message_invoice(
td_api::object_ptr<td_api::InputMessageContent> &&input_message_content, Td *td, DialogId owner_dialog_id,
bool is_premium);
tl_object_ptr<td_api::messageInvoice> get_message_invoice_object(Td *td, bool skip_bot_commands,
int32 max_media_timestamp) const;
tl_object_ptr<telegram_api::inputMediaInvoice> get_input_media_invoice(
Td *td, tl_object_ptr<telegram_api::InputFile> input_file,
tl_object_ptr<telegram_api::InputFile> input_thumbnail) const;
tl_object_ptr<telegram_api::inputBotInlineMessageMediaInvoice> get_input_bot_inline_message_media_invoice(
tl_object_ptr<telegram_api::ReplyMarkup> &&reply_markup, Td *td) const;
vector<FileId> get_file_ids(const Td *td) const;
void delete_thumbnail(Td *td);
bool need_reget() const;
bool has_media_timestamp() const;
bool is_equal_but_different(const InputInvoice &other) const;
const FormattedText *get_caption() const;
int32 get_duration(const Td *td) const;
FileId get_upload_file_id() const;
FileId get_any_file_id() const;
FileId get_thumbnail_file_id(const Td *td) const;
void update_from(const InputInvoice &old_input_invoice);
bool update_extended_media(telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media,
DialogId owner_dialog_id, Td *td);
bool need_poll_extended_media() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
bool operator==(const InputInvoice &lhs, const InputInvoice &rhs);
bool operator!=(const InputInvoice &lhs, const InputInvoice &rhs);
tl_object_ptr<td_api::formattedText> get_product_description_object(const string &description);
} // namespace td

View File

@ -0,0 +1,195 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/InputInvoice.h"
#include "td/telegram/MessageExtendedMedia.hpp"
#include "td/telegram/Photo.hpp"
#include "td/telegram/Version.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void InputInvoice::Invoice::store(StorerT &storer) const {
using td::store;
bool has_tip = max_tip_amount_ != 0;
bool is_recurring = !recurring_payment_terms_of_service_url_.empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(is_test_);
STORE_FLAG(need_name_);
STORE_FLAG(need_phone_number_);
STORE_FLAG(need_email_address_);
STORE_FLAG(need_shipping_address_);
STORE_FLAG(is_flexible_);
STORE_FLAG(send_phone_number_to_provider_);
STORE_FLAG(send_email_address_to_provider_);
STORE_FLAG(has_tip);
STORE_FLAG(is_recurring);
END_STORE_FLAGS();
store(currency_, storer);
store(price_parts_, storer);
if (has_tip) {
store(max_tip_amount_, storer);
store(suggested_tip_amounts_, storer);
}
if (is_recurring) {
store(recurring_payment_terms_of_service_url_, storer);
}
}
template <class ParserT>
void InputInvoice::Invoice::parse(ParserT &parser) {
using td::parse;
bool has_tip;
bool is_recurring;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_test_);
PARSE_FLAG(need_name_);
PARSE_FLAG(need_phone_number_);
PARSE_FLAG(need_email_address_);
PARSE_FLAG(need_shipping_address_);
PARSE_FLAG(is_flexible_);
PARSE_FLAG(send_phone_number_to_provider_);
PARSE_FLAG(send_email_address_to_provider_);
PARSE_FLAG(has_tip);
PARSE_FLAG(is_recurring);
END_PARSE_FLAGS();
parse(currency_, parser);
parse(price_parts_, parser);
if (has_tip) {
parse(max_tip_amount_, parser);
parse(suggested_tip_amounts_, parser);
}
if (is_recurring) {
parse(recurring_payment_terms_of_service_url_, parser);
}
}
template <class StorerT>
void InputInvoice::store(StorerT &storer) const {
using td::store;
bool has_description = !description_.empty();
bool has_photo = !photo_.is_empty();
bool has_start_parameter = !start_parameter_.empty();
bool has_payload = !payload_.empty();
bool has_provider_token = !provider_token_.empty();
bool has_provider_data = !provider_data_.empty();
bool has_total_amount = total_amount_ != 0;
bool has_receipt_message_id = receipt_message_id_.is_valid();
bool has_extended_media = !extended_media_.is_empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_description);
STORE_FLAG(has_photo);
STORE_FLAG(has_start_parameter);
STORE_FLAG(has_payload);
STORE_FLAG(has_provider_token);
STORE_FLAG(has_provider_data);
STORE_FLAG(has_total_amount);
STORE_FLAG(has_receipt_message_id);
STORE_FLAG(has_extended_media);
END_STORE_FLAGS();
store(title_, storer);
if (has_description) {
store(description_, storer);
}
if (has_photo) {
store(photo_, storer);
}
if (has_start_parameter) {
store(start_parameter_, storer);
}
store(invoice_, storer);
if (has_payload) {
store(payload_, storer);
}
if (has_provider_token) {
store(provider_token_, storer);
}
if (has_provider_data) {
store(provider_data_, storer);
}
if (has_total_amount) {
store(total_amount_, storer);
}
if (has_receipt_message_id) {
store(receipt_message_id_, storer);
}
if (has_extended_media) {
store(extended_media_, storer);
}
}
template <class ParserT>
void InputInvoice::parse(ParserT &parser) {
using td::parse;
bool has_description;
bool has_photo;
bool has_start_parameter;
bool has_payload;
bool has_provider_token;
bool has_provider_data;
bool has_total_amount;
bool has_receipt_message_id;
bool has_extended_media;
if (parser.version() >= static_cast<int32>(Version::AddInputInvoiceFlags)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_description);
PARSE_FLAG(has_photo);
PARSE_FLAG(has_start_parameter);
PARSE_FLAG(has_payload);
PARSE_FLAG(has_provider_token);
PARSE_FLAG(has_provider_data);
PARSE_FLAG(has_total_amount);
PARSE_FLAG(has_receipt_message_id);
PARSE_FLAG(has_extended_media);
END_PARSE_FLAGS();
} else {
has_description = true;
has_photo = true;
has_start_parameter = true;
has_payload = true;
has_provider_token = true;
has_provider_data = parser.version() >= static_cast<int32>(Version::AddMessageInvoiceProviderData);
has_total_amount = true;
has_receipt_message_id = true;
has_extended_media = false;
}
parse(title_, parser);
if (has_description) {
parse(description_, parser);
}
if (has_photo) {
parse(photo_, parser);
}
if (has_start_parameter) {
parse(start_parameter_, parser);
}
parse(invoice_, parser);
if (has_payload) {
parse(payload_, parser);
}
if (has_provider_token) {
parse(provider_token_, parser);
}
if (has_provider_data) {
parse(provider_data_, parser);
}
if (has_total_amount) {
parse(total_amount_, parser);
}
if (has_receipt_message_id) {
parse(receipt_message_id_, parser);
}
if (has_extended_media) {
parse(extended_media_, parser);
}
}
} // namespace td

View File

@ -0,0 +1,47 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/utils/common.h"
#include "td/utils/StringBuilder.h"
namespace td {
struct LabeledPricePart {
string label;
int64 amount = 0;
LabeledPricePart() = default;
LabeledPricePart(string &&label, int64 amount) : label(std::move(label)), amount(amount) {
}
template <class StorerT>
void store(StorerT &storer) const {
storer.store_string(label);
storer.store_binary(amount);
}
template <class ParserT>
void parse(ParserT &parser) {
label = parser.template fetch_string<string>();
amount = parser.fetch_long();
}
};
inline bool operator==(const LabeledPricePart &lhs, const LabeledPricePart &rhs) {
return lhs.label == rhs.label && lhs.amount == rhs.amount;
}
inline bool operator!=(const LabeledPricePart &lhs, const LabeledPricePart &rhs) {
return !(lhs == rhs);
}
inline StringBuilder &operator<<(StringBuilder &string_builder, const LabeledPricePart &labeled_price_part) {
return string_builder << '[' << labeled_price_part.label << ": " << labeled_price_part.amount << ']';
}
} // namespace td

View File

@ -7,6 +7,7 @@
#include "td/telegram/LinkManager.h"
#include "td/telegram/AccessRights.h"
#include "td/telegram/BackgroundType.h"
#include "td/telegram/ChannelId.h"
#include "td/telegram/ChannelType.h"
#include "td/telegram/ConfigManager.h"
@ -45,30 +46,6 @@ static bool is_valid_start_parameter(Slice start_parameter) {
return start_parameter.size() <= 64 && is_base64url_characters(start_parameter);
}
static bool is_valid_username(Slice username) {
if (username.empty() || username.size() > 32) {
return false;
}
if (!is_alpha(username[0])) {
return false;
}
for (auto c : username) {
if (!is_alpha(c) && !is_digit(c) && c != '_') {
return false;
}
}
if (username.back() == '_') {
return false;
}
for (size_t i = 1; i < username.size(); i++) {
if (username[i - 1] == '_' && username[i] == '_') {
return false;
}
}
return true;
}
static bool is_valid_phone_number(Slice phone_number) {
if (phone_number.empty() || phone_number.size() > 32) {
return false;
@ -113,6 +90,7 @@ static AdministratorRights get_administrator_rights(Slice rights, bool for_chann
bool can_invite_users = false;
bool can_restrict_members = false;
bool can_pin_messages = false;
bool can_manage_topics = false;
bool can_promote_members = false;
bool can_manage_calls = false;
bool is_anonymous = false;
@ -131,6 +109,8 @@ static AdministratorRights get_administrator_rights(Slice rights, bool for_chann
can_invite_users = true;
} else if (right == "pin_messages") {
can_pin_messages = true;
} else if (right == "manage_topics") {
can_manage_topics = true;
} else if (right == "promote_members") {
can_promote_members = true;
} else if (right == "manage_video_chats") {
@ -143,7 +123,7 @@ static AdministratorRights get_administrator_rights(Slice rights, bool for_chann
}
return AdministratorRights(is_anonymous, can_manage_dialog, can_change_info, can_post_messages, can_edit_messages,
can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages,
can_promote_members, can_manage_calls,
can_manage_topics, can_promote_members, can_manage_calls,
for_channel ? ChannelType::Broadcast : ChannelType::Megagroup);
}
@ -339,13 +319,15 @@ class LinkManager::InternalLinkGame final : public InternalLink {
class LinkManager::InternalLinkInstantView final : public InternalLink {
string url_;
string fallback_url_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeInstantView>(url_);
return td_api::make_object<td_api::internalLinkTypeInstantView>(url_, fallback_url_);
}
public:
explicit InternalLinkInstantView(string url) : url_(std::move(url)) {
InternalLinkInstantView(string url, string fallback_url)
: url_(std::move(url)), fallback_url_(std::move(fallback_url)) {
}
};
@ -772,6 +754,8 @@ void LinkManager::start_up() {
autologin_domains_ = full_split(G()->td_db()->get_binlog_pmc()->get("autologin_domains"), '\xFF');
url_auth_domains_ = full_split(G()->td_db()->get_binlog_pmc()->get("url_auth_domains"), '\xFF');
whitelisted_domains_ = full_split(G()->td_db()->get_binlog_pmc()->get("whitelisted_domains"), '\xFF');
}
void LinkManager::tear_down() {
@ -975,7 +959,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_internal_link(Slice lin
case LinkType::TMe:
return parse_t_me_link_query(info.query_, is_trusted);
case LinkType::Telegraph:
return td::make_unique<InternalLinkInstantView>(PSTRING() << "https://telegra.ph" << info.query_);
return td::make_unique<InternalLinkInstantView>(PSTRING() << "https://telegra.ph" << info.query_, link.str());
default:
UNREACHABLE();
return nullptr;
@ -1257,10 +1241,17 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
if (path[0] == "c") {
if (path.size() >= 3 && to_integer<int64>(path[1]) > 0 && to_integer<int64>(path[2]) > 0) {
// /c/123456789/12345?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
// /c/123456789/1234/12345?single&comment=<message_id>&t=<media_timestamp>
is_first_arg = false;
return td::make_unique<InternalLinkMessage>(
PSTRING() << "tg:privatepost?channel=" << to_integer<int64>(path[1]) << "&post=" << to_integer<int64>(path[2])
<< copy_arg("single") << copy_arg("thread") << copy_arg("comment") << copy_arg("t"));
auto post = to_integer<int64>(path[2]);
auto thread = PSTRING() << copy_arg("thread");
if (path.size() >= 4 && to_integer<int64>(path[3]) > 0) {
thread = PSTRING() << "&thread=" << post;
post = to_integer<int64>(path[3]);
}
return td::make_unique<InternalLinkMessage>(PSTRING() << "tg:privatepost?channel=" << to_integer<int64>(path[1])
<< "&post=" << post << copy_arg("single") << thread
<< copy_arg("comment") << copy_arg("t"));
}
} else if (path[0] == "login") {
if (path.size() >= 2 && !path[1].empty()) {
@ -1364,16 +1355,23 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
} else if (path[0] == "iv") {
if (path.size() == 1 && has_arg("url")) {
// /iv?url=<url>&rhash=<rhash>
return td::make_unique<InternalLinkInstantView>(PSTRING()
<< "https://t.me/iv" << copy_arg("url") << copy_arg("rhash"));
return td::make_unique<InternalLinkInstantView>(
PSTRING() << "https://t.me/iv" << copy_arg("url") << copy_arg("rhash"), get_arg("url"));
}
} else if (is_valid_username(path[0])) {
if (path.size() >= 2 && to_integer<int64>(path[1]) > 0) {
// /<username>/12345?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
// /<username>/1234/12345?single&comment=<message_id>&t=<media_timestamp>
is_first_arg = false;
return td::make_unique<InternalLinkMessage>(
PSTRING() << "tg:resolve?domain=" << url_encode(path[0]) << "&post=" << to_integer<int64>(path[1])
<< copy_arg("single") << copy_arg("thread") << copy_arg("comment") << copy_arg("t"));
auto post = to_integer<int64>(path[1]);
auto thread = PSTRING() << copy_arg("thread");
if (path.size() >= 3 && to_integer<int64>(path[2]) > 0) {
thread = PSTRING() << "&thread=" << post;
post = to_integer<int64>(path[2]);
}
return td::make_unique<InternalLinkMessage>(PSTRING() << "tg:resolve?domain=" << url_encode(path[0])
<< "&post=" << post << copy_arg("single") << thread
<< copy_arg("comment") << copy_arg("t"));
}
auto username = path[0];
for (auto &arg : url_query.args_) {
@ -1488,7 +1486,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_passport(
}
void LinkManager::update_autologin_domains(string autologin_token, vector<string> autologin_domains,
vector<string> url_auth_domains) {
vector<string> url_auth_domains, vector<string> whitelisted_domains) {
autologin_update_time_ = Time::now();
autologin_token_ = std::move(autologin_token);
if (autologin_domains_ != autologin_domains) {
@ -1499,6 +1497,10 @@ void LinkManager::update_autologin_domains(string autologin_token, vector<string
url_auth_domains_ = std::move(url_auth_domains);
G()->td_db()->get_binlog_pmc()->set("url_auth_domains", implode(url_auth_domains_, '\xFF'));
}
if (whitelisted_domains_ != whitelisted_domains) {
whitelisted_domains_ = std::move(whitelisted_domains);
G()->td_db()->get_binlog_pmc()->set("whitelisted_domains", implode(whitelisted_domains_, '\xFF'));
}
}
void LinkManager::get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise) {
@ -1528,6 +1530,9 @@ void LinkManager::get_external_link_info(string &&link, Promise<td_api::object_p
return promise.set_value(std::move(default_result));
}
bool skip_confirm = td::contains(whitelisted_domains_, r_url.ok().host_);
default_result->skip_confirm_ = skip_confirm;
if (!td::contains(autologin_domains_, r_url.ok().host_)) {
if (td::contains(url_auth_domains_, r_url.ok().host_)) {
td_->create_handler<RequestUrlAuthQuery>(std::move(promise))->send(link, FullMessageId(), 0);
@ -1573,7 +1578,7 @@ void LinkManager::get_external_link_info(string &&link, Promise<td_api::object_p
url.query_ = PSTRING() << path << parameters << added_parameter << hash;
promise.set_value(td_api::make_object<td_api::loginUrlInfoOpen>(url.get_url(), false));
promise.set_value(td_api::make_object<td_api::loginUrlInfoOpen>(url.get_url(), skip_confirm));
}
void LinkManager::get_login_url_info(FullMessageId full_message_id, int64 button_id,
@ -1595,6 +1600,23 @@ void LinkManager::get_link_login_url(const string &url, bool allow_write_access,
td_->create_handler<AcceptUrlAuthQuery>(std::move(promise))->send(url, FullMessageId(), 0, allow_write_access);
}
Result<string> LinkManager::get_background_url(const string &name,
td_api::object_ptr<td_api::BackgroundType> background_type) {
TRY_RESULT(type, BackgroundType::get_background_type(background_type.get()));
auto url = PSTRING() << G()->get_option_string("t_me_url", "https://t.me/") << "bg/";
auto link = type.get_link();
if (type.has_file()) {
url += name;
if (!link.empty()) {
url += '?';
url += link;
}
} else {
url += link;
}
return url;
}
string LinkManager::get_dialog_invite_link_hash(Slice invite_link) {
auto link_info = get_link_info(invite_link);
if (link_info.type_ != LinkType::Tg && link_info.type_ != LinkType::TMe) {
@ -1615,6 +1637,37 @@ string LinkManager::get_dialog_invite_link(Slice hash, bool is_internal) {
}
}
string LinkManager::get_instant_view_link_url(Slice link) {
auto link_info = get_link_info(link);
if (link_info.type_ != LinkType::TMe) {
return string();
}
const auto url_query = parse_url_query(link_info.query_);
const auto &path = url_query.path_;
if (path.size() == 1 && path[0] == "iv") {
return url_query.get_arg("url").str();
}
return string();
}
string LinkManager::get_instant_view_link_rhash(Slice link) {
auto link_info = get_link_info(link);
if (link_info.type_ != LinkType::TMe) {
return string();
}
const auto url_query = parse_url_query(link_info.query_);
const auto &path = url_query.path_;
if (path.size() == 1 && path[0] == "iv" && !url_query.get_arg("url").empty()) {
return url_query.get_arg("rhash").str();
}
return string();
}
string LinkManager::get_instant_view_link(Slice url, Slice rhash) {
return PSTRING() << G()->get_option_string("t_me_url", "https://t.me/") << "iv?url=" << url_encode(url)
<< "&rhash=" << url_encode(rhash);
}
UserId LinkManager::get_link_user_id(Slice url) {
string lower_cased_url = to_lower(url);
url = lower_cased_url;
@ -1657,7 +1710,7 @@ UserId LinkManager::get_link_user_id(Slice url) {
return UserId();
}
Result<int64> LinkManager::get_link_custom_emoji_document_id(Slice url) {
Result<CustomEmojiId> LinkManager::get_link_custom_emoji_id(Slice url) {
string lower_cased_url = to_lower(url);
url = lower_cased_url;
@ -1693,7 +1746,7 @@ Result<int64> LinkManager::get_link_custom_emoji_document_id(Slice url) {
if (r_document_id.is_error() || r_document_id.ok() == 0) {
return Status::Error(400, "Invalid custom emoji identifier specified");
}
return r_document_id.ok();
return CustomEmojiId(r_document_id.ok());
}
}
return Status::Error(400, "Custom emoji URL must have an emoji identifier");
@ -1713,6 +1766,7 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
Slice channel_id_slice;
Slice message_id_slice;
Slice comment_message_id_slice = "0";
Slice top_thread_message_id_slice;
Slice media_timestamp_slice;
bool is_single = false;
bool for_comment = false;
@ -1764,10 +1818,12 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
}
if (key_value.first == "thread") {
for_comment = true;
top_thread_message_id_slice = key_value.second;
}
}
} else {
// /c/123456789/12345
// /c/123456789/1234/12345
// /username/12345?single
CHECK(!url.empty() && url[0] == '/');
@ -1806,9 +1862,15 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
}
if (key_value.first == "thread") {
for_comment = true;
top_thread_message_id_slice = key_value.second;
}
}
}
auto slash_pos = message_id_slice.find('/');
if (slash_pos != Slice::npos) {
top_thread_message_id_slice = message_id_slice.substr(0, slash_pos);
message_id_slice.remove_prefix(slash_pos + 1);
}
}
ChannelId channel_id;
@ -1825,6 +1887,18 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
return Status::Error("Wrong message ID");
}
int32 top_thread_message_id = 0;
if (!top_thread_message_id_slice.empty()) {
auto r_top_thread_message_id = to_integer_safe<int32>(top_thread_message_id_slice);
if (r_top_thread_message_id.is_error()) {
return Status::Error("Wrong message thread ID");
}
top_thread_message_id = r_top_thread_message_id.ok();
if (!ServerMessageId(top_thread_message_id).is_valid()) {
return Status::Error("Invalid message thread ID");
}
}
auto r_comment_message_id = to_integer_safe<int32>(comment_message_id_slice);
if (r_comment_message_id.is_error() ||
!(r_comment_message_id.ok() == 0 || ServerMessageId(r_comment_message_id.ok()).is_valid())) {
@ -1873,6 +1947,7 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
info.channel_id = channel_id;
info.message_id = MessageId(ServerMessageId(r_message_id.ok()));
info.comment_message_id = MessageId(ServerMessageId(r_comment_message_id.ok()));
info.top_thread_message_id = MessageId(ServerMessageId(top_thread_message_id));
info.media_timestamp = is_media_timestamp_invalid ? 0 : media_timestamp;
info.is_single = is_single;
info.for_comment = for_comment;

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessageLinkInfo.h"
#include "td/telegram/td_api.h"
@ -59,7 +60,7 @@ class LinkManager final : public Actor {
static unique_ptr<InternalLink> parse_internal_link(Slice link, bool is_trusted = false);
void update_autologin_domains(string autologin_token, vector<string> autologin_domains,
vector<string> url_auth_domains);
vector<string> url_auth_domains, vector<string> whitelisted_domains);
void get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise);
@ -74,13 +75,22 @@ class LinkManager final : public Actor {
void get_link_login_url(const string &url, bool allow_write_access,
Promise<td_api::object_ptr<td_api::httpUrl>> &&promise);
static Result<string> get_background_url(const string &name,
td_api::object_ptr<td_api::BackgroundType> background_type);
static string get_dialog_invite_link_hash(Slice invite_link);
static string get_dialog_invite_link(Slice hash, bool is_internal);
static string get_instant_view_link_url(Slice link);
static string get_instant_view_link_rhash(Slice link);
static string get_instant_view_link(Slice url, Slice rhash);
static UserId get_link_user_id(Slice url);
static Result<int64> get_link_custom_emoji_document_id(Slice url);
static Result<CustomEmojiId> get_link_custom_emoji_id(Slice url);
static Result<MessageLinkInfo> get_message_link_info(Slice url);
@ -150,6 +160,7 @@ class LinkManager final : public Actor {
vector<string> autologin_domains_;
double autologin_update_time_ = 0.0;
vector<string> url_auth_domains_;
vector<string> whitelisted_domains_;
};
} // namespace td

View File

@ -16,6 +16,7 @@
#include "td/telegram/ChatId.h"
#include "td/telegram/Contact.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/DialogAction.h"
#include "td/telegram/DialogParticipant.h"
@ -28,12 +29,19 @@
#include "td/telegram/files/FileLocation.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileType.h"
#include "td/telegram/ForumTopicEditedData.h"
#include "td/telegram/ForumTopicEditedData.hpp"
#include "td/telegram/ForumTopicIcon.h"
#include "td/telegram/ForumTopicIcon.hpp"
#include "td/telegram/ForumTopicManager.h"
#include "td/telegram/Game.h"
#include "td/telegram/Game.hpp"
#include "td/telegram/Global.h"
#include "td/telegram/GroupCallManager.h"
#include "td/telegram/HashtagHints.h"
#include "td/telegram/InputGroupCallId.h"
#include "td/telegram/InputInvoice.h"
#include "td/telegram/InputInvoice.hpp"
#include "td/telegram/InputMessageText.h"
#include "td/telegram/Location.h"
#include "td/telegram/MessageEntity.h"
@ -44,8 +52,8 @@
#include "td/telegram/misc.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/Payments.h"
#include "td/telegram/Payments.hpp"
#include "td/telegram/OrderInfo.h"
#include "td/telegram/OrderInfo.hpp"
#include "td/telegram/Photo.h"
#include "td/telegram/Photo.hpp"
#include "td/telegram/PhotoFormat.h"
@ -454,7 +462,7 @@ class MessageChatSetTtl final : public MessageContent {
class MessageUnsupported final : public MessageContent {
public:
static constexpr int32 CURRENT_VERSION = 13;
static constexpr int32 CURRENT_VERSION = 14;
int32 version = CURRENT_VERSION;
MessageUnsupported() = default;
@ -799,6 +807,33 @@ class MessageGiftPremium final : public MessageContent {
}
};
class MessageTopicCreate final : public MessageContent {
public:
string title;
ForumTopicIcon icon;
MessageTopicCreate() = default;
MessageTopicCreate(string &&title, ForumTopicIcon &&icon) : title(std::move(title)), icon(std::move(icon)) {
}
MessageContentType get_type() const final {
return MessageContentType::TopicCreate;
}
};
class MessageTopicEdit final : public MessageContent {
public:
ForumTopicEditedData edited_data;
MessageTopicEdit() = default;
explicit MessageTopicEdit(ForumTopicEditedData &&edited_data) : edited_data(std::move(edited_data)) {
}
MessageContentType get_type() const final {
return MessageContentType::TopicEdit;
}
};
template <class StorerT>
static void store(const MessageContent *content, StorerT &storer) {
CHECK(content != nullptr);
@ -1127,6 +1162,17 @@ static void store(const MessageContent *content, StorerT &storer) {
store(m->months, storer);
break;
}
case MessageContentType::TopicCreate: {
const auto *m = static_cast<const MessageTopicCreate *>(content);
store(m->title, storer);
store(m->icon, storer);
break;
}
case MessageContentType::TopicEdit: {
const auto *m = static_cast<const MessageTopicEdit *>(content);
store(m->edited_data, storer);
break;
}
default:
UNREACHABLE();
}
@ -1226,14 +1272,7 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
case MessageContentType::Photo: {
auto m = make_unique<MessagePhoto>();
parse(m->photo, parser);
for (auto &photo_size : m->photo.photos) {
if (!photo_size.file_id.is_valid()) {
is_bad = true;
}
}
if (m->photo.is_empty()) {
is_bad = true;
}
is_bad |= m->photo.is_bad();
parse_caption(m->caption, parser);
content = std::move(m);
break;
@ -1584,6 +1623,19 @@ static void parse(unique_ptr<MessageContent> &content, ParserT &parser) {
content = std::move(m);
break;
}
case MessageContentType::TopicCreate: {
auto m = make_unique<MessageTopicCreate>();
parse(m->title, parser);
parse(m->icon, parser);
content = std::move(m);
break;
}
case MessageContentType::TopicEdit: {
auto m = make_unique<MessageTopicEdit>();
parse(m->edited_data, parser);
content = std::move(m);
break;
}
default:
LOG(FATAL) << "Have unknown message content type " << static_cast<int32>(content_type);
}
@ -1642,8 +1694,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
case telegram_api::botInlineMessageMediaInvoice::ID: {
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageMediaInvoice>(bot_inline_message);
reply_markup = std::move(inline_message->reply_markup_);
result.message_content =
make_unique<MessageInvoice>(get_input_invoice(std::move(inline_message), td, DialogId()));
result.message_content = make_unique<MessageInvoice>(InputInvoice(std::move(inline_message), td, DialogId()));
break;
}
case telegram_api::botInlineMessageMediaGeo::ID: {
@ -1767,6 +1818,11 @@ static Result<InputMessageContent> create_input_message_content(
disable_web_page_preview = input_message_text.disable_web_page_preview;
clear_draft = input_message_text.clear_draft;
if (is_bot && static_cast<int64>(utf8_length(input_message_text.text.text)) >
G()->get_option_integer("message_text_length_max")) {
return Status::Error(400, "Message is too long");
}
WebPageId web_page_id;
bool can_add_web_page_previews =
dialog_id.get_type() != DialogType::Channel ||
@ -1911,7 +1967,7 @@ static Result<InputMessageContent> create_input_message_content(
}
td->video_notes_manager_->create_video_note(file_id, string(), thumbnail, input_video_note->duration_,
get_dimensions(length, length, nullptr), false);
get_dimensions(length, length, nullptr), string(), false);
content = make_unique<MessageVideoNote>(file_id, false);
break;
@ -1960,7 +2016,8 @@ static Result<InputMessageContent> create_input_message_content(
return Status::Error(400, "Invoices can be sent only by bots");
}
TRY_RESULT(input_invoice, process_input_message_invoice(std::move(input_message_content), td));
TRY_RESULT(input_invoice, InputInvoice::process_input_message_invoice(std::move(input_message_content), td,
dialog_id, is_premium));
content = make_unique<MessageInvoice>(std::move(input_invoice));
break;
}
@ -2080,14 +2137,14 @@ Result<InputMessageContent> get_input_message_content(
auto input_message = static_cast<td_api::inputMessageDocument *>(input_message_content.get());
auto file_type = input_message->disable_content_type_detection_ ? FileType::DocumentAsFile : FileType::Document;
r_file_id =
td->file_manager_->get_input_file_id(file_type, input_message->document_, dialog_id, false, is_secret);
td->file_manager_->get_input_file_id(file_type, input_message->document_, dialog_id, false, is_secret, true);
input_thumbnail = std::move(input_message->thumbnail_);
break;
}
case td_api::inputMessagePhoto::ID: {
auto input_message = static_cast<td_api::inputMessagePhoto *>(input_message_content.get());
r_file_id = td->file_manager_->get_input_file_id(FileType::Photo, input_message->photo_, dialog_id, false,
is_secret, false, false, true);
r_file_id =
td->file_manager_->get_input_file_id(FileType::Photo, input_message->photo_, dialog_id, false, is_secret);
input_thumbnail = std::move(input_message->thumbnail_);
if (!input_message->added_sticker_file_ids_.empty()) {
sticker_file_ids = td->stickers_manager_->get_attached_sticker_file_ids(input_message->added_sticker_file_ids_);
@ -2158,8 +2215,12 @@ Result<InputMessageContent> get_input_message_content(
}
}
TRY_RESULT(caption, get_formatted_text(td, dialog_id, extract_input_caption(input_message_content),
td->auth_manager_->is_bot(), true, false, false));
bool is_bot = td->auth_manager_->is_bot();
TRY_RESULT(caption, get_formatted_text(td, dialog_id, extract_input_caption(input_message_content), is_bot, true,
false, false));
if (is_bot && static_cast<int64>(utf8_length(caption.text)) > G()->get_option_integer("message_caption_length_max")) {
return Status::Error(400, "Message caption is too long");
}
return create_input_message_content(dialog_id, std::move(input_message_content), td, std::move(caption), file_id,
std::move(thumbnail), std::move(sticker_file_ids), is_premium);
}
@ -2202,6 +2263,8 @@ bool can_have_input_media(const Td *td, const MessageContent *content, bool is_s
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return false;
case MessageContentType::Animation:
case MessageContentType::Audio:
@ -2323,6 +2386,8 @@ SecretInputMedia get_secret_input_media(const MessageContent *content, Td *td,
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
break;
default:
UNREACHABLE();
@ -2363,7 +2428,7 @@ static tl_object_ptr<telegram_api::InputMedia> get_input_media_impl(
}
case MessageContentType::Invoice: {
const auto *m = static_cast<const MessageInvoice *>(content);
return get_input_media_invoice(m->input_invoice, td);
return m->input_invoice.get_input_media_invoice(td, std::move(input_file), std::move(input_thumbnail));
}
case MessageContentType::LiveLocation: {
const auto *m = static_cast<const MessageLiveLocation *>(content);
@ -2442,6 +2507,8 @@ static tl_object_ptr<telegram_api::InputMedia> get_input_media_impl(
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
break;
default:
UNREACHABLE();
@ -2545,6 +2612,10 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) {
auto *m = static_cast<MessageDocument *>(content);
return td->documents_manager_->delete_document_thumbnail(m->file_id);
}
case MessageContentType::Invoice: {
auto *m = static_cast<MessageInvoice *>(content);
return m->input_invoice.delete_thumbnail(td);
}
case MessageContentType::Photo: {
auto *m = static_cast<MessagePhoto *>(content);
return photo_delete_thumbnail(m->photo);
@ -2564,7 +2635,6 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) {
case MessageContentType::Contact:
case MessageContentType::Dice:
case MessageContentType::Game:
case MessageContentType::Invoice:
case MessageContentType::LiveLocation:
case MessageContentType::Location:
case MessageContentType::Venue:
@ -2603,6 +2673,8 @@ void delete_message_content_thumbnail(MessageContent *content, Td *td) {
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
break;
default:
UNREACHABLE();
@ -2785,6 +2857,8 @@ Status can_send_message_content(DialogId dialog_id, const MessageContent *conten
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
UNREACHABLE();
}
return Status::OK();
@ -2914,6 +2988,8 @@ static int32 get_message_content_media_index_mask(const MessageContent *content,
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return 0;
default:
UNREACHABLE();
@ -3036,6 +3112,10 @@ bool can_message_content_have_media_timestamp(const MessageContent *content) {
case MessageContentType::VideoNote:
case MessageContentType::VoiceNote:
return true;
case MessageContentType::Invoice: {
const auto *m = static_cast<const MessageInvoice *>(content);
return m->input_invoice.has_media_timestamp();
}
default:
return has_message_content_web_page(content);
}
@ -3073,7 +3153,10 @@ static void merge_location_access_hash(const Location &first, const Location &se
static bool need_message_text_changed_warning(const MessageText *old_content, const MessageText *new_content) {
if (new_content->text.text == "Unsupported characters" ||
new_content->text.text == "This channel is blocked because it was used to spread pornographic content.") {
new_content->text.text == "This channel is blocked because it was used to spread pornographic content." ||
begins_with(new_content->text.text,
"This group has been temporarily suspended to give its moderators time to clean up after users who "
"posted illegal pornographic content.")) {
// message contained unsupported characters or is restricted, text is replaced
return false;
}
@ -3219,9 +3302,12 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
}
case MessageContentType::Invoice: {
const auto *old_ = static_cast<const MessageInvoice *>(old_content);
const auto *new_ = static_cast<const MessageInvoice *>(new_content);
auto *new_ = static_cast<MessageInvoice *>(new_content);
new_->input_invoice.update_from(old_->input_invoice);
if (old_->input_invoice != new_->input_invoice) {
need_update = true;
} else if (old_->input_invoice.is_equal_but_different(new_->input_invoice)) {
is_content_changed = true;
}
break;
}
@ -3649,6 +3735,22 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
}
break;
}
case MessageContentType::TopicCreate: {
const auto *old_ = static_cast<const MessageTopicCreate *>(old_content);
const auto *new_ = static_cast<const MessageTopicCreate *>(new_content);
if (old_->title != new_->title || old_->icon != new_->icon) {
need_update = true;
}
break;
}
case MessageContentType::TopicEdit: {
const auto *old_ = static_cast<const MessageTopicEdit *>(old_content);
const auto *new_ = static_cast<const MessageTopicEdit *>(new_content);
if (old_->edited_data != new_->edited_data) {
need_update = true;
}
break;
}
case MessageContentType::Unsupported: {
const auto *old_ = static_cast<const MessageUnsupported *>(old_content);
const auto *new_ = static_cast<const MessageUnsupported *>(new_content);
@ -3668,6 +3770,7 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
return false;
}
// secret chats only
LOG(INFO) << "Merge message content of a message with file " << new_file_id;
MessageContentType content_type = message_content->get_type();
switch (content_type) {
@ -3788,6 +3891,8 @@ bool merge_message_content_file_id(Td *td, MessageContent *message_content, File
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
LOG(ERROR) << "Receive new file " << new_file_id << " in a sent message of the type " << content_type;
break;
default:
@ -3806,14 +3911,14 @@ static bool can_be_animated_emoji(const FormattedText &text) {
}
if (text.entities.size() == 1 && text.entities[0].type == MessageEntity::Type::CustomEmoji &&
text.entities[0].offset == 0 && static_cast<size_t>(text.entities[0].length) == utf8_utf16_length(text.text) &&
text.entities[0].document_id != 0) {
text.entities[0].custom_emoji_id.is_valid()) {
return true;
}
return false;
}
static int64 get_custom_emoji_id(const FormattedText &text) {
return text.entities.empty() ? 0 : text.entities[0].document_id;
static CustomEmojiId get_custom_emoji_id(const FormattedText &text) {
return text.entities.empty() ? CustomEmojiId() : text.entities[0].custom_emoji_id;
}
void register_message_content(Td *td, const MessageContent *content, FullMessageId full_message_id,
@ -3829,6 +3934,9 @@ void register_message_content(Td *td, const MessageContent *content, FullMessage
}
return;
}
case MessageContentType::VideoNote:
return td->video_notes_manager_->register_video_note(static_cast<const MessageVideoNote *>(content)->file_id,
full_message_id, source);
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->register_voice_note(static_cast<const MessageVoiceNote *>(content)->file_id,
full_message_id, source);
@ -3863,6 +3971,12 @@ void reregister_message_content(Td *td, const MessageContent *old_content, const
}
break;
}
case MessageContentType::VideoNote:
if (static_cast<const MessageVideoNote *>(old_content)->file_id ==
static_cast<const MessageVideoNote *>(new_content)->file_id) {
return;
}
break;
case MessageContentType::VoiceNote:
if (static_cast<const MessageVoiceNote *>(old_content)->file_id ==
static_cast<const MessageVoiceNote *>(new_content)->file_id) {
@ -3910,6 +4024,9 @@ void unregister_message_content(Td *td, const MessageContent *content, FullMessa
}
return;
}
case MessageContentType::VideoNote:
return td->video_notes_manager_->unregister_video_note(static_cast<const MessageVideoNote *>(content)->file_id,
full_message_id, source);
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->unregister_voice_note(static_cast<const MessageVoiceNote *>(content)->file_id,
full_message_id, source);
@ -3993,8 +4110,8 @@ static auto secret_to_telegram(secret_api::documentAttributeSticker &sticker) {
nullptr);
}
// documentAttributeVideo duration:int w:int h:int = DocumentAttribute;
static auto secret_to_telegram(secret_api::documentAttributeVideo &video) {
// documentAttributeVideo23 duration:int w:int h:int = DocumentAttribute;
static auto secret_to_telegram(secret_api::documentAttributeVideo23 &video) {
return make_tl_object<telegram_api::documentAttributeVideo>(0, false /*ignored*/, false /*ignored*/, video.duration_,
video.w_, video.h_);
}
@ -4007,10 +4124,10 @@ static auto secret_to_telegram(secret_api::documentAttributeFilename &filename)
return make_tl_object<telegram_api::documentAttributeFilename>(filename.file_name_);
}
// documentAttributeVideo66 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
static auto secret_to_telegram(secret_api::documentAttributeVideo66 &video) {
// 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<telegram_api::documentAttributeVideo>(
(video.flags_ & secret_api::documentAttributeVideo66::ROUND_MESSAGE_MASK) != 0
(video.flags_ & secret_api::documentAttributeVideo::ROUND_MESSAGE_MASK) != 0
? telegram_api::documentAttributeVideo::ROUND_MESSAGE_MASK
: 0,
video.round_message_, false, video.duration_, video.w_, video.h_);
@ -4191,7 +4308,7 @@ unique_ptr<MessageContent> get_secret_message_content(
auto media = move_tl_object_as<secret_api::decryptedMessageMediaVideo>(media_ptr);
vector<tl_object_ptr<secret_api::DocumentAttribute>> attributes;
attributes.emplace_back(
make_tl_object<secret_api::documentAttributeVideo>(media->duration_, media->w_, media->h_));
make_tl_object<secret_api::documentAttributeVideo>(0, false, media->duration_, media->w_, media->h_));
media_ptr = make_tl_object<secret_api::decryptedMessageMediaDocument>(
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());
@ -4467,15 +4584,16 @@ unique_ptr<MessageContent> get_message_content(Td *td, FormattedText message,
case telegram_api::messageMediaGame::ID: {
auto media = move_tl_object_as<telegram_api::messageMediaGame>(media_ptr);
auto m = make_unique<MessageGame>(Game(td, via_bot_user_id, std::move(media->game_), message, owner_dialog_id));
auto m = make_unique<MessageGame>(
Game(td, via_bot_user_id, std::move(media->game_), std::move(message), owner_dialog_id));
if (m->game.is_empty()) {
break;
}
return std::move(m);
}
case telegram_api::messageMediaInvoice::ID:
return td::make_unique<MessageInvoice>(
get_input_invoice(move_tl_object_as<telegram_api::messageMediaInvoice>(media_ptr), td, owner_dialog_id));
return td::make_unique<MessageInvoice>(InputInvoice(
move_tl_object_as<telegram_api::messageMediaInvoice>(media_ptr), td, owner_dialog_id, std::move(message)));
case telegram_api::messageMediaWebPage::ID: {
auto media = move_tl_object_as<telegram_api::messageMediaWebPage>(media_ptr);
if (disable_web_page_preview != nullptr) {
@ -4757,6 +4875,8 @@ unique_ptr<MessageContent> dup_message_content(Td *td, DialogId dialog_id, const
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return nullptr;
default:
UNREACHABLE();
@ -5054,6 +5174,19 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
}
return td::make_unique<MessageGiftPremium>(std::move(action->currency_), action->amount_, action->months_);
}
case telegram_api::messageActionTopicCreate::ID: {
auto action = move_tl_object_as<telegram_api::messageActionTopicCreate>(action_ptr);
return td::make_unique<MessageTopicCreate>(std::move(action->title_),
ForumTopicIcon(action->icon_color_, action->icon_emoji_id_));
}
case telegram_api::messageActionTopicEdit::ID: {
auto action = move_tl_object_as<telegram_api::messageActionTopicEdit>(action_ptr);
auto edit_icon_custom_emoji_id = (action->flags_ & telegram_api::messageActionTopicEdit::ICON_EMOJI_ID_MASK) != 0;
auto edit_is_closed = (action->flags_ & telegram_api::messageActionTopicEdit::CLOSED_MASK) != 0;
return td::make_unique<MessageTopicEdit>(ForumTopicEditedData{std::move(action->title_),
edit_icon_custom_emoji_id, action->icon_emoji_id_,
edit_is_closed, action->closed_});
}
default:
UNREACHABLE();
}
@ -5095,7 +5228,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
}
case MessageContentType::Invoice: {
const auto *m = static_cast<const MessageInvoice *>(content);
return get_message_invoice_object(m->input_invoice, td);
return m->input_invoice.get_message_invoice_object(td, skip_bot_commands, max_media_timestamp);
}
case MessageContentType::LiveLocation: {
const auto *m = static_cast<const MessageLiveLocation *>(content);
@ -5330,6 +5463,14 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
return make_tl_object<td_api::messageGiftedPremium>(
m->currency, m->amount, m->months, td->stickers_manager_->get_premium_gift_sticker_object(m->months));
}
case MessageContentType::TopicCreate: {
const auto *m = static_cast<const MessageTopicCreate *>(content);
return td_api::make_object<td_api::messageForumTopicCreated>(m->title, m->icon.get_forum_topic_icon_object());
}
case MessageContentType::TopicEdit: {
const auto *m = static_cast<const MessageTopicEdit *>(content);
return m->edited_data.get_message_content_object();
}
default:
UNREACHABLE();
return nullptr;
@ -5361,6 +5502,8 @@ const FormattedText *get_message_content_caption(const MessageContent *content)
return &static_cast<const MessageAudio *>(content)->caption;
case MessageContentType::Document:
return &static_cast<const MessageDocument *>(content)->caption;
case MessageContentType::Invoice:
return static_cast<const MessageInvoice *>(content)->input_invoice.get_caption();
case MessageContentType::Photo:
return &static_cast<const MessagePhoto *>(content)->caption;
case MessageContentType::Video:
@ -5383,6 +5526,8 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td)
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
return td->audios_manager_->get_audio_duration(audio_file_id);
}
case MessageContentType::Invoice:
return static_cast<const MessageInvoice *>(content)->input_invoice.get_duration(td);
case MessageContentType::Video: {
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
return td->videos_manager_->get_video_duration(video_file_id);
@ -5407,6 +5552,8 @@ int32 get_message_content_media_duration(const MessageContent *content, const Td
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
return td->audios_manager_->get_audio_duration(audio_file_id);
}
case MessageContentType::Invoice:
return static_cast<const MessageInvoice *>(content)->input_invoice.get_duration(td);
case MessageContentType::Text: {
auto web_page_id = static_cast<const MessageText *>(content)->web_page_id;
return td->web_pages_manager_->get_web_page_media_duration(web_page_id);
@ -5428,6 +5575,16 @@ int32 get_message_content_media_duration(const MessageContent *content, const Td
}
}
const Photo *get_message_content_photo(const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Photo:
return &static_cast<const MessagePhoto *>(content)->photo;
default:
break;
}
return nullptr;
}
FileId get_message_content_upload_file_id(const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Animation:
@ -5436,13 +5593,10 @@ FileId get_message_content_upload_file_id(const MessageContent *content) {
return static_cast<const MessageAudio *>(content)->file_id;
case MessageContentType::Document:
return static_cast<const MessageDocument *>(content)->file_id;
case MessageContentType::Invoice:
return static_cast<const MessageInvoice *>(content)->input_invoice.get_upload_file_id();
case MessageContentType::Photo:
for (auto &size : static_cast<const MessagePhoto *>(content)->photo.photos) {
if (size.type == 'i') {
return size.file_id;
}
}
break;
return get_photo_upload_file_id(static_cast<const MessagePhoto *>(content)->photo);
case MessageContentType::Sticker:
return static_cast<const MessageSticker *>(content)->file_id;
case MessageContentType::Video:
@ -5459,10 +5613,11 @@ FileId get_message_content_upload_file_id(const MessageContent *content) {
FileId get_message_content_any_file_id(const MessageContent *content) {
FileId result = get_message_content_upload_file_id(content);
if (!result.is_valid() && content->get_type() == MessageContentType::Photo) {
const auto &sizes = static_cast<const MessagePhoto *>(content)->photo.photos;
if (!sizes.empty()) {
result = sizes.back().file_id;
if (!result.is_valid()) {
if (content->get_type() == MessageContentType::Photo) {
result = get_photo_any_file_id(static_cast<const MessagePhoto *>(content)->photo);
} else if (content->get_type() == MessageContentType::Invoice) {
result = static_cast<const MessageInvoice *>(content)->input_invoice.get_any_file_id();
}
}
return result;
@ -5499,34 +5654,31 @@ void update_message_content_file_id_remote(MessageContent *content, FileId file_
FileId get_message_content_thumbnail_file_id(const MessageContent *content, const Td *td) {
if (!G()->get_option_boolean("disable_minithumbnails")) {
switch (content->get_type()) {
case MessageContentType::Animation:
return td->animations_manager_->get_animation_thumbnail_file_id(
static_cast<const MessageAnimation *>(content)->file_id);
case MessageContentType::Audio:
return td->audios_manager_->get_audio_thumbnail_file_id(static_cast<const MessageAudio *>(content)->file_id);
case MessageContentType::Document:
return td->documents_manager_->get_document_thumbnail_file_id(
static_cast<const MessageDocument *>(content)->file_id);
case MessageContentType::Photo:
for (auto &size : static_cast<const MessagePhoto *>(content)->photo.photos) {
if (size.type == 't') {
return size.file_id;
}
}
break;
case MessageContentType::Sticker:
return td->stickers_manager_->get_sticker_thumbnail_file_id(
static_cast<const MessageSticker *>(content)->file_id);
case MessageContentType::Video:
return td->videos_manager_->get_video_thumbnail_file_id(static_cast<const MessageVideo *>(content)->file_id);
case MessageContentType::VideoNote:
return td->video_notes_manager_->get_video_note_thumbnail_file_id(
static_cast<const MessageVideoNote *>(content)->file_id);
case MessageContentType::VoiceNote:
return FileId();
default:
break;
switch (content->get_type()) {
case MessageContentType::Animation:
return td->animations_manager_->get_animation_thumbnail_file_id(
static_cast<const MessageAnimation *>(content)->file_id);
case MessageContentType::Audio:
return td->audios_manager_->get_audio_thumbnail_file_id(static_cast<const MessageAudio *>(content)->file_id);
case MessageContentType::Document:
return td->documents_manager_->get_document_thumbnail_file_id(
static_cast<const MessageDocument *>(content)->file_id);
case MessageContentType::Invoice:
return static_cast<const MessageInvoice *>(content)->input_invoice.get_thumbnail_file_id(td);
case MessageContentType::Photo:
return get_photo_thumbnail_file_id(static_cast<const MessagePhoto *>(content)->photo);
case MessageContentType::Sticker:
return td->stickers_manager_->get_sticker_thumbnail_file_id(
static_cast<const MessageSticker *>(content)->file_id);
case MessageContentType::Video:
return td->videos_manager_->get_video_thumbnail_file_id(static_cast<const MessageVideo *>(content)->file_id);
case MessageContentType::VideoNote:
return td->video_notes_manager_->get_video_note_thumbnail_file_id(
static_cast<const MessageVideoNote *>(content)->file_id);
case MessageContentType::VoiceNote:
return FileId();
default:
break;
}
}
return FileId();
@ -5569,7 +5721,7 @@ vector<FileId> get_message_content_file_ids(const MessageContent *content, const
case MessageContentType::Game:
return static_cast<const MessageGame *>(content)->game.get_file_ids(td);
case MessageContentType::Invoice:
return get_input_invoice_file_ids(static_cast<const MessageInvoice *>(content)->input_invoice);
return static_cast<const MessageInvoice *>(content)->input_invoice.get_file_ids(td);
case MessageContentType::ChatChangePhoto:
return photo_get_file_ids(static_cast<const MessageChatChangePhoto *>(content)->photo);
case MessageContentType::PassportDataReceived: {
@ -5604,22 +5756,26 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
if (!text->web_page_id.is_valid()) {
return text->text.text;
}
return PSTRING() << text->text.text << " " << td->web_pages_manager_->get_web_page_search_text(text->web_page_id);
return PSTRING() << text->text.text << ' ' << td->web_pages_manager_->get_web_page_search_text(text->web_page_id);
}
case MessageContentType::Animation: {
const auto *animation = static_cast<const MessageAnimation *>(content);
return PSTRING() << td->animations_manager_->get_animation_search_text(animation->file_id) << " "
return PSTRING() << td->animations_manager_->get_animation_search_text(animation->file_id) << ' '
<< animation->caption.text;
}
case MessageContentType::Audio: {
const auto *audio = static_cast<const MessageAudio *>(content);
return PSTRING() << td->audios_manager_->get_audio_search_text(audio->file_id) << " " << audio->caption.text;
return PSTRING() << td->audios_manager_->get_audio_search_text(audio->file_id) << ' ' << audio->caption.text;
}
case MessageContentType::Document: {
const auto *document = static_cast<const MessageDocument *>(content);
return PSTRING() << td->documents_manager_->get_document_search_text(document->file_id) << " "
return PSTRING() << td->documents_manager_->get_document_search_text(document->file_id) << ' '
<< document->caption.text;
}
case MessageContentType::Invoice: {
const auto *invoice = static_cast<const MessageInvoice *>(content);
return invoice->input_invoice.get_caption()->text;
}
case MessageContentType::Photo: {
const auto *photo = static_cast<const MessagePhoto *>(content);
return photo->caption.text;
@ -5634,7 +5790,6 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
}
case MessageContentType::Contact:
case MessageContentType::Game:
case MessageContentType::Invoice:
case MessageContentType::LiveLocation:
case MessageContentType::Location:
case MessageContentType::Sticker:
@ -5674,6 +5829,8 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return string();
default:
UNREACHABLE();
@ -5681,6 +5838,23 @@ string get_message_content_search_text(const Td *td, const MessageContent *conte
}
}
bool update_message_content_extended_media(MessageContent *content,
telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media,
DialogId owner_dialog_id, Td *td) {
CHECK(content != nullptr);
CHECK(content->get_type() == MessageContentType::Invoice);
return static_cast<MessageInvoice *>(content)->input_invoice.update_extended_media(std::move(extended_media),
owner_dialog_id, td);
}
bool need_poll_message_content_extended_media(const MessageContent *content) {
CHECK(content != nullptr);
if (content->get_type() != MessageContentType::Invoice) {
return false;
}
return static_cast<const MessageInvoice *>(content)->input_invoice.need_poll_extended_media();
}
void get_message_content_animated_emoji_click_sticker(const MessageContent *content, FullMessageId full_message_id,
Td *td, Promise<td_api::object_ptr<td_api::sticker>> &&promise) {
if (content->get_type() != MessageContentType::Text) {
@ -5718,6 +5892,10 @@ bool need_reget_message_content(const MessageContent *content) {
const auto *m = static_cast<const MessageUnsupported *>(content);
return m->version != MessageUnsupported::CURRENT_VERSION;
}
case MessageContentType::Invoice: {
const auto *m = static_cast<const MessageInvoice *>(content);
return m->input_invoice.need_reget();
}
default:
return false;
}
@ -5927,6 +6105,10 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::GiftPremium:
break;
case MessageContentType::TopicCreate:
break;
case MessageContentType::TopicEdit:
break;
default:
UNREACHABLE();
break;
@ -5934,6 +6116,21 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
add_formatted_text_dependencies(dependencies, get_message_content_text(message_content));
}
void update_forum_topic_info_by_service_message_content(Td *td, const MessageContent *content, DialogId dialog_id,
MessageId top_thread_message_id) {
if (!top_thread_message_id.is_valid()) {
return;
}
switch (content->get_type()) {
case MessageContentType::TopicEdit:
return td->forum_topic_manager_->on_forum_topic_edited(
dialog_id, top_thread_message_id, static_cast<const MessageTopicEdit *>(content)->edited_data);
default:
// nothing to do
return;
}
}
void on_sent_message_content(Td *td, const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Animation:
@ -5957,10 +6154,10 @@ void move_message_content_sticker_set_to_top(Td *td, const MessageContent *conte
if (text == nullptr) {
return;
}
vector<int64> custom_emoji_ids;
vector<CustomEmojiId> custom_emoji_ids;
for (auto &entity : text->entities) {
if (entity.type == MessageEntity::Type::CustomEmoji) {
custom_emoji_ids.push_back(entity.document_id);
custom_emoji_ids.push_back(entity.custom_emoji_id);
}
}
if (!custom_emoji_ids.empty()) {
@ -6017,4 +6214,28 @@ void update_used_hashtags(Td *td, const MessageContent *content) {
}
}
void recognize_message_content_speech(Td *td, const MessageContent *content, FullMessageId full_message_id,
Promise<Unit> &&promise) {
switch (content->get_type()) {
case MessageContentType::VideoNote:
return td->video_notes_manager_->recognize_speech(full_message_id, std::move(promise));
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->recognize_speech(full_message_id, std::move(promise));
default:
return promise.set_error(Status::Error(400, "Invalid message specified"));
}
}
void rate_message_content_speech_recognition(Td *td, const MessageContent *content, FullMessageId full_message_id,
bool is_good, Promise<Unit> &&promise) {
switch (content->get_type()) {
case MessageContentType::VideoNote:
return td->video_notes_manager_->rate_speech_recognition(full_message_id, is_good, std::move(promise));
case MessageContentType::VoiceNote:
return td->voice_notes_manager_->rate_speech_recognition(full_message_id, is_good, std::move(promise));
default:
return promise.set_error(Status::Error(400, "Invalid message specified"));
}
}
} // namespace td

View File

@ -216,6 +216,8 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td);
int32 get_message_content_media_duration(const MessageContent *content, const Td *td);
const Photo *get_message_content_photo(const MessageContent *content);
FileId get_message_content_upload_file_id(const MessageContent *content);
FileId get_message_content_any_file_id(const MessageContent *content);
@ -228,6 +230,12 @@ vector<FileId> get_message_content_file_ids(const MessageContent *content, const
string get_message_content_search_text(const Td *td, const MessageContent *content);
bool update_message_content_extended_media(MessageContent *content,
telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media,
DialogId owner_dialog_id, Td *td);
bool need_poll_message_content_extended_media(const MessageContent *content);
void get_message_content_animated_emoji_click_sticker(const MessageContent *content, FullMessageId full_message_id,
Td *td, Promise<td_api::object_ptr<td_api::sticker>> &&promise);
@ -244,6 +252,9 @@ void update_failed_to_send_message_content(Td *td, unique_ptr<MessageContent> &c
void add_message_content_dependencies(Dependencies &dependencies, const MessageContent *message_content);
void update_forum_topic_info_by_service_message_content(Td *td, const MessageContent *content, DialogId dialog_id,
MessageId top_thread_message_id);
void on_sent_message_content(Td *td, const MessageContent *content);
void move_message_content_sticker_set_to_top(Td *td, const MessageContent *content);
@ -256,4 +267,10 @@ void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date);
void update_used_hashtags(Td *td, const MessageContent *content);
void recognize_message_content_speech(Td *td, const MessageContent *content, FullMessageId full_message_id,
Promise<Unit> &&promise);
void rate_message_content_speech_recognition(Td *td, const MessageContent *content, FullMessageId full_message_id,
bool is_good, Promise<Unit> &&promise);
} // namespace td

View File

@ -110,6 +110,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType cont
return string_builder << "WebViewDataReceived";
case MessageContentType::GiftPremium:
return string_builder << "GiftPremium";
case MessageContentType::TopicCreate:
return string_builder << "TopicCreate";
case MessageContentType::TopicEdit:
return string_builder << "TopicEdit";
default:
UNREACHABLE();
return string_builder;
@ -168,6 +172,8 @@ bool is_allowed_media_group_content(MessageContentType content_type) {
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return false;
default:
UNREACHABLE();
@ -234,6 +240,8 @@ bool is_secret_message_content(int32 ttl, MessageContentType content_type) {
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return false;
default:
UNREACHABLE();
@ -293,6 +301,8 @@ bool is_service_message_content(MessageContentType content_type) {
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return true;
default:
UNREACHABLE();
@ -352,6 +362,8 @@ bool can_have_message_content_caption(MessageContentType content_type) {
case MessageContentType::WebViewDataSent:
case MessageContentType::WebViewDataReceived:
case MessageContentType::GiftPremium:
case MessageContentType::TopicCreate:
case MessageContentType::TopicEdit:
return false;
default:
UNREACHABLE();
@ -364,6 +376,7 @@ uint64 get_message_content_chain_id(MessageContentType content_type) {
case MessageContentType::Animation:
case MessageContentType::Audio:
case MessageContentType::Document:
case MessageContentType::Invoice:
case MessageContentType::Photo:
case MessageContentType::Sticker:
case MessageContentType::Video:

View File

@ -64,7 +64,9 @@ enum class MessageContentType : int32 {
ChatSetTheme,
WebViewDataSent,
WebViewDataReceived,
GiftPremium
GiftPremium,
TopicCreate,
TopicEdit
};
StringBuilder &operator<<(StringBuilder &string_builder, MessageContentType content_type);

View File

@ -19,7 +19,6 @@ struct MessageCopyOptions {
bool send_copy = false;
bool replace_caption = false;
FormattedText new_caption;
MessageId top_thread_message_id;
MessageId reply_to_message_id;
unique_ptr<ReplyMarkup> reply_markup;
@ -31,8 +30,7 @@ struct MessageCopyOptions {
if (!send_copy) {
return true;
}
if ((replace_caption && !new_caption.text.empty()) || top_thread_message_id.is_valid() ||
reply_to_message_id.is_valid() || reply_markup != nullptr) {
if ((replace_caption && !new_caption.text.empty()) || reply_to_message_id.is_valid() || reply_markup != nullptr) {
return false;
}
return true;
@ -45,9 +43,6 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, MessageCopyOptio
if (copy_options.replace_caption) {
string_builder << ", new_caption = " << copy_options.new_caption;
}
if (copy_options.top_thread_message_id.is_valid()) {
string_builder << ", in thread of " << copy_options.top_thread_message_id;
}
if (copy_options.reply_to_message_id.is_valid()) {
string_builder << ", in reply to " << copy_options.reply_to_message_id;
}

View File

@ -122,8 +122,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &me
if (message_entity.user_id.is_valid()) {
string_builder << ", " << message_entity.user_id;
}
if (message_entity.document_id != 0) {
string_builder << ", emoji = " << message_entity.document_id;
if (message_entity.custom_emoji_id.is_valid()) {
string_builder << ", " << message_entity.custom_emoji_id;
}
string_builder << ']';
return string_builder;
@ -173,7 +173,7 @@ tl_object_ptr<td_api::TextEntityType> MessageEntity::get_text_entity_type_object
case MessageEntity::Type::Spoiler:
return make_tl_object<td_api::textEntityTypeSpoiler>();
case MessageEntity::Type::CustomEmoji:
return make_tl_object<td_api::textEntityTypeCustomEmoji>(document_id);
return make_tl_object<td_api::textEntityTypeCustomEmoji>(custom_emoji_id.get());
default:
UNREACHABLE();
return nullptr;
@ -1322,7 +1322,7 @@ vector<Slice> find_mentions(Slice str) {
auto mentions = match_mentions(str);
td::remove_if(mentions, [](Slice mention) {
mention.remove_prefix(1);
if (mention.size() >= 5) {
if (mention.size() >= 4) {
return false;
}
auto lowered_mention = to_lower(mention);
@ -1421,7 +1421,7 @@ void remove_empty_entities(vector<MessageEntity> &entities) {
case MessageEntity::Type::MentionName:
return !entity.user_id.is_valid();
case MessageEntity::Type::CustomEmoji:
return entity.document_id == 0;
return !entity.custom_emoji_id.is_valid();
default:
return false;
}
@ -1563,12 +1563,12 @@ static bool are_entities_valid(const vector<MessageEntity> &entities) {
}
// parents are not pre after this point
if (is_pre_entity(entity.type) && (nested_entity_type_mask & ~get_blockquote_entities_mask()) != 0) {
// Pre and Code can't be contained in other entities, except blockquote
// Pre and Code can't be part of other entities, except blockquote
return false;
}
if ((is_continuous_entity(entity.type) || is_blockquote_entity(entity.type)) &&
(nested_entity_type_mask & get_continuous_entities_mask()) != 0) {
// continuous and blockquote can't be contained in continuous
// continuous and blockquote can't be part of other continuous entity
return false;
}
if ((nested_entity_type_mask & get_splittable_entities_mask()) != 0) {
@ -1793,7 +1793,7 @@ string get_first_url(const FormattedText &text) {
if (entity.length <= 4) {
continue;
}
Slice url = utf8_utf16_substr(text.text, entity.offset, entity.length);
auto url = utf8_utf16_substr(text.text, entity.offset, entity.length);
string scheme = to_lower(url.substr(0, 4));
if (scheme == "ton:" || begins_with(scheme, "tg:") || scheme == "ftp:" || is_plain_domain(url)) {
continue;
@ -2126,7 +2126,7 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
auto type = nested_entities.back().type;
auto argument = std::move(nested_entities.back().argument);
UserId user_id;
int64 document_id = 0;
CustomEmojiId custom_emoji_id;
bool skip_entity = utf16_offset == nested_entities.back().entity_offset;
switch (type) {
case MessageEntity::Type::Bold:
@ -2192,7 +2192,7 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
return Status::Error(400, PSLICE()
<< "Can't find end of a custom emoji URL at byte offset " << url_begin_pos);
}
TRY_RESULT_ASSIGN(document_id, LinkManager::get_link_custom_emoji_document_id(url));
TRY_RESULT_ASSIGN(custom_emoji_id, LinkManager::get_link_custom_emoji_id(url));
break;
}
default:
@ -2205,8 +2205,8 @@ static Result<vector<MessageEntity>> do_parse_markdown_v2(CSlice text, string &r
auto entity_length = utf16_offset - entity_offset;
if (user_id.is_valid()) {
entities.emplace_back(entity_offset, entity_length, user_id);
} else if (document_id != 0) {
entities.emplace_back(type, entity_offset, entity_length, document_id);
} else if (custom_emoji_id.is_valid()) {
entities.emplace_back(type, entity_offset, entity_length, custom_emoji_id);
} else {
entities.emplace_back(type, entity_offset, entity_length, std::move(argument));
}
@ -3170,7 +3170,8 @@ static Result<vector<MessageEntity>> do_parse_html(CSlice text, string &result)
if (r_document_id.is_error() || r_document_id.ok() == 0) {
return Status::Error(400, "Invalid custom emoji identifier specified");
}
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity_offset, entity_length, r_document_id.ok());
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity_offset, entity_length,
CustomEmojiId(r_document_id.ok()));
} else if (tag_name == "a") {
auto url = std::move(nested_entities.back().argument);
if (url.empty()) {
@ -3308,8 +3309,8 @@ vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entiti
break;
case MessageEntity::Type::CustomEmoji:
if (layer >= static_cast<int32>(SecretChatLayer::SpoilerAndCustomEmojiEntities)) {
result.push_back(
make_tl_object<secret_api::messageEntityCustomEmoji>(entity.offset, entity.length, entity.document_id));
result.push_back(make_tl_object<secret_api::messageEntityCustomEmoji>(entity.offset, entity.length,
entity.custom_emoji_id.get()));
}
break;
default:
@ -3425,7 +3426,11 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
break;
case td_api::textEntityTypeCustomEmoji::ID: {
auto entity = static_cast<td_api::textEntityTypeCustomEmoji *>(input_entity->type_.get());
entities.emplace_back(MessageEntity::Type::CustomEmoji, offset, length, entity->custom_emoji_id_);
CustomEmojiId custom_emoji_id(entity->custom_emoji_id_);
if (!custom_emoji_id.is_valid()) {
return Status::Error(400, "Invalid custom emoji identifier specified");
}
entities.emplace_back(MessageEntity::Type::CustomEmoji, offset, length, custom_emoji_id);
break;
}
default:
@ -3564,7 +3569,8 @@ vector<MessageEntity> get_message_entities(const ContactsManager *contacts_manag
}
case telegram_api::messageEntityCustomEmoji::ID: {
auto entity = static_cast<const telegram_api::messageEntityCustomEmoji *>(server_entity.get());
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_, entity->document_id_);
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_,
CustomEmojiId(entity->document_id_));
break;
}
default:
@ -3580,7 +3586,7 @@ vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_a
constexpr size_t MAX_CUSTOM_EMOJI_ENTITIES = 100;
vector<MessageEntity> entities;
entities.reserve(secret_entities.size());
vector<int64> document_ids;
vector<CustomEmojiId> custom_emoji_ids;
for (auto &secret_entity : secret_entities) {
switch (secret_entity->get_id()) {
case secret_api::messageEntityUnknown::ID:
@ -3683,11 +3689,11 @@ vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_a
}
case secret_api::messageEntityCustomEmoji::ID: {
auto entity = static_cast<const secret_api::messageEntityCustomEmoji *>(secret_entity.get());
if (is_premium || !td->stickers_manager_->is_premium_custom_emoji(entity->document_id_, false)) {
if (document_ids.size() < MAX_CUSTOM_EMOJI_ENTITIES) {
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_,
entity->document_id_);
document_ids.push_back(entity->document_id_);
CustomEmojiId custom_emoji_id(entity->document_id_);
if (is_premium || !td->stickers_manager_->is_premium_custom_emoji(custom_emoji_id, false)) {
if (custom_emoji_ids.size() < MAX_CUSTOM_EMOJI_ENTITIES) {
entities.emplace_back(MessageEntity::Type::CustomEmoji, entity->offset_, entity->length_, custom_emoji_id);
custom_emoji_ids.push_back(custom_emoji_id);
}
}
break;
@ -3701,10 +3707,10 @@ vector<MessageEntity> get_message_entities(Td *td, vector<tl_object_ptr<secret_a
}
}
if (!document_ids.empty() && !is_premium) {
if (!custom_emoji_ids.empty() && !is_premium) {
// preload custom emoji to check that they aren't premium
td->stickers_manager_->get_custom_emoji_stickers(
std::move(document_ids), true,
std::move(custom_emoji_ids), true,
PromiseCreator::lambda(
[promise = load_data_multipromise.get_promise()](td_api::object_ptr<td_api::stickers> result) mutable {
promise.set_value(Unit());
@ -4236,8 +4242,6 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
// new whitespace-only entities could be added after splitting of entities
remove_invalid_entities(text, entities);
// TODO MAX_MESSAGE_LENGTH and MAX_CAPTION_LENGTH
return Status::OK();
}
@ -4425,8 +4429,8 @@ vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(co
break;
}
case MessageEntity::Type::CustomEmoji:
result.push_back(
make_tl_object<telegram_api::messageEntityCustomEmoji>(entity.offset, entity.length, entity.document_id));
result.push_back(make_tl_object<telegram_api::messageEntityCustomEmoji>(entity.offset, entity.length,
entity.custom_emoji_id.get()));
break;
default:
UNREACHABLE();
@ -4470,7 +4474,7 @@ vector<tl_object_ptr<telegram_api::MessageEntity>> get_input_message_entities(co
void remove_premium_custom_emoji_entities(const Td *td, vector<MessageEntity> &entities, bool remove_unknown) {
td::remove_if(entities, [&](const MessageEntity &entity) {
return entity.type == MessageEntity::Type::CustomEmoji &&
td->stickers_manager_->is_premium_custom_emoji(entity.document_id, remove_unknown);
td->stickers_manager_->is_premium_custom_emoji(entity.custom_emoji_id, remove_unknown);
});
}

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/CustomEmojiId.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/secret_api.h"
#include "td/telegram/td_api.h"
@ -60,7 +61,7 @@ class MessageEntity {
int32 media_timestamp = -1;
string argument;
UserId user_id;
int64 document_id = 0;
CustomEmojiId custom_emoji_id;
MessageEntity() = default;
@ -74,8 +75,8 @@ class MessageEntity {
: type(type), offset(offset), length(length), media_timestamp(media_timestamp) {
CHECK(type == Type::MediaTimestamp);
}
MessageEntity(Type type, int32 offset, int32 length, int64 document_id)
: type(type), offset(offset), length(length), document_id(document_id) {
MessageEntity(Type type, int32 offset, int32 length, CustomEmojiId custom_emoji_id)
: type(type), offset(offset), length(length), custom_emoji_id(custom_emoji_id) {
CHECK(type == Type::CustomEmoji);
}
@ -84,7 +85,7 @@ class MessageEntity {
bool operator==(const MessageEntity &other) const {
return offset == other.offset && length == other.length && type == other.type &&
media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id &&
document_id == other.document_id;
custom_emoji_id == other.custom_emoji_id;
}
bool operator<(const MessageEntity &other) const {

View File

@ -28,7 +28,7 @@ void MessageEntity::store(StorerT &storer) const {
store(media_timestamp, storer);
}
if (type == Type::CustomEmoji) {
store(document_id, storer);
store(custom_emoji_id, storer);
}
}
@ -48,7 +48,7 @@ void MessageEntity::parse(ParserT &parser) {
parse(media_timestamp, parser);
}
if (type == Type::CustomEmoji) {
parse(document_id, parser);
parse(custom_emoji_id, parser);
}
}

View File

@ -0,0 +1,328 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/MessageExtendedMedia.h"
#include "td/telegram/Document.h"
#include "td/telegram/DocumentsManager.h"
#include "td/telegram/MessageContent.h"
#include "td/telegram/MessageContentType.h"
#include "td/telegram/PhotoSize.h"
#include "td/telegram/Td.h"
#include "td/telegram/VideosManager.h"
#include "td/utils/algorithm.h"
#include "td/utils/logging.h"
namespace td {
MessageExtendedMedia::MessageExtendedMedia(
Td *td, telegram_api::object_ptr<telegram_api::MessageExtendedMedia> &&extended_media, FormattedText &&caption,
DialogId owner_dialog_id) {
if (extended_media == nullptr) {
return;
}
caption_ = std::move(caption);
switch (extended_media->get_id()) {
case telegram_api::messageExtendedMediaPreview::ID: {
auto media = move_tl_object_as<telegram_api::messageExtendedMediaPreview>(extended_media);
type_ = Type::Preview;
duration_ = media->video_duration_;
dimensions_ = get_dimensions(media->w_, media->h_, "MessageExtendedMedia");
if (media->thumb_ != nullptr) {
if (media->thumb_->get_id() == telegram_api::photoStrippedSize::ID) {
auto thumb = move_tl_object_as<telegram_api::photoStrippedSize>(media->thumb_);
minithumbnail_ = thumb->bytes_.as_slice().str();
} else {
LOG(ERROR) << "Receive " << to_string(media->thumb_);
}
}
break;
}
case telegram_api::messageExtendedMedia::ID: {
auto media = move_tl_object_as<telegram_api::messageExtendedMedia>(extended_media);
type_ = Type::Unsupported;
switch (media->media_->get_id()) {
case telegram_api::messageMediaPhoto::ID: {
auto photo = move_tl_object_as<telegram_api::messageMediaPhoto>(media->media_);
if (photo->photo_ == nullptr) {
break;
}
photo_ = get_photo(td->file_manager_.get(), std::move(photo->photo_), owner_dialog_id);
if (photo_.is_empty()) {
break;
}
type_ = Type::Photo;
break;
}
case telegram_api::messageMediaDocument::ID: {
auto document = move_tl_object_as<telegram_api::messageMediaDocument>(media->media_);
if (document->document_ == nullptr) {
break;
}
auto document_ptr = std::move(document->document_);
int32 document_id = document_ptr->get_id();
if (document_id == telegram_api::documentEmpty::ID) {
break;
}
CHECK(document_id == telegram_api::document::ID);
auto parsed_document = td->documents_manager_->on_get_document(
move_tl_object_as<telegram_api::document>(document_ptr), owner_dialog_id, nullptr);
if (parsed_document.empty() || parsed_document.type != Document::Type::Video) {
break;
}
CHECK(parsed_document.file_id.is_valid());
video_file_id_ = parsed_document.file_id;
type_ = Type::Video;
break;
}
default:
break;
}
if (type_ == Type::Unsupported) {
unsupported_version_ = CURRENT_VERSION;
}
break;
}
default:
UNREACHABLE();
}
}
Result<MessageExtendedMedia> MessageExtendedMedia::get_message_extended_media(
Td *td, td_api::object_ptr<td_api::InputMessageContent> &&extended_media_content, DialogId owner_dialog_id,
bool is_premium) {
if (extended_media_content == nullptr) {
return MessageExtendedMedia();
}
if (!owner_dialog_id.is_valid()) {
return Status::Error(400, "Extended media can't be added to the invoice");
}
auto input_content_type = extended_media_content->get_id();
if (input_content_type != td_api::inputMessagePhoto::ID && input_content_type != td_api::inputMessageVideo::ID) {
return Status::Error("Invalid extended media content specified");
}
TRY_RESULT(input_message_content,
get_input_message_content(owner_dialog_id, std::move(extended_media_content), td, is_premium));
if (input_message_content.ttl != 0) {
return Status::Error("Can't use self-destructing extended media");
}
auto content = input_message_content.content.get();
auto content_type = content->get_type();
MessageExtendedMedia result;
CHECK(content_type == MessageContentType::Photo || content_type == MessageContentType::Video);
result.caption_ = *get_message_content_caption(content);
if (content_type == MessageContentType::Photo) {
result.type_ = Type::Photo;
result.photo_ = *get_message_content_photo(content);
} else {
CHECK(content_type == MessageContentType::Video);
result.type_ = Type::Video;
result.video_file_id_ = get_message_content_upload_file_id(content);
}
return result;
}
void MessageExtendedMedia::update_from(const MessageExtendedMedia &old_extended_media) {
if (!is_media() && old_extended_media.is_media()) {
*this = old_extended_media;
}
}
bool MessageExtendedMedia::update_to(Td *td,
telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media_ptr,
DialogId owner_dialog_id) {
MessageExtendedMedia new_extended_media(td, std::move(extended_media_ptr), FormattedText(caption_), owner_dialog_id);
if (!new_extended_media.is_media() && is_media()) {
return false;
}
if (*this != new_extended_media || is_equal_but_different(new_extended_media)) {
*this = std::move(new_extended_media);
return true;
}
return false;
}
td_api::object_ptr<td_api::MessageExtendedMedia> MessageExtendedMedia::get_message_extended_media_object(
Td *td, bool skip_bot_commands, int32 max_media_timestamp) const {
if (type_ == Type::Empty) {
return nullptr;
}
auto caption = get_formatted_text_object(caption_, skip_bot_commands, max_media_timestamp);
switch (type_) {
case Type::Unsupported:
return td_api::make_object<td_api::messageExtendedMediaUnsupported>(std::move(caption));
case Type::Preview:
return td_api::make_object<td_api::messageExtendedMediaPreview>(dimensions_.width, dimensions_.height, duration_,
get_minithumbnail_object(minithumbnail_),
std::move(caption));
case Type::Photo: {
auto photo = get_photo_object(td->file_manager_.get(), photo_);
CHECK(photo != nullptr);
return td_api::make_object<td_api::messageExtendedMediaPhoto>(std::move(photo), std::move(caption));
}
case Type::Video:
return td_api::make_object<td_api::messageExtendedMediaVideo>(
td->videos_manager_->get_video_object(video_file_id_), std::move(caption));
default:
UNREACHABLE();
return nullptr;
}
}
void MessageExtendedMedia::append_file_ids(const Td *td, vector<FileId> &file_ids) const {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
append(file_ids, photo_get_file_ids(photo_));
break;
case Type::Video:
Document(Document::Type::Video, video_file_id_).append_file_ids(td, file_ids);
break;
default:
UNREACHABLE();
break;
}
}
void MessageExtendedMedia::delete_thumbnail(Td *td) {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
photo_delete_thumbnail(photo_);
break;
case Type::Video:
td->videos_manager_->delete_video_thumbnail(video_file_id_);
break;
default:
UNREACHABLE();
break;
}
}
int32 MessageExtendedMedia::get_duration(const Td *td) const {
if (!has_media_timestamp()) {
return 0;
}
return td->videos_manager_->get_video_duration(video_file_id_);
}
FileId MessageExtendedMedia::get_upload_file_id() const {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
return get_photo_upload_file_id(photo_);
case Type::Video:
return video_file_id_;
default:
UNREACHABLE();
break;
}
return FileId();
}
FileId MessageExtendedMedia::get_any_file_id() const {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
return get_photo_any_file_id(photo_);
case Type::Video:
return video_file_id_;
default:
UNREACHABLE();
break;
}
return FileId();
}
FileId MessageExtendedMedia::get_thumbnail_file_id(const Td *td) const {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
return get_photo_thumbnail_file_id(photo_);
case Type::Video:
return td->videos_manager_->get_video_thumbnail_file_id(video_file_id_);
default:
UNREACHABLE();
break;
}
return FileId();
}
telegram_api::object_ptr<telegram_api::InputMedia> MessageExtendedMedia::get_input_media(
Td *td, tl_object_ptr<telegram_api::InputFile> input_file,
tl_object_ptr<telegram_api::InputFile> input_thumbnail) const {
switch (type_) {
case Type::Empty:
case Type::Unsupported:
case Type::Preview:
break;
case Type::Photo:
return photo_get_input_media(td->file_manager_.get(), photo_, std::move(input_file), 0);
case Type::Video:
return td->videos_manager_->get_input_media(video_file_id_, std::move(input_file), std::move(input_thumbnail), 0);
default:
UNREACHABLE();
break;
}
return nullptr;
}
bool MessageExtendedMedia::is_equal_but_different(const MessageExtendedMedia &other) const {
return type_ == Type::Unsupported && other.type_ == Type::Unsupported &&
unsupported_version_ != other.unsupported_version_;
}
bool operator==(const MessageExtendedMedia &lhs, const MessageExtendedMedia &rhs) {
if (lhs.type_ != rhs.type_ || lhs.caption_ != rhs.caption_) {
return false;
}
switch (lhs.type_) {
case MessageExtendedMedia::Type::Empty:
return true;
case MessageExtendedMedia::Type::Unsupported:
// don't compare unsupported_version_
return true;
case MessageExtendedMedia::Type::Preview:
return lhs.duration_ == rhs.duration_ && lhs.dimensions_ == rhs.dimensions_ &&
lhs.minithumbnail_ == rhs.minithumbnail_;
case MessageExtendedMedia::Type::Photo:
return lhs.photo_ == rhs.photo_;
case MessageExtendedMedia::Type::Video:
return lhs.video_file_id_ == rhs.video_file_id_;
default:
UNREACHABLE();
return true;
}
}
bool operator!=(const MessageExtendedMedia &lhs, const MessageExtendedMedia &rhs) {
return !(lhs == rhs);
}
} // namespace td

View File

@ -0,0 +1,118 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/Dimensions.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/Photo.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
class Td;
class MessageExtendedMedia {
enum class Type : int32 { Empty, Unsupported, Preview, Photo, Video };
Type type_ = Type::Empty;
FormattedText caption_;
static constexpr int32 CURRENT_VERSION = 1;
// for Unsupported
int32 unsupported_version_ = 0;
// for Preview
int32 duration_ = 0;
Dimensions dimensions_;
string minithumbnail_;
// for Photo
Photo photo_;
// for Video
FileId video_file_id_;
friend bool operator==(const MessageExtendedMedia &lhs, const MessageExtendedMedia &rhs);
bool is_media() const {
return type_ != Type::Empty && type_ != Type::Preview;
}
public:
MessageExtendedMedia() = default;
MessageExtendedMedia(Td *td, telegram_api::object_ptr<telegram_api::MessageExtendedMedia> &&extended_media,
FormattedText &&caption, DialogId owner_dialog_id);
static Result<MessageExtendedMedia> get_message_extended_media(
Td *td, td_api::object_ptr<td_api::InputMessageContent> &&extended_media_content, DialogId owner_dialog_id,
bool is_premium);
bool is_empty() const {
return type_ == Type::Empty;
}
void update_from(const MessageExtendedMedia &old_extended_media);
bool update_to(Td *td, telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media_ptr,
DialogId owner_dialog_id);
td_api::object_ptr<td_api::MessageExtendedMedia> get_message_extended_media_object(Td *td, bool skip_bot_commands,
int32 max_media_timestamp) const;
void append_file_ids(const Td *td, vector<FileId> &file_ids) const;
void delete_thumbnail(Td *td);
bool need_reget() const {
return type_ == Type::Unsupported && unsupported_version_ < CURRENT_VERSION;
}
bool need_poll() const {
return type_ == Type::Preview;
}
bool has_media_timestamp() const {
return type_ == Type::Video;
}
bool is_equal_but_different(const MessageExtendedMedia &other) const;
int32 get_duration(const Td *td) const;
const FormattedText *get_caption() const {
return &caption_;
}
FileId get_upload_file_id() const;
FileId get_any_file_id() const;
FileId get_thumbnail_file_id(const Td *td) const;
telegram_api::object_ptr<telegram_api::InputMedia> get_input_media(
Td *td, tl_object_ptr<telegram_api::InputFile> input_file,
tl_object_ptr<telegram_api::InputFile> input_thumbnail) const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
bool operator==(const MessageExtendedMedia &lhs, const MessageExtendedMedia &rhs);
bool operator!=(const MessageExtendedMedia &lhs, const MessageExtendedMedia &rhs);
} // namespace td

View File

@ -0,0 +1,115 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/MessageExtendedMedia.h"
#include "td/telegram/Photo.hpp"
#include "td/telegram/Td.h"
#include "td/telegram/VideosManager.h"
#include "td/utils/logging.h"
#include "td/utils/tl_helpers.h"
namespace td {
template <class StorerT>
void MessageExtendedMedia::store(StorerT &storer) const {
bool has_caption = !caption_.text.empty();
bool has_unsupported_version = unsupported_version_ != 0;
bool has_duration = duration_ != 0;
bool has_dimensions = dimensions_.width != 0 || dimensions_.height != 0;
bool has_minithumbnail = !minithumbnail_.empty();
bool has_photo = !photo_.is_empty();
bool has_video = video_file_id_.is_valid();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_caption);
STORE_FLAG(has_unsupported_version);
STORE_FLAG(has_duration);
STORE_FLAG(has_dimensions);
STORE_FLAG(has_minithumbnail);
STORE_FLAG(has_photo);
STORE_FLAG(has_video);
END_STORE_FLAGS();
td::store(type_, storer);
if (has_caption) {
td::store(caption_, storer);
}
if (has_unsupported_version) {
td::store(unsupported_version_, storer);
}
if (has_duration) {
td::store(duration_, storer);
}
if (has_dimensions) {
td::store(dimensions_, storer);
}
if (has_minithumbnail) {
td::store(minithumbnail_, storer);
}
if (has_photo) {
td::store(photo_, storer);
}
if (has_video) {
Td *td = storer.context()->td().get_actor_unsafe();
td->videos_manager_->store_video(video_file_id_, storer);
}
}
template <class ParserT>
void MessageExtendedMedia::parse(ParserT &parser) {
bool has_caption;
bool has_unsupported_version;
bool has_duration;
bool has_dimensions;
bool has_minithumbnail;
bool has_photo;
bool has_video;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_caption);
PARSE_FLAG(has_unsupported_version);
PARSE_FLAG(has_duration);
PARSE_FLAG(has_dimensions);
PARSE_FLAG(has_minithumbnail);
PARSE_FLAG(has_photo);
PARSE_FLAG(has_video);
END_PARSE_FLAGS();
td::parse(type_, parser);
if (has_caption) {
td::parse(caption_, parser);
}
if (has_unsupported_version) {
td::parse(unsupported_version_, parser);
}
if (has_duration) {
td::parse(duration_, parser);
}
if (has_dimensions) {
td::parse(dimensions_, parser);
}
if (has_minithumbnail) {
td::parse(minithumbnail_, parser);
}
bool is_bad = false;
if (has_photo) {
td::parse(photo_, parser);
is_bad = photo_.is_bad();
}
if (has_video) {
Td *td = parser.context()->td().get_actor_unsafe();
video_file_id_ = td->videos_manager_->parse_video(parser);
is_bad = !video_file_id_.is_valid();
}
if (is_bad) {
LOG(ERROR) << "Failed to parse MessageExtendedMedia";
photo_ = Photo();
video_file_id_ = FileId();
type_ = Type::Unsupported;
unsupported_version_ = 0;
}
}
} // namespace td

View File

@ -23,6 +23,8 @@ struct MessageLinkInfo {
bool is_single = false;
int32 media_timestamp = 0;
MessageId top_thread_message_id;
DialogId comment_dialog_id;
MessageId comment_message_id;
bool for_comment = false;

View File

@ -9,6 +9,7 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/Global.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/MessagesManager.h"
@ -29,6 +30,8 @@
#include "td/utils/emoji.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/utf8.h"
@ -219,6 +222,9 @@ class SendReactionQuery final : public Td::ResultHandler {
}
void on_error(Status status) final {
if (status.message() == "MESSAGE_NOT_MODIFIED") {
return promise_.set_value(Unit());
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendReactionQuery");
promise_.set_error(std::move(status));
}
@ -550,19 +556,19 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
if (dialog_type == DialogType::User) {
auto user_id = dialog_id.get_user_id();
if (!td->contacts_manager_->have_min_user(user_id)) {
LOG(ERROR) << "Have no info about " << user_id;
LOG(ERROR) << "Receive unknown " << user_id;
continue;
}
} else if (dialog_type == DialogType::Channel) {
auto channel_id = dialog_id.get_channel_id();
auto min_channel = td->contacts_manager_->get_min_channel(channel_id);
if (min_channel == nullptr) {
LOG(ERROR) << "Have no info about reacted " << channel_id;
LOG(ERROR) << "Receive unknown reacted " << channel_id;
continue;
}
recent_chooser_min_channels.emplace_back(channel_id, *min_channel);
} else {
LOG(ERROR) << "Have no info about reacted " << dialog_id;
LOG(ERROR) << "Receive unknown reacted " << dialog_id;
continue;
}
}
@ -762,6 +768,69 @@ vector<string> MessageReactions::get_chosen_reactions() const {
return reaction_order;
}
bool MessageReactions::are_consistent_with_list(const string &reaction, FlatHashMap<string, vector<DialogId>> reactions,
int32 total_count) const {
auto are_consistent = [](const vector<DialogId> &lhs, const vector<DialogId> &rhs) {
size_t i = 0;
size_t max_i = td::min(lhs.size(), rhs.size());
while (i < max_i && lhs[i] == rhs[i]) {
i++;
}
return i == max_i;
};
if (reaction.empty()) {
// received list and total_count for all reactions
int32 old_total_count = 0;
for (const auto &message_reaction : reactions_) {
CHECK(!message_reaction.get_reaction().empty());
if (!are_consistent(reactions[message_reaction.get_reaction()],
message_reaction.get_recent_chooser_dialog_ids())) {
return false;
}
old_total_count += message_reaction.get_choose_count();
reactions.erase(message_reaction.get_reaction());
}
return old_total_count == total_count && reactions.empty();
}
// received list and total_count for a single reaction
const auto *message_reaction = get_reaction(reaction);
if (message_reaction == nullptr) {
return reactions.count(reaction) == 0 && total_count == 0;
} else {
return are_consistent(reactions[reaction], message_reaction->get_recent_chooser_dialog_ids()) &&
message_reaction->get_choose_count() == total_count;
}
}
vector<td_api::object_ptr<td_api::messageReaction>> MessageReactions::get_message_reactions_object(
Td *td, UserId my_user_id, UserId peer_user_id) const {
return transform(reactions_, [td, my_user_id, peer_user_id](const MessageReaction &reaction) {
return reaction.get_message_reaction_object(td, my_user_id, peer_user_id);
});
}
void MessageReactions::add_min_channels(Td *td) const {
for (const auto &reaction : reactions_) {
for (const auto &recent_chooser_min_channel : reaction.get_recent_chooser_min_channels()) {
LOG(INFO) << "Add min reacted " << recent_chooser_min_channel.first;
td->contacts_manager_->add_min_channel(recent_chooser_min_channel.first, recent_chooser_min_channel.second);
}
}
}
void MessageReactions::add_dependencies(Dependencies &dependencies) const {
for (const auto &reaction : reactions_) {
const auto &dialog_ids = reaction.get_recent_chooser_dialog_ids();
for (auto dialog_id : dialog_ids) {
// don't load the dialog itself
// it will be created in get_message_reaction_object if needed
dependencies.add_dialog_dependencies(dialog_id);
}
}
}
bool MessageReactions::need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions) {
if (old_reactions == nullptr) {

View File

@ -13,6 +13,7 @@
#include "td/telegram/MinChannel.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/FlatHashMap.h"
@ -23,9 +24,15 @@
namespace td {
class Dependencies;
class Td;
class MessageReaction {
static constexpr int32 MAX_CHOOSE_COUNT = 2147483640;
static constexpr size_t MAX_RECENT_CHOOSERS = 3;
string reaction_;
int32 choose_count_ = 0;
bool is_chosen_ = false;
@ -63,16 +70,6 @@ class MessageReaction {
void update_recent_chooser_dialog_ids(const MessageReaction &old_reaction);
public:
static constexpr size_t MAX_RECENT_CHOOSERS = 3;
static constexpr int32 MAX_CHOOSE_COUNT = 2147483640;
MessageReaction() = default;
const string &get_reaction() const {
return reaction_;
}
int32 get_choose_count() const {
return choose_count_;
}
@ -88,6 +85,13 @@ class MessageReaction {
td_api::object_ptr<td_api::messageReaction> get_message_reaction_object(Td *td, UserId my_user_id,
UserId peer_user_id) const;
public:
MessageReaction() = default;
const string &get_reaction() const {
return reaction_;
}
template <class StorerT>
void store(StorerT &storer) const;
@ -166,6 +170,16 @@ struct MessageReactions {
vector<string> get_chosen_reactions() const;
bool are_consistent_with_list(const string &reaction, FlatHashMap<string, vector<DialogId>> reactions,
int32 total_count) const;
vector<td_api::object_ptr<td_api::messageReaction>> get_message_reactions_object(Td *td, UserId my_user_id,
UserId peer_user_id) const;
void add_min_channels(Td *td) const;
void add_dependencies(Dependencies &dependencies) const;
static bool need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions);

View File

@ -0,0 +1,62 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/MessageReplyHeader.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/ScheduledServerMessageId.h"
#include "td/telegram/ServerMessageId.h"
#include "td/utils/logging.h"
namespace td {
MessageReplyHeader::MessageReplyHeader(tl_object_ptr<telegram_api::messageReplyHeader> &&reply_header,
DialogId dialog_id, MessageId message_id, int32 date, bool can_have_thread) {
if (reply_header == nullptr) {
return;
}
if (reply_header->reply_to_scheduled_) {
reply_to_message_id = MessageId(ScheduledServerMessageId(reply_header->reply_to_msg_id_), date);
if (message_id.is_scheduled()) {
auto reply_to_peer_id = std::move(reply_header->reply_to_peer_id_);
if (reply_to_peer_id != nullptr) {
reply_in_dialog_id = DialogId(reply_to_peer_id);
LOG(ERROR) << "Receive reply to " << FullMessageId{reply_in_dialog_id, reply_to_message_id} << " in "
<< FullMessageId{dialog_id, message_id};
reply_to_message_id = MessageId();
reply_in_dialog_id = DialogId();
}
} else {
LOG(ERROR) << "Receive reply to " << reply_to_message_id << " in " << FullMessageId{dialog_id, message_id};
reply_to_message_id = MessageId();
}
} else {
reply_to_message_id = MessageId(ServerMessageId(reply_header->reply_to_msg_id_));
auto reply_to_peer_id = std::move(reply_header->reply_to_peer_id_);
if (reply_to_peer_id != nullptr) {
reply_in_dialog_id = DialogId(reply_to_peer_id);
if (!reply_in_dialog_id.is_valid()) {
LOG(ERROR) << "Receive reply in invalid " << to_string(reply_to_peer_id);
reply_to_message_id = MessageId();
reply_in_dialog_id = DialogId();
}
if (reply_in_dialog_id == dialog_id) {
reply_in_dialog_id = DialogId(); // just in case
}
}
if (reply_to_message_id.is_valid() && !message_id.is_scheduled() && !reply_in_dialog_id.is_valid()) {
if ((reply_header->flags_ & telegram_api::messageReplyHeader::REPLY_TO_TOP_ID_MASK) != 0) {
top_thread_message_id = MessageId(ServerMessageId(reply_header->reply_to_top_id_));
} else if (can_have_thread) {
top_thread_message_id = reply_to_message_id;
}
is_topic_message = reply_header->forum_topic_;
}
}
}
} // namespace td

View File

@ -0,0 +1,29 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
namespace td {
struct MessageReplyHeader {
MessageId reply_to_message_id;
DialogId reply_in_dialog_id;
MessageId top_thread_message_id;
bool is_topic_message = false;
MessageReplyHeader() = default;
MessageReplyHeader(tl_object_ptr<telegram_api::messageReplyHeader> &&reply_header, DialogId dialog_id,
MessageId message_id, int32 date, bool can_have_thread);
};
} // namespace td

View File

@ -19,13 +19,17 @@
namespace td {
MessageReplyInfo::MessageReplyInfo(Td *td, tl_object_ptr<telegram_api::messageReplies> &&reply_info, bool is_bot) {
if (reply_info == nullptr || is_bot || reply_info->channel_id_ == 777) {
if (reply_info == nullptr) {
return;
}
if (reply_info->replies_ < 0) {
LOG(ERROR) << "Receive wrong " << to_string(reply_info);
return;
}
if (is_bot || reply_info->channel_id_ == 777) {
is_dropped = true;
return;
}
reply_count = reply_info->replies_;
pts = reply_info->replies_pts_;
@ -56,19 +60,19 @@ MessageReplyInfo::MessageReplyInfo(Td *td, tl_object_ptr<telegram_api::messageRe
if (dialog_type == DialogType::User) {
auto replier_user_id = dialog_id.get_user_id();
if (!td->contacts_manager_->have_min_user(replier_user_id)) {
LOG(ERROR) << "Have no info about replied " << replier_user_id;
LOG(ERROR) << "Receive unknown replied " << replier_user_id;
continue;
}
} else if (dialog_type == DialogType::Channel) {
auto replier_channel_id = dialog_id.get_channel_id();
auto min_channel = td->contacts_manager_->get_min_channel(replier_channel_id);
if (min_channel == nullptr) {
LOG(ERROR) << "Have no info about replied " << replier_channel_id;
LOG(ERROR) << "Receive unknown replied " << replier_channel_id;
continue;
}
replier_min_channels.emplace_back(replier_channel_id, *min_channel);
} else {
LOG(ERROR) << "Have no info about replied " << dialog_id;
LOG(ERROR) << "Receive unknown replied " << dialog_id;
continue;
}
}

View File

@ -32,6 +32,7 @@ struct MessageReplyInfo {
MessageId last_read_inbox_message_id;
MessageId last_read_outbox_message_id;
bool is_comment = false;
bool is_dropped = false;
static constexpr size_t MAX_RECENT_REPLIERS = 3;
@ -43,6 +44,10 @@ struct MessageReplyInfo {
return reply_count < 0;
}
bool was_dropped() const {
return is_dropped;
}
bool need_update_to(const MessageReplyInfo &other) const;
bool update_max_message_ids(MessageId other_max_message_id, MessageId other_last_read_inbox_message_id,

View File

@ -94,7 +94,7 @@ vector<DialogId> get_message_sender_dialog_ids(Td *td,
}
if (dialog_id.get_type() == DialogType::User) {
if (!td->contacts_manager_->have_user(dialog_id.get_user_id())) {
LOG(ERROR) << "Have no info about " << dialog_id.get_user_id();
LOG(ERROR) << "Receive unknown " << dialog_id.get_user_id();
continue;
}
} else {

View File

@ -7,6 +7,7 @@
#include "td/telegram/MessagesDb.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Version.h"
#include "td/db/SqliteConnectionSafe.h"

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogListId.h"
#include "td/telegram/DialogLocation.h"
#include "td/telegram/DialogNotificationSettings.h"
#include "td/telegram/DialogParticipant.h"
#include "td/telegram/DialogSource.h"
#include "td/telegram/EncryptedFile.h"
@ -32,6 +33,7 @@
#include "td/telegram/MessageCopyOptions.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/MessageLinkInfo.h"
#include "td/telegram/MessageReplyHeader.h"
#include "td/telegram/MessageReplyInfo.h"
#include "td/telegram/MessagesDb.h"
#include "td/telegram/MessageSearchFilter.h"
@ -44,7 +46,7 @@
#include "td/telegram/NotificationGroupKey.h"
#include "td/telegram/NotificationGroupType.h"
#include "td/telegram/NotificationId.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/NotificationSettingsScope.h"
#include "td/telegram/Photo.h"
#include "td/telegram/RecentDialogList.h"
#include "td/telegram/ReplyMarkup.h"
@ -58,6 +60,7 @@
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/telegram/Usernames.h"
#include "td/actor/actor.h"
#include "td/actor/MultiPromise.h"
@ -139,7 +142,7 @@ class MessagesManager final : public Actor {
static constexpr int32 SEND_MESSAGE_FLAG_FROM_BACKGROUND = 1 << 6;
static constexpr int32 SEND_MESSAGE_FLAG_CLEAR_DRAFT = 1 << 7;
static constexpr int32 SEND_MESSAGE_FLAG_WITH_MY_SCORE = 1 << 8;
static constexpr int32 SEND_MESSAGE_FLAG_GROUP_MEDIA = 1 << 9;
static constexpr int32 SEND_MESSAGE_FLAG_IS_FROM_THREAD = 1 << 9;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE = 1 << 10;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_MESSAGE = 1 << 11;
static constexpr int32 SEND_MESSAGE_FLAG_HAS_SEND_AS = 1 << 13;
@ -254,16 +257,16 @@ class MessagesManager final : public Actor {
bool is_channel_message, bool is_scheduled, bool have_previous, bool have_next,
const char *source);
void open_secret_message(SecretChatId secret_chat_id, int64 random_id, Promise<>);
void open_secret_message(SecretChatId secret_chat_id, int64 random_id, Promise<Unit>);
void on_send_secret_message_success(int64 random_id, MessageId message_id, int32 date, unique_ptr<EncryptedFile> file,
Promise<> promise);
void on_send_secret_message_error(int64 random_id, Status error, Promise<> promise);
Promise<Unit> promise);
void on_send_secret_message_error(int64 random_id, Status error, Promise<Unit> promise);
void delete_secret_messages(SecretChatId secret_chat_id, std::vector<int64> random_ids, Promise<> promise);
void delete_secret_messages(SecretChatId secret_chat_id, std::vector<int64> random_ids, Promise<Unit> promise);
void delete_secret_chat_history(SecretChatId secret_chat_id, bool remove_from_dialog_list, MessageId last_message_id,
Promise<> promise);
Promise<Unit> promise);
void read_secret_chat_outbox(SecretChatId secret_chat_id, int32 up_to_date, int32 read_date);
@ -271,13 +274,13 @@ class MessagesManager final : public Actor {
void on_get_secret_message(SecretChatId secret_chat_id, UserId user_id, MessageId message_id, int32 date,
unique_ptr<EncryptedFile> file, tl_object_ptr<secret_api::decryptedMessage> message,
Promise<> promise);
Promise<Unit> promise);
void on_secret_chat_screenshot_taken(SecretChatId secret_chat_id, UserId user_id, MessageId message_id, int32 date,
int64 random_id, Promise<> promise);
int64 random_id, Promise<Unit> promise);
void on_secret_chat_ttl_changed(SecretChatId secret_chat_id, UserId user_id, MessageId message_id, int32 date,
int32 ttl, int64 random_id, Promise<> promise);
int32 ttl, int64 random_id, Promise<Unit> promise);
void on_update_sent_text_message(int64 random_id, tl_object_ptr<telegram_api::MessageMedia> message_media,
vector<tl_object_ptr<telegram_api::MessageEntity>> &&entities);
@ -293,7 +296,8 @@ class MessagesManager final : public Actor {
bool on_update_message_id(int64 random_id, MessageId new_message_id, const string &source);
void on_update_dialog_draft_message(DialogId dialog_id, tl_object_ptr<telegram_api::DraftMessage> &&draft_message);
void on_update_dialog_draft_message(DialogId dialog_id, MessageId top_thread_message_id,
tl_object_ptr<telegram_api::DraftMessage> &&draft_message);
void on_update_dialog_is_pinned(FolderId folder_id, DialogId dialog_id, bool is_pinned);
@ -366,6 +370,9 @@ class MessagesManager final : public Actor {
void on_update_some_live_location_viewed(Promise<Unit> &&promise);
void on_update_message_extended_media(FullMessageId full_message_id,
telegram_api::object_ptr<telegram_api::MessageExtendedMedia> extended_media);
void on_external_update_message_content(FullMessageId full_message_id);
void on_update_message_content(FullMessageId full_message_id);
@ -397,6 +404,8 @@ class MessagesManager final : public Actor {
void delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke, Promise<Unit> &&promise);
void delete_topic_history(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
void delete_all_call_messages(bool revoke, Promise<Unit> &&promise);
void delete_dialog_messages_by_sender(DialogId dialog_id, DialogId sender_dialog_id, Promise<Unit> &&promise);
@ -408,9 +417,9 @@ class MessagesManager final : public Actor {
void on_update_dialog_group_call_rights(DialogId dialog_id);
void read_all_dialog_mentions(DialogId dialog_id, Promise<Unit> &&promise);
void read_all_dialog_mentions(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
void read_all_dialog_reactions(DialogId dialog_id, Promise<Unit> &&promise);
void read_all_dialog_reactions(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
Status add_recently_found_dialog(DialogId dialog_id) TD_WARN_UNUSED_RESULT;
@ -427,7 +436,7 @@ class MessagesManager final : public Actor {
void reload_voice_chat_on_search(const string &username);
void get_dialog_send_message_as_dialog_ids(DialogId dialog_id,
Promise<td_api::object_ptr<td_api::messageSenders>> &&promise,
Promise<td_api::object_ptr<td_api::chatMessageSenders>> &&promise,
bool is_recursive = false);
void set_dialog_default_send_message_as_dialog_id(DialogId dialog_id, DialogId message_sender_dialog_id,
@ -457,8 +466,8 @@ class MessagesManager final : public Actor {
int64 query_id, const string &result_id,
bool hide_via_bot) TD_WARN_UNUSED_RESULT;
Result<td_api::object_ptr<td_api::messages>> forward_messages(DialogId to_dialog_id, DialogId from_dialog_id,
vector<MessageId> message_ids,
Result<td_api::object_ptr<td_api::messages>> forward_messages(DialogId to_dialog_id, MessageId top_thread_message_id,
DialogId from_dialog_id, vector<MessageId> message_ids,
tl_object_ptr<td_api::messageSendOptions> &&options,
bool in_game_share,
vector<MessageCopyOptions> &&copy_options,
@ -557,7 +566,7 @@ class MessagesManager final : public Actor {
void pin_dialog_message(DialogId dialog_id, MessageId message_id, bool disable_notification, bool only_for_self,
bool is_unpin, Promise<Unit> &&promise);
void unpin_all_dialog_messages(DialogId dialog_id, Promise<Unit> &&promise);
void unpin_all_dialog_messages(DialogId dialog_id, MessageId top_thread_message_id, Promise<Unit> &&promise);
void get_dialog_info_full(DialogId dialog_id, Promise<Unit> &&promise, const char *source);
@ -645,12 +654,16 @@ class MessagesManager final : public Actor {
void translate_text(const string &text, const string &from_language_code, const string &to_language_code,
Promise<td_api::object_ptr<td_api::text>> &&promise);
void recognize_speech(FullMessageId full_message_id, Promise<Unit> &&promise);
void rate_speech_recognition(FullMessageId full_message_id, bool is_good, Promise<Unit> &&promise);
bool is_message_edited_recently(FullMessageId full_message_id, int32 seconds);
bool is_deleted_secret_chat(DialogId dialog_id) const;
Result<std::pair<string, bool>> get_message_link(FullMessageId full_message_id, int32 media_timestamp, bool for_group,
bool for_comment);
bool in_message_thread);
string get_message_embedding_code(FullMessageId full_message_id, bool for_group, Promise<Unit> &&promise);
@ -715,6 +728,8 @@ class MessagesManager final : public Actor {
void finish_get_message_views(DialogId dialog_id, const vector<MessageId> &message_ids);
void finish_get_message_extended_media(DialogId dialog_id, const vector<MessageId> &message_ids);
Status open_message_content(FullMessageId full_message_id) TD_WARN_UNUSED_RESULT;
void click_animated_emoji_message(FullMessageId full_message_id,
@ -767,7 +782,7 @@ class MessagesManager final : public Actor {
const char *source);
FoundMessages offline_search_messages(DialogId dialog_id, const string &query, string offset, int32 limit,
MessageSearchFilter filter, int64 &random_id, Promise<> &&promise);
MessageSearchFilter filter, int64 &random_id, Promise<Unit> &&promise);
std::pair<int32, vector<FullMessageId>> search_messages(FolderId folder_id, bool ignore_folder_id,
const string &query, int32 offset_date,
@ -806,6 +821,9 @@ class MessagesManager final : public Actor {
void get_dialog_message_count(DialogId dialog_id, MessageSearchFilter filter, bool return_local,
Promise<int32> &&promise);
void get_dialog_message_position(FullMessageId full_message_id, MessageSearchFilter filter,
MessageId top_thread_message_id, Promise<int32> &&promise);
vector<MessageId> get_dialog_scheduled_messages(DialogId dialog_id, bool force, bool ignore_result,
Promise<Unit> &&promise);
@ -845,13 +863,11 @@ class MessagesManager final : public Actor {
bool is_old_channel_update(DialogId dialog_id, int32 new_pts);
bool is_update_about_username_change_received(DialogId dialog_id) const;
void on_dialog_bots_updated(DialogId dialog_id, vector<UserId> bot_user_ids, bool from_database);
void on_dialog_photo_updated(DialogId dialog_id);
void on_dialog_title_updated(DialogId dialog_id);
void on_dialog_username_updated(DialogId dialog_id, const string &old_username, const string &new_username);
void on_dialog_usernames_updated(DialogId dialog_id, const Usernames &old_usernames, const Usernames &new_usernames);
void on_dialog_default_permissions_updated(DialogId dialog_id);
void on_dialog_has_protected_content_updated(DialogId dialog_id);
@ -1030,8 +1046,7 @@ class MessagesManager final : public Actor {
bool disable_web_page_preview = false;
int64 random_id = 0;
tl_object_ptr<telegram_api::messageFwdHeader> forward_header;
MessageId reply_to_message_id;
tl_object_ptr<telegram_api::messageReplyHeader> reply_header;
MessageReplyHeader reply_header;
UserId via_bot_user_id;
int32 view_count = 0;
int32 forward_count = 0;
@ -1140,6 +1155,7 @@ class MessagesManager final : public Actor {
string author_signature;
bool is_channel_post = false;
bool is_topic_message = false;
bool is_outgoing = false;
bool is_failed_to_send = false;
bool disable_notification = false;
@ -1172,6 +1188,8 @@ class MessagesManager final : public Actor {
bool has_get_message_views_query = false;
bool need_view_counter_increment = false;
bool has_get_extended_media_query = false;
DialogId real_forward_from_dialog_id; // for resend_message
MessageId real_forward_from_message_id; // for resend_message
@ -1414,7 +1432,7 @@ class MessagesManager final : public Actor {
MessageId suffix_load_first_message_id_; // identifier of some message such all suffix messages in range
// [suffix_load_first_message_id_, last_message_id] are loaded
MessageId suffix_load_query_message_id_;
std::vector<std::pair<Promise<>, std::function<bool(const Message *)>>> suffix_load_queries_;
std::vector<std::pair<Promise<Unit>, std::function<bool(const Message *)>>> suffix_load_queries_;
FlatHashMap<MessageId, int64, MessageIdHash> pending_viewed_live_locations; // message_id -> task_id
FlatHashSet<MessageId, MessageIdHash> pending_viewed_message_ids;
@ -1724,7 +1742,7 @@ class MessagesManager final : public Actor {
MessageId last_message_id;
bool remove_from_dialog_list = false;
Promise<> success_promise;
Promise<Unit> success_promise;
};
struct MessageSendOptions {
@ -1753,6 +1771,7 @@ class MessagesManager final : public Actor {
class DeleteMessageLogEvent;
class DeleteMessagesOnServerLogEvent;
class DeleteScheduledMessagesOnServerLogEvent;
class DeleteTopicHistoryOnServerLogEvent;
class ForwardMessagesLogEvent;
class GetChannelDifferenceLogEvent;
class ReadAllDialogMentionsOnServerLogEvent;
@ -1793,6 +1812,7 @@ class MessagesManager final : public Actor {
static constexpr size_t MAX_DIALOG_FILTER_TITLE_LENGTH = 12; // server side limit for dialog filter title
static constexpr int32 MAX_PRIVATE_MESSAGE_TTL = 60; // server side limit
static constexpr int32 DIALOG_FILTERS_CACHE_TIME = 86400;
static constexpr size_t MIN_DELETED_ASYNCHRONOUSLY_MESSAGES = 10u;
static constexpr int64 SPONSORED_DIALOG_ORDER = static_cast<int64>(2147483647) << 32;
static constexpr int32 MIN_PINNED_DIALOG_DATE = 2147000000; // some big date
@ -1817,8 +1837,7 @@ class MessagesManager final : public Actor {
static constexpr int32 LIVE_LOCATION_VIEW_PERIOD = 60; // seconds, server-side limit
static constexpr int32 UPDATE_VIEWED_MESSAGES_PERIOD = 15; // seconds
static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 3 * 86400;
static constexpr int32 USERNAME_CACHE_EXPIRE_TIME_SHORT = 900;
static constexpr int32 USERNAME_CACHE_EXPIRE_TIME = 86400;
static constexpr int32 AUTH_NOTIFICATION_ID_CACHE_TIME = 7 * 86400;
static constexpr size_t MAX_SAVED_AUTH_NOTIFICATION_IDS = 100;
@ -1867,10 +1886,10 @@ class MessagesManager final : public Actor {
void finish_add_secret_message(unique_ptr<PendingSecretMessage> pending_secret_message);
void finish_delete_secret_messages(DialogId dialog_id, std::vector<int64> random_ids, Promise<> promise);
void finish_delete_secret_messages(DialogId dialog_id, std::vector<int64> random_ids, Promise<Unit> promise);
void finish_delete_secret_chat_history(DialogId dialog_id, bool remove_from_dialog_list, MessageId last_message_id,
Promise<> promise);
Promise<Unit> promise);
MessageInfo parse_telegram_api_message(tl_object_ptr<telegram_api::Message> message_ptr, bool is_scheduled,
const char *source) const;
@ -1959,14 +1978,17 @@ class MessagesManager final : public Actor {
void add_postponed_channel_update(DialogId dialog_id, tl_object_ptr<telegram_api::Update> &&update, int32 new_pts,
int32 pts_count, Promise<Unit> &&promise);
void process_channel_update(tl_object_ptr<telegram_api::Update> &&update_ptr);
bool process_channel_update(tl_object_ptr<telegram_api::Update> &&update_ptr);
void on_message_edited(FullMessageId full_message_id, int32 pts, bool had_message);
void delete_messages_from_updates(const vector<MessageId> &message_ids);
void delete_dialog_messages(DialogId dialog_id, const vector<MessageId> &message_ids, bool from_updates,
bool skip_update_for_not_found_messages, const char *source);
void delete_dialog_messages(DialogId dialog_id, const vector<MessageId> &message_ids,
bool force_update_for_not_found_messages, const char *source);
void delete_dialog_messages(Dialog *d, const vector<MessageId> &message_ids, bool force_update_for_not_found_messages,
const char *source);
void update_dialog_pinned_messages_from_updates(DialogId dialog_id, const vector<MessageId> &message_ids,
bool is_pin);
@ -1977,12 +1999,13 @@ class MessagesManager final : public Actor {
const vector<MessageId> &message_ids, bool drop_author, bool drop_media_captions,
uint64 log_event_id);
void send_forward_message_query(int32 flags, DialogId to_dialog_id, DialogId from_dialog_id,
tl_object_ptr<telegram_api::InputPeer> as_input_peer, vector<MessageId> message_ids,
vector<int64> random_ids, int32 schedule_date, Promise<Unit> promise);
void send_forward_message_query(int32 flags, DialogId to_dialog_id, MessageId top_thread_message_id,
DialogId from_dialog_id, tl_object_ptr<telegram_api::InputPeer> as_input_peer,
vector<MessageId> message_ids, vector<int64> random_ids, int32 schedule_date,
Promise<Unit> promise);
Result<td_api::object_ptr<td_api::message>> forward_message(DialogId to_dialog_id, DialogId from_dialog_id,
MessageId message_id,
Result<td_api::object_ptr<td_api::message>> forward_message(DialogId to_dialog_id, MessageId top_thread_message_id,
DialogId from_dialog_id, MessageId message_id,
tl_object_ptr<td_api::messageSendOptions> &&options,
bool in_game_share,
MessageCopyOptions &&copy_options) TD_WARN_UNUSED_RESULT;
@ -1996,7 +2019,6 @@ class MessagesManager final : public Actor {
struct ForwardedMessages {
struct CopiedMessage {
unique_ptr<MessageContent> content;
MessageId top_thread_message_id;
MessageId reply_to_message_id;
MessageId original_message_id;
MessageId original_reply_to_message_id;
@ -2017,12 +2039,13 @@ class MessagesManager final : public Actor {
bool drop_media_captions = false;
Dialog *from_dialog;
MessageId top_thread_message_id;
Dialog *to_dialog;
MessageSendOptions message_send_options;
};
Result<ForwardedMessages> get_forwarded_messages(DialogId to_dialog_id, DialogId from_dialog_id,
const vector<MessageId> &message_ids,
Result<ForwardedMessages> get_forwarded_messages(DialogId to_dialog_id, MessageId top_thread_message_id,
DialogId from_dialog_id, const vector<MessageId> &message_ids,
tl_object_ptr<td_api::messageSendOptions> &&options,
bool in_game_share, vector<MessageCopyOptions> &&copy_options);
@ -2101,7 +2124,7 @@ class MessagesManager final : public Actor {
bool can_unload_message(const Dialog *d, const Message *m) const;
void unload_message(Dialog *d, MessageId message_id);
unique_ptr<Message> unload_message(Dialog *d, MessageId message_id);
unique_ptr<Message> delete_message(Dialog *d, MessageId message_id, bool is_permanently_deleted,
bool *need_update_dialog_pos, const char *source);
@ -2140,6 +2163,9 @@ class MessagesManager final : public Actor {
void delete_dialog_history_on_server(DialogId dialog_id, MessageId max_message_id, bool remove_from_dialog_list,
bool revoke, bool allow_error, uint64 log_event_id, Promise<Unit> &&promise);
void delete_topic_history_on_server(DialogId dialog_id, MessageId top_thread_message_id, uint64 log_event_id,
Promise<Unit> &&promise);
void delete_all_call_messages_on_server(bool revoke, uint64 log_event_id, Promise<Unit> &&promise);
void block_message_sender_from_replies_on_server(MessageId message_id, bool need_delete_message,
@ -2186,7 +2212,12 @@ class MessagesManager final : public Actor {
bool has_reply_info, tl_object_ptr<telegram_api::messageReplies> &&reply_info,
bool has_reactions, unique_ptr<MessageReactions> &&reactions);
bool is_active_message_reply_info(DialogId dialog_id, const MessageReplyInfo &info) const;
bool is_thread_message(DialogId dialog_id, const Message *m) const;
bool is_thread_message(DialogId dialog_id, MessageId message_id, const MessageReplyInfo &reply_info,
MessageContentType content_type) const;
bool is_active_message_reply_info(DialogId dialog_id, const MessageReplyInfo &reply_info) const;
bool is_visible_message_reply_info(DialogId dialog_id, const Message *m) const;
@ -2841,7 +2872,7 @@ class MessagesManager final : public Actor {
void edit_dialog_filter(unique_ptr<DialogFilter> new_dialog_filter, const char *source);
void delete_dialog_filter(DialogFilterId dialog_filter_id, const char *source);
int32 delete_dialog_filter(DialogFilterId dialog_filter_id, const char *source);
static bool set_dialog_filters_order(vector<unique_ptr<DialogFilter>> &dialog_filters,
vector<DialogFilterId> dialog_filter_ids);
@ -2853,7 +2884,8 @@ class MessagesManager final : public Actor {
int32 get_server_main_dialog_list_position() const;
static vector<DialogFilterId> get_dialog_filter_ids(const vector<unique_ptr<DialogFilter>> &dialog_filters);
static vector<DialogFilterId> get_dialog_filter_ids(const vector<unique_ptr<DialogFilter>> &dialog_filters,
int32 main_dialog_list_position);
static vector<FolderId> get_dialog_filter_folder_ids(const DialogFilter *filter);
@ -3068,8 +3100,6 @@ class MessagesManager final : public Actor {
const DialogPhoto *get_dialog_photo(DialogId dialog_id) const;
string get_dialog_username(DialogId dialog_id) const;
RestrictedRights get_dialog_default_permissions(DialogId dialog_id) const;
bool get_dialog_has_protected_content(DialogId dialog_id) const;
@ -3293,6 +3323,8 @@ class MessagesManager final : public Actor {
static uint64 save_delete_dialog_history_on_server_log_event(DialogId dialog_id, MessageId max_message_id,
bool remove_from_dialog_list, bool revoke);
static uint64 save_delete_topic_history_on_server_log_event(DialogId dialog_id, MessageId top_thread_message_id);
static uint64 save_delete_all_call_messages_on_server_log_event(bool revoke);
static uint64 save_block_message_sender_from_replies_on_server_log_event(MessageId message_id,
@ -3335,9 +3367,9 @@ class MessagesManager final : public Actor {
void suffix_load_loop(Dialog *d);
static void suffix_load_update_first_message_id(Dialog *d);
void suffix_load_query_ready(DialogId dialog_id);
void suffix_load_add_query(Dialog *d, std::pair<Promise<>, std::function<bool(const Message *)>> query);
void suffix_load_till_date(Dialog *d, int32 date, Promise<> promise);
void suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<> promise);
void suffix_load_add_query(Dialog *d, std::pair<Promise<Unit>, std::function<bool(const Message *)>> query);
void suffix_load_till_date(Dialog *d, int32 date, Promise<Unit> promise);
void suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<Unit> promise);
bool is_group_dialog(DialogId dialog_id) const;
@ -3512,17 +3544,6 @@ class MessagesManager final : public Actor {
FlatHashSet<DialogId, DialogIdHash> postponed_chat_read_inbox_updates_;
struct PendingGetMessageRequest {
MessageId message_id;
Promise<Unit> promise;
tl_object_ptr<telegram_api::InputMessage> input_message;
PendingGetMessageRequest(MessageId message_id, Promise<Unit> promise,
tl_object_ptr<telegram_api::InputMessage> input_message)
: message_id(message_id), promise(std::move(promise)), input_message(std::move(input_message)) {
}
};
FlatHashMap<string, vector<Promise<Unit>>> search_public_dialogs_queries_;
FlatHashMap<string, vector<DialogId>> found_public_dialogs_; // TODO time bound cache
FlatHashMap<string, vector<DialogId>> found_on_server_dialogs_; // TODO time bound cache
@ -3628,7 +3649,7 @@ class MessagesManager final : public Actor {
Timeout reload_dialog_filters_timeout_;
Hints dialogs_hints_; // search dialogs by title and username
Hints dialogs_hints_; // search dialogs by title and usernames
FlatHashSet<FullMessageId, FullMessageIdHash> active_live_location_full_message_ids_;
bool are_active_live_location_messages_loaded_ = false;
@ -3638,11 +3659,15 @@ class MessagesManager final : public Actor {
struct ResolvedUsername {
DialogId dialog_id;
double expires_at;
double expires_at = 0.0;
ResolvedUsername() = default;
ResolvedUsername(DialogId dialog_id, double expires_at) : dialog_id(dialog_id), expires_at(expires_at) {
}
};
FlatHashMap<string, ResolvedUsername> resolved_usernames_;
FlatHashMap<string, DialogId> inaccessible_resolved_usernames_;
WaitFreeHashMap<string, ResolvedUsername> resolved_usernames_;
WaitFreeHashMap<string, DialogId> inaccessible_resolved_usernames_;
FlatHashSet<string> reload_voice_chat_on_search_usernames_;
struct GetDialogsTask {

View File

@ -2685,7 +2685,7 @@ void NotificationManager::process_push_notification(string payload, Promise<Unit
}
auto receiver_id = r_receiver_id.move_as_ok();
auto encryption_keys = td_->device_token_manager_->get_actor_unsafe()->get_encryption_keys();
auto encryption_keys = td_->device_token_manager_.get_actor_unsafe()->get_encryption_keys();
VLOG(notifications) << "Process push notification \"" << format::escaped(payload)
<< "\" with receiver_id = " << receiver_id << " and " << encryption_keys.size()
<< " encryption keys";
@ -3261,7 +3261,7 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
return Status::Error(406, "Phone call notification is not supported");
}
if (begins_with(loc_key, "REACT_")) {
if (begins_with(loc_key, "REACT_") || loc_key == "READ_REACTION") {
// TODO REACT_* notifications
return Status::Error(406, "Reaction notifications are unsupported");
}
@ -3331,8 +3331,9 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), sender_access_hash, user_name,
string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string(), nullptr);
false /*ignored*/, false /*ignored*/, false /*ignored*/, 0, sender_user_id.get(), sender_access_hash, user_name,
string(), string(), string(), std::move(sender_photo), nullptr, 0, Auto(), string(), string(), nullptr,
vector<telegram_api::object_ptr<telegram_api::username>>());
td_->contacts_manager_->on_get_user(std::move(user), "process_push_notification_payload");
}
@ -3689,8 +3690,9 @@ void NotificationManager::add_message_push_notification(DialogId dialog_id, Mess
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/, false /*ignored*/,
false /*ignored*/, false /*ignored*/, false /*ignored*/, sender_user_id.get(), 0, user_name, string(), string(),
string(), nullptr, nullptr, 0, Auto(), string(), string(), nullptr);
false /*ignored*/, false /*ignored*/, false /*ignored*/, 0, sender_user_id.get(), 0, user_name, string(),
string(), string(), nullptr, nullptr, 0, Auto(), string(), string(), nullptr,
vector<telegram_api::object_ptr<telegram_api::username>>());
td_->contacts_manager_->on_get_user(std::move(user), "add_message_push_notification");
}

View File

@ -19,6 +19,7 @@
#include "td/telegram/NotificationType.h"
#include "td/telegram/Photo.h"
#include "td/telegram/td_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"
#include "td/actor/MultiTimeout.h"

View File

@ -1,221 +0,0 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/Global.h"
#include "td/utils/common.h"
#include <limits>
namespace td {
StringBuilder &operator<<(StringBuilder &string_builder, const DialogNotificationSettings &notification_settings) {
return string_builder << "[" << notification_settings.mute_until << ", " << notification_settings.sound << ", "
<< notification_settings.show_preview << ", " << notification_settings.silent_send_message
<< ", " << notification_settings.disable_pinned_message_notifications << ", "
<< notification_settings.disable_mention_notifications << ", "
<< notification_settings.use_default_mute_until << ", "
<< notification_settings.use_default_show_preview << ", "
<< notification_settings.use_default_disable_pinned_message_notifications << ", "
<< notification_settings.use_default_disable_mention_notifications << ", "
<< notification_settings.is_synchronized << "]";
}
StringBuilder &operator<<(StringBuilder &string_builder, NotificationSettingsScope scope) {
switch (scope) {
case NotificationSettingsScope::Private:
return string_builder << "notification settings for private chats";
case NotificationSettingsScope::Group:
return string_builder << "notification settings for group chats";
case NotificationSettingsScope::Channel:
return string_builder << "notification settings for channel chats";
default:
UNREACHABLE();
return string_builder;
}
}
StringBuilder &operator<<(StringBuilder &string_builder, const ScopeNotificationSettings &notification_settings) {
return string_builder << "[" << notification_settings.mute_until << ", " << notification_settings.sound << ", "
<< notification_settings.show_preview << ", " << notification_settings.is_synchronized << ", "
<< notification_settings.disable_pinned_message_notifications << ", "
<< notification_settings.disable_mention_notifications << "]";
}
td_api::object_ptr<td_api::NotificationSettingsScope> get_notification_settings_scope_object(
NotificationSettingsScope scope) {
switch (scope) {
case NotificationSettingsScope::Private:
return td_api::make_object<td_api::notificationSettingsScopePrivateChats>();
case NotificationSettingsScope::Group:
return td_api::make_object<td_api::notificationSettingsScopeGroupChats>();
case NotificationSettingsScope::Channel:
return td_api::make_object<td_api::notificationSettingsScopeChannelChats>();
default:
UNREACHABLE();
return nullptr;
}
}
td_api::object_ptr<td_api::chatNotificationSettings> get_chat_notification_settings_object(
const DialogNotificationSettings *notification_settings) {
CHECK(notification_settings != nullptr);
return td_api::make_object<td_api::chatNotificationSettings>(
notification_settings->use_default_mute_until, max(0, notification_settings->mute_until - G()->unix_time()),
is_notification_sound_default(notification_settings->sound),
get_notification_sound_ringtone_id(notification_settings->sound), notification_settings->use_default_show_preview,
notification_settings->show_preview, notification_settings->use_default_disable_pinned_message_notifications,
notification_settings->disable_pinned_message_notifications,
notification_settings->use_default_disable_mention_notifications,
notification_settings->disable_mention_notifications);
}
td_api::object_ptr<td_api::scopeNotificationSettings> get_scope_notification_settings_object(
const ScopeNotificationSettings *notification_settings) {
CHECK(notification_settings != nullptr);
return td_api::make_object<td_api::scopeNotificationSettings>(
max(0, notification_settings->mute_until - G()->unix_time()),
get_notification_sound_ringtone_id(notification_settings->sound), notification_settings->show_preview,
notification_settings->disable_pinned_message_notifications,
notification_settings->disable_mention_notifications);
}
telegram_api::object_ptr<telegram_api::InputNotifyPeer> get_input_notify_peer(NotificationSettingsScope scope) {
switch (scope) {
case NotificationSettingsScope::Private:
return telegram_api::make_object<telegram_api::inputNotifyUsers>();
case NotificationSettingsScope::Group:
return telegram_api::make_object<telegram_api::inputNotifyChats>();
case NotificationSettingsScope::Channel:
return telegram_api::make_object<telegram_api::inputNotifyBroadcasts>();
default:
return nullptr;
}
}
NotificationSettingsScope get_notification_settings_scope(
const td_api::object_ptr<td_api::NotificationSettingsScope> &scope) {
CHECK(scope != nullptr);
switch (scope->get_id()) {
case td_api::notificationSettingsScopePrivateChats::ID:
return NotificationSettingsScope::Private;
case td_api::notificationSettingsScopeGroupChats::ID:
return NotificationSettingsScope::Group;
case td_api::notificationSettingsScopeChannelChats::ID:
return NotificationSettingsScope::Channel;
default:
UNREACHABLE();
return NotificationSettingsScope::Private;
}
}
static int32 get_mute_until(int32 mute_for) {
if (mute_for <= 0) {
return 0;
}
const int32 MAX_PRECISE_MUTE_FOR = 366 * 86400;
int32 current_time = G()->unix_time();
if (mute_for > MAX_PRECISE_MUTE_FOR || mute_for >= std::numeric_limits<int32>::max() - current_time) {
return std::numeric_limits<int32>::max();
}
return mute_for + current_time;
}
Result<DialogNotificationSettings> get_dialog_notification_settings(
td_api::object_ptr<td_api::chatNotificationSettings> &&notification_settings, bool old_silent_send_message) {
if (notification_settings == nullptr) {
return Status::Error(400, "New notification settings must be non-empty");
}
int32 mute_until =
notification_settings->use_default_mute_for_ ? 0 : get_mute_until(notification_settings->mute_for_);
return DialogNotificationSettings(
notification_settings->use_default_mute_for_, mute_until,
get_notification_sound(notification_settings->use_default_sound_, notification_settings->sound_id_),
notification_settings->use_default_show_preview_, notification_settings->show_preview_, old_silent_send_message,
notification_settings->use_default_disable_pinned_message_notifications_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->use_default_disable_mention_notifications_,
notification_settings->disable_mention_notifications_);
}
Result<ScopeNotificationSettings> get_scope_notification_settings(
td_api::object_ptr<td_api::scopeNotificationSettings> &&notification_settings) {
if (notification_settings == nullptr) {
return Status::Error(400, "New notification settings must be non-empty");
}
auto mute_until = get_mute_until(notification_settings->mute_for_);
return ScopeNotificationSettings(mute_until, get_notification_sound(false, notification_settings->sound_id_),
notification_settings->show_preview_,
notification_settings->disable_pinned_message_notifications_,
notification_settings->disable_mention_notifications_);
}
static unique_ptr<NotificationSound> get_peer_notification_sound(
tl_object_ptr<telegram_api::peerNotifySettings> &settings) {
telegram_api::NotificationSound *sound =
#if TD_ANDROID
settings->android_sound_.get();
#elif TD_DARWIN_IOS || TD_DARWIN_TV_OS || TD_DARWIN_WATCH_OS
settings->ios_sound_.get();
#else
settings->other_sound_.get();
#endif
return get_notification_sound(sound);
}
DialogNotificationSettings get_dialog_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_use_default_disable_pinned_message_notifications,
bool old_disable_pinned_message_notifications,
bool old_use_default_disable_mention_notifications,
bool old_disable_mention_notifications) {
if (settings == nullptr) {
return DialogNotificationSettings();
}
bool use_default_mute_until = (settings->flags_ & telegram_api::peerNotifySettings::MUTE_UNTIL_MASK) == 0;
bool use_default_show_preview = (settings->flags_ & telegram_api::peerNotifySettings::SHOW_PREVIEWS_MASK) == 0;
auto mute_until = use_default_mute_until || settings->mute_until_ <= G()->unix_time() ? 0 : settings->mute_until_;
bool silent_send_message =
(settings->flags_ & telegram_api::peerNotifySettings::SILENT_MASK) == 0 ? false : settings->silent_;
return {use_default_mute_until,
mute_until,
get_peer_notification_sound(settings),
use_default_show_preview,
settings->show_previews_,
silent_send_message,
old_use_default_disable_pinned_message_notifications,
old_disable_pinned_message_notifications,
old_use_default_disable_mention_notifications,
old_disable_mention_notifications};
}
ScopeNotificationSettings get_scope_notification_settings(tl_object_ptr<telegram_api::peerNotifySettings> &&settings,
bool old_disable_pinned_message_notifications,
bool old_disable_mention_notifications) {
if (settings == nullptr) {
return ScopeNotificationSettings();
}
auto mute_until = (settings->flags_ & telegram_api::peerNotifySettings::MUTE_UNTIL_MASK) == 0 ||
settings->mute_until_ <= G()->unix_time()
? 0
: settings->mute_until_;
auto show_preview =
(settings->flags_ & telegram_api::peerNotifySettings::SHOW_PREVIEWS_MASK) == 0 ? false : settings->show_previews_;
return {mute_until, get_peer_notification_sound(settings), show_preview, old_disable_pinned_message_notifications,
old_disable_mention_notifications};
}
bool are_default_dialog_notification_settings(const DialogNotificationSettings &settings, bool compare_sound) {
return settings.use_default_mute_until && (!compare_sound || is_notification_sound_default(settings.sound)) &&
settings.use_default_show_preview && settings.use_default_disable_pinned_message_notifications &&
settings.use_default_disable_mention_notifications;
}
} // namespace td

View File

@ -22,9 +22,9 @@
#include "td/telegram/logevent/LogEventHelper.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/NotificationManager.h"
#include "td/telegram/NotificationSettings.hpp"
#include "td/telegram/NotificationSound.h"
#include "td/telegram/OptionManager.h"
#include "td/telegram/ScopeNotificationSettings.hpp"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/telegram_api.h"

View File

@ -7,9 +7,11 @@
#pragma once
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogNotificationSettings.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/NotificationSettings.h"
#include "td/telegram/NotificationSettingsScope.h"
#include "td/telegram/ScopeNotificationSettings.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"

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