Update tdlib

This commit is contained in:
Andrea Cavalli 2022-03-15 11:36:21 +01:00
commit e047add8a2
186 changed files with 8936 additions and 1955 deletions

View File

@ -317,6 +317,8 @@ set(TDLIB_SOURCE
td/telegram/DialogSource.cpp
td/telegram/Document.cpp
td/telegram/DocumentsManager.cpp
td/telegram/DownloadManager.cpp
td/telegram/DownloadManagerCallback.cpp
td/telegram/DraftMessage.cpp
td/telegram/FileReferenceManager.cpp
td/telegram/files/FileBitmask.cpp
@ -479,6 +481,7 @@ set(TDLIB_SOURCE
td/telegram/CallId.h
td/telegram/CallManager.h
td/telegram/CallbackQueriesManager.h
td/telegram/ChainId.h
td/telegram/ChannelId.h
td/telegram/ChatId.h
td/telegram/ClientActor.h
@ -509,6 +512,8 @@ set(TDLIB_SOURCE
td/telegram/DialogSource.h
td/telegram/Document.h
td/telegram/DocumentsManager.h
td/telegram/DownloadManager.h
td/telegram/DownloadManagerCallback.h
td/telegram/DraftMessage.h
td/telegram/EncryptedFile.h
td/telegram/FileReferenceManager.h
@ -682,6 +687,7 @@ set(TDLIB_SOURCE
td/telegram/files/FileId.hpp
td/telegram/files/FileLocation.hpp
td/telegram/files/FileManager.hpp
td/telegram/files/FileSourceId.hpp
td/telegram/Game.hpp
td/telegram/InputMessageText.hpp
td/telegram/MessageEntity.hpp
@ -711,6 +717,11 @@ set(MEMPROF_SOURCE
memprof/memprof.h
)
set(MEMPROF_STAT_SOURCE
memprof/memprof_stat.cpp
memprof/memprof_stat.h
)
#RULES
file(MAKE_DIRECTORY auto)
@ -744,6 +755,10 @@ if (MEMPROF)
endif()
endif()
add_library(memprof_stat EXCLUDE_FROM_ALL STATIC ${MEMPROF_STAT_SOURCE})
target_include_directories(memprof_stat PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(memprof_stat PRIVATE tdutils)
add_library(tdapi ${TL_TD_API_SOURCE})
target_include_directories(tdapi PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> INTERFACE $<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDE_DIR}>)

View File

@ -165,8 +165,8 @@ function split_file($file, $chunks, $undo) {
$deps = array(); // all functions from the same subarray must be in the same file
$parents = array();
foreach ($functions as $i => $f) {
if (preg_match_all('/(?J)(create_handler|create_net_actor)<(?<name>[A-Z][A-Za-z]*)>|'.
'(?<name>[A-Z][A-Za-z]*) (final )?: public (Td::ResultHandler|NetActor|Request)|'.
if (preg_match_all('/(?J)create_handler<(?<name>[A-Z][A-Za-z]*)>|'.
'(?<name>[A-Z][A-Za-z]*) (final )?: public (Td::ResultHandler|Request)|'.
'(CREATE_REQUEST|CREATE_NO_ARGS_REQUEST)[(](?<name>[A-Z][A-Za-z]*)|'.
'(?<name>complete_pending_preauthentication_requests)|'.
'(?<name>get_message_history_slice)|'.

View File

@ -72,3 +72,22 @@ if (NOT WIN32 AND NOT CYGWIN)
add_executable(bench_queue bench_queue.cpp)
target_link_libraries(bench_queue PRIVATE tdutils)
endif()
if (TD_TEST_FOLLY AND TD_WITH_ABSEIL)
find_package(ABSL QUIET)
find_package(folly QUIET)
find_package(gflags QUIET)
if (ABSL_FOUND AND folly_FOUND)
add_executable(memory-hashset-memprof EXCLUDE_FROM_ALL hashset_memory.cpp)
target_compile_definitions(memory-hashset-memprof PRIVATE USE_MEMPROF=1)
target_link_libraries(memory-hashset-memprof PRIVATE tdutils memprof_stat Folly::folly absl::flat_hash_map absl::hash)
add_executable(memory-hashset-os hashset_memory.cpp)
target_compile_definitions(memory-hashset-os PRIVATE USE_MEMPROF=0)
target_link_libraries(memory-hashset-os PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
add_executable(hashmap-build hashmap_build.cpp)
target_link_libraries(hashmap-build PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
endif()
endif()

View File

@ -261,8 +261,9 @@ class QueryBench final : public td::Benchmark {
send_lambda(client_, [&] { val = n_ * n_; });
} else if (type == 5) {
send_closure(client_, &ClientActor::f_promise,
td::PromiseCreator::lambda(
[id = actor_id(this), n = n_](td::Unit) { send_closure(id, &ServerActor::result, n * n); }));
td::PromiseCreator::lambda([actor_id = actor_id(this), n = n_](td::Unit) {
send_closure(actor_id, &ServerActor::result, n * n);
}));
return;
}
}

View File

@ -5,5 +5,4 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
int main() {
return 0;
}

View File

@ -114,5 +114,4 @@ int main() {
// empty
}
scheduler->finish();
return 0;
}

545
benchmark/hashmap_build.cpp Normal file
View File

@ -0,0 +1,545 @@
//
// 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/utils/FlatHashMap.h"
#ifdef SCOPE_EXIT
#undef SCOPE_EXIT
#endif
#include <absl/container/flat_hash_map.h>
#include <array>
#include <folly/container/F14Map.h>
#include <map>
#include <unordered_map>
#define test_map td::FlatHashMap
//#define test_map folly::F14FastMap
//#define test_map absl::flat_hash_map
//#define test_map std::map
//#define test_map std::unordered_map
//#define CREATE_MAP(num) CREATE_MAP_IMPL(num)
#define CREATE_MAP(num)
#define CREATE_MAP_IMPL(num) \
int f_##num() { \
test_map<int, std::array<char, num>> m; \
m.emplace(1, std::array<char, num>{}); \
int sum = 0; \
for (auto &it : m) { \
sum += it.first; \
} \
auto it = m.find(1); \
sum += it->first; \
m.erase(it); \
return sum; \
} \
int x_##num = f_##num()
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
CREATE_MAP(__LINE__);
int main() {
}

View File

@ -0,0 +1,192 @@
//
// 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)
//
#if USE_MEMPROF
#include "memprof/memprof_stat.h"
#endif
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashMapChunks.h"
#include "td/utils/FlatHashTable.h"
#include "td/utils/logging.h"
#include "td/utils/MapNode.h"
#include "td/utils/misc.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Slice.h"
#include "td/utils/StringBuilder.h"
#ifdef SCOPE_EXIT
#undef SCOPE_EXIT
#endif
#include <absl/container/flat_hash_map.h>
#include <array>
#include <folly/container/F14Map.h>
#include <functional>
#include <map>
#include <unordered_map>
static int mem_stat_i = -1;
static int mem_stat_cur = 0;
static bool use_memprof() {
#if USE_MEMPROF
return mem_stat_i < 0 && is_memprof_on();
#else
return mem_stat_i < 0;
#endif
}
static td::uint64 get_memory() {
#if USE_MEMPROF
if (use_memprof()) {
return get_used_memory_size();
}
#endif
CHECK(!use_memprof());
return td::mem_stat().ok().resident_size_;
}
template <class T>
class Generator {
public:
T next() {
UNREACHABLE();
}
static size_t dyn_size() {
UNREACHABLE();
}
};
template <class T>
class IntGenerator {
public:
T next() {
return ++value;
}
static size_t dyn_size() {
return 0;
}
private:
T value{};
};
template <>
class Generator<td::uint32> final : public IntGenerator<td::uint32> {};
template <>
class Generator<td::uint64> final : public IntGenerator<td::uint64> {};
template <class T>
class Generator<td::unique_ptr<T>> {
public:
td::unique_ptr<T> next() {
return td::make_unique<T>();
}
static std::size_t dyn_size() {
return sizeof(T);
}
};
template <class T, class KeyT, class ValueT>
static void measure(td::StringBuilder &sb, td::Slice name, td::Slice key_name, td::Slice value_name) {
mem_stat_cur++;
if (mem_stat_i >= 0 && mem_stat_cur != mem_stat_i) {
return;
}
sb << name << "<" << key_name << "," << value_name << "> " << (use_memprof() ? "memprof" : "os") << "\n";
std::size_t ideal_size = sizeof(KeyT) + sizeof(ValueT) + Generator<ValueT>::dyn_size();
sb << "\tempty:" << sizeof(T);
struct Stat {
int pi;
double min_ratio;
double max_ratio;
};
td::vector<Stat> stat;
stat.reserve(1024);
for (std::size_t size : {1000000u}) {
Generator<KeyT> key_generator;
Generator<ValueT> value_generator;
auto start_mem = get_memory();
T ht;
auto ratio = [&] {
auto end_mem = get_memory();
auto used_mem = end_mem - start_mem;
return static_cast<double>(used_mem) / (static_cast<double>(ideal_size) * static_cast<double>(ht.size()));
};
double min_ratio;
double max_ratio;
auto reset = [&] {
min_ratio = 1e100;
max_ratio = 0;
};
auto update = [&] {
auto x = ratio();
min_ratio = td::min(min_ratio, x);
max_ratio = td::max(max_ratio, x);
};
reset();
int p = 10;
int pi = 1;
for (std::size_t i = 0; i < size; i++) {
ht.emplace(key_generator.next(), value_generator.next());
update();
if ((i + 1) % p == 0) {
stat.emplace_back(Stat{pi, min_ratio, max_ratio});
reset();
pi++;
p *= 10;
}
}
}
for (auto &s : stat) {
sb << " 10^" << s.pi << ":" << s.min_ratio << "->" << s.max_ratio;
}
sb << '\n';
}
template <std::size_t size>
using Bytes = std::array<char, size>;
template <template <typename... Args> class T>
void print_memory_stats(td::Slice name) {
td::string big_buff(1 << 16, '\0');
td::StringBuilder sb(big_buff, false);
#define MEASURE(KeyT, ValueT) measure<T<KeyT, ValueT>, KeyT, ValueT>(sb, name, #KeyT, #ValueT);
MEASURE(td::uint32, td::uint32);
MEASURE(td::uint64, td::unique_ptr<Bytes<360>>);
if (!sb.as_cslice().empty()) {
LOG(PLAIN) << '\n' << sb.as_cslice() << '\n';
}
}
template <class KeyT, class ValueT, class HashT = std::hash<KeyT>, class EqT = std::equal_to<KeyT>>
using FlatHashMapImpl = td::FlatHashTable<td::MapNode<KeyT, ValueT>, HashT, EqT>;
#define FOR_EACH_TABLE(F) \
F(FlatHashMapImpl) \
F(folly::F14FastMap) \
F(absl::flat_hash_map) \
F(std::unordered_map) \
F(std::map)
#define BENCHMARK_MEMORY(T) print_memory_stats<T>(#T);
int main(int argc, const char *argv[]) {
// Usage:
// % benchmark/memory-hashset-os 0
// Number of benchmarks = 10
// % for i in {1..10}; do ./benchmark/memory-hashset-os $i; done
if (argc > 1) {
mem_stat_i = td::to_integer<td::int32>(td::Slice(argv[1]));
}
FOR_EACH_TABLE(BENCHMARK_MEMORY);
if (mem_stat_i <= 0) {
LOG(PLAIN) << "Number of benchmarks = " << mem_stat_cur << "\n";
}
}

View File

@ -207,6 +207,7 @@
<option>D</option>
<option>Elixir</option>
<option>C</option>
<option>G</option>
<option>Other</option>
</select>
</div>
@ -384,6 +385,7 @@ function getExampleAnchor(language) {
case 'D':
case 'Elixir':
case 'C':
case 'G':
return language.toLowerCase();
case 'Object Pascal':
return 'object-pascal';

View File

@ -34,6 +34,7 @@ Choose your preferred programming language to see examples of usage and a detail
- [Elixir](#elixir)
- [1С](#1s)
- [C](#c)
- [G](#g)
- [Other](#other)
<a name="python"></a>
@ -284,6 +285,13 @@ TDLib can be used from the C programming language through the [JSON](https://git
You can also try to use our [C](https://github.com/tdlight-team/tdlight/blob/master/td/telegram/td_c_client.h) client, which was used by the private TDLib-based version of [telegram-cli](https://github.com/vysheng/tg).
<a name="g"></a>
## Using TDLib from G projects
TDLib can be used from the G graphical programming language in LabVIEW development environment.
See [TDLib bindings for LabVIEW](https://github.com/IvanLisRus/Telegram-Client_TDLib) for examples of such usage.
<a name="other"></a>
## Using TDLib from other programming languages

View File

@ -35,7 +35,7 @@ class TdClient {
* @param {Object} options - Options for TDLib instance creation.
* @param {TdClient~updateCallback} options.onUpdate - Callback for all incoming updates.
* @param {string} [options.instanceName=tdlib] - Name of the TDLib instance. Currently only one instance of TdClient with a given name is allowed. All but one instances with the same name will be automatically closed. Usually, the newest non-background instance is kept alive. Files will be stored in an IndexedDb table with the same name.
* @param {boolean} [options.isBackground=false] - Pass true, if the instance is opened from the background.
* @param {boolean} [options.isBackground=false] - Pass true if the instance is opened from the background.
* @param {string} [options.jsLogVerbosityLevel=info] - The initial verbosity level of the JavaScript part of the code (one of 'error', 'warning', 'info', 'log', 'debug').
* @param {number} [options.logVerbosityLevel=2] - The initial verbosity level for the TDLib internal logging (0-1023).
* @param {boolean} [options.useDatabase=true] - Pass false to use TDLib without database and secret chats. It will significantly improve loading time, but some functionality will be unavailable.

View File

@ -27,17 +27,17 @@ bool is_memprof_on() {
return true;
}
#define my_assert(f) \
if (!(f)) { \
std::abort(); \
}
#if USE_MEMPROF_SAFE
double get_fast_backtrace_success_rate() {
return 0;
}
#else
#define my_assert(f) \
if (!(f)) { \
std::abort(); \
}
#if TD_LINUX
extern void *__libc_stack_end;
#endif
@ -265,12 +265,14 @@ void free(void *data_void) {
#endif
return free_old(info);
}
void *calloc(std::size_t size_a, std::size_t size_b) {
auto size = size_a * size_b;
void *res = malloc_with_frame(size, get_backtrace());
std::memset(res, 0, size);
return res;
}
void *realloc(void *ptr, std::size_t size) {
if (ptr == nullptr) {
return malloc_with_frame(size, get_backtrace());
@ -282,6 +284,7 @@ void *realloc(void *ptr, std::size_t size) {
free(ptr);
return new_ptr;
}
void *memalign(std::size_t aligment, std::size_t size) {
my_assert(false && "Memalign is unsupported");
return nullptr;

166
memprof/memprof_stat.cpp Normal file
View File

@ -0,0 +1,166 @@
//
// 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 "memprof/memprof_stat.h"
#include "td/utils/port/platform.h"
#if (TD_DARWIN || TD_LINUX)
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <new>
#include <utility>
#include <vector>
#include <dlfcn.h>
#include <execinfo.h>
bool is_memprof_on() {
return true;
}
#define my_assert(f) \
if (!(f)) { \
std::abort(); \
}
struct malloc_info {
std::int32_t magic;
std::int32_t size;
};
static std::atomic<std::size_t> total_memory_used;
void register_xalloc(malloc_info *info, std::int32_t diff) {
my_assert(info->size >= 0);
// TODO: this is very slow in case of several threads.
// Currently this statistics is intended only for memory benchmarks.
total_memory_used.fetch_add(diff * info->size, std::memory_order_relaxed);
}
std::size_t get_used_memory_size() {
return total_memory_used.load();
}
extern "C" {
static constexpr std::size_t RESERVED_SIZE = 16;
static constexpr std::int32_t MALLOC_INFO_MAGIC = 0x27138373;
static void *do_malloc(std::size_t size) {
static_assert(RESERVED_SIZE % alignof(std::max_align_t) == 0, "fail");
static_assert(RESERVED_SIZE >= sizeof(malloc_info), "fail");
#if TD_DARWIN
static void *malloc_void = dlsym(RTLD_NEXT, "malloc");
static auto malloc_old = *reinterpret_cast<decltype(malloc) **>(&malloc_void);
#else
extern decltype(malloc) __libc_malloc;
static auto malloc_old = __libc_malloc;
#endif
auto *info = static_cast<malloc_info *>(malloc_old(size + RESERVED_SIZE));
auto *buf = reinterpret_cast<char *>(info);
info->magic = MALLOC_INFO_MAGIC;
info->size = static_cast<std::int32_t>(size);
register_xalloc(info, +1);
void *data = buf + RESERVED_SIZE;
return data;
}
static malloc_info *get_info(void *data_void) {
auto *data = static_cast<char *>(data_void);
auto *buf = data - RESERVED_SIZE;
auto *info = reinterpret_cast<malloc_info *>(buf);
my_assert(info->magic == MALLOC_INFO_MAGIC);
return info;
}
void *malloc(std::size_t size) {
return do_malloc(size);
}
void free(void *data_void) {
if (data_void == nullptr) {
return;
}
auto *info = get_info(data_void);
register_xalloc(info, -1);
#if TD_DARWIN
static void *free_void = dlsym(RTLD_NEXT, "free");
static auto free_old = *reinterpret_cast<decltype(free) **>(&free_void);
#else
extern decltype(free) __libc_free;
static auto free_old = __libc_free;
#endif
return free_old(info);
}
void *calloc(std::size_t size_a, std::size_t size_b) {
auto size = size_a * size_b;
void *res = do_malloc(size);
std::memset(res, 0, size);
return res;
}
void *realloc(void *ptr, std::size_t size) {
if (ptr == nullptr) {
return do_malloc(size);
}
auto *info = get_info(ptr);
auto *new_ptr = do_malloc(size);
auto to_copy = std::min(static_cast<std::int32_t>(size), info->size);
std::memcpy(new_ptr, ptr, to_copy);
free(ptr);
return new_ptr;
}
void *memalign(std::size_t alignment, std::size_t size) {
auto res = malloc(size);
my_assert(reinterpret_cast<std::uintptr_t>(res) % alignment == 0);
return res;
}
int posix_memalign(void **memptr, size_t alignment, size_t size) {
auto res = malloc(size);
my_assert(reinterpret_cast<std::uintptr_t>(res) % alignment == 0);
*memptr = res;
return 0;
}
}
// c++14 guarantees that it is enough to override these two operators.
void *operator new(std::size_t count) {
return do_malloc(count);
}
void operator delete(void *ptr) noexcept(true) {
free(ptr);
}
// because of gcc warning: the program should also define 'void operator delete(void*, std::size_t)'
void operator delete(void *ptr, std::size_t) noexcept(true) {
free(ptr);
}
// c++17
// void *operator new(std::size_t count, std::align_val_t al);
// void operator delete(void *ptr, std::align_val_t al);
#else
bool is_memprof_on() {
return false;
}
std::size_t get_used_memory_size() {
return 0;
}
#endif

13
memprof/memprof_stat.h Normal file
View File

@ -0,0 +1,13 @@
//
// 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 <cstddef>
bool is_memprof_on();
std::size_t get_used_memory_size();

View File

@ -77,7 +77,7 @@ textEntities entities:vector<textEntity> = TextEntities;
formattedText text:string entities:vector<textEntity> = FormattedText;
//@description Contains Telegram terms of service @text Text of the terms of service @min_user_age The minimum age of a user to be able to accept the terms; 0 if any @show_popup True, if a blocking popup with terms of service must be shown to the user
//@description Contains Telegram terms of service @text Text of the terms of service @min_user_age The minimum age of a user to be able to accept the terms; 0 if age isn't restricted @show_popup True, if a blocking popup with terms of service must be shown to the user
termsOfService text:formattedText min_user_age:int32 show_popup:Bool = TermsOfService;
@ -147,7 +147,7 @@ localFile path:string can_be_downloaded:Bool can_be_deleted:Bool is_downloading_
//@description Represents a remote file
//@id Remote file identifier; may be empty. Can be used by the current user across application restarts or even from other devices. Uniquely identifies a file, but a file can have a lot of different valid identifiers.
//-If the ID starts with "http://" or "https://", it represents the HTTP URL of the file. TDLib is currently unable to download files if only their URL is known.
//-If downloadFile is called on such a file or if it is sent to a secret chat, TDLib starts a file generation process by sending updateFileGenerationStart to the application with the HTTP URL in the original_path and "#url#" as the conversion string. Application must generate the file by downloading it to the specified location
//-If downloadFile/addFileToDownloads is called on such a file or if it is sent to a secret chat, TDLib starts a file generation process by sending updateFileGenerationStart to the application with the HTTP URL in the original_path and "#url#" as the conversion string. Application must generate the file by downloading it to the specified location
//@unique_id Unique file identifier; may be empty if unknown. The unique file identifier which is the same for the same file even for different users and is persistent over time
//@is_uploading_active True, if the file is currently being uploaded (or a remote copy is being generated by some other means)
//@is_uploading_completed True, if a remote copy is fully available
@ -318,7 +318,7 @@ voiceNote duration:int32 waveform:bytes mime_type:string voice:file = VoiceNote;
//@description Describes an animated representation of an emoji
//@sticker Animated sticker for the emoji
//@fitzpatrick_type Emoji modifier fitzpatrick type; 0-6; 0 if none
//@sound File containing the sound to be played when the animated emoji is clicked if any; may be null. The sound is encoded with the Opus codec, and stored inside an OGG container
//@sound File containing the sound to be played when the animated emoji 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;
//@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
@ -448,7 +448,7 @@ inputChatPhotoAnimation animation:InputFile main_frame_timestamp:double = InputC
//@restriction_reason If non-empty, it contains a human-readable description of the reason why access to this user must be restricted
//@is_scam True, if many users reported this user as a scam
//@is_fake True, if many users reported this user as a fake account
//@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. It can't be passed to any method except GetUser
//@have_access If false, the user is inaccessible, and the only information known about the user is inside this class. Identifier of the user can't be passed to any method except GetUser
//@type Type of the user
//@language_code IETF language tag of the user's language; only available to bots
user id:int53 first_name:string last_name:string username:string phone_number:string status:UserStatus profile_photo:profilePhoto is_contact:Bool is_mutual_contact:Bool is_verified:Bool is_support:Bool restriction_reason:string is_scam:Bool is_fake:Bool have_access:Bool type:UserType language_code:string = User;
@ -468,7 +468,7 @@ user id:int53 first_name:string last_name:string username:string phone_number:st
//@commands For bots, list of the bot commands
userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool need_phone_number_privacy_exception:Bool bio:string share_text:string description:string group_in_common_count:int32 commands:vector<botCommand> = UserFullInfo;
//@description Represents a list of users @total_count Approximate total count of users found @user_ids A list of user identifiers
//@description Represents a list of users @total_count Approximate total number of users found @user_ids A list of user identifiers
users total_count:int32 user_ids:vector<int53> = Users;
@ -539,7 +539,7 @@ chatMemberStatusBanned banned_until_date:int32 = ChatMemberStatus;
//@status Status of the member in the chat
chatMember member_id:MessageSender inviter_user_id:int53 joined_chat_date:int32 status:ChatMemberStatus = ChatMember;
//@description Contains a list of chat members @total_count Approximate total count of chat members found @members A list of chat members
//@description Contains a list of chat members @total_count Approximate total number of chat members found @members A list of chat members
chatMembers total_count:int32 members:vector<chatMember> = ChatMembers;
@ -609,7 +609,7 @@ supergroupMembersFilterBots = SupergroupMembersFilter;
//@is_revoked True, if the link was revoked
chatInviteLink invite_link:string name:string creator_user_id:int53 date:int32 edit_date:int32 expiration_date:int32 member_limit:int32 member_count:int32 pending_join_request_count:int32 creates_join_request:Bool is_primary:Bool is_revoked:Bool = ChatInviteLink;
//@description Contains a list of chat invite links @total_count Approximate total count of chat invite links found @invite_links List of invite links
//@description Contains a list of chat invite links @total_count Approximate total number of chat invite links found @invite_links List of invite links
chatInviteLinks total_count:int32 invite_links:vector<chatInviteLink> = ChatInviteLinks;
//@description Describes a chat administrator with a number of active and revoked chat invite links
@ -624,7 +624,7 @@ chatInviteLinkCounts invite_link_counts:vector<chatInviteLinkCount> = ChatInvite
//@description Describes a chat member joined a chat via an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat @approver_user_id User identifier of the chat administrator, approved user join request
chatInviteLinkMember user_id:int53 joined_chat_date:int32 approver_user_id:int53 = ChatInviteLinkMember;
//@description Contains a list of chat members joined a chat via an invite link @total_count Approximate total count of chat members found @members List of chat members, joined a chat via an invite link
//@description Contains a list of chat members joined a chat via an invite link @total_count Approximate total number of chat members found @members List of chat members, joined a chat via an invite link
chatInviteLinkMembers total_count:int32 members:vector<chatInviteLinkMember> = ChatInviteLinkMembers;
//@description Contains information about a chat invite link
@ -643,7 +643,7 @@ chatInviteLinkInfo chat_id:int53 accessible_for:int32 type:ChatType title:string
//@description Describes a user that sent a join request and waits for administrator approval @user_id User identifier @date Point in time (Unix timestamp) when the user sent the join request @bio A short bio of the user
chatJoinRequest user_id:int53 date:int32 bio:string = ChatJoinRequest;
//@description Contains a list of requests to join a chat @total_count Approximate total count of requests found @requests List of the requests
//@description Contains a list of requests to join a chat @total_count Approximate total number of requests found @requests List of the requests
chatJoinRequests total_count:int32 requests:vector<chatJoinRequest> = ChatJoinRequests;
//@description Contains information about pending join requests for a chat @total_count Total number of pending join requests @user_ids Identifiers of at most 3 users sent the newest pending join requests
@ -743,7 +743,7 @@ messageSenderUser user_id:int53 = MessageSender;
messageSenderChat chat_id:int53 = MessageSender;
//@description Represents a list of message senders @total_count Approximate total count of messages senders found @senders List of message senders
//@description Represents a list of message senders @total_count Approximate total number of messages senders found @senders List of message senders
messageSenders total_count:int32 senders:vector<MessageSender> = MessageSenders;
@ -834,7 +834,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
//@can_get_added_reactions True, if the list of added reactions is available through getMessageAddedReactions
//@can_get_statistics True, if the message statistics are available through getMessageStatistics
//@can_get_message_thread True, if the message thread info is available through getMessageThread
//@can_get_message_thread True, if information about the message thread is available through getMessageThread
//@can_get_viewers True, if chat members already viewed the message can be received through getMessageViewers
//@can_get_media_timestamp_links True, if media timestamp links can be generated for media timestamp entities in the message text, caption or web page description through getMessageLink
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
@ -858,16 +858,16 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool n
//@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 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;
//@description Contains a list of messages @total_count Approximate total count of messages found @messages List of messages; messages may be null
//@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;
//@description Contains a list of messages found by a search @total_count Approximate total count of messages found; -1 if unknown @messages List of messages @next_offset The offset for the next request. If empty, there are no more results
//@description Contains a list of messages found by a search @total_count Approximate total number of messages found; -1 if unknown @messages List of messages @next_offset The offset for the next request. If empty, there are no more results
foundMessages total_count:int32 messages:vector<message> next_offset:string = FoundMessages;
//@description Contains information about a message in a specific position @position 0-based message position in the full list of suitable messages @message_id Message identifier @date Point in time (Unix timestamp) when the message was sent
messagePosition position:int32 message_id:int53 date:int32 = MessagePosition;
//@description Contains a list of message positions @total_count Total count of messages found @positions List of message positions
//@description Contains a list of message positions @total_count Total number of messages found @positions List of message positions
messagePositions total_count:int32 positions:vector<messagePosition> = MessagePositions;
//@description Contains information about found messages sent on a specific day @total_count Total number of found messages sent on the day @message First message sent on the day
@ -886,6 +886,27 @@ messageCalendar total_count:int32 days:vector<messageCalendarDay> = MessageCalen
sponsoredMessage message_id:int53 sponsor_chat_id:int53 sponsor_chat_info:chatInviteLinkInfo link:InternalLinkType content:MessageContent = SponsoredMessage;
//@description Describes a file added to file download list
//@file_id File identifier
//@message The message with the file
//@add_date Point in time (Unix timestamp) when the file was added to the download list
//@complete_date Point in time (Unix timestamp) when the file downloading was completed; 0 if the file downloading isn't completed
//@is_paused True, if downloading of the file is paused
fileDownload file_id:int32 message:message add_date:int32 complete_date:int32 is_paused:Bool = FileDownload;
//@description Contains number of being downloaded and recently downloaded files found
//@active_count Number of active file downloads found, including paused
//@paused_count Number of paused file downloads found
//@completed_count Number of completed file downloads found
downloadedFileCounts active_count:int32 paused_count:int32 completed_count:int32 = DownloadedFileCounts;
//@description Contains a list of downloaded files, found by a search
//@total_counts Total number of suitable files, ignoring offset
//@files The list of files
//@next_offset The offset for the next request. If empty, there are no more results
foundFileDownloads total_counts:downloadedFileCounts files:vector<fileDownload> next_offset:string = FoundFileDownloads;
//@class NotificationSettingsScope @description Describes the types of chats to which notification settings are relevant
//@description Notification settings applied to all private and secret chats when the corresponding chat setting has a default value
@ -1040,7 +1061,7 @@ videoChat group_call_id:int32 has_participants:Bool default_participant_id:Messa
//@client_data Application-specific data associated with the chat. (For example, the chat scroll position or local chat notification settings can be stored here.) Persistent if the message database is used
chat id:int53 type:ChatType title:string photo:chatPhotoInfo permissions:chatPermissions last_message:message positions:vector<chatPosition> message_sender_id:MessageSender has_protected_content:Bool is_marked_as_unread:Bool is_blocked:Bool has_scheduled_messages:Bool can_be_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_be_reported:Bool default_disable_notification: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 available_reactions:vector<string> message_ttl:int32 theme_name:string action_bar:ChatActionBar video_chat:videoChat pending_join_requests:chatJoinRequestsInfo reply_markup_message_id:int53 draft_message:draftMessage client_data:string = Chat;
//@description Represents a list of chats @total_count Approximate total count of chats found @chat_ids List of chat identifiers
//@description Represents a list of chats @total_count Approximate total number of chats found @chat_ids List of chat identifiers
chats total_count:int32 chat_ids:vector<int53> = Chats;
@ -2316,6 +2337,19 @@ groupCallVideoQualityMedium = GroupCallVideoQuality;
groupCallVideoQualityFull = GroupCallVideoQuality;
//@description Describes an available stream in a group call
//@channel_id Identifier of an audio/video channel
//@scale Scale of segment durations in the stream. The duration is 1000/(2**scale) milliseconds
//@time_offset Point in time when the stream currently ends; Unix timestamp in milliseconds
groupCallStream channel_id:int32 scale:int32 time_offset:int53 = GroupCallStream;
//@description Represents a list of group call streams @streams A list of group call streams
groupCallStreams streams:vector<groupCallStream> = GroupCallStreams;
//@description Represents an RTMP url @url The URL @stream_key Stream key
rtmpUrl url:string stream_key:string = RtmpUrl;
//@description Describes a recently speaking participant in a group call @participant_id Group call participant identifier @is_speaking True, is the user has spoken recently
groupCallRecentSpeaker participant_id:MessageSender is_speaking:Bool = GroupCallRecentSpeaker;
@ -2325,10 +2359,12 @@ groupCallRecentSpeaker participant_id:MessageSender is_speaking:Bool = GroupCall
//@scheduled_start_date Point in time (Unix timestamp) when the group call is supposed to be started by an administrator; 0 if it is already active or was ended
//@enabled_start_notification True, if the group call is scheduled and the current user will receive a notification when the group call will start
//@is_active True, if the call is active
//@is_rtmp_stream True, if the chat is an RTMP stream instead of an ordinary video chat
//@is_joined True, if the call is joined
//@need_rejoin True, if user was kicked from the call because of network loss and the call needs to be rejoined
//@can_be_managed True, if the current user can manage the group call
//@participant_count Number of participants in the group call
//@has_hidden_listeners True, if group call participants, which are muted, aren't returned in participant list
//@loaded_all_participants True, if all group call participants are loaded
//@recent_speakers At most 3 recently speaking users in the group call
//@is_my_video_enabled True, if the current user's video is enabled
@ -2339,7 +2375,7 @@ groupCallRecentSpeaker participant_id:MessageSender is_speaking:Bool = GroupCall
//@record_duration Duration of the ongoing group call recording, in seconds; 0 if none. An updateGroupCall update is not triggered when value of this field changes, but the same recording goes on
//@is_video_recorded True, if a video file is being recorded for the call
//@duration Call duration, in seconds; for ended calls only
groupCall id:int32 title:string scheduled_start_date:int32 enabled_start_notification:Bool is_active:Bool is_joined:Bool need_rejoin:Bool can_be_managed:Bool participant_count:int32 loaded_all_participants:Bool recent_speakers:vector<groupCallRecentSpeaker> is_my_video_enabled:Bool is_my_video_paused:Bool can_enable_video:Bool mute_new_participants:Bool can_toggle_mute_new_participants:Bool record_duration:int32 is_video_recorded:Bool duration:int32 = GroupCall;
groupCall id:int32 title:string scheduled_start_date:int32 enabled_start_notification:Bool is_active:Bool is_rtmp_stream:Bool is_joined:Bool need_rejoin:Bool can_be_managed:Bool participant_count:int32 has_hidden_listeners:Bool loaded_all_participants:Bool recent_speakers:vector<groupCallRecentSpeaker> is_my_video_enabled:Bool is_my_video_paused:Bool can_enable_video:Bool mute_new_participants:Bool can_toggle_mute_new_participants:Bool record_duration:int32 is_video_recorded:Bool duration:int32 = GroupCall;
//@description Describes a group of video synchronization source identifiers @semantics The semantics of sources, one of "SIM" or "FID" @source_ids The list of synchronization source identifiers
groupCallVideoSourceGroup semantics:string source_ids:vector<int32> = GroupCallVideoSourceGroup;
@ -2416,7 +2452,7 @@ phoneNumberAuthenticationSettings allow_flash_call:Bool allow_missed_call:Bool i
//@description Represents a reaction applied to a message @reaction Text representation of the reaction @sender_id Identifier of the chat member, applied the reaction
addedReaction reaction:string sender_id:MessageSender = AddedReaction;
//@description Represents a list of reactions added to a message @total_count The total count of found reactions @reactions The list of added reactions @next_offset The offset for the next request. If empty, there are no more results
//@description Represents a list of reactions added to a message @total_count The total number of found reactions @reactions The list of added reactions @next_offset The offset for the next request. If empty, there are no more results
addedReactions total_count:int32 reactions:vector<addedReaction> next_offset:string = AddedReactions;
//@description Represents a list of available reactions @reactions List of reactions
@ -2745,7 +2781,7 @@ chatEventLogFilters message_edits:Bool message_deletions:Bool message_pins:Bool
//@description An ordinary language pack string @value String value
languagePackStringValueOrdinary value:string = LanguagePackStringValue;
//@description A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info
//@description A language pack string which has different forms based on the number of some object it mentions. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more information
//@zero_value Value for zero objects @one_value Value for one object @two_value Value for two objects
//@few_value Value for few objects @many_value Value for many objects @other_value Default value
languagePackStringValuePluralized zero_value:string one_value:string two_value:string few_value:string many_value:string other_value:string = LanguagePackStringValue;
@ -2763,7 +2799,7 @@ languagePackStrings strings:vector<languagePackString> = LanguagePackStrings;
//@description Contains information about a language pack @id Unique language pack identifier
//@base_language_pack_id Identifier of a base language pack; may be empty. If a string is missed in the language pack, then it must be fetched from base language pack. Unsupported in custom language packs
//@name Language name @native_name Name of the language in that language
//@plural_code A language code to be used to apply plural forms. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more info
//@plural_code A language code to be used to apply plural forms. See https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html for more information
//@is_official True, if the language pack is official @is_rtl True, if the language pack strings are RTL @is_beta True, if the language pack is a beta language pack
//@is_installed True, if the language pack is installed by the current user
//@total_string_count Total number of non-deleted strings from the language pack @translated_string_count Total number of translated strings from the language pack
@ -3248,6 +3284,12 @@ chatReportReasonUnrelatedLocation = ChatReportReason;
//@description The chat represents a fake account
chatReportReasonFake = ChatReportReason;
//@description The chat has illegal drugs related content
chatReportReasonIllegalDrugs = ChatReportReason;
//@description The chat contains messages with personal details
chatReportReasonPersonalDetails = ChatReportReason;
//@description A custom reason provided by the user
chatReportReasonCustom = ChatReportReason;
@ -3289,6 +3331,9 @@ internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkTy
//@description The link is a link to a language pack. Call getLanguagePackInfo with the given language pack identifier to process the link @language_pack_id Language pack identifier
internalLinkTypeLanguagePack language_pack_id:string = InternalLinkType;
//@description The link is a link to the language settings section of the app
internalLinkTypeLanguageSettings = InternalLinkType;
//@description The link is a link to a Telegram message. Call getMessageLinkInfo with the given URL to process the link @url URL to be passed to getMessageLinkInfo
internalLinkTypeMessage url:string = InternalLinkType;
@ -3305,6 +3350,9 @@ internalLinkTypePassportDataRequest bot_user_id:int53 scope:string public_key:st
//@hash Hash value from the link @phone_number Phone number value from the link
internalLinkTypePhoneNumberConfirmation hash:string phone_number:string = InternalLinkType;
//@description The link is a link to the privacy and security settings section of the app
internalLinkTypePrivacyAndSecuritySettings = InternalLinkType;
//@description The link is a link to a proxy. Call addProxy with the given parameters to process the link and add the proxy
//@server Proxy server IP address @port Proxy server port @type Type of the proxy
internalLinkTypeProxy server:string port:int32 type:ProxyType = InternalLinkType;
@ -3334,6 +3382,9 @@ internalLinkTypeUnknownDeepLink link:string = InternalLinkType;
//@description The link is a link to an unsupported proxy. An alert can be shown to the user
internalLinkTypeUnsupportedProxy = InternalLinkType;
//@description The link is a link to a user by its phone number. Call searchUserByPhoneNumber with the given phone number to process the link @phone_number Phone number of the user
internalLinkTypeUserPhoneNumber phone_number:string = InternalLinkType;
//@description The link is a link to a video chat. Call searchPublicChat with the given chat username, and then joinGroupCall with the given invite hash to process the link
//@chat_username Username of the chat with the video chat @invite_hash If non-empty, invite hash to be used to join the video chat without being muted by administrators
//@is_live_stream True, if the video chat is expected to be a live stream in a channel or a broadcast group
@ -3535,7 +3586,7 @@ tMeUrlTypeUser user_id:int53 = TMeUrlType;
//@description A URL linking to a public supergroup or channel @supergroup_id Identifier of the supergroup or channel
tMeUrlTypeSupergroup supergroup_id:int53 = TMeUrlType;
//@description A chat invite link @info Chat invite link info
//@description A chat invite link @info Information about the chat invite link
tMeUrlTypeChatInvite info:chatInviteLinkInfo = TMeUrlType;
//@description A URL linking to a sticker set @sticker_set_id Identifier of the sticker set
@ -3868,7 +3919,7 @@ updateChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Update;
//@description The list of chat filters or a chat filter has changed @chat_filters The new list of chat filters
updateChatFilters chat_filters:vector<chatFilterInfo> = Update;
//@description The number of online group members has changed. This update with non-zero count is sent only for currently opened chats. There is no guarantee that it will be sent just after the count has changed @chat_id Identifier of the chat @online_member_count New number of online members in the chat, or 0 if unknown
//@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 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
@ -3948,6 +3999,24 @@ updateFileGenerationStart generation_id:int64 original_path:string destination_p
//@description File generation is no longer needed @generation_id Unique identifier for the generation process
updateFileGenerationStop generation_id:int64 = Update;
//@description The state of the file download list has changed
//@total_size Total size of files in the file download list, in bytes
//@total_count Total number of files in the file download list
//@downloaded_size Total downloaded size of files in the file download list, in bytes
updateFileDownloads total_size:int53 total_count:int32 downloaded_size:int53 = Update;
//@description A file was added to the file download list. This update is sent only after file download list is loaded for the first time @file_download The added file download @counts New number of being downloaded and recently downloaded files found
updateFileAddedToDownloads file_download:fileDownload counts:downloadedFileCounts = Update;
//@description A file download was changed. This update is sent only after file download list is loaded for the first time @file_id File identifier
//@complete_date Point in time (Unix timestamp) when the file downloading was completed; 0 if the file downloading isn't completed
//@is_paused True, if downloading of the file is paused
//@counts New number of being downloaded and recently downloaded files found
updateFileDownload file_id:int32 complete_date:int32 is_paused:Bool counts:downloadedFileCounts = Update;
//@description A file was removed from the file download list. This update is sent only after file download list is loaded for the first time @file_id File identifier @counts New number of being downloaded and recently downloaded files found
updateFileRemovedFromDownloads file_id:int32 counts:downloadedFileCounts = Update;
//@description New call was created or information about a call was updated @call New data about a call
updateCall call:call = Update;
@ -4190,7 +4259,7 @@ setDatabaseEncryptionKey new_encryption_key:bytes = Ok;
getPasswordState = PasswordState;
//@description Changes the password for the current user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed
//@old_password Previous password of the user @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true if the recovery email address must be changed @new_recovery_email_address New recovery email address; may be empty
//@old_password Previous password of the user @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true to change also the recovery email address @new_recovery_email_address New recovery email address; may be empty
setPassword old_password:string new_password:string new_hint:string set_recovery_email_address:Bool new_recovery_email_address:string = PasswordState;
//@description Returns a 2-step verification recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The password for the current user
@ -4259,7 +4328,7 @@ getChat chat_id:int53 = Chat;
//@description Returns information about a message @chat_id Identifier of the chat the message belongs to @message_id Identifier of the message to get
getMessage chat_id:int53 message_id:int53 = Message;
//@description Returns information about a message, if it is available locally without sending network request. This is an offline request @chat_id Identifier of the chat the message belongs to @message_id Identifier of the message to get
//@description Returns information about a message, if it is available without sending network request. This is an offline request @chat_id Identifier of the chat the message belongs to @message_id Identifier of the message to get
getMessageLocally chat_id:int53 message_id:int53 = Message;
//@description Returns information about a message that is replied by a given message. Also returns the pinned message, the game message, and the invoice message for messages of the types messagePinMessage, messageGameScore, and messagePaymentSuccessful respectively
@ -4358,7 +4427,7 @@ getGroupsInCommon user_id:int53 offset_chat_id:int53 limit:int32 = Chats;
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset up to 99 to get additionally some newer messages
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. If the offset is negative, the limit must be greater than or equal to -offset. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
//@only_local If true, returns only messages that are available locally without sending network requests
//@only_local Pass true to get only messages that are available without sending network requests
getChatHistory chat_id:int53 from_message_id:int53 offset:int32 limit:int32 only_local:Bool = Messages;
//@description Returns messages in a message thread of a message. Can be used only if message.can_get_message_thread == true. Message thread of a channel message is in the channel's linked supergroup.
@ -4371,7 +4440,7 @@ getChatHistory chat_id:int53 from_message_id:int53 offset:int32 limit:int32 only
getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 offset:int32 limit:int32 = Messages;
//@description Deletes all messages in the chat. Use chat.can_be_deleted_only_for_self and chat.can_be_deleted_for_all_users fields to find whether and how the method can be applied to the chat
//@chat_id Chat identifier @remove_from_chat_list Pass true if the chat needs to be removed from the chat list @revoke Pass true to delete chat history for all users
//@chat_id Chat identifier @remove_from_chat_list Pass true to remove the chat from all chat lists @revoke Pass true to delete chat history for all users
deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
//@description Deletes a chat along with all messages in the corresponding chat for all chat members; requires owner privileges. For group chats this will release the username and remove all members. Chats with more than 1000 members can't be deleted using this method @chat_id Chat identifier
@ -4412,9 +4481,15 @@ searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter
//@description Searches for call messages. Returns the results in reverse chronological order (i. e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
//@from_message_id Identifier of the message from which to search; use 0 to get results from the last message
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit @only_missed If true, returns only messages with missed/declined calls
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
//@only_missed Pass true to search only for messages with missed/declined calls
searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages;
//@description Searches for outgoing messages with content of the type messageDocument in all chats except secret chats. Returns the results in reverse chronological order
//@query Query to search for in document file name and message caption
//@limit The maximum number of messages to be returned; up to 100
searchOutgoingDocumentMessages query:string limit:int32 = FoundMessages;
//@description Deletes all call messages @revoke Pass true to delete the messages for all users
deleteAllCallMessages revoke:Bool = Ok;
@ -4441,7 +4516,7 @@ getChatSparseMessagePositions chat_id:int53 filter:SearchMessagesFilter from_mes
//@from_message_id The message identifier from which to return information about messages; use 0 to get results from the last message
getChatMessageCalendar chat_id:int53 filter:SearchMessagesFilter from_message_id:int53 = MessageCalendar;
//@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 If true, returns count that is available locally without sending network requests, returning -1 if the number of messages is unknown
//@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 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
@ -4511,7 +4586,8 @@ sendMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 opti
//@reply_to_message_id Identifier of a message to reply to or 0
//@options Options to be used to send the messages; pass null to use default options
//@input_message_contents Contents of messages to be sent. At most 10 messages can be added to an album
sendMessageAlbum chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions input_message_contents:vector<InputMessageContent> = Messages;
//@only_preview Pass true to get fake messages instead of actually sending them
sendMessageAlbum chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions input_message_contents:vector<InputMessageContent> only_preview:Bool = Messages;
//@description Invites a bot to a chat (if it is not yet a member) and sends it the /start command. Bots can't be invited to a private chat other than the chat with the bot. Bots can't be invited to channels (although they can be added as admins) and secret chats. Returns the sent message
//@bot_user_id Identifier of the bot @chat_id Identifier of the target chat @parameter A hidden parameter sent to the bot for deep linking purposes (https://core.telegram.org/bots#deep-linking)
@ -4524,7 +4600,7 @@ sendBotStartMessage bot_user_id:int53 chat_id:int53 parameter:string = Message;
//@options Options to be used to send the message; pass null to use default options
//@query_id Identifier of the inline query
//@result_id Identifier of the inline result
//@hide_via_bot If true, there will be no mention of a bot, via which the message is sent. Can be used only for bots GetOption("animation_search_bot_username"), GetOption("photo_search_bot_username") and GetOption("venue_search_bot_username")
//@hide_via_bot Pass true to hide the bot, via which the message is sent. Can be used only for bots GetOption("animation_search_bot_username"), GetOption("photo_search_bot_username"), and GetOption("venue_search_bot_username")
sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions query_id:int64 result_id:string hide_via_bot:Bool = Message;
//@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
@ -4532,9 +4608,9 @@ sendInlineQueryResultMessage chat_id:int53 message_thread_id:int53 reply_to_mess
//@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 If true, content of the messages will be copied without reference to the original sender. Always true if the messages are forwarded to a secret chat or are local
//@remove_caption If true, media caption of message copies will be removed. Ignored if send_copy is false
//@only_preview If true, messages will not be forwarded and instead fake messages will be returned
//@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;
//@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.
@ -4647,8 +4723,8 @@ getMessageAvailableReactions chat_id:int53 message_id:int53 = AvailableReactions
//@description Changes chosen reaction for a message
//@chat_id Identifier of the chat to which the message belongs
//@message_id Identifier of the message
//@reaction Text representation of the new chosen reaction. Can be an empty string or the currently chosen reaction to remove the reaction
//@is_big True, if the reaction is added with a big animation
//@reaction Text representation of the new chosen reaction. Can be an empty string or the currently chosen non-big reaction to remove the reaction
//@is_big Pass true if the reaction is added with a big animation
setMessageReaction chat_id:int53 message_id:int53 reaction:string is_big:Bool = Ok;
//@description Returns reactions added for a message, along with their sender
@ -4725,7 +4801,7 @@ getLoginUrlInfo chat_id:int53 message_id:int53 button_id:int53 = LoginUrlInfo;
//@description Returns an HTTP URL which can be used to automatically authorize the user on a website after clicking an inline button of type inlineKeyboardButtonTypeLoginUrl.
//-Use the method getLoginUrlInfo to find whether a prior user confirmation is needed. If an error is returned, then the button must be handled as an ordinary URL button
//@chat_id Chat identifier of the message with the button @message_id Message identifier of the message with the button @button_id Button identifier
//@allow_write_access True, if the user allowed the bot to send them messages
//@allow_write_access Pass true to allow the bot to send messages to the current user
getLoginUrl chat_id:int53 message_id:int53 button_id:int53 allow_write_access:Bool = HttpUrl;
@ -4739,7 +4815,7 @@ getInlineQueryResults bot_user_id:int53 chat_id:int53 user_location:location que
//@description Sets the result of an inline query; for bots only
//@inline_query_id Identifier of the inline query
//@is_personal True, if the result of the query can be cached for the specified user
//@is_personal Pass true if results may be cached and returned only for the user that sent the query. By default, results may be returned to any user who sends the same query
//@results The results of the query
//@cache_time Allowed time to cache the results of the query, in seconds
//@next_offset Offset for the next inline query; pass an empty string if there are no more results
@ -4751,7 +4827,7 @@ answerInlineQuery inline_query_id:int64 is_personal:Bool results:vector<InputInl
//@description Sends a callback query to a bot and returns an answer. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @chat_id Identifier of the chat with the message @message_id Identifier of the message from which the query originated @payload Query payload
getCallbackQueryAnswer chat_id:int53 message_id:int53 payload:CallbackQueryPayload = CallbackQueryAnswer;
//@description Sets the result of a callback query; for bots only @callback_query_id Identifier of the callback query @text Text of the answer @show_alert If true, an alert must be shown to the user instead of a toast notification @url URL to be opened @cache_time Time during which the result of the query can be cached, in seconds
//@description Sets the result of a callback query; for bots only @callback_query_id Identifier of the callback query @text Text of the answer @show_alert Pass true to show an alert to the user instead of a toast notification @url URL to be opened @cache_time Time during which the result of the query can be cached, in seconds
answerCallbackQuery callback_query_id:int64 text:string show_alert:Bool url:string cache_time:int32 = Ok;
@ -4762,11 +4838,12 @@ answerShippingQuery shipping_query_id:int64 shipping_options:vector<shippingOpti
answerPreCheckoutQuery pre_checkout_query_id:int64 error_message:string = Ok;
//@description Updates the game score of the specified user in the game; for bots only @chat_id The chat to which the message with the game belongs @message_id Identifier of the message @edit_message True, if the message needs to be edited @user_id User identifier @score The new score
//@description Updates the game score of the specified user in the game; for bots only @chat_id The chat to which the message with the game belongs @message_id Identifier of the message
//@edit_message Pass true to edit the game message to include the current scoreboard @user_id User identifier @score The new score
//@force Pass true to update the score even if it decreases. If the score is 0, the user will be deleted from the high score table
setGameScore chat_id:int53 message_id:int53 edit_message:Bool user_id:int53 score:int32 force:Bool = Message;
//@description Updates the game score of the specified user in a game; for bots only @inline_message_id Inline message identifier @edit_message True, if the message needs to be edited @user_id User identifier @score The new score
//@description Updates the game score of the specified user in a game; for bots only @inline_message_id Inline message identifier @edit_message Pass true to edit the game message to include the current scoreboard @user_id User identifier @score The new score
//@force Pass true to update the score even if it decreases. If the score is 0, the user will be deleted from the high score table
setInlineGameScore inline_message_id:string edit_message:Bool user_id:int53 score:int32 force:Bool = Ok;
@ -4797,7 +4874,7 @@ closeChat chat_id:int53 = Ok;
//@chat_id Chat identifier
//@message_thread_id If not 0, a message thread identifier in which the messages are being viewed
//@message_ids The identifiers of the messages being viewed
//@force_read True, if messages in closed chats must be marked as read by the request
//@force_read Pass true to mark as read the specified messages even the chat is closed
viewMessages chat_id:int53 message_thread_id:int53 message_ids:vector<int53> force_read:Bool = Ok;
//@description Informs TDLib that the message content has been opened (e.g., the user has opened a photo, video, document, location or venue, or has listened to an audio file or voice note message). An updateMessageContentOpened update will be generated if something has changed @chat_id Chat identifier of the message @message_id Identifier of the message with the opened content
@ -4813,7 +4890,7 @@ getInternalLinkType link:string = InternalLinkType;
getExternalLinkInfo link:string = LoginUrlInfo;
//@description Returns an HTTP URL which can be used to automatically authorize the current user on a website after clicking an HTTP link. Use the method getExternalLinkInfo to find whether a prior user confirmation is needed
//@link The HTTP link @allow_write_access True, if the current user allowed the bot, returned in getExternalLinkInfo, to send them messages
//@link The HTTP link @allow_write_access Pass true if the current user allowed the bot, returned in getExternalLinkInfo, to send them messages
getExternalLink link:string allow_write_access:Bool = HttpUrl;
@ -4824,13 +4901,13 @@ readAllChatMentions chat_id:int53 = Ok;
readAllChatReactions chat_id:int53 = Ok;
//@description Returns an existing chat corresponding to a given user @user_id User identifier @force If true, the chat will be created without network request. In this case all information about the chat except its type, title and photo can be incorrect
//@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;
//@description Returns an existing chat corresponding to a known basic group @basic_group_id Basic group identifier @force If true, the chat will be created without network request. In this case all information about the chat except its type, title and photo can be incorrect
//@description Returns an existing chat corresponding to a known basic group @basic_group_id Basic group 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
createBasicGroupChat basic_group_id:int53 force:Bool = Chat;
//@description Returns an existing chat corresponding to a known supergroup or channel @supergroup_id Supergroup or channel identifier @force If true, the chat will be created without network request. In this case all information about the chat except its type, title and photo can be incorrect
//@description Returns an existing chat corresponding to a known supergroup or channel @supergroup_id Supergroup or channel 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
createSupergroupChat supergroup_id:int53 force:Bool = Chat;
//@description Returns an existing chat corresponding to a known secret chat @secret_chat_id Secret chat identifier
@ -4841,10 +4918,10 @@ createNewBasicGroupChat user_ids:vector<int53> title:string = Chat;
//@description Creates a new supergroup or channel and sends a corresponding messageSupergroupChatCreate. Returns the newly created chat
//@title Title of the new chat; 1-128 characters
//@is_channel True, if a channel chat needs to be created
//@is_channel Pass true to create a channel chat
//@param_description Chat description; 0-255 characters
//@location Chat location if a location-based supergroup is being created; pass null to create an ordinary supergroup chat
//@for_import True, if the supergroup is created for importing messages using importMessage
//@for_import Pass true to create a supergroup for importing messages using importMessage
createNewSupergroupChat title:string is_channel:Bool description:string location:chatLocation for_import:Bool = Chat;
//@description Creates a new secret chat. Returns the newly created chat @user_id Identifier of the target user
@ -4911,7 +4988,7 @@ setChatDraftMessage chat_id:int53 message_thread_id:int53 draft_message:draftMes
setChatNotificationSettings chat_id:int53 notification_settings:chatNotificationSettings = Ok;
//@description Changes the ability of users to save, forward, or copy chat content. Supported only for basic groups, supergroups and channels. Requires owner privileges
//@chat_id Chat identifier @has_protected_content True, if chat content can't be saved locally, forwarded, or copied
//@chat_id Chat identifier @has_protected_content New value of has_protected_content
toggleChatHasProtectedContent chat_id:int53 has_protected_content:Bool = Ok;
//@description Changes the marked as unread state of a chat @chat_id Chat identifier @is_marked_as_unread New value of is_marked_as_unread
@ -4920,7 +4997,7 @@ toggleChatIsMarkedAsUnread chat_id:int53 is_marked_as_unread:Bool = Ok;
//@description Changes the value of the default disable_notification parameter, used when a message is sent to a chat @chat_id Chat identifier @default_disable_notification New value of default_disable_notification
toggleChatDefaultDisableNotification chat_id:int53 default_disable_notification:Bool = Ok;
//@description Changes reactions, available in a chat. Available for basic groups, supergroups, and channels. Requires can_change_info administrator right @chat_id Identifier of the chat @available_reactions New list of reactions, available in the chat. All reactions must be active and order of the reactions must be the same as in updateReactions
//@description Changes reactions, available in a chat. Available for basic groups, supergroups, and channels. Requires can_change_info administrator right @chat_id Identifier of the chat @available_reactions New list of reactions, available in the chat. All reactions must be active
setChatAvailableReactions chat_id:int53 available_reactions:vector<string> = Ok;
//@description Changes application-specific data associated with a chat @chat_id Chat identifier @client_data New value of client_data
@ -4942,8 +5019,8 @@ setChatSlowModeDelay chat_id:int53 slow_mode_delay:int32 = Ok;
//@description Pins a message in a chat; requires can_pin_messages rights or can_edit_messages rights in the channel
//@chat_id Identifier of the chat
//@message_id Identifier of the new pinned message
//@disable_notification True, if there must be no notification about the pinned message. Notifications are always disabled in channels and private chats
//@only_for_self True, if the message needs to be pinned for one side only; private chats only
//@disable_notification Pass true to disable notification about the pinned message. Notifications are always disabled in channels and private chats
//@only_for_self Pass true to pin the message only for self; private chats only
pinChatMessage chat_id:int53 message_id:int53 disable_notification:Bool only_for_self:Bool = Ok;
//@description Removes a pinned message 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 @message_id Identifier of the removed pinned message
@ -4999,13 +5076,13 @@ searchChatMembers chat_id:int53 query:string limit:int32 filter:ChatMembersFilte
getChatAdministrators chat_id:int53 = ChatAdministrators;
//@description Clears draft messages in all chats @exclude_secret_chats If true, local draft messages in secret chats will not be cleared
//@description Clears message drafts in all chats @exclude_secret_chats Pass true to keep local message drafts in secret chats
clearAllDraftMessages exclude_secret_chats:Bool = Ok;
//@description Returns list of chats with non-default notification settings
//@scope If specified, only chats from the scope will be returned; pass null to return chats from all scopes
//@compare_sound If true, also chats with non-default sound will be returned
//@compare_sound Pass true to include in the response chats with only non-default sound
getChatNotificationSettingsExceptions scope:NotificationSettingsScope compare_sound:Bool = Chats;
//@description Returns the notification settings for chats of a given type @scope Types of chats for which to return the notification settings information
@ -5019,7 +5096,7 @@ resetAllNotificationSettings = Ok;
//@description Changes the pinned state of a chat. There can be up to GetOption("pinned_chat_count_max")/GetOption("pinned_archived_chat_count_max") pinned non-secret chats and the same number of secret chats in the main/archive chat list
//@chat_list Chat list in which to change the pinned state of the chat @chat_id Chat identifier @is_pinned True, if the chat is pinned
//@chat_list Chat list in which to change the pinned state of the chat @chat_id Chat identifier @is_pinned Pass true to pin the chat; pass false to unpin it
toggleChatIsPinned chat_list:ChatList chat_id:int53 is_pinned:Bool = Ok;
//@description Changes the order of pinned chats @chat_list Chat list in which to change the order of pinned chats @chat_ids The new list of pinned chats
@ -5028,11 +5105,10 @@ setPinnedChats chat_list:ChatList chat_ids:vector<int53> = Ok;
//@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates
//@file_id Identifier of the file to download
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile was called will be downloaded first
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile/addFileToDownloads was called will be downloaded first
//@offset The starting position from which the file needs to be downloaded
//@limit Number of bytes which need to be downloaded starting from the "offset" position before the download will automatically be canceled; use 0 to download without a limit
//@synchronous If false, this request returns file state just after the download has been started. If true, this request returns file state only after
//-the download has succeeded, has failed, has been canceled or a new downloadFile request with different offset/limit parameters was sent
//@synchronous Pass true to return response only after the file download has succeeded, has failed, has been canceled, or a new downloadFile request with different offset/limit parameters was sent; pass false to return file state immediately, just after the download has been started
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 synchronous:Bool = File;
//@description Returns file downloaded prefix size from a given offset, in bytes @file_id Identifier of the file @offset Offset from which downloaded prefix size needs to be calculated
@ -5077,6 +5153,39 @@ readFilePart file_id:int32 offset:int32 count:int32 = FilePart;
//@description Deletes a file from the TDLib file cache @file_id Identifier of the file to delete
deleteFile file_id:int32 = Ok;
//@description Adds a file from a message to the list of file downloads. Download progress and completion of the download will be notified through updateFile updates.
//-If message database is used, the list of file downloads is persistent across application restarts. The downloading is independent from download using downloadFile, i.e. it continues if downloadFile is canceled or is used to download a part of the file
//@file_id Identifier of the file to download
//@chat_id Chat identifier of the message with the file
//@message_id Message identifier
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile/addFileToDownloads was called will be downloaded first
addFileToDownloads file_id:int32 chat_id:int53 message_id:int53 priority:int32 = File;
//@description Changes pause state of a file in the file download list
//@file_id Identifier of the downloaded file
//@is_paused Pass true if the download is paused
toggleDownloadIsPaused file_id:int32 is_paused:Bool = Ok;
//@description Changes pause state of all files in the file download list @are_paused Pass true to pause all downloads; pass false to unpause them
toggleAllDownloadsArePaused are_paused:Bool = Ok;
//@description Removes a file from the file download list @file_id Identifier of the downloaded file @delete_from_cache Pass true to delete the file from the TDLib file cache
removeFileFromDownloads file_id:int32 delete_from_cache:Bool = Ok;
//@description Removes all files from the file download list
//@only_active Pass true to remove only active downloads, including paused
//@only_completed Pass true to remove only completed downloads
//@delete_from_cache Pass true to delete the file from the TDLib file cache
removeAllFilesFromDownloads only_active:Bool only_completed:Bool delete_from_cache:Bool = Ok;
//@description Searches for files in the file download list or recently downloaded files from the list
//@query Query to search for; may be empty to return all downloaded files
//@only_active Pass true to search only for active downloads, including paused
//@only_completed Pass true to search only for completed downloads
//@offset Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results
//@limit The maximum number of files to be returned
searchFileDownloads query:string only_active:Bool only_completed:Bool offset:string limit:int32 = FoundFileDownloads;
//@description Returns information about a file with messages exported from another app @message_file_head Beginning of the message file; up to 100 first lines
getMessageFileType message_file_head:string = MessageFileType;
@ -5100,7 +5209,7 @@ replacePrimaryChatInviteLink chat_id:int53 = ChatInviteLink;
//@name Invite link name; 0-32 characters
//@expiration_date Point in time (Unix timestamp) when the link will expire; pass 0 if never
//@member_limit The maximum number of chat members that can join the chat via the link simultaneously; 0-99999; pass 0 if not limited
//@creates_join_request True, if the link only creates join request. If true, member_limit must not be specified
//@creates_join_request Pass true if users joining the chat via the link need to be approved by chat administrators. In this case, member_limit must be 0
createChatInviteLink chat_id:int53 name:string expiration_date:int32 member_limit:int32 creates_join_request:Bool = ChatInviteLink;
//@description Edits a non-primary invite link for a chat. Available for basic groups, supergroups, and channels. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links
@ -5109,7 +5218,7 @@ createChatInviteLink chat_id:int53 name:string expiration_date:int32 member_limi
//@name Invite link name; 0-32 characters
//@expiration_date Point in time (Unix timestamp) when the link will expire; pass 0 if never
//@member_limit The maximum number of chat members that can join the chat via the link simultaneously; 0-99999; pass 0 if not limited
//@creates_join_request True, if the link only creates join request. If true, member_limit must not be specified
//@creates_join_request Pass true if users joining the chat via the link need to be approved by chat administrators. In this case, member_limit must be 0
editChatInviteLink chat_id:int53 invite_link:string name:string expiration_date:int32 member_limit:int32 creates_join_request:Bool = ChatInviteLink;
//@description Returns information about an invite link. Requires administrator privileges and can_invite_users right in the chat to get own links and owner privileges to get other links
@ -5161,17 +5270,17 @@ joinChatByInviteLink invite_link:string = Chat;
//@limit The maximum number of requests to join the chat to return
getChatJoinRequests chat_id:int53 invite_link:string query:string offset_request:chatJoinRequest limit:int32 = ChatJoinRequests;
//@description Handles a pending join request in a chat @chat_id Chat identifier @user_id Identifier of the user that sent the request @approve True, if the request is approved. Otherwise the request is declined
//@description Handles a pending join request in a chat @chat_id Chat identifier @user_id Identifier of the user that sent the request @approve Pass true to approve the request; pass false to decline it
processChatJoinRequest chat_id:int53 user_id:int53 approve:Bool = Ok;
//@description Handles all pending join requests for a given link in a chat
//@chat_id Chat identifier
//@invite_link Invite link for which to process join requests. If empty, all join requests will be processed. Requires administrator privileges and can_invite_users right in the chat for own links and owner privileges for other links
//@approve True, if the requests are approved. Otherwise the requests are declined
//@approve Pass true to approve all requests; pass false to decline them
processChatJoinRequests chat_id:int53 invite_link:string approve:Bool = Ok;
//@description Creates a new call @user_id Identifier of the user to be called @protocol The call protocols supported by the application @is_video True, if a video call needs to be created
//@description Creates a new call @user_id Identifier of the user to be called @protocol The call protocols supported by the application @is_video Pass true to create a video call
createCall user_id:int53 protocol:callProtocol is_video:Bool = CallId;
//@description Accepts an incoming call @call_id Call identifier @protocol The call protocols supported by the application
@ -5180,7 +5289,7 @@ acceptCall call_id:int32 protocol:callProtocol = Ok;
//@description Sends call signaling data @call_id Call identifier @data The data
sendCallSignalingData call_id:int32 data:bytes = Ok;
//@description Discards a call @call_id Call identifier @is_disconnected True, if the user was disconnected @duration The call duration, in seconds @is_video True, if the call was a video call @connection_id Identifier of the connection used during the call
//@description Discards a call @call_id Call identifier @is_disconnected Pass true if the user was disconnected @duration The call duration, in seconds @is_video Pass true if the call was a video call @connection_id Identifier of the connection used during the call
discardCall call_id:int32 is_disconnected:Bool duration:int32 is_video:Bool connection_id:int64 = Ok;
//@description Sends a call rating @call_id Call identifier @rating Call rating; 1-5 @comment An optional user comment if the rating is less than 5 @problems List of the exact types of problems with the call, specified by the user
@ -5200,7 +5309,14 @@ setVideoChatDefaultParticipant chat_id:int53 default_participant_id:MessageSende
//@chat_id Chat identifier, in which the video chat will be created
//@title Group call title; if empty, chat title will be used
//@start_date Point in time (Unix timestamp) when the group call is supposed to be started by an administrator; 0 to start the video chat immediately. The date must be at least 10 seconds and at most 8 days in the future
createVideoChat chat_id:int53 title:string start_date:int32 = GroupCallId;
//@is_rtmp_stream Pass true to create an RTMP stream instead of an ordinary video chat; requires creator privileges
createVideoChat chat_id:int53 title:string start_date:int32 is_rtmp_stream:Bool = GroupCallId;
//@description Returns RTMP URL for streaming to the chat; requires creator privileges @chat_id Chat identifier
getVideoChatRtmpUrl chat_id:int53 = RtmpUrl;
//@description Replaces the current RTMP URL for streaming to the chat; requires creator privileges @chat_id Chat identifier
replaceVideoChatRtmpUrl chat_id:int53 = RtmpUrl;
//@description Returns information about a group call @group_call_id Group call identifier
getGroupCall group_call_id:int32 = GroupCall;
@ -5217,8 +5333,8 @@ toggleGroupCallEnabledStartNotification group_call_id:int32 enabled_start_notifi
//@participant_id Identifier of a group call participant, which will be used to join the call; pass null to join as self; video chats only
//@audio_source_id Caller audio channel synchronization source identifier; received from tgcalls
//@payload Group call join payload; received from tgcalls
//@is_muted True, if the user's microphone is muted
//@is_my_video_enabled True, if the user's video is enabled
//@is_muted Pass true to join the call with muted microphone
//@is_my_video_enabled Pass true if the user's video is enabled
//@invite_hash If non-empty, invite hash to be used to join the group call without being muted by administrators
joinGroupCall group_call_id:int32 participant_id:MessageSender audio_source_id:int32 payload:string is_muted:Bool is_my_video_enabled:Bool invite_hash:string = Text;
@ -5267,11 +5383,11 @@ toggleGroupCallIsMyVideoPaused group_call_id:int32 is_my_video_paused:Bool = Ok;
toggleGroupCallIsMyVideoEnabled group_call_id:int32 is_my_video_enabled:Bool = Ok;
//@description Informs TDLib that speaking state of a participant of an active group has changed @group_call_id Group call identifier
//@audio_source Group call participant's synchronization audio source identifier, or 0 for the current user @is_speaking True, if the user is speaking
//@audio_source Group call participant's synchronization audio source identifier, or 0 for the current user @is_speaking Pass true if the user is speaking
setGroupCallParticipantIsSpeaking group_call_id:int32 audio_source:int32 is_speaking:Bool = Ok;
//@description Toggles whether a participant of an active group call is muted, unmuted, or allowed to unmute themselves
//@group_call_id Group call identifier @participant_id Participant identifier @is_muted Pass true if the user must be muted and false otherwise
//@group_call_id Group call identifier @participant_id Participant identifier @is_muted Pass true to mute the user; pass false to unmute the them
toggleGroupCallParticipantIsMuted group_call_id:int32 participant_id:MessageSender is_muted:Bool = Ok;
//@description Changes volume level of a participant of an active group call. If the current user can manage the group call, then the participant's volume level will be changed for all users with the default volume level
@ -5294,6 +5410,9 @@ leaveGroupCall group_call_id:int32 = Ok;
//@description Ends a group call. Requires groupCall.can_be_managed @group_call_id Group call identifier
endGroupCall group_call_id:int32 = Ok;
//@description Returns information about available group call streams @group_call_id Group call identifier
getGroupCallStreams group_call_id:int32 = GroupCallStreams;
//@description Returns a file with a segment of a group call stream in a modified OGG format for audio or MPEG-4 format for video
//@group_call_id Group call identifier
//@time_offset Point in time when the stream segment begins; Unix timestamp in milliseconds
@ -5308,17 +5427,17 @@ toggleMessageSenderIsBlocked sender_id:MessageSender is_blocked:Bool = Ok;
//@description Blocks an original sender of a message in the Replies chat
//@message_id The identifier of an incoming message in the Replies chat
//@delete_message Pass true if the message must be deleted
//@delete_all_messages Pass true if all messages from the same sender must be deleted
//@report_spam Pass true if the sender must be reported to the Telegram moderators
//@delete_message Pass true to delete the message
//@delete_all_messages Pass true to delete all messages from the same sender
//@report_spam Pass true to report the sender to the Telegram moderators
blockMessageSenderFromReplies message_id:int53 delete_message:Bool delete_all_messages:Bool report_spam:Bool = Ok;
//@description Returns users and chats that were blocked by the current user @offset Number of users and chats to skip in the result; must be non-negative @limit The maximum number of users and chats to return; up to 100
getBlockedMessageSenders offset:int32 limit:int32 = MessageSenders;
//@description Adds a user to the contact list or edits an existing contact by their user identifier @contact The contact to add or edit; phone number can be empty and needs to be specified only if known, vCard is ignored
//@share_phone_number True, if the new contact needs to be allowed to see current user's phone number. A corresponding rule to userPrivacySettingShowPhoneNumber will be added if needed. Use the field userFullInfo.need_phone_number_privacy_exception to check whether the current user needs to be asked to share their phone number
//@description Adds a user to the contact list or edits an existing contact by their user identifier @contact The contact to add or edit; phone number may be empty and needs to be specified only if known, vCard is ignored
//@share_phone_number Pass true to share the current user's phone number with the new contact. A corresponding rule to userPrivacySettingShowPhoneNumber will be added if needed. Use the field userFullInfo.need_phone_number_privacy_exception to check whether the current user needs to be asked to share their phone number
addContact contact:contact share_phone_number:Bool = Ok;
//@description Adds new contacts or edits existing contacts by their phone numbers; contacts' user identifiers are ignored @contacts The list of contacts to import or edit; contacts' vCard are ignored and are not imported
@ -5344,6 +5463,9 @@ changeImportedContacts contacts:vector<contact> = ImportedContacts;
clearImportedContacts = Ok;
//@description Searches a user by their phone number @phone_number Phone number to search for
searchUserByPhoneNumber phone_number:string = User;
//@description Shares the phone number of the current user with a mutual contact. Supposed to be called when the user clicks on chatActionBarSharePhoneNumber @user_id Identifier of the user with whom to share the phone number. The user must be a mutual contact
sharePhoneNumber user_id:int53 = Ok;
@ -5419,7 +5541,7 @@ removeFavoriteSticker sticker:InputFile = Ok;
//@description Returns emoji corresponding to a sticker. The list is only for informational purposes, because a sticker is always sent with a fixed emoji from the corresponding Sticker object @sticker Sticker file identifier
getStickerEmojis sticker:InputFile = Emojis;
//@description Searches for emojis by keywords. Supported only if the file database is enabled @text Text to search for @exact_match True, if only emojis, which exactly match text needs to be returned @input_language_codes List of possible IETF language tags of the user's input language; may be empty if unknown
//@description Searches for emojis by keywords. Supported only if the file database is enabled @text Text to search for @exact_match Pass true if only emojis, which exactly match the text, needs to be returned @input_language_codes List of possible IETF language tags of the user's input language; may be empty if unknown
searchEmojis text:string exact_match:Bool input_language_codes:vector<string> = Emojis;
//@description Returns an animated emoji corresponding to a given emoji. Returns a 404 error if the emoji has no animated emoji @emoji The emoji
@ -5454,7 +5576,7 @@ removeRecentHashtag hashtag:string = Ok;
//@description Returns a web page preview by the text of the message. Do not call this function too often. Returns a 404 error if the web page has no preview @text Message text with formatting
getWebPagePreview text:formattedText = WebPage;
//@description Returns an instant view version of a web page if available. Returns a 404 error if the web page has no instant view page @url The web page URL @force_full If true, the full instant view for the web page will be returned
//@description Returns an instant view version of a web page if available. Returns a 404 error if the web page has no instant view page @url The web page URL @force_full Pass true to get full instant view for the web page
getWebPageInstantView url:string force_full:Bool = WebPageInstantView;
@ -5513,10 +5635,10 @@ terminateSession session_id:int64 = Ok;
//@description Terminates all other sessions of the current user
terminateAllOtherSessions = Ok;
//@description Toggles whether a session can accept incoming calls @session_id Session identifier @can_accept_calls True, if incoming calls can be accepted by the session
//@description Toggles whether a session can accept incoming calls @session_id Session identifier @can_accept_calls Pass true to allow accepting incoming calls by the session; pass false otherwise
toggleSessionCanAcceptCalls session_id:int64 can_accept_calls:Bool = Ok;
//@description Toggles whether a session can accept incoming secret chats @session_id Session identifier @can_accept_secret_chats True, if incoming secret chats can be accepted by the session
//@description Toggles whether a session can accept incoming secret chats @session_id Session identifier @can_accept_secret_chats Pass true to allow accepring secret chats by the session; pass false otherwise
toggleSessionCanAcceptSecretChats session_id:int64 can_accept_secret_chats:Bool = Ok;
//@description Changes the period of inactivity after which sessions will automatically be terminated @inactive_session_ttl_days New number of days of inactivity before sessions will be automatically terminated; 1-366 days
@ -5576,7 +5698,7 @@ getPaymentForm chat_id:int53 message_id:int53 theme:paymentFormTheme = PaymentFo
//@chat_id Chat identifier of the Invoice message
//@message_id Message identifier
//@order_info The order information, provided by the user; pass null if empty
//@allow_save True, if the order information can be saved
//@allow_save Pass true to save the order information
validateOrderInfo chat_id:int53 message_id:int53 order_info:orderInfo allow_save:Bool = ValidatedOrderInfo;
//@description Sends a filled-out payment form to the bot for final verification @chat_id Chat identifier of the Invoice message @message_id Message identifier
@ -5587,10 +5709,10 @@ sendPaymentForm chat_id:int53 message_id:int53 payment_form_id:int64 order_info_
//@description Returns information about a successful payment @chat_id Chat identifier of the PaymentSuccessful message @message_id Message identifier
getPaymentReceipt chat_id:int53 message_id:int53 = PaymentReceipt;
//@description Returns saved order info, if any
//@description Returns saved order information. Returns a 404 error if there is no saved order information
getSavedOrderInfo = OrderInfo;
//@description Deletes saved order info
//@description Deletes saved order information
deleteSavedOrderInfo = Ok;
//@description Deletes saved credentials for all payment provider bots
@ -5601,7 +5723,7 @@ deleteSavedCredentials = Ok;
getSupportUser = User;
//@description Returns backgrounds installed by the user @for_dark_theme True, if the backgrounds must be ordered for dark theme
//@description Returns backgrounds installed by the user @for_dark_theme Pass true to order returned backgrounds for a dark theme
getBackgrounds for_dark_theme:Bool = Backgrounds;
//@description Constructs a persistent HTTP URL for a background @name Background name @type Background type
@ -5613,7 +5735,7 @@ searchBackground name:string = Background;
//@description Changes the background selected by the user; adds background to the list of installed backgrounds
//@background The input background to use; pass null to create a new filled backgrounds or to remove the current background
//@type Background type; pass null to use the default type of the remote background or to remove the current background
//@for_dark_theme True, if the background is chosen for dark theme
//@for_dark_theme Pass true if the background is changed for a dark theme
setBackground background:InputBackground type:BackgroundType for_dark_theme:Bool = Background;
//@description Removes background from the list of installed backgrounds @background_id The background identifier
@ -5623,7 +5745,7 @@ removeBackground background_id:int64 = Ok;
resetBackgrounds = Ok;
//@description Returns information about the current localization target. This is an offline request if only_local is true. Can be called before authorization @only_local If true, returns only locally available information without sending network requests
//@description Returns information about the current localization target. This is an offline request if only_local is true. Can be called before authorization @only_local Pass true to get only locally available information without sending network requests
getLocalizationTargetInfo only_local:Bool = LocalizationTargetInfo;
//@description Returns information about a language pack. Returned language pack identifier may be different from a provided one. Can be called before authorization @language_pack_id Language pack identifier
@ -5696,7 +5818,7 @@ deleteAccount reason:string = Ok;
removeChatActionBar chat_id:int53 = Ok;
//@description Reports a chat to the Telegram moderators. A chat can be reported only from the chat action bar, or if chat.can_be_reported
//@chat_id Chat identifier @message_ids Identifiers of reported messages, if any @reason The reason for reporting the chat @text Additional report details; 0-1024 characters
//@chat_id Chat identifier @message_ids Identifiers of reported messages; may be empty to report the whole chat @reason The reason for reporting the chat @text Additional report details; 0-1024 characters
reportChat chat_id:int53 message_ids:vector<int53> reason:ChatReportReason text:string = Ok;
//@description Reports a chat photo to the Telegram moderators. A chat photo can be reported only if chat.can_be_reported
@ -5729,7 +5851,7 @@ getMemoryStatistics full:Bool = MemoryStatistics;
//@description Optimizes storage usage, i.e. deletes some files and returns new storage usage statistics. Secret thumbnails can't be deleted
//@size Limit on the total size of files after deletion, in bytes. Pass -1 to use the default limit
//@ttl Limit on the time that has passed since the last time a file was accessed (or creation time for some filesystems). Pass -1 to use the default limit
//@count Limit on the total count of files after deletion. Pass -1 to use the default limit
//@count Limit on the total number of files after deletion. Pass -1 to use the default limit
//@immunity_delay The amount of time after the creation of a file during which it can't be deleted, in seconds. Pass -1 to use the default value
//@file_types If non-empty, only files with the given types are considered. By default, all types except thumbnails, profile photos, stickers and wallpapers are deleted
//@chat_ids If non-empty, only files from the given chats are considered. Use 0 as chat identifier to delete files not belonging to any chat (e.g., profile photos)
@ -5743,7 +5865,7 @@ optimizeStorage size:int53 ttl:int32 count:int32 immunity_delay:int32 file_types
//-Network type is used to check whether the library can use the network at all and also for collecting detailed network data usage statistics @type The new network type; pass null to set network type to networkTypeOther
setNetworkType type:NetworkType = Ok;
//@description Returns network data usage statistics. Can be called before authorization @only_current If true, returns only data for the current library launch
//@description Returns network data usage statistics. Can be called before authorization @only_current Pass true to get statistics only for the current library launch
getNetworkStatistics only_current:Bool = NetworkStatistics;
//@description Adds the specified data to data usage statistics. Can be called before authorization @entry The network statistics entry with the data to be added to statistics
@ -5909,10 +6031,10 @@ getApplicationConfig = JsonValue;
saveApplicationLogEvent type:string chat_id:int53 data:JsonValue = Ok;
//@description Adds a proxy server for network requests. Can be called before authorization @server Proxy server IP address @port Proxy server port @enable True, if the proxy needs to be enabled @type Proxy type
//@description Adds a proxy server for network requests. Can be called before authorization @server Proxy server IP address @port Proxy server port @enable Pass true to immediately enable the proxy @type Proxy type
addProxy server:string port:int32 enable:Bool type:ProxyType = Proxy;
//@description Edits an existing proxy server for network requests. Can be called before authorization @proxy_id Proxy identifier @server Proxy server IP address @port Proxy server port @enable True, if the proxy needs to be enabled @type Proxy type
//@description Edits an existing proxy server for network requests. Can be called before authorization @proxy_id Proxy identifier @server Proxy server IP address @port Proxy server port @enable Pass true to immediately enable the proxy @type Proxy type
editProxy proxy_id:int32 server:string port:int32 enable:Bool type:ProxyType = Proxy;
//@description Enables a proxy. Only one proxy can be enabled at a time. Can be called before authorization @proxy_id Proxy identifier

View File

@ -225,6 +225,8 @@ inputReportReasonOther#c1e4a2b1 = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#cf366521 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string = UserFull;
@ -1216,7 +1218,7 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
@ -1311,6 +1313,12 @@ messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel;
phone.groupCallStreamChannels#d0e482b2 channels:Vector<GroupCallStreamChannel> = phone.GroupCallStreamChannels;
phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1439,6 +1447,7 @@ contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id
contacts.acceptContact#f831a20f id:InputUser = Updates;
contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates;
contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
@ -1598,6 +1607,7 @@ messages.setDefaultReaction#d960c4d4 reaction:string = 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.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1711,7 +1721,7 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
phone.createGroupCall#48cdc6d8 flags:# peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates;
phone.createGroupCall#48cdc6d8 flags:# rtmp_stream:flags.2?true peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates;
phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true video_stopped:flags.2?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates;
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
@ -1730,6 +1740,8 @@ phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates;
phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool;
phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = Updates;
phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates;
phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels;
phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;

View File

@ -136,7 +136,7 @@ void gen_tl_constructor_from_string(StringBuilder &sb, Slice name, const Vec &ve
return;
}
sb << " {\n";
sb << " static const std::unordered_map<Slice, int32, SliceHash> m = {\n";
sb << " static const FlatHashMap<Slice, int32, SliceHash> m = {\n";
bool is_first = true;
for (auto &p : vec) {
@ -216,10 +216,10 @@ void gen_json_converter_file(const tl::simple::Schema &schema, const std::string
sb << "#include \"td/utils/base64.h\"\n";
sb << "#include \"td/utils/common.h\"\n";
sb << "#include \"td/utils/FlatHashMap.h\"\n";
sb << "#include \"td/utils/Slice.h\"\n\n";
sb << "#include <functional>\n";
sb << "#include <unordered_map>\n\n";
sb << "#include <functional>\n\n";
}
sb << "namespace td {\n";
sb << "namespace td_api {\n";

View File

@ -169,13 +169,17 @@ class AuthData {
void on_api_response() {
if (use_pfs()) {
if (tmp_auth_key_.auth_flag()) {
tmp_auth_key_.remove_header();
}
tmp_auth_key_.remove_header();
} else {
if (main_auth_key_.auth_flag()) {
main_auth_key_.remove_header();
}
main_auth_key_.remove_header();
}
}
void on_connection_not_inited() {
if (use_pfs()) {
tmp_auth_key_.restore_header();
} else {
main_auth_key_.restore_header();
}
}

View File

@ -42,11 +42,14 @@ class AuthKey {
return have_header_ || Time::now() < header_expires_at_;
}
void remove_header() {
if (have_header_) {
if (auth_flag_ && have_header_) {
have_header_ = false;
header_expires_at_ = Time::now() + 3;
}
}
void restore_header() {
have_header_ = true;
}
double expires_at() const {
return expires_at_;

View File

@ -15,6 +15,7 @@
#include "td/net/DarwinHttp.h"
#endif
#include "td/utils/FlatHashMap.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
@ -26,7 +27,6 @@
#include "td/utils/StorerBase.h"
#include <memory>
#include <unordered_map>
#include <utility>
namespace td {
@ -67,6 +67,7 @@ class RawConnectionDefault final : public RawConnection {
bool use_quick_ack = false;
if (quick_ack_token != 0 && transport_->support_quick_ack()) {
CHECK(info.message_ack & (1u << 31));
auto tmp = quick_ack_to_token_.emplace(info.message_ack, quick_ack_token);
if (tmp.second) {
use_quick_ack = true;
@ -130,7 +131,7 @@ class RawConnectionDefault final : public RawConnection {
PublicFields extra_;
BufferedFd<SocketFd> socket_fd_;
unique_ptr<IStreamTransport> transport_;
std::unordered_map<uint32, uint64> quick_ack_to_token_;
FlatHashMap<uint32, uint64> quick_ack_to_token_;
bool has_error_{false};
unique_ptr<StatsCallback> stats_callback_;
@ -218,11 +219,15 @@ class RawConnectionDefault final : public RawConnection {
}
Status on_quick_ack(uint32 quick_ack, Callback &callback) {
if ((quick_ack & (1u << 31)) == 0) {
LOG(ERROR) << "Receive invalid quick_ack " << quick_ack;
return Status::OK();
}
auto it = quick_ack_to_token_.find(quick_ack);
if (it == quick_ack_to_token_.end()) {
LOG(WARNING) << Status::Error(PSLICE() << "Unknown quick_ack " << quick_ack);
LOG(WARNING) << "Receive unknown quick_ack " << quick_ack;
return Status::OK();
// TODO: return Status::Error(PSLICE() << "Unknown quick_ack " << quick_ack);
}
auto token = it->second;
quick_ack_to_token_.erase(it);

View File

@ -437,13 +437,13 @@ Status SessionConnection::on_packet(const MsgInfo &info, const mtproto_api::msgs
if (it == service_queries_.end()) {
return Status::Error("Unknown msgs_state_info");
}
SCOPE_EXIT {
service_queries_.erase(it);
};
if (it->second.type != ServiceQuery::GetStateInfo) {
return Status::Error("Got msg_state_info in response not to GetStateInfo");
auto query = std::move(it->second);
service_queries_.erase(it);
if (query.type != ServiceQuery::GetStateInfo) {
return Status::Error("Receive msg_state_info in response not to GetStateInfo");
}
return on_msgs_state_info(it->second.message_ids, msgs_state_info.info_);
return on_msgs_state_info(query.message_ids, msgs_state_info.info_);
}
Status SessionConnection::on_packet(const MsgInfo &info, const mtproto_api::msgs_all_info &msgs_all_info) {
@ -573,8 +573,9 @@ void SessionConnection::on_message_failed(uint64 id, Status status) {
auto cit = container_to_service_msg_.find(id);
if (cit != container_to_service_msg_.end()) {
for (auto nid : cit->second) {
on_message_failed_inner(nid);
auto message_ids = cit->second;
for (auto message_id : message_ids) {
on_message_failed_inner(message_id);
}
} else {
on_message_failed_inner(id);
@ -586,21 +587,25 @@ void SessionConnection::on_message_failed_inner(uint64 id) {
if (it == service_queries_.end()) {
return;
}
switch (it->second.type) {
auto query = std::move(it->second);
service_queries_.erase(it);
switch (query.type) {
case ServiceQuery::ResendAnswer: {
for (auto message_id : it->second.message_ids) {
for (auto message_id : query.message_ids) {
resend_answer(message_id);
}
break;
}
case ServiceQuery::GetStateInfo: {
for (auto message_id : it->second.message_ids) {
for (auto message_id : query.message_ids) {
get_state_info(message_id);
}
break;
}
default:
UNREACHABLE();
}
service_queries_.erase(id);
}
bool SessionConnection::must_flush_packet() {
@ -956,6 +961,7 @@ void SessionConnection::flush_packet() {
std::any_of(queries.begin(), queries.end(), [](const auto &query) { return query.use_quick_ack; });
{
// LOG(ERROR) << (auth_data_->get_header().empty() ? '-' : '+');
uint64 parent_message_id = 0;
auto storer = PacketStorer<CryptoImpl>(
queries, auth_data_->get_header(), std::move(to_ack), ping_id, ping_disconnect_delay() + 2, max_delay,
@ -967,11 +973,10 @@ void SessionConnection::flush_packet() {
}
if (resend_answer_id) {
service_queries_.insert({resend_answer_id, ServiceQuery{ServiceQuery::ResendAnswer, std::move(to_resend_answer)}});
service_queries_.emplace(resend_answer_id, ServiceQuery{ServiceQuery::ResendAnswer, std::move(to_resend_answer)});
}
if (get_state_info_id) {
service_queries_.insert(
{get_state_info_id, ServiceQuery{ServiceQuery::GetStateInfo, std::move(to_get_state_info)}});
service_queries_.emplace(get_state_info_id, ServiceQuery{ServiceQuery::GetStateInfo, std::move(to_get_state_info)});
}
if (ping_id != 0) {
last_ping_container_id_ = container_id;

View File

@ -11,6 +11,7 @@
#include "td/mtproto/RawConnection.h"
#include "td/utils/buffer.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Named.h"
@ -22,7 +23,6 @@
#include "td/utils/StringBuilder.h"
#include "td/utils/tl_parsers.h"
#include <unordered_map>
#include <utility>
namespace td {
@ -176,10 +176,10 @@ class SessionConnection final
vector<int64> to_resend_answer_;
vector<int64> to_cancel_answer_;
vector<int64> to_get_state_info_;
std::unordered_map<uint64, ServiceQuery> service_queries_;
FlatHashMap<uint64, ServiceQuery> service_queries_;
// nobody cleans up this map. But it should be really small.
std::unordered_map<uint64, vector<uint64>> container_to_service_msg_;
FlatHashMap<uint64, vector<uint64>> container_to_service_msg_;
double last_read_at_ = 0;
double last_ping_at_ = 0;

View File

@ -264,7 +264,7 @@ FileId AnimationsManager::dup_animation(FileId new_id, FileId old_id) {
const Animation *old_animation = get_animation(old_id);
CHECK(old_animation != nullptr);
auto &new_animation = animations_[new_id];
CHECK(!new_animation);
CHECK(new_animation == nullptr);
new_animation = make_unique<Animation>(*old_animation);
new_animation->file_id = new_id;
new_animation->thumbnail.file_id = td_->file_manager_->dup_file_id(new_animation->thumbnail.file_id);

View File

@ -18,10 +18,9 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <unordered_map>
namespace td {
class Td;
@ -144,7 +143,7 @@ class AnimationsManager final : public Actor {
Td *td_;
ActorShared<> parent_;
std::unordered_map<FileId, unique_ptr<Animation>, FileIdHash> animations_;
FlatHashMap<FileId, unique_ptr<Animation>, FileIdHash> animations_;
int32 saved_animations_limit_ = 200;
vector<FileId> saved_animation_ids_;

View File

@ -97,7 +97,7 @@ FileId AudiosManager::dup_audio(FileId new_id, FileId old_id) {
const Audio *old_audio = get_audio(old_id);
CHECK(old_audio != nullptr);
auto &new_audio = audios_[new_id];
CHECK(!new_audio);
CHECK(new_audio == nullptr);
new_audio = make_unique<Audio>(*old_audio);
new_audio->file_id = new_id;
new_audio->thumbnail.file_id = td_->file_manager_->dup_file_id(new_audio->thumbnail.file_id);

View File

@ -14,8 +14,7 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include <unordered_map>
#include "td/utils/FlatHashMap.h"
namespace td {
@ -77,7 +76,7 @@ class AudiosManager {
FileId on_get_audio(unique_ptr<Audio> new_audio, bool replace);
Td *td_;
std::unordered_map<FileId, unique_ptr<Audio>, FileIdHash> audios_;
FlatHashMap<FileId, unique_ptr<Audio>, FileIdHash> audios_;
};
} // namespace td

View File

@ -675,6 +675,7 @@ BackgroundId BackgroundManager::set_background(const td_api::InputBackground *in
}
auto file_id = r_file_id.move_as_ok();
LOG(INFO) << "Receive file " << file_id << " for input background";
CHECK(file_id.is_valid());
auto it = file_id_to_background_id_.find(file_id);
if (it != file_id_to_background_id_.end()) {
@ -975,7 +976,11 @@ void BackgroundManager::add_background(const Background &background, bool replac
LOG(INFO) << "Add " << background.id << " of " << background.type;
CHECK(background.id.is_valid());
auto *result = &backgrounds_[background.id];
auto &result_ptr = backgrounds_[background.id];
if (result_ptr == nullptr) {
result_ptr = make_unique<Background>();
}
auto *result = result_ptr.get();
FileSourceId file_source_id;
auto it = background_id_to_file_source_id_.find(background.id);
@ -1039,9 +1044,9 @@ void BackgroundManager::add_background(const Background &background, bool replac
for (auto file_id : Document(Document::Type::General, result->file_id).get_file_ids(td_)) {
td_->file_manager_->add_file_source(file_id, result->file_source_id);
}
}
file_id_to_background_id_.emplace(result->file_id, result->id);
file_id_to_background_id_.emplace(result->file_id, result->id);
}
} else {
// if file_source_id is valid, then this is a new background with result->file_id == FileId()
// then background.file_id == FileId(), then this is a fill background, which can't have file_source_id
@ -1054,7 +1059,7 @@ BackgroundManager::Background *BackgroundManager::get_background_ref(BackgroundI
if (p == backgrounds_.end()) {
return nullptr;
} else {
return &p->second;
return p->second.get();
}
}
@ -1063,7 +1068,7 @@ const BackgroundManager::Background *BackgroundManager::get_background(Backgroun
if (p == backgrounds_.end()) {
return nullptr;
} else {
return &p->second;
return p->second.get();
}
}
@ -1261,6 +1266,10 @@ td_api::object_ptr<td_api::backgrounds> BackgroundManager::get_backgrounds_objec
}
FileSourceId BackgroundManager::get_background_file_source_id(BackgroundId background_id, int64 access_hash) {
if (!background_id.is_valid()) {
return FileSourceId();
}
Background *background = get_background_ref(background_id);
if (background != nullptr) {
if (!background->file_source_id.is_valid()) {

View File

@ -19,11 +19,11 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Status.h"
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace td {
@ -162,17 +162,17 @@ class BackgroundManager final : public Actor {
void do_upload_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme,
tl_object_ptr<telegram_api::InputFile> &&input_file, Promise<Unit> &&promise);
std::unordered_map<BackgroundId, Background, BackgroundIdHash> backgrounds_;
FlatHashMap<BackgroundId, unique_ptr<Background>, BackgroundIdHash> backgrounds_;
std::unordered_map<BackgroundId, std::pair<int64, FileSourceId>, BackgroundIdHash>
FlatHashMap<BackgroundId, std::pair<int64, FileSourceId>, BackgroundIdHash>
background_id_to_file_source_id_; // id -> [access_hash, file_source_id]
std::unordered_map<string, BackgroundId> name_to_background_id_;
FlatHashMap<string, BackgroundId> name_to_background_id_;
std::unordered_map<FileId, BackgroundId, FileIdHash> file_id_to_background_id_;
FlatHashMap<FileId, BackgroundId, FileIdHash> file_id_to_background_id_;
std::unordered_set<string> loaded_from_database_backgrounds_;
std::unordered_map<string, vector<Promise<Unit>>> being_loaded_from_database_backgrounds_;
FlatHashSet<string> loaded_from_database_backgrounds_;
FlatHashMap<string, vector<Promise<Unit>>> being_loaded_from_database_backgrounds_;
BackgroundId set_background_id_[2];
BackgroundType set_background_type_[2];
@ -192,7 +192,7 @@ class BackgroundManager final : public Actor {
: type_(type), for_dark_theme_(for_dark_theme), promise_(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedFileInfo, FileIdHash> being_uploaded_files_;
FlatHashMap<FileId, UploadedFileInfo, FileIdHash> being_uploaded_files_;
BackgroundId max_local_background_id_;
vector<BackgroundId> local_background_ids_[2];

View File

@ -25,12 +25,13 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/SliceBuilder.h"
#include <tuple>
#include <unordered_set>
namespace td {
@ -253,7 +254,7 @@ void CallActor::rate_call(int32 rating, string comment, vector<td_api::object_pt
comment.clear();
}
std::unordered_set<string> tags;
FlatHashSet<string> tags;
for (auto &problem : problems) {
if (problem == nullptr) {
continue;
@ -636,7 +637,9 @@ void CallActor::try_send_request_query() {
auto timeout = static_cast<double>(call_receive_timeout_ms) * 0.001;
LOG(INFO) << "Set call timeout to " << timeout;
set_timeout_in(timeout);
query->total_timeout_limit_ = max(timeout, 10.0);
query->total_timeout_limit_ =
static_cast<int32>(clamp(call_receive_timeout_ms + 999, static_cast<int64>(10000), static_cast<int64>(100000))) /
1000;
request_query_ref_ = query.get_weak();
send_with_promise(std::move(query),
PromiseCreator::lambda([actor_id = actor_id(this)](Result<NetQueryPtr> r_net_query) {

View File

@ -13,11 +13,10 @@
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <map>
#include <unordered_map>
namespace td {
@ -48,7 +47,7 @@ class CallManager final : public Actor {
};
std::map<int64, CallInfo> call_info_;
int32 next_call_id_{1};
std::unordered_map<CallId, ActorOwn<CallActor>, CallIdHash> id_to_actor_;
FlatHashMap<CallId, ActorOwn<CallActor>, CallIdHash> id_to_actor_;
ActorId<CallActor> get_call_actor(CallId call_id);
CallId create_call_actor();

50
td/telegram/ChainId.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/DialogId.h"
#include "td/telegram/FolderId.h"
#include "td/telegram/FullMessageId.h"
#include "td/telegram/MessageContentType.h"
#include "td/telegram/PollId.h"
#include "td/utils/common.h"
#include <functional>
namespace td {
class ChainId {
uint64 id = 0;
public:
ChainId(DialogId dialog_id, MessageContentType message_content_type)
: id((static_cast<uint64>(dialog_id.get()) << 10) + get_message_content_chain_id(message_content_type)) {
}
ChainId(DialogId dialog_id) : id((static_cast<uint64>(dialog_id.get()) << 10) + 10) {
}
ChainId(FullMessageId full_message_id) : ChainId(full_message_id.get_dialog_id()) {
id += static_cast<uint64>(full_message_id.get_message_id().get()) << 10;
}
ChainId(FolderId folder_id) : id((static_cast<uint64>(folder_id.get() + (1 << 30)) << 10)) {
}
ChainId(PollId poll_id) : id(static_cast<uint64>(poll_id.get())) {
}
ChainId(const string &str) : id(std::hash<string>()(str)) {
}
uint64 get() const {
return id;
}
};
} // namespace td

View File

@ -16,6 +16,8 @@
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/ExitGuard.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/MpscPollableQueue.h"
@ -29,8 +31,6 @@
#include <memory>
#include <mutex>
#include <queue>
#include <unordered_map>
#include <unordered_set>
namespace td {
@ -204,8 +204,8 @@ class ClientManager::Impl final {
unique_ptr<ConcurrentScheduler> concurrent_scheduler_;
ClientId client_id_{0};
Td::Options options_;
std::unordered_set<int32> pending_clients_;
std::unordered_map<int32, ActorOwn<Td>> tds_;
FlatHashSet<int32> pending_clients_;
FlatHashMap<int32, ActorOwn<Td>> tds_;
};
class Client::Impl final {
@ -264,7 +264,7 @@ class MultiTd final : public Actor {
private:
Td::Options options_;
std::unordered_map<int32, ActorOwn<Td>> tds_;
FlatHashMap<int32, ActorOwn<Td>> tds_;
};
class TdReceiver {
@ -580,7 +580,7 @@ class ClientManager::Impl final {
std::shared_ptr<MultiImpl> impl;
bool is_closed = false;
};
std::unordered_map<ClientId, MultiImplInfo> impls_;
FlatHashMap<ClientId, MultiImplInfo> impls_;
TdReceiver receiver_;
};

View File

@ -10,6 +10,7 @@
#include "td/telegram/td_api_json.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/SliceBuilder.h"
@ -119,7 +120,7 @@ static ClientManager *get_manager() {
}
static std::mutex extra_mutex;
static std::unordered_map<int64, string> extra;
static FlatHashMap<int64, string> extra;
static std::atomic<uint64> extra_id{1};
int json_create_client_id() {

View File

@ -8,13 +8,13 @@
#include "td/telegram/Client.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Slice.h"
#include <atomic>
#include <cstdint>
#include <mutex>
#include <string>
#include <unordered_map>
namespace td {
@ -30,7 +30,7 @@ class ClientJson final {
private:
Client client_;
std::mutex mutex_; // for extra_
std::unordered_map<std::int64_t, std::string> extra_;
FlatHashMap<std::int64_t, std::string> extra_;
std::atomic<std::uint64_t> extra_id_{1};
};

View File

@ -48,6 +48,7 @@
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/emoji.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/format.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
@ -63,7 +64,6 @@
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
namespace td {
@ -261,33 +261,45 @@ static ActorOwn<> get_simple_config_dns(Slice address, Slice host, Promise<Simpl
name = is_test ? "tapv3.stel.com" : "apv3.stel.com";
}
auto get_config = [](HttpQuery &http_query) -> Result<string> {
VLOG(config_recoverer) << "Receive DNS response " << http_query.content_;
TRY_RESULT(json, json_decode(http_query.content_));
if (json.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object");
}
auto &answer_object = json.get_object();
TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false));
auto &answer_array = answer.get_array();
vector<string> parts;
for (auto &answer_part : answer_array) {
if (answer_part.type() != JsonValue::Type::Object) {
auto get_data = [](JsonValue &answer) -> Result<string> {
auto &answer_array = answer.get_array();
vector<string> parts;
for (auto &answer_part : answer_array) {
if (answer_part.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object");
}
auto &data_object = answer_part.get_object();
TRY_RESULT(part, get_json_object_string_field(data_object, "data", false));
parts.push_back(std::move(part));
}
if (parts.size() != 2) {
return Status::Error("Expected data in two parts");
}
string data;
if (parts[0].size() < parts[1].size()) {
data = parts[1] + parts[0];
} else {
data = parts[0] + parts[1];
}
return data;
};
if (!http_query.get_arg("Answer").empty()) {
VLOG(config_recoverer) << "Receive DNS response " << http_query.get_arg("Answer");
TRY_RESULT(answer, json_decode(http_query.get_arg("Answer")));
if (answer.type() != JsonValue::Type::Array) {
return Status::Error("Expected JSON array");
}
return get_data(answer);
} else {
VLOG(config_recoverer) << "Receive DNS response " << http_query.content_;
TRY_RESULT(json, json_decode(http_query.content_));
if (json.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object");
}
auto &data_object = answer_part.get_object();
TRY_RESULT(part, get_json_object_string_field(data_object, "data", false));
parts.push_back(std::move(part));
auto &answer_object = json.get_object();
TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false));
return get_data(answer);
}
if (parts.size() != 2) {
return Status::Error("Expected data in two parts");
}
string data;
if (parts[0].size() < parts[1].size()) {
data = parts[1] + parts[0];
} else {
data = parts[0] + parts[1];
}
return data;
};
return get_simple_config_impl(std::move(promise), scheduler_id,
PSTRING() << "https://" << address << "?name=" << url_encode(name) << "&type=TXT",
@ -800,10 +812,9 @@ class ConfigRecoverer final : public Actor {
if (need_simple_config) {
ref_cnt_++;
VLOG(config_recoverer) << "Ask simple config with turn " << simple_config_turn_;
auto promise =
PromiseCreator::lambda([actor_id = actor_shared(this)](Result<SimpleConfigResult> r_simple_config) {
send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false);
});
auto promise = PromiseCreator::lambda([self = actor_shared(this)](Result<SimpleConfigResult> r_simple_config) {
send_closure(self, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false);
});
auto get_simple_config = [&] {
switch (simple_config_turn_ % 10) {
case 6:
@ -1495,8 +1506,8 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
vector<tl_object_ptr<telegram_api::jsonObjectValue>> new_values;
string ignored_restriction_reasons;
vector<string> dice_emojis;
std::unordered_map<string, size_t> dice_emoji_index;
std::unordered_map<string, string> dice_emoji_success_value;
FlatHashMap<string, size_t> dice_emoji_index;
FlatHashMap<string, string> dice_emoji_success_value;
vector<string> emoji_sounds;
string animation_search_provider;
string animation_search_emojis;
@ -1560,7 +1571,7 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
auto success_values = std::move(static_cast<telegram_api::jsonObject *>(value)->value_);
for (auto &success_value : success_values) {
CHECK(success_value != nullptr);
if (success_value->value_->get_id() == telegram_api::jsonObject::ID) {
if (!success_value->key_.empty() && success_value->value_->get_id() == telegram_api::jsonObject::ID) {
int32 dice_value = -1;
int32 frame_start = -1;
for (auto &dice_key_value :
@ -1785,11 +1796,12 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
if (!dice_emojis.empty()) {
vector<string> dice_success_values(dice_emojis.size());
for (auto &it : dice_emoji_success_value) {
if (dice_emoji_index.find(it.first) == dice_emoji_index.end()) {
auto dice_emoji_it = dice_emoji_index.find(it.first);
if (dice_emoji_it == dice_emoji_index.end()) {
LOG(ERROR) << "Can't find emoji " << it.first;
continue;
}
dice_success_values[dice_emoji_index[it.first]] = it.second;
dice_success_values[dice_emoji_it->second] = it.second;
}
shared_config.set_option_string("dice_success_values", implode(dice_success_values, ','));
shared_config.set_option_string("dice_emojis", implode(dice_emojis, '\x01'));

View File

@ -69,6 +69,8 @@
#include <algorithm>
#include <limits>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace td {
@ -188,6 +190,50 @@ class AddContactQuery final : public Td::ResultHandler {
}
};
class ResolvePhoneQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
string phone_number_;
public:
explicit ResolvePhoneQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &phone_number) {
phone_number_ = phone_number;
send_query(G()->net_query_creator().create(telegram_api::contacts_resolvePhone(phone_number)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::contacts_resolvePhone>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(DEBUG) << "Receive result for ResolvePhoneQuery: " << to_string(ptr);
td_->contacts_manager_->on_get_users(std::move(ptr->users_), "ResolvePhoneQuery");
td_->contacts_manager_->on_get_chats(std::move(ptr->chats_), "ResolvePhoneQuery");
DialogId dialog_id(ptr->peer_);
if (dialog_id.get_type() != DialogType::User) {
LOG(ERROR) << "Receive " << dialog_id << " by " << phone_number_;
return on_error(Status::Error(500, "Receive invalid response"));
}
td_->contacts_manager_->on_resolved_phone_number(phone_number_, dialog_id.get_user_id());
promise_.set_value(Unit());
}
void on_error(Status status) final {
if (status.message() == Slice("PHONE_NOT_OCCUPIED")) {
td_->contacts_manager_->on_resolved_phone_number(phone_number_, UserId());
return promise_.set_value(Unit());
}
promise_.set_error(std::move(status));
}
};
class AcceptContactQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
UserId user_id_;
@ -3035,7 +3081,7 @@ class GetMegagroupStatsQuery final : public Td::ResultHandler {
flags |= telegram_api::stats_getMegagroupStats::DARK_MASK;
}
send_query(G()->net_query_creator().create(
telegram_api::stats_getMegagroupStats(flags, false /*ignored*/, std::move(input_channel)), dc_id));
telegram_api::stats_getMegagroupStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -3073,7 +3119,7 @@ class GetBroadcastStatsQuery final : public Td::ResultHandler {
flags |= telegram_api::stats_getBroadcastStats::DARK_MASK;
}
send_query(G()->net_query_creator().create(
telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), dc_id));
telegram_api::stats_getBroadcastStats(flags, false /*ignored*/, std::move(input_channel)), {}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -3086,7 +3132,7 @@ class GetBroadcastStatsQuery final : public Td::ResultHandler {
for (auto &info : result->recent_message_interactions_) {
td_->messages_manager_->on_update_message_interaction_info({DialogId(channel_id_), MessageId(info->message_id_)},
info->view_count_, info->forward_count_, false,
nullptr, false, nullptr);
nullptr);
}
promise_.set_value(std::move(result));
}
@ -3124,7 +3170,7 @@ class GetMessageStatsQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(
telegram_api::stats_getMessageStats(flags, false /*ignored*/, std::move(input_channel),
message_id.get_server_message_id().get()),
dc_id));
{}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -3155,7 +3201,7 @@ class LoadAsyncGraphQuery final : public Td::ResultHandler {
if (x != 0) {
flags |= telegram_api::stats_loadAsyncGraph::X_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), dc_id));
send_query(G()->net_query_creator().create(telegram_api::stats_loadAsyncGraph(flags, token, x), {}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -3442,13 +3488,8 @@ void ContactsManager::on_channel_participant_cache_timeout(ChannelId channel_id)
auto &participants = channel_participants_it->second.participants_;
auto min_access_date = G()->unix_time() - CHANNEL_PARTICIPANT_CACHE_TIME;
for (auto it = participants.begin(); it != participants.end();) {
if (it->second.last_access_date_ < min_access_date) {
it = participants.erase(it);
} else {
++it;
}
}
table_remove_if(participants,
[min_access_date](const auto &it) { return it.second.last_access_date_ < min_access_date; });
if (participants.empty()) {
channel_participants_.erase(channel_participants_it);
@ -3631,6 +3672,7 @@ void ContactsManager::User::parse(ParserT &parser) {
cache_version = 0;
}
clean_phone_number(phone_number);
if (first_name.empty() && last_name.empty()) {
first_name = phone_number;
}
@ -5664,6 +5706,42 @@ std::pair<int32, vector<UserId>> ContactsManager::search_contacts(const string &
return {narrow_cast<int32>(result.first), std::move(user_ids)};
}
UserId ContactsManager::search_user_by_phone_number(string phone_number, Promise<Unit> &&promise) {
clean_phone_number(phone_number);
if (phone_number.empty()) {
promise.set_error(Status::Error(200, "Phone number is invalid"));
return UserId();
}
auto it = resolved_phone_numbers_.find(phone_number);
if (it != resolved_phone_numbers_.end()) {
promise.set_value(Unit());
return it->second;
}
td_->create_handler<ResolvePhoneQuery>(std::move(promise))->send(phone_number);
return UserId();
}
void ContactsManager::on_resolved_phone_number(const string &phone_number, UserId user_id) {
if (!user_id.is_valid()) {
resolved_phone_numbers_.emplace(phone_number, user_id); // negative cache
return;
}
auto it = resolved_phone_numbers_.find(phone_number);
if (it != resolved_phone_numbers_.end()) {
if (it->second != user_id) {
LOG(ERROR) << "Resolve phone number \"" << phone_number << "\" to " << user_id << ", but have it in "
<< it->second;
}
return;
}
LOG(ERROR) << "Resolve phone number \"" << phone_number << "\" to " << user_id << ", but doesn't have it";
resolved_phone_numbers_[phone_number] = user_id; // always update cached value
}
void ContactsManager::share_phone_number(UserId user_id, Promise<Unit> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
@ -6164,9 +6242,11 @@ void ContactsManager::send_update_profile_photo_query(FileId file_id, int64 old_
void ContactsManager::upload_profile_photo(FileId file_id, bool is_animation, double main_frame_timestamp,
Promise<Unit> &&promise, int reupload_count, vector<int> bad_parts) {
CHECK(file_id.is_valid());
CHECK(uploaded_profile_photos_.find(file_id) == uploaded_profile_photos_.end());
uploaded_profile_photos_.emplace(
file_id, UploadedProfilePhoto{main_frame_timestamp, is_animation, reupload_count, std::move(promise)});
bool is_inserted = uploaded_profile_photos_
.emplace(file_id, UploadedProfilePhoto{main_frame_timestamp, is_animation, reupload_count,
std::move(promise)})
.second;
CHECK(is_inserted);
LOG(INFO) << "Ask to upload " << (is_animation ? "animated" : "static") << " profile photo " << file_id
<< " with bad parts " << bad_parts;
// TODO use force_reupload if reupload_count >= 1, replace reupload_count with is_reupload
@ -6693,7 +6773,7 @@ void ContactsManager::report_channel_spam(ChannelId channel_id, const vector<Mes
return promise.set_error(Status::Error(400, "Spam can be reported only by chat administrators"));
}
std::unordered_map<DialogId, vector<MessageId>, DialogIdHash> server_message_ids;
FlatHashMap<DialogId, vector<MessageId>, DialogIdHash> server_message_ids;
for (auto &message_id : message_ids) {
if (message_id.is_valid_scheduled()) {
return promise.set_error(Status::Error(400, "Can't report scheduled messages"));
@ -7431,15 +7511,22 @@ void ContactsManager::delete_all_revoked_dialog_invite_links(DialogId dialog_id,
td_->create_handler<DeleteRevokedExportedChatInvitesQuery>(std::move(promise))->send(dialog_id, creator_user_id);
}
void ContactsManager::check_dialog_invite_link(const string &invite_link, Promise<Unit> &&promise) const {
if (invite_link_infos_.count(invite_link) > 0) {
return promise.set_value(Unit());
void ContactsManager::check_dialog_invite_link(const string &invite_link, bool force, Promise<Unit> &&promise) {
auto it = invite_link_infos_.find(invite_link);
if (it != invite_link_infos_.end()) {
auto dialog_id = it->second->dialog_id;
if (!force && dialog_id.get_type() == DialogType::Chat && !get_chat_is_active(dialog_id.get_chat_id())) {
invite_link_infos_.erase(it);
} else {
return promise.set_value(Unit());
}
}
if (!DialogInviteLink::is_valid_invite_link(invite_link)) {
return promise.set_error(Status::Error(400, "Wrong invite link"));
}
CHECK(!invite_link.empty());
td_->create_handler<CheckChatInviteQuery>(std::move(promise))->send(invite_link);
}
@ -7699,10 +7786,10 @@ void ContactsManager::get_created_public_dialogs(PublicDialogType type,
vector<ChannelId> channel_ids;
for (auto &r_channel_id : r_channel_ids) {
auto channel_id = r_channel_id.move_as_ok();
add_dialog_and_dependencies(dependencies, DialogId(channel_id));
dependencies.add_dialog_and_dependencies(DialogId(channel_id));
channel_ids.push_back(channel_id);
}
if (!resolve_dependencies_force(td_, dependencies, "get_created_public_dialogs")) {
if (!dependencies.resolve_force(td_, "get_created_public_dialogs")) {
G()->td_db()->get_binlog_pmc()->erase(pmc_key);
} else {
created_public_channels_[index] = std::move(channel_ids);
@ -8067,7 +8154,7 @@ void ContactsManager::on_get_contacts(tl_object_ptr<telegram_api::contacts_Conta
}
auto contacts = move_tl_object_as<telegram_api::contacts_contacts>(new_contacts);
std::unordered_set<UserId, UserIdHash> contact_user_ids;
FlatHashSet<UserId, UserIdHash> contact_user_ids;
for (auto &user : contacts->users_) {
auto user_id = get_user_id(user);
if (!user_id.is_valid()) {
@ -8121,7 +8208,7 @@ void ContactsManager::save_contacts_to_database() {
G()->td_db()->get_binlog_pmc()->set("saved_contact_count", to_string(saved_contact_count_));
G()->td_db()->get_binlog()->force_sync(PromiseCreator::lambda([user_ids = std::move(user_ids)](Result<> result) {
if (result.is_ok()) {
LOG(INFO) << "Save contacts to database";
LOG(INFO) << "Saved contacts to database";
G()->td_db()->get_sqlite_pmc()->set(
"user_contacts", log_event_store(user_ids).as_slice().str(), PromiseCreator::lambda([](Result<> result) {
if (result.is_ok()) {
@ -8310,11 +8397,22 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
bool have_access_hash = (flags & USER_FLAG_HAS_ACCESS_HASH) != 0;
bool is_received = (flags & USER_FLAG_IS_INACCESSIBLE) == 0;
bool is_contact = (flags & USER_FLAG_IS_CONTACT) != 0;
if (!is_received && !have_user_force(user_id)) {
// we must preload information about received inaccessible users from database in order to not save
// the min-user to the database and to not override access_hash and another info
LOG(INFO) << "Receive inaccessible " << user_id;
if (!have_min_user(user_id)) {
if (!is_received) {
// we must preload received inaccessible users from database in order to not save
// the min-user to the database and to not override access_hash and another info
if (!have_user_force(user_id)) {
LOG(INFO) << "Receive inaccessible " << user_id;
}
} else if (is_contact && !are_contacts_loaded_) {
// preload contact users from database to know that is_contact didn't changed
// and the list of contacts doesn't need to be saved to the database
if (!have_user_force(user_id)) {
LOG(INFO) << "Receive contact " << user_id << " for the first time";
}
}
}
User *u = add_user(user_id, "on_get_user");
@ -8338,7 +8436,6 @@ void ContactsManager::on_get_user(tl_object_ptr<telegram_api::User> &&user_ptr,
if (is_received) {
on_update_user_online(u, user_id, std::move(user->status_));
auto is_contact = (flags & USER_FLAG_IS_CONTACT) != 0;
auto is_mutual_contact = (flags & USER_FLAG_IS_MUTUAL_CONTACT) != 0;
on_update_user_is_contact(u, user_id, is_contact, is_mutual_contact);
}
@ -8517,7 +8614,7 @@ void ContactsManager::on_binlog_user_event(BinlogEvent &&event) {
log_event_parse(log_event, event.data_).ensure();
auto user_id = log_event.user_id;
if (have_user(user_id)) {
if (have_min_user(user_id)) {
LOG(ERROR) << "Skip adding already added " << user_id;
binlog_erase(G()->td_db()->get_binlog(), event.id_);
return;
@ -8632,6 +8729,7 @@ void ContactsManager::on_load_user_from_database(UserId user_id, string value, b
return;
}
CHECK(user_id.is_valid());
if (!loaded_from_database_users_.insert(user_id).second) {
return;
}
@ -8933,6 +9031,7 @@ void ContactsManager::on_load_chat_from_database(ChatId chat_id, string value, b
return;
}
CHECK(chat_id.is_valid());
if (!loaded_from_database_chats_.insert(chat_id).second) {
return;
}
@ -9180,6 +9279,7 @@ void ContactsManager::on_load_channel_from_database(ChannelId channel_id, string
return;
}
CHECK(channel_id.is_valid());
if (!loaded_from_database_channels_.insert(channel_id).second) {
return;
}
@ -9436,6 +9536,7 @@ void ContactsManager::on_load_secret_chat_from_database(SecretChatId secret_chat
return;
}
CHECK(secret_chat_id.is_valid());
if (!loaded_from_database_secret_chats_.insert(secret_chat_id).second) {
return;
}
@ -9554,8 +9655,8 @@ void ContactsManager::on_load_user_full_from_database(UserId user_id, string val
}
Dependencies dependencies;
dependencies.user_ids.insert(user_id);
if (!resolve_dependencies_force(td_, dependencies, "on_load_user_full_from_database")) {
dependencies.add(user_id);
if (!dependencies.resolve_force(td_, "on_load_user_full_from_database")) {
users_full_.erase(user_id);
G()->td_db()->get_sqlite_pmc()->erase(get_user_full_database_key(user_id), Auto());
return;
@ -9652,14 +9753,14 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val
}
Dependencies dependencies;
dependencies.chat_ids.insert(chat_id);
dependencies.user_ids.insert(chat_full->creator_user_id);
dependencies.add(chat_id);
dependencies.add(chat_full->creator_user_id);
for (auto &participant : chat_full->participants) {
add_message_sender_dependencies(dependencies, participant.dialog_id_);
dependencies.user_ids.insert(participant.inviter_user_id_);
dependencies.add_message_sender_dependencies(participant.dialog_id_);
dependencies.add(participant.inviter_user_id_);
}
dependencies.user_ids.insert(chat_full->invite_link.get_creator_user_id());
if (!resolve_dependencies_force(td_, dependencies, "on_load_chat_full_from_database")) {
dependencies.add(chat_full->invite_link.get_creator_user_id());
if (!dependencies.resolve_force(td_, "on_load_chat_full_from_database")) {
chats_full_.erase(chat_id);
G()->td_db()->get_sqlite_pmc()->erase(get_chat_full_database_key(chat_id), Auto());
return;
@ -9761,14 +9862,16 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s
}
Dependencies dependencies;
dependencies.channel_ids.insert(channel_id);
dependencies.add(channel_id);
// must not depend on the linked_dialog_id itself, because message database can be disabled
// the Dialog will be forcely created in update_channel_full
add_dialog_dependencies(dependencies, DialogId(channel_full->linked_channel_id));
dependencies.chat_ids.insert(channel_full->migrated_from_chat_id);
dependencies.user_ids.insert(channel_full->bot_user_ids.begin(), channel_full->bot_user_ids.end());
dependencies.user_ids.insert(channel_full->invite_link.get_creator_user_id());
if (!resolve_dependencies_force(td_, dependencies, source)) {
dependencies.add_dialog_dependencies(DialogId(channel_full->linked_channel_id));
dependencies.add(channel_full->migrated_from_chat_id);
for (auto bot_user_id : channel_full->bot_user_ids) {
dependencies.add(bot_user_id);
}
dependencies.add(channel_full->invite_link.get_creator_user_id());
if (!dependencies.resolve_force(td_, source)) {
channels_full_.erase(channel_id);
G()->td_db()->get_sqlite_pmc()->erase(get_channel_full_database_key(channel_id), Auto());
return;
@ -9909,6 +10012,12 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
});
u->is_photo_changed = false;
}
if (u->is_phone_number_changed) {
if (!u->phone_number.empty() && !td_->auth_manager_->is_bot()) {
resolved_phone_numbers_[u->phone_number] = user_id;
}
u->is_phone_number_changed = false;
}
if (u->is_status_changed && user_id != get_my_id()) {
auto left_time = get_user_was_online(u, user_id) - G()->server_time_cached();
if (left_time >= 0 && left_time < 30 * 86400) {
@ -9933,7 +10042,8 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo
}
LOG(DEBUG) << "Update " << user_id << ": need_save_to_database = " << u->need_save_to_database
<< ", is_changed = " << u->is_changed << ", is_status_changed = " << u->is_status_changed;
<< ", is_changed = " << u->is_changed << ", is_status_changed = " << u->is_status_changed
<< ", from_binlog = " << from_binlog << ", from_database = " << from_database;
u->need_save_to_database |= u->is_changed;
if (u->need_save_to_database) {
if (!from_database) {
@ -11080,8 +11190,18 @@ void ContactsManager::on_update_user_phone_number(UserId user_id, string &&phone
}
void ContactsManager::on_update_user_phone_number(User *u, UserId user_id, string &&phone_number) {
clean_phone_number(phone_number);
if (u->phone_number != phone_number) {
if (!u->phone_number.empty()) {
auto it = resolved_phone_numbers_.find(u->phone_number);
CHECK(it != resolved_phone_numbers_.end());
if (it->second == user_id) {
resolved_phone_numbers_.erase(it);
}
}
u->phone_number = std::move(phone_number);
u->is_phone_number_changed = true;
LOG(DEBUG) << "Phone number has changed for " << user_id;
u->is_changed = true;
}
@ -11166,7 +11286,7 @@ void ContactsManager::register_user_photo(User *u, UserId user_id, const Photo &
CHECK(file_type == FileType::Photo);
CHECK(u != nullptr);
auto photo_id = photo.id.get();
if (u->photo_ids.emplace(photo_id).second) {
if (photo_id != 0 && u->photo_ids.emplace(photo_id).second) {
VLOG(file_references) << "Register photo " << photo_id << " of " << user_id;
if (user_id == get_my_id()) {
my_photo_file_id_[photo_id] = first_file_id;
@ -11663,7 +11783,7 @@ void ContactsManager::update_user_online_member_count(User *u) {
auto now = G()->unix_time_cached();
vector<DialogId> expired_dialog_ids;
for (auto &it : u->online_member_dialogs) {
for (const auto &it : u->online_member_dialogs) {
auto dialog_id = it.first;
auto time = it.second;
if (time < now - MessagesManager::ONLINE_MEMBER_COUNT_CACHE_EXPIRE_TIME) {
@ -11720,6 +11840,7 @@ void ContactsManager::update_dialog_online_member_count(const vector<DialogParti
if (td_->auth_manager_->is_bot()) {
return;
}
CHECK(dialog_id.is_valid());
int32 online_member_count = 0;
int32 time = G()->unix_time();
@ -12076,8 +12197,11 @@ void ContactsManager::on_get_channel_participants(
search_among_dialogs(dialog_ids, additional_query, additional_limit);
total_count = result_dialog_ids.first;
std::unordered_set<DialogId, DialogIdHash> result_dialog_ids_set(result_dialog_ids.second.begin(),
result_dialog_ids.second.end());
FlatHashSet<DialogId, DialogIdHash> result_dialog_ids_set;
for (auto result_dialog_id : result_dialog_ids.second) {
CHECK(result_dialog_id.is_valid());
result_dialog_ids_set.insert(result_dialog_id);
}
auto all_participants = std::move(result);
result.clear();
for (auto &participant : all_participants) {
@ -12102,6 +12226,8 @@ bool ContactsManager::have_channel_participant_cache(ChannelId channel_id) const
void ContactsManager::add_channel_participant_to_cache(ChannelId channel_id,
const DialogParticipant &dialog_participant,
bool allow_replace) {
CHECK(channel_id.is_valid());
CHECK(dialog_participant.is_valid());
auto &participants = channel_participants_[channel_id];
if (participants.participants_.empty()) {
channel_participant_cache_timeout_.set_timeout_in(channel_id.get(), CHANNEL_PARTICIPANT_CACHE_TIME);
@ -12401,7 +12527,7 @@ void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_dr
if (channel_full != nullptr) {
do_invalidate_channel_full(channel_full, channel_id, need_drop_slow_mode_delay);
update_channel_full(channel_full, channel_id, "invalidate_channel_full");
} else {
} else if (channel_id.is_valid()) {
invalidated_channels_full_.insert(channel_id);
}
}
@ -12767,7 +12893,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link,
invite_link_info = make_unique<InviteLinkInfo>();
}
invite_link_info->dialog_id = dialog_id;
if (accessible_before != 0) {
if (accessible_before != 0 && dialog_id.is_valid()) {
auto &access = dialog_access_by_invite_link_[dialog_id];
access.invite_links.insert(invite_link);
if (access.accessible_before < accessible_before) {
@ -13832,7 +13958,7 @@ void ContactsManager::update_contacts_hints(const User *u, UserId user_id, bool
int64 key = user_id.get();
string old_value = contacts_hints_.key_to_string(key);
string new_value = is_contact ? u->first_name + " " + u->last_name + " " + u->username : "";
string new_value = is_contact ? (PSTRING() << u->first_name << ' ' << u->last_name << ' ' << u->username) : string();
if (new_value != old_value) {
if (is_contact) {
@ -13845,7 +13971,7 @@ void ContactsManager::update_contacts_hints(const User *u, UserId user_id, bool
if (G()->parameters().use_chat_info_db) {
// update contacts database
if (!are_contacts_loaded_) {
if (!from_database && load_contacts_queries_.empty()) {
if (!from_database && load_contacts_queries_.empty() && is_contact && u->is_is_contact_changed) {
search_contacts("", std::numeric_limits<int32>::max(), Auto());
}
} else {
@ -14117,6 +14243,9 @@ void ContactsManager::reload_user_full(UserId user_id) {
void ContactsManager::send_get_user_full_query(UserId user_id, tl_object_ptr<telegram_api::InputUser> &&input_user,
Promise<Unit> &&promise, const char *source) {
LOG(INFO) << "Get full " << user_id << " from " << source;
if (!user_id.is_valid()) {
return promise.set_error(Status::Error(500, "Invalid user_id"));
}
auto send_query =
PromiseCreator::lambda([td = td_, input_user = std::move(input_user)](Result<Promise<Unit>> &&promise) mutable {
if (promise.is_ok() && !G()->close_flag()) {
@ -14214,6 +14343,10 @@ void ContactsManager::reload_user_profile_photo(UserId user_id, int64 photo_id,
}
FileSourceId ContactsManager::get_user_profile_photo_file_source_id(UserId user_id, int64 photo_id) {
if (!user_id.is_valid()) {
return FileSourceId();
}
auto u = get_user(user_id);
if (u != nullptr && u->photo_ids.count(photo_id) != 0) {
VLOG(file_references) << "Don't need to create file source for photo " << photo_id << " of " << user_id;
@ -14230,6 +14363,10 @@ FileSourceId ContactsManager::get_user_profile_photo_file_source_id(UserId user_
}
FileSourceId ContactsManager::get_chat_full_file_source_id(ChatId chat_id) {
if (!chat_id.is_valid()) {
return FileSourceId();
}
if (get_chat_full(chat_id) != nullptr) {
VLOG(file_references) << "Don't need to create file source for full " << chat_id;
// chat full was already added, source ID was registered and shouldn't be needed
@ -14245,6 +14382,10 @@ FileSourceId ContactsManager::get_chat_full_file_source_id(ChatId chat_id) {
}
FileSourceId ContactsManager::get_channel_full_file_source_id(ChannelId channel_id) {
if (!channel_id.is_valid()) {
return FileSourceId();
}
if (get_channel_full(channel_id) != nullptr) {
VLOG(file_references) << "Don't need to create file source for full " << channel_id;
// channel full was already added, source ID was registered and shouldn't be needed
@ -14404,6 +14545,9 @@ void ContactsManager::reload_chat_full(ChatId chat_id, Promise<Unit> &&promise)
void ContactsManager::send_get_chat_full_query(ChatId chat_id, Promise<Unit> &&promise, const char *source) {
LOG(INFO) << "Get full " << chat_id << " from " << source;
if (!chat_id.is_valid()) {
return promise.set_error(Status::Error(500, "Invalid chat_id"));
}
auto send_query = PromiseCreator::lambda([td = td_, chat_id](Result<Promise<Unit>> &&promise) {
if (promise.is_ok() && !G()->close_flag()) {
td->create_handler<GetFullChatQuery>(promise.move_as_ok())->send(chat_id);
@ -14611,7 +14755,7 @@ const MinChannel *ContactsManager::get_min_channel(ChannelId channel_id) const {
}
void ContactsManager::add_min_channel(ChannelId channel_id, const MinChannel &min_channel) {
if (have_channel(channel_id) || have_min_channel(channel_id)) {
if (have_channel(channel_id) || have_min_channel(channel_id) || !channel_id.is_valid()) {
return;
}
min_channels_[channel_id] = td::make_unique<MinChannel>(min_channel);
@ -15256,7 +15400,9 @@ void ContactsManager::finish_get_channel_participant(ChannelId channel_id, Dialo
Promise<DialogParticipant> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
LOG(INFO) << "Receive a member " << dialog_participant.dialog_id_ << " of a channel " << channel_id;
CHECK(dialog_participant.is_valid()); // checked in GetChannelParticipantQuery
LOG(INFO) << "Receive " << dialog_participant.dialog_id_ << " as a member of a channel " << channel_id;
dialog_participant.status_.update_restrictions();
if (have_channel_participant_cache(channel_id)) {
@ -15423,6 +15569,7 @@ void ContactsManager::on_update_dialog_administrators(DialogId dialog_id, vector
bool have_access, bool from_database) {
LOG(INFO) << "Update administrators in " << dialog_id << " to " << format::as_array(administrators);
if (have_access) {
CHECK(dialog_id.is_valid());
std::sort(administrators.begin(), administrators.end(),
[](const DialogAdministrator &lhs, const DialogAdministrator &rhs) {
return lhs.get_user_id().get() < rhs.get_user_id().get();
@ -16442,30 +16589,30 @@ void ContactsManager::get_current_state(vector<td_api::object_ptr<td_api::Update
}
}
for (auto &it : users_) {
for (const auto &it : users_) {
updates.push_back(td_api::make_object<td_api::updateUser>(get_user_object(it.first, it.second.get())));
}
for (auto &it : channels_) {
for (const auto &it : channels_) {
updates.push_back(td_api::make_object<td_api::updateSupergroup>(get_supergroup_object(it.first, it.second.get())));
}
for (auto &it : chats_) { // chat object can contain channel_id, so it must be sent after channels
for (const auto &it : chats_) { // chat object can contain channel_id, so it must be sent after channels
updates.push_back(
td_api::make_object<td_api::updateBasicGroup>(get_basic_group_object_const(it.first, it.second.get())));
}
for (auto &it : secret_chats_) { // secret chat object contains user_id, so it must be sent after users
for (const auto &it : secret_chats_) { // secret chat object contains user_id, so it must be sent after users
updates.push_back(
td_api::make_object<td_api::updateSecretChat>(get_secret_chat_object_const(it.first, it.second.get())));
}
for (auto &it : users_full_) {
for (const auto &it : users_full_) {
updates.push_back(td_api::make_object<td_api::updateUserFullInfo>(
it.first.get(), get_user_full_info_object(it.first, it.second.get())));
}
for (auto &it : channels_full_) {
for (const auto &it : channels_full_) {
updates.push_back(td_api::make_object<td_api::updateSupergroupFullInfo>(
it.first.get(), get_supergroup_full_info_object(it.second.get(), it.first)));
}
for (auto &it : chats_full_) {
for (const auto &it : chats_full_) {
updates.push_back(td_api::make_object<td_api::updateBasicGroupFullInfo>(
it.first.get(), get_basic_group_full_info_object(it.second.get())));
}

View File

@ -40,6 +40,8 @@
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Hints.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
@ -47,8 +49,6 @@
#include <functional>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace td {
@ -307,6 +307,10 @@ class ContactsManager final : public Actor {
void on_update_contacts_reset();
UserId search_user_by_phone_number(string phone_number, Promise<Unit> &&promise);
void on_resolved_phone_number(const string &phone_number, UserId user_id);
void share_phone_number(UserId user_id, Promise<Unit> &&promise);
void search_dialogs_nearby(const Location &location, Promise<td_api::object_ptr<td_api::chatsNearby>> &&promise);
@ -419,7 +423,7 @@ class ContactsManager final : public Actor {
void delete_all_revoked_dialog_invite_links(DialogId dialog_id, UserId creator_user_id, Promise<Unit> &&promise);
void check_dialog_invite_link(const string &invite_link, Promise<Unit> &&promise) const;
void check_dialog_invite_link(const string &invite_link, bool force, Promise<Unit> &&promise);
void import_dialog_invite_link(const string &invite_link, Promise<DialogId> &&promise);
@ -627,9 +631,9 @@ class ContactsManager final : public Actor {
string language_code;
std::unordered_set<int64> photo_ids;
FlatHashSet<int64> photo_ids;
std::unordered_map<DialogId, int32, DialogIdHash> online_member_dialogs; // id -> time
FlatHashMap<DialogId, int32, DialogIdHash> online_member_dialogs; // id -> time
static constexpr uint32 CACHE_VERSION = 4;
uint32 cache_version = 0;
@ -657,6 +661,7 @@ class ContactsManager final : public Actor {
bool is_name_changed = true;
bool is_username_changed = true;
bool is_photo_changed = true;
bool is_phone_number_changed = true;
bool is_is_contact_changed = true;
bool is_is_deleted_changed = true;
bool is_changed = true; // have new changes that need to be sent to the client and database
@ -1193,7 +1198,7 @@ class ContactsManager final : public Actor {
static bool is_valid_username(const string &username);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name, string &&username);
static void on_update_user_phone_number(User *u, UserId user_id, string &&phone_number);
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);
void on_update_user_is_contact(User *u, UserId user_id, bool is_contact, bool is_mutual_contact);
@ -1640,42 +1645,42 @@ class ContactsManager final : public Actor {
UserId support_user_id_;
int32 my_was_online_local_ = 0;
std::unordered_map<UserId, unique_ptr<User>, UserIdHash> users_;
std::unordered_map<UserId, unique_ptr<UserFull>, UserIdHash> users_full_;
std::unordered_map<UserId, UserPhotos, UserIdHash> user_photos_;
mutable std::unordered_set<UserId, UserIdHash> unknown_users_;
std::unordered_map<UserId, tl_object_ptr<telegram_api::UserProfilePhoto>, UserIdHash> pending_user_photos_;
FlatHashMap<UserId, unique_ptr<User>, UserIdHash> users_;
FlatHashMap<UserId, unique_ptr<UserFull>, UserIdHash> users_full_;
FlatHashMap<UserId, UserPhotos, UserIdHash> user_photos_;
mutable FlatHashSet<UserId, UserIdHash> unknown_users_;
FlatHashMap<UserId, tl_object_ptr<telegram_api::UserProfilePhoto>, UserIdHash> pending_user_photos_;
struct UserIdPhotoIdHash {
std::size_t operator()(const std::pair<UserId, int64> &pair) const {
return UserIdHash()(pair.first) * 2023654985u + std::hash<int64>()(pair.second);
}
};
std::unordered_map<std::pair<UserId, int64>, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_;
std::unordered_map<int64, FileId> my_photo_file_id_;
FlatHashMap<std::pair<UserId, int64>, FileSourceId, UserIdPhotoIdHash> user_profile_photo_file_source_ids_;
FlatHashMap<int64, FileId> my_photo_file_id_;
std::unordered_map<ChatId, unique_ptr<Chat>, ChatIdHash> chats_;
std::unordered_map<ChatId, unique_ptr<ChatFull>, ChatIdHash> chats_full_;
mutable std::unordered_set<ChatId, ChatIdHash> unknown_chats_;
std::unordered_map<ChatId, FileSourceId, ChatIdHash> chat_full_file_source_ids_;
FlatHashMap<ChatId, unique_ptr<Chat>, ChatIdHash> chats_;
FlatHashMap<ChatId, unique_ptr<ChatFull>, ChatIdHash> chats_full_;
mutable FlatHashSet<ChatId, ChatIdHash> unknown_chats_;
FlatHashMap<ChatId, FileSourceId, ChatIdHash> chat_full_file_source_ids_;
std::unordered_map<ChannelId, unique_ptr<MinChannel>, ChannelIdHash> min_channels_;
std::unordered_map<ChannelId, unique_ptr<Channel>, ChannelIdHash> channels_;
std::unordered_map<ChannelId, unique_ptr<ChannelFull>, ChannelIdHash> channels_full_;
mutable std::unordered_set<ChannelId, ChannelIdHash> unknown_channels_;
std::unordered_set<ChannelId, ChannelIdHash> invalidated_channels_full_;
std::unordered_map<ChannelId, FileSourceId, ChannelIdHash> channel_full_file_source_ids_;
FlatHashMap<ChannelId, unique_ptr<MinChannel>, ChannelIdHash> min_channels_;
FlatHashMap<ChannelId, unique_ptr<Channel>, ChannelIdHash> channels_;
FlatHashMap<ChannelId, unique_ptr<ChannelFull>, ChannelIdHash> channels_full_;
mutable FlatHashSet<ChannelId, ChannelIdHash> unknown_channels_;
FlatHashSet<ChannelId, ChannelIdHash> invalidated_channels_full_;
FlatHashMap<ChannelId, FileSourceId, ChannelIdHash> channel_full_file_source_ids_;
std::unordered_map<SecretChatId, unique_ptr<SecretChat>, SecretChatIdHash> secret_chats_;
mutable std::unordered_set<SecretChatId, SecretChatIdHash> unknown_secret_chats_;
FlatHashMap<SecretChatId, unique_ptr<SecretChat>, SecretChatIdHash> secret_chats_;
mutable FlatHashSet<SecretChatId, SecretChatIdHash> unknown_secret_chats_;
std::unordered_map<UserId, vector<SecretChatId>, UserIdHash> secret_chats_with_user_;
FlatHashMap<UserId, vector<SecretChatId>, UserIdHash> secret_chats_with_user_;
struct DialogAccessByInviteLink {
std::unordered_set<string> invite_links;
FlatHashSet<string> invite_links;
int32 accessible_before = 0;
};
std::unordered_map<string, unique_ptr<InviteLinkInfo>> invite_link_infos_;
std::unordered_map<DialogId, DialogAccessByInviteLink, DialogIdHash> dialog_access_by_invite_link_;
FlatHashMap<string, unique_ptr<InviteLinkInfo>> invite_link_infos_;
FlatHashMap<DialogId, DialogAccessByInviteLink, DialogIdHash> dialog_access_by_invite_link_;
bool created_public_channels_inited_[2] = {false, false};
vector<ChannelId> created_public_channels_[2];
@ -1687,28 +1692,28 @@ class ContactsManager final : public Actor {
bool inactive_channels_inited_ = false;
vector<ChannelId> inactive_channels_;
std::unordered_map<UserId, vector<Promise<Unit>>, UserIdHash> load_user_from_database_queries_;
std::unordered_set<UserId, UserIdHash> loaded_from_database_users_;
std::unordered_set<UserId, UserIdHash> unavailable_user_fulls_;
FlatHashMap<UserId, vector<Promise<Unit>>, UserIdHash> load_user_from_database_queries_;
FlatHashSet<UserId, UserIdHash> loaded_from_database_users_;
FlatHashSet<UserId, UserIdHash> unavailable_user_fulls_;
std::unordered_map<ChatId, vector<Promise<Unit>>, ChatIdHash> load_chat_from_database_queries_;
std::unordered_set<ChatId, ChatIdHash> loaded_from_database_chats_;
std::unordered_set<ChatId, ChatIdHash> unavailable_chat_fulls_;
FlatHashMap<ChatId, vector<Promise<Unit>>, ChatIdHash> load_chat_from_database_queries_;
FlatHashSet<ChatId, ChatIdHash> loaded_from_database_chats_;
FlatHashSet<ChatId, ChatIdHash> unavailable_chat_fulls_;
std::unordered_map<ChannelId, vector<Promise<Unit>>, ChannelIdHash> load_channel_from_database_queries_;
std::unordered_set<ChannelId, ChannelIdHash> loaded_from_database_channels_;
std::unordered_set<ChannelId, ChannelIdHash> unavailable_channel_fulls_;
FlatHashMap<ChannelId, vector<Promise<Unit>>, ChannelIdHash> load_channel_from_database_queries_;
FlatHashSet<ChannelId, ChannelIdHash> loaded_from_database_channels_;
FlatHashSet<ChannelId, ChannelIdHash> unavailable_channel_fulls_;
std::unordered_map<SecretChatId, vector<Promise<Unit>>, SecretChatIdHash> load_secret_chat_from_database_queries_;
std::unordered_set<SecretChatId, SecretChatIdHash> loaded_from_database_secret_chats_;
FlatHashMap<SecretChatId, vector<Promise<Unit>>, SecretChatIdHash> load_secret_chat_from_database_queries_;
FlatHashSet<SecretChatId, SecretChatIdHash> loaded_from_database_secret_chats_;
QueryCombiner get_user_full_queries_{"GetUserFullCombiner", 2.0};
QueryCombiner get_chat_full_queries_{"GetChatFullCombiner", 2.0};
std::unordered_map<DialogId, vector<DialogAdministrator>, DialogIdHash> dialog_administrators_;
FlatHashMap<DialogId, vector<DialogAdministrator>, DialogIdHash> dialog_administrators_;
std::unordered_map<DialogId, vector<SuggestedAction>, DialogIdHash> dialog_suggested_actions_;
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> dismiss_suggested_action_queries_;
FlatHashMap<DialogId, vector<SuggestedAction>, DialogIdHash> dialog_suggested_actions_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> dismiss_suggested_action_queries_;
class UploadProfilePhotoCallback;
std::shared_ptr<UploadProfilePhotoCallback> upload_profile_photo_callback_;
@ -1726,7 +1731,7 @@ class ContactsManager final : public Actor {
, promise(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedProfilePhoto, FileIdHash> uploaded_profile_photos_; // file_id -> promise
FlatHashMap<FileId, UploadedProfilePhoto, FileIdHash> uploaded_profile_photos_; // file_id -> promise
struct ImportContactsTask {
Promise<Unit> promise_;
@ -1734,11 +1739,13 @@ class ContactsManager final : public Actor {
vector<UserId> imported_user_ids_;
vector<int32> unimported_contact_invites_;
};
std::unordered_map<int64, unique_ptr<ImportContactsTask>> import_contact_tasks_;
FlatHashMap<int64, unique_ptr<ImportContactsTask>> import_contact_tasks_;
std::unordered_map<int64, std::pair<vector<UserId>, vector<int32>>> imported_contacts_;
FlatHashMap<int64, std::pair<vector<UserId>, vector<int32>>> imported_contacts_;
std::unordered_map<ChannelId, vector<DialogParticipant>, ChannelIdHash> cached_channel_participants_;
FlatHashMap<ChannelId, vector<DialogParticipant>, ChannelIdHash> cached_channel_participants_;
FlatHashMap<string, UserId> resolved_phone_numbers_;
// bot-administrators only
struct ChannelParticipantInfo {
@ -1747,9 +1754,9 @@ class ContactsManager final : public Actor {
int32 last_access_date_ = 0;
};
struct ChannelParticipants {
std::unordered_map<DialogId, ChannelParticipantInfo, DialogIdHash> participants_;
FlatHashMap<DialogId, ChannelParticipantInfo, DialogIdHash> participants_;
};
std::unordered_map<ChannelId, ChannelParticipants, ChannelIdHash> channel_participants_;
FlatHashMap<ChannelId, ChannelParticipants, ChannelIdHash> channel_participants_;
bool are_contacts_loaded_ = false;
int32 next_contacts_sync_date_ = 0;
@ -1770,17 +1777,17 @@ class ContactsManager final : public Actor {
vector<DialogNearby> users_nearby_;
vector<DialogNearby> channels_nearby_;
std::unordered_set<UserId, UserIdHash> all_users_nearby_;
FlatHashSet<UserId, UserIdHash> all_users_nearby_;
int32 location_visibility_expire_date_ = 0;
int32 pending_location_visibility_expire_date_ = -1;
bool is_set_location_visibility_request_sent_ = false;
Location last_user_location_;
std::unordered_map<ChannelId, ChannelId, ChannelIdHash> linked_channel_ids_;
FlatHashMap<ChannelId, ChannelId, ChannelIdHash> linked_channel_ids_;
std::unordered_set<UserId, UserIdHash> restricted_user_ids_;
std::unordered_set<ChannelId, ChannelIdHash> restricted_channel_ids_;
FlatHashSet<UserId, UserIdHash> restricted_user_ids_;
FlatHashSet<ChannelId, ChannelIdHash> restricted_channel_ids_;
vector<Contact> next_all_imported_contacts_;
vector<size_t> imported_contacts_unique_id_;

View File

@ -8,6 +8,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
@ -163,6 +164,9 @@ void CountryInfoManager::do_get_countries(string language_code, bool is_recursiv
if (is_recursive) {
return promise.set_error(Status::Error(500, "Requested data is inaccessible"));
}
if (language_code.empty()) {
return promise.set_error(Status::Error(400, "Invalid language code specified"));
}
load_country_list(language_code, 0,
PromiseCreator::lambda([actor_id = actor_id(this), language_code,
promise = std::move(promise)](Result<Unit> &&result) mutable {
@ -176,7 +180,7 @@ void CountryInfoManager::do_get_countries(string language_code, bool is_recursiv
void CountryInfoManager::get_phone_number_info(string phone_number_prefix,
Promise<td_api::object_ptr<td_api::phoneNumberInfo>> &&promise) {
td::remove_if(phone_number_prefix, [](char c) { return c < '0' || c > '9'; });
clean_phone_number(phone_number_prefix);
if (phone_number_prefix.empty()) {
return promise.set_value(td_api::make_object<td_api::phoneNumberInfo>(nullptr, string(), string()));
}
@ -203,6 +207,9 @@ void CountryInfoManager::do_get_phone_number_info(string phone_number_prefix, st
if (is_recursive) {
return promise.set_error(Status::Error(500, "Requested data is inaccessible"));
}
if (language_code.empty()) {
return promise.set_error(Status::Error(400, "Invalid language code specified"));
}
load_country_list(language_code, 0,
PromiseCreator::lambda([actor_id = actor_id(this), phone_number_prefix, language_code,
promise = std::move(promise)](Result<Unit> &&result) mutable {
@ -216,7 +223,7 @@ void CountryInfoManager::do_get_phone_number_info(string phone_number_prefix, st
td_api::object_ptr<td_api::phoneNumberInfo> CountryInfoManager::get_phone_number_info_sync(const string &language_code,
string phone_number_prefix) {
td::remove_if(phone_number_prefix, [](char c) { return !is_digit(c); });
clean_phone_number(phone_number_prefix);
if (phone_number_prefix.empty()) {
return td_api::make_object<td_api::phoneNumberInfo>(nullptr, string(), string());
}
@ -537,6 +544,6 @@ const CountryInfoManager::CountryList *CountryInfoManager::get_country_list(Coun
int32 CountryInfoManager::manager_count_ = 0;
std::mutex CountryInfoManager::country_mutex_;
std::unordered_map<string, unique_ptr<CountryInfoManager::CountryList>> CountryInfoManager::countries_;
FlatHashMap<string, unique_ptr<CountryInfoManager::CountryList>> CountryInfoManager::countries_;
} // namespace td

View File

@ -13,11 +13,11 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <mutex>
#include <unordered_map>
namespace td {
@ -76,9 +76,9 @@ class CountryInfoManager final : public Actor {
static int32 manager_count_;
static std::unordered_map<string, unique_ptr<CountryList>> countries_;
static FlatHashMap<string, unique_ptr<CountryList>> countries_;
std::unordered_map<string, vector<Promise<Unit>>> pending_load_country_queries_;
FlatHashMap<string, vector<Promise<Unit>>> pending_load_country_queries_;
Td *td_;
ActorShared<> parent_;

View File

@ -16,25 +16,55 @@
namespace td {
void add_dialog_and_dependencies(Dependencies &dependencies, DialogId dialog_id) {
if (dialog_id.is_valid() && dependencies.dialog_ids.insert(dialog_id).second) {
add_dialog_dependencies(dependencies, dialog_id);
void Dependencies::add(UserId user_id) {
if (user_id.is_valid()) {
user_ids.insert(user_id);
}
}
void add_dialog_dependencies(Dependencies &dependencies, DialogId dialog_id) {
void Dependencies::add(ChatId chat_id) {
if (chat_id.is_valid()) {
chat_ids.insert(chat_id);
}
}
void Dependencies::add(ChannelId channel_id) {
if (channel_id.is_valid()) {
channel_ids.insert(channel_id);
}
}
void Dependencies::add(SecretChatId secret_chat_id) {
if (secret_chat_id.is_valid()) {
secret_chat_ids.insert(secret_chat_id);
}
}
void Dependencies::add(WebPageId web_page_id) {
if (web_page_id.is_valid()) {
web_page_ids.insert(web_page_id);
}
}
void Dependencies::add_dialog_and_dependencies(DialogId dialog_id) {
if (dialog_id.is_valid() && dialog_ids.insert(dialog_id).second) {
add_dialog_dependencies(dialog_id);
}
}
void Dependencies::add_dialog_dependencies(DialogId dialog_id) {
switch (dialog_id.get_type()) {
case DialogType::User:
dependencies.user_ids.insert(dialog_id.get_user_id());
add(dialog_id.get_user_id());
break;
case DialogType::Chat:
dependencies.chat_ids.insert(dialog_id.get_chat_id());
add(dialog_id.get_chat_id());
break;
case DialogType::Channel:
dependencies.channel_ids.insert(dialog_id.get_channel_id());
add(dialog_id.get_channel_id());
break;
case DialogType::SecretChat:
dependencies.secret_chat_ids.insert(dialog_id.get_secret_chat_id());
add(dialog_id.get_secret_chat_id());
break;
case DialogType::None:
break;
@ -43,30 +73,30 @@ void add_dialog_dependencies(Dependencies &dependencies, DialogId dialog_id) {
}
}
void add_message_sender_dependencies(Dependencies &dependencies, DialogId dialog_id) {
void Dependencies::add_message_sender_dependencies(DialogId dialog_id) {
if (dialog_id.get_type() == DialogType::User) {
dependencies.user_ids.insert(dialog_id.get_user_id());
add(dialog_id.get_user_id());
} else {
add_dialog_and_dependencies(dependencies, dialog_id);
add_dialog_and_dependencies(dialog_id);
}
}
bool resolve_dependencies_force(Td *td, const Dependencies &dependencies, const char *source) {
bool Dependencies::resolve_force(Td *td, const char *source) const {
bool success = true;
for (auto user_id : dependencies.user_ids) {
if (user_id.is_valid() && !td->contacts_manager_->have_user_force(user_id)) {
for (auto user_id : user_ids) {
if (!td->contacts_manager_->have_user_force(user_id)) {
LOG(ERROR) << "Can't find " << user_id << " from " << source;
success = false;
}
}
for (auto chat_id : dependencies.chat_ids) {
if (chat_id.is_valid() && !td->contacts_manager_->have_chat_force(chat_id)) {
for (auto chat_id : chat_ids) {
if (!td->contacts_manager_->have_chat_force(chat_id)) {
LOG(ERROR) << "Can't find " << chat_id << " from " << source;
success = false;
}
}
for (auto channel_id : dependencies.channel_ids) {
if (channel_id.is_valid() && !td->contacts_manager_->have_channel_force(channel_id)) {
for (auto channel_id : channel_ids) {
if (!td->contacts_manager_->have_channel_force(channel_id)) {
if (td->contacts_manager_->have_min_channel(channel_id)) {
LOG(INFO) << "Can't find " << channel_id << " from " << source << ", but have it as a min-channel";
continue;
@ -75,21 +105,21 @@ bool resolve_dependencies_force(Td *td, const Dependencies &dependencies, const
success = false;
}
}
for (auto secret_chat_id : dependencies.secret_chat_ids) {
if (secret_chat_id.is_valid() && !td->contacts_manager_->have_secret_chat_force(secret_chat_id)) {
for (auto secret_chat_id : secret_chat_ids) {
if (!td->contacts_manager_->have_secret_chat_force(secret_chat_id)) {
LOG(ERROR) << "Can't find " << secret_chat_id << " from " << source;
success = false;
}
}
for (auto dialog_id : dependencies.dialog_ids) {
if (dialog_id.is_valid() && !td->messages_manager_->have_dialog_force(dialog_id, source)) {
for (auto dialog_id : dialog_ids) {
if (!td->messages_manager_->have_dialog_force(dialog_id, source)) {
LOG(ERROR) << "Can't find " << dialog_id << " from " << source;
td->messages_manager_->force_create_dialog(dialog_id, "resolve_dependencies_force", true);
success = false;
}
}
for (auto web_page_id : dependencies.web_page_ids) {
if (web_page_id.is_valid() && !td->web_pages_manager_->have_web_page_force(web_page_id)) {
for (auto web_page_id : web_page_ids) {
if (!td->web_pages_manager_->have_web_page_force(web_page_id)) {
LOG(INFO) << "Can't find " << web_page_id << " from " << source;
success = false;
}

View File

@ -13,27 +13,42 @@
#include "td/telegram/UserId.h"
#include "td/telegram/WebPageId.h"
#include <unordered_set>
#include "td/utils/FlatHashSet.h"
namespace td {
class Td;
struct Dependencies {
std::unordered_set<UserId, UserIdHash> user_ids;
std::unordered_set<ChatId, ChatIdHash> chat_ids;
std::unordered_set<ChannelId, ChannelIdHash> channel_ids;
std::unordered_set<SecretChatId, SecretChatIdHash> secret_chat_ids;
std::unordered_set<DialogId, DialogIdHash> dialog_ids;
std::unordered_set<WebPageId, WebPageIdHash> web_page_ids;
class Dependencies {
FlatHashSet<UserId, UserIdHash> user_ids;
FlatHashSet<ChatId, ChatIdHash> chat_ids;
FlatHashSet<ChannelId, ChannelIdHash> channel_ids;
FlatHashSet<SecretChatId, SecretChatIdHash> secret_chat_ids;
FlatHashSet<DialogId, DialogIdHash> dialog_ids;
FlatHashSet<WebPageId, WebPageIdHash> web_page_ids;
public:
void add(UserId user_id);
void add(ChatId chat_id);
void add(ChannelId channel_id);
void add(SecretChatId secret_chat_id);
void add(WebPageId web_page_id);
void add_dialog_and_dependencies(DialogId dialog_id);
void add_dialog_dependencies(DialogId dialog_id);
void add_message_sender_dependencies(DialogId dialog_id);
bool resolve_force(Td *td, const char *source) const;
const FlatHashSet<DialogId, DialogIdHash> &get_dialog_ids() const {
return dialog_ids;
}
};
void add_dialog_and_dependencies(Dependencies &dependencies, DialogId dialog_id);
void add_dialog_dependencies(Dependencies &dependencies, DialogId dialog_id);
void add_message_sender_dependencies(Dependencies &dependencies, DialogId dialog_id);
bool resolve_dependencies_force(Td *td, const Dependencies &dependencies, const char *source);
} // namespace td

View File

@ -204,6 +204,10 @@ static td_api::object_ptr<td_api::ChatEventAction> get_chat_event_action_object(
auto action = move_tl_object_as<telegram_api::channelAdminLogEventActionChangeStickerSet>(action_ptr);
auto old_sticker_set_id = td->stickers_manager_->add_sticker_set(std::move(action->prev_stickerset_));
auto new_sticker_set_id = td->stickers_manager_->add_sticker_set(std::move(action->new_stickerset_));
if (!old_sticker_set_id.is_valid() || !new_sticker_set_id.is_valid()) {
LOG(ERROR) << "Skip " << to_string(action);
return nullptr;
}
return td_api::make_object<td_api::chatEventStickerSetChanged>(old_sticker_set_id.get(),
new_sticker_set_id.get());
}

View File

@ -10,11 +10,10 @@
#include "td/utils/algorithm.h"
#include "td/utils/emoji.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include <unordered_set>
namespace td {
unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(telegram_api::object_ptr<telegram_api::dialogFilter> filter,
@ -28,7 +27,7 @@ unique_ptr<DialogFilter> DialogFilter::get_dialog_filter(telegram_api::object_pt
dialog_filter->dialog_filter_id = dialog_filter_id;
dialog_filter->title = std::move(filter->title_);
dialog_filter->emoji = std::move(filter->emoticon_);
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> added_dialog_ids;
dialog_filter->pinned_dialog_ids = InputDialogId::get_input_dialog_ids(filter->pinned_peers_, &added_dialog_ids);
dialog_filter->included_dialog_ids = InputDialogId::get_input_dialog_ids(filter->include_peers_, &added_dialog_ids);
dialog_filter->excluded_dialog_ids = InputDialogId::get_input_dialog_ids(filter->exclude_peers_, &added_dialog_ids);
@ -271,7 +270,7 @@ unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogF
LOG(INFO) << "Pinned chats was not changed locally in " << dialog_filter_id << ", keep remote changes";
size_t kept_server_dialogs = 0;
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> removed_dialog_ids;
auto old_it = old_server_dialog_ids.rbegin();
for (auto &input_dialog_id : reversed(new_server_dialog_ids)) {
auto dialog_id = input_dialog_id.get_dialog_id();
@ -283,12 +282,14 @@ unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogF
}
// remove the dialog, it could be added back later
CHECK(old_it->get_dialog_id().is_valid());
removed_dialog_ids.insert(old_it->get_dialog_id());
++old_it;
}
}
while (old_it < old_server_dialog_ids.rend()) {
// remove the dialog, it could be added back later
CHECK(old_it->get_dialog_id().is_valid());
removed_dialog_ids.insert(old_it->get_dialog_id());
++old_it;
}
@ -310,11 +311,12 @@ unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogF
}
// merge additions and deletions from other clients to the local changes
std::unordered_set<DialogId, DialogIdHash> deleted_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> deleted_dialog_ids;
for (const auto &old_dialog_id : old_server_dialog_ids) {
CHECK(old_dialog_id.get_dialog_id().is_valid());
deleted_dialog_ids.insert(old_dialog_id.get_dialog_id());
}
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> added_dialog_ids;
for (const auto &new_dialog_id : new_server_dialog_ids) {
auto dialog_id = new_dialog_id.get_dialog_id();
if (deleted_dialog_ids.erase(dialog_id) == 0) {
@ -347,10 +349,12 @@ unique_ptr<DialogFilter> DialogFilter::merge_dialog_filter_changes(const DialogF
new_server_filter->excluded_dialog_ids);
{
std::unordered_set<DialogId, DialogIdHash> added_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> added_dialog_ids;
auto remove_duplicates = [&added_dialog_ids](auto &input_dialog_ids) {
td::remove_if(input_dialog_ids, [&added_dialog_ids](auto input_dialog_id) {
return !added_dialog_ids.insert(input_dialog_id.get_dialog_id()).second;
auto dialog_id = input_dialog_id.get_dialog_id();
CHECK(dialog_id.is_valid());
return !added_dialog_ids.insert(dialog_id).second;
});
};
remove_duplicates(new_filter->pinned_dialog_ids);
@ -455,7 +459,7 @@ bool DialogFilter::are_flags_equal(const DialogFilter &lhs, const DialogFilter &
lhs.include_groups == rhs.include_groups && lhs.include_channels == rhs.include_channels;
}
std::unordered_map<string, string> DialogFilter::emoji_to_icon_name_;
std::unordered_map<string, string> DialogFilter::icon_name_to_emoji_;
FlatHashMap<string, string> DialogFilter::emoji_to_icon_name_;
FlatHashMap<string, string> DialogFilter::icon_name_to_emoji_;
} // namespace td

View File

@ -12,11 +12,10 @@
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include <unordered_map>
namespace td {
class DialogFilter {
@ -75,8 +74,8 @@ class DialogFilter {
static bool are_flags_equal(const DialogFilter &lhs, const DialogFilter &rhs);
private:
static std::unordered_map<string, string> emoji_to_icon_name_;
static std::unordered_map<string, string> icon_name_to_emoji_;
static FlatHashMap<string, string> emoji_to_icon_name_;
static FlatHashMap<string, string> icon_name_to_emoji_;
static void init_icon_names();

View File

@ -661,7 +661,7 @@ FileId DocumentsManager::dup_document(FileId new_id, FileId old_id) {
const GeneralDocument *old_document = get_document(old_id);
CHECK(old_document != nullptr);
auto &new_document = documents_[new_id];
CHECK(!new_document);
CHECK(new_document == nullptr);
new_document = make_unique<GeneralDocument>(*old_document);
new_document->file_id = new_id;
new_document->thumbnail.file_id = td_->file_manager_->dup_file_id(new_document->thumbnail.file_id);

View File

@ -18,8 +18,8 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include <unordered_map>
#include <utility>
namespace td {
@ -127,7 +127,7 @@ class DocumentsManager {
FileId on_get_document(unique_ptr<GeneralDocument> new_document, bool replace);
Td *td_;
std::unordered_map<FileId, unique_ptr<GeneralDocument>, FileIdHash> documents_; // file_id -> GeneralDocument
FlatHashMap<FileId, unique_ptr<GeneralDocument>, FileIdHash> documents_; // file_id -> GeneralDocument
};
} // namespace td

View File

@ -0,0 +1,819 @@
//
// 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/DownloadManager.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileId.hpp"
#include "td/telegram/files/FileSourceId.hpp"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/TdDb.h"
#include "td/telegram/TdParameters.h"
#include "td/actor/MultiPromise.h"
#include "td/utils/algorithm.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Hints.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include "td/utils/tl_helpers.h"
#include <algorithm>
#include <functional>
#include <limits>
#include <set>
namespace td {
struct FileDownloadInDatabase {
int64 download_id{};
FileId file_id;
FileSourceId file_source_id;
int32 priority{};
int32 created_at{};
int32 completed_at{};
bool is_paused{};
template <class StorerT>
void store(StorerT &storer) const {
BEGIN_STORE_FLAGS();
STORE_FLAG(is_paused);
END_STORE_FLAGS();
td::store(download_id, storer);
td::store(file_id, storer);
td::store(file_source_id, storer);
td::store(priority, storer);
td::store(created_at, storer);
td::store(completed_at, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(is_paused);
END_PARSE_FLAGS();
td::parse(download_id, parser);
td::parse(file_id, parser);
td::parse(file_source_id, parser);
td::parse(priority, parser);
td::parse(created_at, parser);
td::parse(completed_at, parser);
}
};
class DownloadManagerImpl final : public DownloadManager {
public:
explicit DownloadManagerImpl(unique_ptr<Callback> callback) : callback_(std::move(callback)) {
}
void start_up() final {
init();
}
void after_get_difference() final {
load_database_files();
}
Status toggle_is_paused(FileId file_id, bool is_paused) final {
TRY_STATUS(check_is_active());
TRY_RESULT(file_info_ptr, get_file_info(file_id));
toggle_is_paused(*file_info_ptr, is_paused);
return Status::OK();
}
Status toggle_all_is_paused(bool is_paused) final {
TRY_STATUS(check_is_active());
for (auto &it : files_) {
toggle_is_paused(*it.second, is_paused);
}
return Status::OK();
}
Status remove_file(FileId file_id, FileSourceId file_source_id, bool delete_from_cache) final {
LOG(INFO) << "Remove from downloads file " << file_id << " from " << file_source_id;
TRY_STATUS(check_is_active());
TRY_RESULT(file_info_ptr, get_file_info(file_id, file_source_id));
auto &file_info = *file_info_ptr;
auto download_id = file_info.download_id;
if (!is_completed(file_info) && !file_info.is_paused) {
callback_->pause_file(file_info.internal_file_id);
}
unregister_file_info(file_info);
if (delete_from_cache) {
callback_->delete_file(file_info.internal_file_id);
}
by_internal_file_id_.erase(file_info.internal_file_id);
by_file_id_.erase(file_info.file_id);
hints_.remove(download_id);
completed_download_ids_.erase(download_id);
remove_from_database(file_info);
files_.erase(download_id);
if (is_search_inited_) {
callback_->update_file_removed(file_id, file_counters_);
}
update_counters();
on_file_viewed(download_id);
return Status::OK();
}
Status remove_file_if_finished(FileId file_id) {
TRY_STATUS(check_is_active());
TRY_RESULT(file_info_ptr, get_file_info(file_id, {}));
if (!is_completed(*file_info_ptr)) {
return Status::Error("File is active");
}
return remove_file(file_id, {}, false);
}
Status remove_all_files(bool only_active, bool only_completed, bool delete_from_cache) final {
TRY_STATUS(check_is_active());
vector<FileId> to_remove;
for (auto &it : files_) {
FileInfo &file_info = *it.second;
if (only_active && is_completed(file_info)) {
continue;
}
if (only_completed && !is_completed(file_info)) {
continue;
}
to_remove.push_back(file_info.file_id);
}
for (auto file_id : to_remove) {
remove_file(file_id, {}, delete_from_cache);
}
return Status::OK();
}
Status add_file(FileId file_id, FileSourceId file_source_id, string search_text, int8 priority) final {
TRY_STATUS(check_is_active());
remove_file(file_id, {}, false);
auto download_id = next_download_id();
auto file_info = make_unique<FileInfo>();
file_info->download_id = download_id;
file_info->file_id = file_id;
file_info->file_source_id = file_source_id;
file_info->is_paused = false;
file_info->priority = priority;
file_info->created_at = G()->unix_time();
file_info->need_save_to_database = true;
add_file_info(std::move(file_info), search_text);
return Status::OK();
}
Status change_search_text(FileId file_id, FileSourceId file_source_id, string search_text) final {
if (!is_search_inited_) {
return Status::OK();
}
TRY_STATUS(check_is_active());
TRY_RESULT(file_info_ptr, get_file_info(file_id, file_source_id));
auto &file_info = *file_info_ptr;
hints_.add(file_info.download_id, search_text.empty() ? string(" ") : search_text);
return Status::OK();
}
void hints_synchronized(Result<Unit>) {
if (G()->close_flag()) {
return;
}
LOG(INFO) << "DownloadManager: hints are synchronized";
is_search_inited_ = true;
}
void search(string query, bool only_active, bool only_completed, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundFileDownloads>> promise) final {
return do_search(std::move(query), only_active, only_completed, std::move(offset), limit, std::move(promise),
Unit{});
}
void do_search(string query, bool only_active, bool only_completed, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundFileDownloads>> promise, Result<Unit>) {
TRY_STATUS_PROMISE(promise, G()->close_status());
TRY_STATUS_PROMISE(promise, check_is_active());
if (!is_search_inited_) {
Promise<Unit> lock;
if (load_search_text_multipromise_.promise_count() == 0) {
load_search_text_multipromise_.add_promise(
promise_send_closure(actor_id(this), &DownloadManagerImpl::hints_synchronized));
load_search_text_multipromise_.set_ignore_errors(true);
lock = load_search_text_multipromise_.get_promise();
prepare_hints();
}
load_search_text_multipromise_.add_promise(promise_send_closure(actor_id(this), &DownloadManagerImpl::do_search,
std::move(query), only_active, only_completed,
std::move(offset), limit, std::move(promise)));
lock.set_value(Unit());
return;
}
if (limit <= 0) {
return promise.set_error(Status::Error(400, "Limit must be positive"));
}
int64 offset_int64 = std::numeric_limits<int64>::max();
if (!offset.empty()) {
auto r_offset = to_integer_safe<int64>(offset);
if (r_offset.is_error()) {
return promise.set_error(Status::Error(400, "Invalid offset"));
}
offset_int64 = r_offset.move_as_ok();
}
auto download_ids = hints_.search(query, 10000, true).second;
FileCounters counters;
td::remove_if(download_ids, [&](int64 download_id) {
auto r = get_file_info(download_id);
CHECK(r.is_ok());
auto &file_info = *r.ok();
if (is_completed(file_info)) {
counters.completed_count++;
if (only_active) {
return true;
}
} else {
counters.active_count++;
if (file_info.is_paused) {
counters.paused_count++;
}
if (only_completed) {
return true;
}
}
if (download_id >= offset_int64) {
return true;
}
return false;
});
std::sort(download_ids.begin(), download_ids.end(), std::greater<>());
if (static_cast<int32>(download_ids.size()) > limit) {
download_ids.resize(limit);
}
auto file_downloads = transform(download_ids, [&](int64 download_id) {
on_file_viewed(download_id);
auto it = files_.find(download_id);
CHECK(it != files_.end());
const FileInfo &file_info = *it->second;
return callback_->get_file_download_object(file_info.file_id, file_info.file_source_id, file_info.created_at,
file_info.completed_at, file_info.is_paused);
});
td::remove_if(file_downloads, [](const auto &file_download) { return file_download->message_ == nullptr; });
string next_offset;
if (!download_ids.empty()) {
next_offset = to_string(download_ids.back());
}
promise.set_value(td_api::make_object<td_api::foundFileDownloads>(counters.get_downloaded_file_counts_object(),
std::move(file_downloads), next_offset));
}
void update_file_download_state(FileId internal_file_id, int64 downloaded_size, int64 size, int64 expected_size,
bool is_paused) final {
if (!callback_ || !is_database_loaded_) {
return;
}
LOG(INFO) << "Update file download state for file " << internal_file_id << " of size " << size << '/'
<< expected_size << " to downloaded_size = " << downloaded_size << " and is_paused = " << is_paused;
auto r_file_info_ptr = get_file_info_by_internal(internal_file_id);
if (r_file_info_ptr.is_error()) {
return;
}
auto &file_info = *r_file_info_ptr.ok();
if (file_info.link_token != get_link_token()) {
LOG(INFO) << "Ignore update_file_download_state because of outdated link_token";
return;
}
bool need_update = false;
with_file_info(file_info, [&](FileInfo &file_info) {
file_info.size = size;
file_info.expected_size = expected_size;
file_info.downloaded_size = downloaded_size;
if (is_paused && file_info.is_paused != is_paused) {
file_info.is_paused = is_paused;
file_info.need_save_to_database = true;
need_update = true;
}
});
if (is_search_inited_ && need_update) {
callback_->update_file_changed(file_info.file_id, file_info.completed_at, file_info.is_paused, file_counters_);
}
}
void update_file_deleted(FileId internal_file_id) final {
if (!callback_ || !is_database_loaded_) {
return;
}
auto r_file_info_ptr = get_file_info_by_internal(internal_file_id);
if (r_file_info_ptr.is_error()) {
return;
}
auto &file_info = *r_file_info_ptr.ok();
remove_file(file_info.file_id, {}, false);
}
void update_file_viewed(FileId file_id, FileSourceId file_source_id) final {
if (unviewed_completed_download_ids_.empty() || !callback_ || !is_database_loaded_) {
return;
}
auto r_file_info_ptr = get_file_info(file_id, file_source_id);
if (r_file_info_ptr.is_error()) {
return;
}
auto &file_info = *r_file_info_ptr.ok();
on_file_viewed(file_info.download_id);
}
private:
unique_ptr<Callback> callback_;
struct FileInfo {
int64 download_id{};
FileId file_id;
FileId internal_file_id;
FileSourceId file_source_id;
int8 priority;
bool is_paused{};
bool is_counted{};
mutable bool is_registered{};
mutable bool need_save_to_database{};
int64 size{};
int64 expected_size{};
int64 downloaded_size{};
int32 created_at{};
int32 completed_at{};
uint64 link_token{};
};
FlatHashMap<FileId, int64, FileIdHash> by_file_id_;
FlatHashMap<FileId, int64, FileIdHash> by_internal_file_id_;
FlatHashMap<int64, unique_ptr<FileInfo>> files_;
std::set<int64> completed_download_ids_;
FlatHashSet<int64> unviewed_completed_download_ids_;
Hints hints_;
Counters counters_;
Counters sent_counters_;
FileCounters file_counters_;
bool is_inited_{false};
bool is_database_loaded_{false};
bool is_search_inited_{false};
int64 max_download_id_{0};
uint64 last_link_token_{0};
MultiPromiseActor load_search_text_multipromise_{"LoadFileSearchTextMultiPromiseActor"};
int64 next_download_id() {
return ++max_download_id_;
}
static bool is_completed(const FileInfo &file_info) {
return file_info.completed_at != 0;
}
static int64 get_file_size(const FileInfo &file_info) {
return file_info.size == 0 ? max(file_info.downloaded_size + 1, file_info.expected_size) : file_info.size;
}
static bool is_database_enabled() {
return G()->parameters().use_message_db;
}
static string pmc_key(const FileInfo &file_info) {
return PSTRING() << "dlds#" << file_info.download_id;
}
void sync_with_database(const FileInfo &file_info) {
if (!file_info.need_save_to_database) {
return;
}
file_info.need_save_to_database = false;
if (!is_database_enabled()) {
return;
}
LOG(INFO) << "Saving to download database file " << file_info.file_id << '/' << file_info.internal_file_id
<< " with is_paused = " << file_info.is_paused;
FileDownloadInDatabase to_save;
to_save.download_id = file_info.download_id;
to_save.file_source_id = file_info.file_source_id;
to_save.is_paused = file_info.is_paused;
to_save.priority = file_info.priority;
to_save.created_at = file_info.created_at;
to_save.completed_at = file_info.completed_at;
to_save.file_id = file_info.file_id;
G()->td_db()->get_binlog_pmc()->set(pmc_key(file_info), log_event_store(to_save).as_slice().str());
}
static void remove_from_database(const FileInfo &file_info) {
if (!is_database_enabled()) {
return;
}
G()->td_db()->get_binlog_pmc()->erase(pmc_key(file_info));
}
void init() {
if (is_inited_) {
return;
}
if (is_database_enabled()) {
auto serialized_counter = G()->td_db()->get_binlog_pmc()->get("dlds_counter");
if (!serialized_counter.empty()) {
log_event_parse(sent_counters_, serialized_counter).ensure();
if (sent_counters_.downloaded_size == sent_counters_.total_size || sent_counters_.total_size == 0) {
G()->td_db()->get_binlog_pmc()->erase("dlds_counter");
sent_counters_ = Counters();
}
}
} else {
G()->td_db()->get_binlog_pmc()->erase("dlds_counter");
G()->td_db()->get_binlog_pmc()->erase_by_prefix("dlds#");
}
callback_->update_counters(sent_counters_);
is_inited_ = true;
}
void add_file_from_database(FileDownloadInDatabase in_db) {
if (!in_db.file_id.is_valid() || !in_db.file_source_id.is_valid()) {
LOG(INFO) << "Skip adding file " << in_db.file_id << " from " << in_db.file_source_id;
return;
}
if (by_file_id_.count(in_db.file_id) != 0) {
// file has already been added
return;
}
auto file_info = make_unique<FileInfo>();
file_info->download_id = in_db.download_id;
file_info->file_id = in_db.file_id;
file_info->file_source_id = in_db.file_source_id;
file_info->is_paused = in_db.is_paused;
file_info->priority = narrow_cast<int8>(in_db.priority);
file_info->completed_at = in_db.completed_at;
file_info->created_at = in_db.created_at;
add_file_info(std::move(file_info), "");
}
void load_database_files() {
if (is_database_loaded_) {
return;
}
if (!is_database_enabled()) {
is_database_loaded_ = true;
return;
}
CHECK(is_inited_);
LOG(INFO) << "Start Download Manager database loading";
auto downloads_in_kv = G()->td_db()->get_binlog_pmc()->prefix_get("dlds#");
for (auto &it : downloads_in_kv) {
Slice key = it.first;
Slice value = it.second;
FileDownloadInDatabase in_db;
log_event_parse(in_db, value).ensure();
CHECK(in_db.download_id == to_integer_safe<int64>(key).ok());
max_download_id_ = max(in_db.download_id, max_download_id_);
add_file_from_database(in_db);
}
is_database_loaded_ = true;
update_counters();
check_completed_downloads_size();
LOG(INFO) << "Finish Download Manager database loading";
}
void prepare_hints() {
for (auto &it : files_) {
const auto &file_info = *it.second;
send_closure(G()->file_reference_manager(), &FileReferenceManager::get_file_search_text, file_info.file_source_id,
callback_->get_file_view(file_info.file_id).get_unique_file_id(),
[actor_id = actor_id(this), promise = load_search_text_multipromise_.get_promise(),
download_id = it.first](Result<string> r_search_text) mutable {
send_closure(actor_id, &DownloadManagerImpl::add_download_to_hints, download_id,
std::move(r_search_text), std::move(promise));
});
}
}
void add_download_to_hints(int64 download_id, Result<string> r_search_text, Promise<Unit> promise) {
auto it = files_.find(download_id);
if (it == files_.end()) {
return promise.set_value(Unit());
}
if (r_search_text.is_error()) {
if (!G()->close_flag()) {
remove_file(it->second->file_id, {}, false);
}
} else {
auto search_text = r_search_text.move_as_ok();
// TODO: This is a race. Synchronous call would be better.
hints_.add(download_id, search_text.empty() ? string(" ") : search_text);
}
promise.set_value(Unit());
}
void add_file_info(unique_ptr<FileInfo> &&file_info, const string &search_text) {
CHECK(file_info != nullptr);
auto download_id = file_info->download_id;
file_info->internal_file_id = callback_->dup_file_id(file_info->file_id);
auto file_view = callback_->get_sync_file_view(file_info->file_id);
CHECK(!file_view.empty());
file_info->size = file_view.size();
file_info->expected_size = file_view.expected_size();
file_info->downloaded_size = file_view.local_total_size();
file_info->is_counted = !is_completed(*file_info);
if (file_info->completed_at > 0 && (file_info->size == 0 || file_info->downloaded_size != file_info->size)) {
LOG(INFO) << "Skip adding file " << file_info->file_id << " to recently downloaded files, because local size is "
<< file_info->downloaded_size << " instead of expected " << file_info->size;
remove_from_database(*file_info);
return;
}
by_internal_file_id_[file_info->internal_file_id] = download_id;
by_file_id_[file_info->file_id] = download_id;
hints_.add(download_id, search_text.empty() ? string(" ") : search_text);
file_info->link_token = ++last_link_token_;
LOG(INFO) << "Adding to downloads file " << file_info->file_id << '/' << file_info->internal_file_id << " of size "
<< file_info->size << '/' << file_info->expected_size
<< " with downloaded_size = " << file_info->downloaded_size
<< " and is_paused = " << file_info->is_paused;
auto it = files_.emplace(download_id, std::move(file_info)).first;
bool was_completed = is_completed(*it->second);
register_file_info(*it->second); // must be called before start_file, which can call update_file_download_state
if (is_completed(*it->second)) {
bool is_inserted = completed_download_ids_.insert(it->second->download_id).second;
CHECK(is_inserted == was_completed);
} else {
if (!it->second->is_paused) {
callback_->start_file(it->second->internal_file_id, it->second->priority,
actor_shared(this, it->second->link_token));
}
}
if (is_search_inited_) {
callback_->update_file_added(it->second->file_id, it->second->file_source_id, it->second->created_at,
it->second->completed_at, it->second->is_paused, file_counters_);
}
}
void timeout_expired() final {
clear_counters();
}
void clear_counters() {
if (!is_database_loaded_) {
return;
}
CHECK(counters_ == sent_counters_);
if (counters_.downloaded_size != counters_.total_size || counters_.total_size == 0) {
return;
}
for (auto &it : files_) {
if (is_completed(*it.second) || !it.second->is_paused) {
it.second->is_counted = false;
}
}
counters_ = Counters();
update_counters();
}
void tear_down() final {
callback_.reset();
}
void toggle_is_paused(const FileInfo &file_info, bool is_paused) {
if (is_completed(file_info) || is_paused == file_info.is_paused) {
return;
}
LOG(INFO) << "Change is_paused state of file " << file_info.file_id << " to " << is_paused;
with_file_info(file_info, [&](FileInfo &file_info) {
file_info.is_paused = is_paused;
file_info.need_save_to_database = true;
file_info.link_token = ++last_link_token_;
});
if (is_paused) {
callback_->pause_file(file_info.internal_file_id);
} else {
callback_->start_file(file_info.internal_file_id, file_info.priority, actor_shared(this, file_info.link_token));
}
if (is_search_inited_) {
callback_->update_file_changed(file_info.file_id, file_info.completed_at, file_info.is_paused, file_counters_);
}
}
void update_counters() {
if (!is_database_loaded_) {
return;
}
if (counters_ == sent_counters_) {
return;
}
CHECK(counters_.total_size >= 0);
CHECK(counters_.total_count >= 0);
CHECK(counters_.downloaded_size >= 0);
if ((counters_.downloaded_size == counters_.total_size && counters_.total_size != 0) || counters_ == Counters()) {
if (counters_.total_size != 0) {
constexpr double EMPTY_UPDATE_DELAY = 60.0;
set_timeout_in(EMPTY_UPDATE_DELAY);
} else {
cancel_timeout();
}
G()->td_db()->get_binlog_pmc()->erase("dlds_counter");
} else {
cancel_timeout();
G()->td_db()->get_binlog_pmc()->set("dlds_counter", log_event_store(counters_).as_slice().str());
}
sent_counters_ = counters_;
callback_->update_counters(counters_);
}
Result<const FileInfo *> get_file_info(FileId file_id, FileSourceId file_source_id = {}) {
auto it = by_file_id_.find(file_id);
if (it == by_file_id_.end()) {
return Status::Error(400, "Can't find file");
}
return get_file_info(it->second, file_source_id);
}
Result<const FileInfo *> get_file_info_by_internal(FileId file_id) {
auto it = by_internal_file_id_.find(file_id);
if (it == by_internal_file_id_.end()) {
return Status::Error(400, "Can't find file");
}
return get_file_info(it->second);
}
Result<const FileInfo *> get_file_info(int64 download_id, FileSourceId file_source_id = {}) {
auto it = files_.find(download_id);
if (it == files_.end()) {
return Status::Error(400, "Can't find file");
}
if (file_source_id.is_valid() && file_source_id != it->second->file_source_id) {
return Status::Error(400, "Can't find file with such source");
}
return it->second.get();
}
void unregister_file_info(const FileInfo &file_info) {
CHECK(file_info.is_registered);
file_info.is_registered = false;
if (file_info.is_counted && (is_completed(file_info) || !file_info.is_paused)) {
LOG(INFO) << "Unregister file " << file_info.file_id;
counters_.downloaded_size -= file_info.downloaded_size;
counters_.total_size -= get_file_size(file_info);
counters_.total_count--;
}
if (is_completed(file_info)) {
file_counters_.completed_count--;
CHECK(file_counters_.completed_count >= 0);
} else {
if (file_info.is_paused) {
file_counters_.paused_count--;
CHECK(file_counters_.paused_count >= 0);
}
file_counters_.active_count--;
CHECK(file_counters_.active_count >= file_counters_.paused_count);
}
}
void register_file_info(FileInfo &file_info) {
CHECK(!file_info.is_registered);
file_info.is_registered = true;
bool need_update = false;
if (!is_completed(file_info) && file_info.size != 0 && file_info.downloaded_size == file_info.size) {
LOG(INFO) << "Register file " << file_info.file_id;
file_info.is_paused = false;
file_info.completed_at = G()->unix_time();
file_info.need_save_to_database = true;
bool is_inserted = completed_download_ids_.insert(file_info.download_id).second;
CHECK(is_inserted);
if (file_info.is_counted) {
unviewed_completed_download_ids_.insert(file_info.download_id);
}
need_update = true;
}
if (file_info.is_counted && (is_completed(file_info) || !file_info.is_paused)) {
counters_.downloaded_size += file_info.downloaded_size;
counters_.total_size += get_file_size(file_info);
counters_.total_count++;
}
if (is_completed(file_info)) {
file_counters_.completed_count++;
} else {
if (file_info.is_paused) {
file_counters_.paused_count++;
}
file_counters_.active_count++;
}
if (is_search_inited_ && need_update) {
callback_->update_file_changed(file_info.file_id, file_info.completed_at, file_info.is_paused, file_counters_);
}
sync_with_database(file_info);
update_counters();
CHECK(file_info.is_registered);
check_completed_downloads_size();
}
void check_completed_downloads_size() {
constexpr size_t MAX_COMPLETED_DOWNLOADS = 200;
while (completed_download_ids_.size() > MAX_COMPLETED_DOWNLOADS) {
auto download_id = *completed_download_ids_.begin();
auto file_info = get_file_info(download_id).move_as_ok();
remove_file(file_info->file_id, FileSourceId(), false);
}
}
void on_file_viewed(int64 download_id) {
if (unviewed_completed_download_ids_.empty()) {
return;
}
unviewed_completed_download_ids_.erase(download_id);
if (unviewed_completed_download_ids_.empty()) {
clear_counters();
}
}
template <class F>
void with_file_info(const FileInfo &const_file_info, F &&f) {
unregister_file_info(const_file_info);
auto &file_info = const_cast<FileInfo &>(const_file_info);
f(file_info);
register_file_info(file_info);
}
Status check_is_active() {
if (!callback_) {
LOG(ERROR) << "DownloadManager is closed";
return Status::Error(500, "DownloadManager is closed");
}
CHECK(is_inited_);
load_database_files();
return Status::OK();
}
};
unique_ptr<DownloadManager> DownloadManager::create(unique_ptr<Callback> callback) {
return make_unique<DownloadManagerImpl>(std::move(callback));
}
td_api::object_ptr<td_api::updateFileDownloads> DownloadManager::Counters::get_update_file_downloads_object() const {
return td_api::make_object<td_api::updateFileDownloads>(total_size, total_count, downloaded_size);
}
td_api::object_ptr<td_api::downloadedFileCounts> DownloadManager::FileCounters::get_downloaded_file_counts_object()
const {
return td_api::make_object<td_api::downloadedFileCounts>(active_count, paused_count, completed_count);
}
template <class StorerT>
void DownloadManager::Counters::store(StorerT &storer) const {
BEGIN_STORE_FLAGS();
END_STORE_FLAGS();
td::store(total_size, storer);
td::store(total_count, storer);
td::store(downloaded_size, storer);
}
template <class ParserT>
void DownloadManager::Counters::parse(ParserT &parser) {
BEGIN_PARSE_FLAGS();
END_PARSE_FLAGS();
td::parse(total_size, parser);
td::parse(total_count, parser);
td::parse(downloaded_size, parser);
}
} // namespace td

View File

@ -0,0 +1,109 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/td_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
class DownloadManager : public Actor {
public:
struct Counters {
int64 total_size{};
int32 total_count{};
int64 downloaded_size{};
bool operator==(const Counters &other) const {
return total_size == other.total_size && total_count == other.total_count &&
downloaded_size == other.downloaded_size;
}
td_api::object_ptr<td_api::updateFileDownloads> get_update_file_downloads_object() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
struct FileCounters {
int32 active_count{};
int32 paused_count{};
int32 completed_count{};
bool operator==(const FileCounters &other) const {
return active_count == other.active_count && paused_count == other.paused_count &&
completed_count == other.completed_count;
}
td_api::object_ptr<td_api::downloadedFileCounts> get_downloaded_file_counts_object() const;
template <class StorerT>
void store(StorerT &storer) const;
template <class ParserT>
void parse(ParserT &parser);
};
// Callback is needed to make DownloadManager testable
class Callback {
public:
virtual ~Callback() = default;
virtual void update_counters(Counters counters) = 0;
virtual void update_file_added(FileId file_id, FileSourceId file_source_id, int32 add_date, int32 complete_date,
bool is_paused, FileCounters counters) = 0;
virtual void update_file_changed(FileId file_id, int32 complete_date, bool is_paused, FileCounters counters) = 0;
virtual void update_file_removed(FileId file_id, FileCounters counters) = 0;
virtual void start_file(FileId file_id, int8 priority, ActorShared<DownloadManager> download_manager) = 0;
virtual void pause_file(FileId file_id) = 0;
virtual void delete_file(FileId file_id) = 0;
virtual FileId dup_file_id(FileId file_id) = 0;
virtual FileView get_file_view(FileId file_id) = 0;
virtual FileView get_sync_file_view(FileId file_id) = 0;
virtual td_api::object_ptr<td_api::fileDownload> get_file_download_object(FileId file_id,
FileSourceId file_source_id,
int32 add_date, int32 complete_date,
bool is_paused) = 0;
};
static unique_ptr<DownloadManager> create(unique_ptr<Callback> callback);
//
// public interface for user
//
virtual void after_get_difference() = 0;
virtual Status add_file(FileId file_id, FileSourceId file_source_id, string search_text, int8 priority) = 0;
virtual Status change_search_text(FileId file_id, FileSourceId file_source_id, string search_text) = 0;
virtual Status toggle_is_paused(FileId file_id, bool is_paused) = 0;
virtual Status toggle_all_is_paused(bool is_paused) = 0;
virtual void search(string query, bool only_active, bool only_completed, string offset, int32 limit,
Promise<td_api::object_ptr<td_api::foundFileDownloads>> promise) = 0;
virtual Status remove_file(FileId file_id, FileSourceId file_source_id, bool delete_from_cache) = 0;
virtual Status remove_file_if_finished(FileId file_id) = 0;
virtual Status remove_all_files(bool only_active, bool only_completed, bool delete_from_cache) = 0;
//
// private interface to handle all kinds of updates
//
virtual void update_file_download_state(FileId internal_file_id, int64 downloaded_size, int64 size,
int64 expected_size, bool is_paused) = 0;
virtual void update_file_deleted(FileId internal_file_id) = 0;
virtual void update_file_viewed(FileId file_id, FileSourceId file_source_id) = 0;
};
} // namespace td

View File

@ -0,0 +1,112 @@
//
// 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/DownloadManagerCallback.h"
#include "td/telegram/FileReferenceManager.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/Td.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
void DownloadManagerCallback::update_counters(DownloadManager::Counters counters) {
send_closure(td_->actor_id(td_), &Td::send_update, counters.get_update_file_downloads_object());
}
void DownloadManagerCallback::update_file_added(FileId file_id, FileSourceId file_source_id, int32 add_date,
int32 complete_date, bool is_paused,
DownloadManager::FileCounters counters) {
send_closure(td_->actor_id(td_), &Td::send_update,
td_api::make_object<td_api::updateFileAddedToDownloads>(
get_file_download_object(file_id, file_source_id, add_date, complete_date, is_paused),
counters.get_downloaded_file_counts_object()));
}
void DownloadManagerCallback::update_file_changed(FileId file_id, int32 complete_date, bool is_paused,
DownloadManager::FileCounters counters) {
send_closure(td_->actor_id(td_), &Td::send_update,
td_api::make_object<td_api::updateFileDownload>(file_id.get(), complete_date, is_paused,
counters.get_downloaded_file_counts_object()));
}
void DownloadManagerCallback::update_file_removed(FileId file_id, DownloadManager::FileCounters counters) {
send_closure(td_->actor_id(td_), &Td::send_update,
td_api::make_object<td_api::updateFileRemovedFromDownloads>(
file_id.get(), counters.get_downloaded_file_counts_object()));
}
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);
}
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);
}
void DownloadManagerCallback::delete_file(FileId file_id) {
send_closure_later(td_->file_manager_actor_, &FileManager::delete_file, file_id, Promise<Unit>(),
"download manager callback");
}
FileId DownloadManagerCallback::dup_file_id(FileId file_id) {
return td_->file_manager_->dup_file_id(file_id);
}
FileView DownloadManagerCallback::get_file_view(FileId file_id) {
return td_->file_manager_->get_file_view(file_id);
}
FileView DownloadManagerCallback::get_sync_file_view(FileId file_id) {
return td_->file_manager_->get_sync_file_view(file_id);
}
td_api::object_ptr<td_api::fileDownload> DownloadManagerCallback::get_file_download_object(
FileId file_id, FileSourceId file_source_id, int32 add_date, int32 complete_date, bool is_paused) {
return td_api::make_object<td_api::fileDownload>(td_->file_manager_->get_file_view(file_id).file_id().get(),
td_->file_reference_manager_->get_message_object(file_source_id),
add_date, complete_date, is_paused);
}
std::shared_ptr<FileManager::DownloadCallback> DownloadManagerCallback::make_download_file_callback(
Td *td, ActorShared<DownloadManager> download_manager) {
class Impl final : public FileManager::DownloadCallback {
public:
Impl(Td *td, ActorShared<DownloadManager> download_manager)
: td_(td), download_manager_(std::move(download_manager)) {
}
void on_progress(FileId file_id) final {
send_update(file_id, false);
}
void on_download_ok(FileId file_id) final {
send_update(file_id, false);
}
void on_download_error(FileId file_id, Status error) final {
send_update(file_id, true);
}
private:
Td *td_;
ActorShared<DownloadManager> download_manager_;
void send_update(FileId file_id, bool is_paused) const {
auto file_view = td_->file_manager_->get_file_view(file_id);
send_closure_later(download_manager_, &DownloadManager::update_file_download_state, file_id,
file_view.local_total_size(), file_view.size(), file_view.expected_size(), is_paused);
}
};
return std::make_shared<Impl>(td, std::move(download_manager));
}
} // namespace td

View File

@ -0,0 +1,64 @@
//
// 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/DownloadManager.h"
#include "td/telegram/files/FileId.h"
#include "td/telegram/files/FileManager.h"
#include "td/telegram/files/FileSourceId.h"
#include "td/telegram/td_api.h"
#include "td/actor/actor.h"
#include "td/utils/common.h"
#include <memory>
namespace td {
class Td;
class DownloadManagerCallback final : public DownloadManager::Callback {
public:
DownloadManagerCallback(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
void update_counters(DownloadManager::Counters counters) final;
void update_file_added(FileId file_id, FileSourceId file_source_id, int32 add_date, int32 complete_date,
bool is_paused, DownloadManager::FileCounters counters) final;
void update_file_changed(FileId file_id, int32 complete_date, bool is_paused,
DownloadManager::FileCounters counters) final;
void update_file_removed(FileId file_id, DownloadManager::FileCounters counters) final;
void start_file(FileId file_id, int8 priority, ActorShared<DownloadManager> download_manager) final;
void pause_file(FileId file_id) final;
void delete_file(FileId file_id) final;
FileId dup_file_id(FileId file_id) final;
FileView get_file_view(FileId file_id) final;
FileView get_sync_file_view(FileId file_id) final;
td_api::object_ptr<td_api::fileDownload> get_file_download_object(FileId file_id, FileSourceId file_source_id,
int32 add_date, int32 complete_date,
bool is_paused);
private:
Td *td_;
ActorShared<> parent_;
static std::shared_ptr<FileManager::DownloadCallback> make_download_file_callback(
Td *td, ActorShared<DownloadManager> download_manager);
};
} // namespace td

View File

@ -126,12 +126,14 @@ FileSourceId FileReferenceManager::create_app_config_file_source() {
}
bool FileReferenceManager::add_file_source(NodeId node_id, FileSourceId file_source_id) {
CHECK(node_id.is_valid());
bool is_added = nodes_[node_id].file_source_ids.add(file_source_id);
VLOG(file_references) << "Add " << (is_added ? "new" : "old") << ' ' << file_source_id << " for file " << node_id;
return is_added;
}
bool FileReferenceManager::remove_file_source(NodeId node_id, FileSourceId file_source_id) {
CHECK(node_id.is_valid());
bool is_removed = nodes_[node_id].file_source_ids.remove(file_source_id);
if (is_removed) {
VLOG(file_references) << "Remove " << file_source_id << " from file " << node_id;
@ -170,6 +172,7 @@ void FileReferenceManager::merge(NodeId to_node_id, NodeId from_node_id) {
return;
}
CHECK(to_node_id.is_valid());
auto &to = nodes_[to_node_id];
auto &from = from_it->second;
VLOG(file_references) << "Merge " << to.file_source_ids.size() << " and " << from.file_source_ids.size()
@ -193,6 +196,7 @@ void FileReferenceManager::merge(NodeId to_node_id, NodeId from_node_id) {
}
void FileReferenceManager::run_node(NodeId node_id) {
CHECK(node_id.is_valid());
Node &node = nodes_[node_id];
if (!node.query) {
return;
@ -232,6 +236,7 @@ void FileReferenceManager::run_node(NodeId node_id) {
void FileReferenceManager::send_query(Destination dest, FileSourceId file_source_id) {
VLOG(file_references) << "Send file reference repair query for file " << dest.node_id << " with generation "
<< dest.generation << " from " << file_source_id;
CHECK(dest.node_id.is_valid());
auto &node = nodes_[dest.node_id];
node.query->active_queries++;
@ -314,6 +319,7 @@ FileReferenceManager::Destination FileReferenceManager::on_query_result(Destinat
VLOG(file_references) << "Receive result of file reference repair query for file " << dest.node_id
<< " with generation " << dest.generation << " from " << file_source_id << ": " << status << " "
<< sub;
CHECK(dest.node_id.is_valid());
auto &node = nodes_[dest.node_id];
auto query = node.query.get();
@ -351,6 +357,7 @@ void FileReferenceManager::repair_file_reference(NodeId node_id, Promise<> promi
auto main_file_id = G()->td().get_actor_unsafe()->file_manager_->get_file_view(node_id).file_id();
VLOG(file_references) << "Repair file reference for file " << node_id << "/" << main_file_id;
node_id = main_file_id;
CHECK(node_id.is_valid());
auto &node = nodes_[node_id];
if (!node.query) {
node.query = make_unique<Query>();
@ -388,6 +395,31 @@ void FileReferenceManager::reload_photo(PhotoSizeSource source, Promise<Unit> pr
}
}
void FileReferenceManager::get_file_search_text(FileSourceId file_source_id, string unique_file_id,
Promise<string> promise) {
auto index = static_cast<size_t>(file_source_id.get()) - 1;
CHECK(index < file_sources_.size());
file_sources_[index].visit(overloaded(
[&](const FileSourceMessage &source) {
send_closure_later(G()->messages_manager(), &MessagesManager::get_message_file_search_text,
source.full_message_id, std::move(unique_file_id), std::move(promise));
},
[&](const auto &source) { promise.set_error(Status::Error(500, "Unsupported file source")); }));
}
td_api::object_ptr<td_api::message> FileReferenceManager::get_message_object(FileSourceId file_source_id) const {
auto index = static_cast<size_t>(file_source_id.get()) - 1;
CHECK(index < file_sources_.size());
td_api::object_ptr<td_api::message> result;
file_sources_[index].visit(overloaded(
[&](const FileSourceMessage &source) {
result = G()->td().get_actor_unsafe()->messages_manager_->get_message_object(source.full_message_id,
"FileReferenceManager");
},
[&](const auto &source) { LOG(ERROR) << "Unsupported file source"; }));
return result;
}
void FileReferenceManager::memory_stats(vector<string> &output) {
output.push_back("\"nodes_\":"); output.push_back(std::to_string(nodes_.size()));
output.push_back(",");

View File

@ -14,19 +14,19 @@
#include "td/telegram/FullMessageId.h"
#include "td/telegram/PhotoSizeSource.h"
#include "td/telegram/SetWithPosition.h"
#include "td/telegram/td_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/Variant.h"
#include <unordered_map>
namespace td {
class Td;
@ -56,6 +56,10 @@ class FileReferenceManager final : public Actor {
using NodeId = FileId;
void repair_file_reference(NodeId node_id, Promise<> promise);
void get_file_search_text(FileSourceId file_source_id, string unique_file_id, Promise<string> promise);
td_api::object_ptr<td_api::message> get_message_object(FileSourceId file_source_id) const;
static void reload_photo(PhotoSizeSource source, Promise<Unit> promise);
bool add_file_source(NodeId node_id, FileSourceId file_source_id);
@ -152,7 +156,7 @@ class FileReferenceManager final : public Actor {
int64 query_generation_{0};
std::unordered_map<NodeId, Node, FileIdHash> nodes_;
FlatHashMap<NodeId, Node, FileIdHash> nodes_;
void run_node(NodeId node);
void send_query(Destination dest, FileSourceId file_source_id);

View File

@ -8,17 +8,15 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ChainId.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/Global.h"
#include "td/telegram/InlineQueriesManager.h"
#include "td/telegram/MessageContentType.h"
#include "td/telegram/MessageId.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/SequenceDispatcher.h"
#include "td/telegram/Td.h"
#include "td/telegram/UpdatesManager.h"
@ -28,16 +26,16 @@
namespace td {
class SetGameScoreActor final : public NetActorOnce {
class SetGameScoreQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit SetGameScoreActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
explicit SetGameScoreQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, MessageId message_id, bool edit_message,
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force, uint64 sequence_dispatcher_id) {
tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force) {
int32 flags = 0;
if (edit_message) {
flags |= telegram_api::messages_setGameScore::EDIT_MESSAGE_MASK;
@ -50,19 +48,14 @@ class SetGameScoreActor final : public NetActorOnce {
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
if (input_peer == nullptr) {
on_error(Status::Error(400, "Can't access the chat"));
stop();
return;
return on_error(Status::Error(400, "Can't access the chat"));
}
CHECK(input_user != nullptr);
auto query = G()->net_query_creator().create(
send_query(G()->net_query_creator().create(
telegram_api::messages_setGameScore(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
message_id.get_server_message_id().get(), std::move(input_user), score));
query->debug("send to MultiSequenceDispatcher");
send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_dispatcher_id);
message_id.get_server_message_id().get(), std::move(input_user), score),
{{dialog_id}}));
}
void on_result(BufferSlice packet) final {
@ -77,8 +70,8 @@ class SetGameScoreActor final : public NetActorOnce {
}
void on_error(Status status) final {
LOG(INFO) << "Receive error for SetGameScore: " << status;
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreActor");
LOG(INFO) << "Receive error for SetGameScoreQuery: " << status;
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreQuery");
promise_.set_error(std::move(status));
}
};
@ -107,7 +100,7 @@ class SetInlineGameScoreQuery final : public Td::ResultHandler {
send_query(G()->net_query_creator().create(
telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/,
std::move(input_bot_inline_message_id), std::move(input_user), score),
dc_id));
{}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -178,7 +171,7 @@ class GetInlineGameHighScoresQuery final : public Td::ResultHandler {
auto dc_id = DcId::internal(InlineQueriesManager::get_inline_message_dc_id(input_bot_inline_message_id));
send_query(G()->net_query_creator().create(
telegram_api::messages_getInlineGameHighScores(std::move(input_bot_inline_message_id), std::move(input_user)),
dc_id));
{}, dc_id));
}
void on_result(BufferSlice packet) final {
@ -233,9 +226,8 @@ void GameManager::set_game_score(FullMessageId full_message_id, bool edit_messag
}
send_closure(actor_id, &GameManager::on_set_game_score, full_message_id, std::move(promise));
});
send_closure(td_->create_net_actor<SetGameScoreActor>(std::move(query_promise)), &SetGameScoreActor::send, dialog_id,
full_message_id.get_message_id(), edit_message, r_input_user.move_as_ok(), score, force,
MessagesManager::get_sequence_dispatcher_id(dialog_id, MessageContentType::None));
td_->create_handler<SetGameScoreQuery>(std::move(query_promise))
->send(dialog_id, full_message_id.get_message_id(), edit_message, r_input_user.move_as_ok(), score, force);
}
void GameManager::on_set_game_score(FullMessageId full_message_id,

View File

@ -248,6 +248,9 @@ int64 Global::get_location_key(double latitude, double longitude) {
double f = std::tan(PI / 4 - latitude / 2);
key += static_cast<int64>(f * std::cos(longitude) * 128) * 256;
key += static_cast<int64>(f * std::sin(longitude) * 128);
if (key == 0) {
key = 1;
}
return key;
}

View File

@ -18,6 +18,7 @@
#include "td/actor/SchedulerLocalStorage.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@ -26,9 +27,9 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <unordered_map>
namespace td {
class AnimationsManager;
class BackgroundManager;
class CallManager;
@ -36,6 +37,7 @@ class ConfigManager;
class ConfigShared;
class ConnectionCreator;
class ContactsManager;
class DownloadManager;
class FileManager;
class FileReferenceManager;
class GameManager;
@ -256,6 +258,13 @@ class Global final : public ActorContext {
contacts_manager_ = contacts_manager;
}
ActorId<DownloadManager> download_manager() const {
return download_manager_;
}
void set_download_manager(ActorId<DownloadManager> download_manager) {
download_manager_ = std::move(download_manager);
}
ActorId<FileManager> file_manager() const {
return file_manager_;
}
@ -495,6 +504,7 @@ class Global final : public ActorContext {
ActorId<CallManager> call_manager_;
ActorId<ConfigManager> config_manager_;
ActorId<ContactsManager> contacts_manager_;
ActorId<DownloadManager> download_manager_;
ActorId<FileManager> file_manager_;
ActorId<FileReferenceManager> file_reference_manager_;
ActorId<GameManager> game_manager_;
@ -551,7 +561,7 @@ class Global final : public ActorContext {
static int64 get_location_key(double latitude, double longitude);
std::unordered_map<int64, int64> location_access_hashes_;
FlatHashMap<int64, int64> location_access_hashes_;
int32 to_unix_time(double server_time) const;

View File

@ -25,17 +25,50 @@
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
#include "td/utils/SliceBuilder.h"
#include <map>
#include <unordered_set>
#include <utility>
namespace td {
class GetGroupCallStreamChannelsQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::groupCallStreams>> promise_;
public:
explicit GetGroupCallStreamChannelsQuery(Promise<td_api::object_ptr<td_api::groupCallStreams>> &&promise)
: promise_(std::move(promise)) {
}
void send(InputGroupCallId input_group_call_id, DcId stream_dc_id) {
send_query(G()->net_query_creator().create(
telegram_api::phone_getGroupCallStreamChannels(input_group_call_id.get_input_group_call()), {}, stream_dc_id,
NetQuery::Type::DownloadSmall));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::phone_getGroupCallStreamChannels>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
auto streams = transform(ptr->channels_, [](const tl_object_ptr<telegram_api::groupCallStreamChannel> &channel) {
return td_api::make_object<td_api::groupCallStream>(channel->channel_, channel->scale_,
channel->last_timestamp_ms_);
});
promise_.set_value(td_api::make_object<td_api::groupCallStreams>(std::move(streams)));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetGroupCallStreamQuery final : public Td::ResultHandler {
Promise<string> promise_;
@ -54,7 +87,7 @@ class GetGroupCallStreamQuery final : public Td::ResultHandler {
int32 flags = 0;
auto query = G()->net_query_creator().create(
telegram_api::upload_getFile(flags, false /*ignored*/, false /*ignored*/, std::move(input_stream), 0, 1 << 20),
stream_dc_id, NetQuery::Type::DownloadSmall);
{}, stream_dc_id, NetQuery::Type::DownloadSmall);
query->total_timeout_limit_ = 0;
send_query(std::move(query));
}
@ -162,7 +195,7 @@ class CreateGroupCallQuery final : public Td::ResultHandler {
explicit CreateGroupCallQuery(Promise<InputGroupCallId> &&promise) : promise_(std::move(promise)) {
}
void send(DialogId dialog_id, const string &title, int32 start_date) {
void send(DialogId dialog_id, const string &title, int32 start_date, bool is_rtmp_stream) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
@ -175,8 +208,11 @@ class CreateGroupCallQuery final : public Td::ResultHandler {
if (start_date > 0) {
flags |= telegram_api::phone_createGroupCall::SCHEDULE_DATE_MASK;
}
send_query(G()->net_query_creator().create(
telegram_api::phone_createGroupCall(flags, std::move(input_peer), Random::secure_int32(), title, start_date)));
if (is_rtmp_stream) {
flags |= telegram_api::phone_createGroupCall::RTMP_STREAM_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::phone_createGroupCall(
flags, false, std::move(input_peer), Random::secure_int32(), title, start_date)));
}
void on_result(BufferSlice packet) final {
@ -213,6 +249,41 @@ class CreateGroupCallQuery final : public Td::ResultHandler {
}
};
class GetGroupCallRtmpStreamUrlGroupCallQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::rtmpUrl>> promise_;
DialogId dialog_id_;
public:
explicit GetGroupCallRtmpStreamUrlGroupCallQuery(Promise<td_api::object_ptr<td_api::rtmpUrl>> &&promise)
: promise_(std::move(promise)) {
}
void send(DialogId dialog_id, bool revoke) {
dialog_id_ = dialog_id;
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
CHECK(input_peer != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::phone_getGroupCallStreamRtmpUrl(std::move(input_peer), revoke)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::phone_getGroupCallStreamRtmpUrl>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
promise_.set_value(td_api::make_object<td_api::rtmpUrl>(ptr->url_, ptr->key_));
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetGroupCallRtmpStreamUrlGroupCallQuery");
promise_.set_error(std::move(status));
}
};
class GetGroupCallQuery final : public Td::ResultHandler {
Promise<tl_object_ptr<telegram_api::phone_groupCall>> promise_;
@ -817,12 +888,14 @@ struct GroupCallManager::GroupCall {
string title;
bool is_inited = false;
bool is_active = false;
bool is_rtmp_stream = false;
bool is_joined = false;
bool need_rejoin = false;
bool is_being_left = false;
bool is_speaking = false;
bool can_self_unmute = false;
bool can_be_managed = false;
bool has_hidden_listeners = false;
bool syncing_participants = false;
bool need_syncing_participants = false;
bool loaded_all_participants = false;
@ -886,7 +959,7 @@ struct GroupCallManager::GroupCallParticipants {
vector<DialogId> administrator_dialog_ids;
struct PendingUpdates {
std::unordered_map<DialogId, GroupCallParticipant, DialogIdHash> updates;
FlatHashMap<DialogId, unique_ptr<GroupCallParticipant>, DialogIdHash> updates;
};
std::map<int32, PendingUpdates> pending_version_updates_;
std::map<int32, PendingUpdates> pending_mute_updates_;
@ -1258,7 +1331,7 @@ void GroupCallManager::set_group_call_default_join_as(DialogId dialog_id, Dialog
td_->messages_manager_->on_update_dialog_default_join_group_call_as_dialog_id(dialog_id, as_dialog_id, true);
}
void GroupCallManager::create_voice_chat(DialogId dialog_id, string title, int32 start_date,
void GroupCallManager::create_voice_chat(DialogId dialog_id, string title, int32 start_date, bool is_rtmp_stream,
Promise<GroupCallId> &&promise) {
if (!dialog_id.is_valid()) {
return promise.set_error(Status::Error(400, "Invalid chat identifier specified"));
@ -1283,7 +1356,25 @@ void GroupCallManager::create_voice_chat(DialogId dialog_id, string title, int32
std::move(promise));
}
});
td_->create_handler<CreateGroupCallQuery>(std::move(query_promise))->send(dialog_id, title, start_date);
td_->create_handler<CreateGroupCallQuery>(std::move(query_promise))
->send(dialog_id, title, start_date, is_rtmp_stream);
}
void GroupCallManager::get_voice_chat_rtmp_stream_url(DialogId dialog_id, bool revoke,
Promise<td_api::object_ptr<td_api::rtmpUrl>> &&promise) {
if (!dialog_id.is_valid()) {
return promise.set_error(Status::Error(400, "Invalid chat identifier specified"));
}
if (!td_->messages_manager_->have_dialog_force(dialog_id, "get_voice_chat_rtmp_stream_url")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
return promise.set_error(Status::Error(400, "Can't access chat"));
}
TRY_STATUS_PROMISE(promise, can_manage_group_calls(dialog_id));
td_->create_handler<GetGroupCallRtmpStreamUrlGroupCallQuery>(std::move(promise))->send(dialog_id, revoke);
}
void GroupCallManager::on_voice_chat_created(DialogId dialog_id, InputGroupCallId input_group_call_id,
@ -1344,6 +1435,9 @@ void GroupCallManager::reload_group_call(InputGroupCallId input_group_call_id,
if (td_->auth_manager_->is_bot()) {
return promise.set_error(Status::Error(400, "Bots can't get group call info"));
}
if (!input_group_call_id.is_valid()) {
return promise.set_error(Status::Error(400, "Invalid group call identifier specified"));
}
auto &queries = load_group_call_queries_[input_group_call_id];
queries.push_back(std::move(promise));
@ -1756,9 +1850,9 @@ void GroupCallManager::on_update_group_call_participants(
}
if (GroupCallParticipant::is_versioned_update(group_call_participant)) {
pending_version_updates[dialog_id] = std::move(participant);
pending_version_updates[dialog_id] = td::make_unique<GroupCallParticipant>(std::move(participant));
} else {
pending_mute_updates[dialog_id] = std::move(participant);
pending_mute_updates[dialog_id] = td::make_unique<GroupCallParticipant>(std::move(participant));
}
}
@ -1795,7 +1889,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
}
auto &participants = it->second.updates;
for (auto &participant_it : participants) {
auto &participant = participant_it.second;
auto &participant = *participant_it.second;
on_participant_speaking_in_group_call(input_group_call_id, participant);
auto mute_diff = process_group_call_participant(input_group_call_id, std::move(participant));
CHECK(mute_diff.first == 0);
@ -1814,7 +1908,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
auto &participants = it->second.updates;
if (version <= group_call->version) {
for (auto &participant_it : participants) {
auto &participant = participant_it.second;
auto &participant = *participant_it.second;
on_participant_speaking_in_group_call(input_group_call_id, participant);
if (participant.is_self || participant.joined_date != 0) {
auto new_diff = process_group_call_participant(input_group_call_id, std::move(participant));
@ -1831,7 +1925,7 @@ bool GroupCallManager::process_pending_group_call_participant_updates(InputGroup
if (version == group_call->version + 1) {
group_call->version = version;
for (auto &participant_it : participants) {
auto &participant = participant_it.second;
auto &participant = *participant_it.second;
if (participant.is_self && group_call->is_joined &&
(participant.joined_date == 0) == (participant.audio_source == group_call->audio_source)) {
is_left = true;
@ -1985,10 +2079,11 @@ void GroupCallManager::process_group_call_participants(
return;
}
std::unordered_set<DialogId, DialogIdHash> old_participant_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> old_participant_dialog_ids;
if (is_sync) {
auto *group_call_participants = add_group_call_participants(input_group_call_id);
for (auto &participant : group_call_participants->participants) {
CHECK(participant.dialog_id.is_valid());
old_participant_dialog_ids.insert(participant.dialog_id);
}
}
@ -2310,6 +2405,66 @@ int32 GroupCallManager::cancel_join_group_call_presentation_request(InputGroupCa
return audio_source;
}
void GroupCallManager::get_group_call_streams(GroupCallId group_call_id,
Promise<td_api::object_ptr<td_api::groupCallStreams>> &&promise) {
TRY_STATUS_PROMISE(promise, G()->close_status());
TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id));
auto *group_call = get_group_call(input_group_call_id);
if (group_call == nullptr || !group_call->is_inited) {
reload_group_call(input_group_call_id,
PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, promise = std::move(promise)](
Result<td_api::object_ptr<td_api::groupCall>> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &GroupCallManager::get_group_call_streams, group_call_id,
std::move(promise));
}
}));
return;
}
if (!group_call->is_active || !group_call->stream_dc_id.is_exact()) {
return promise.set_error(Status::Error(400, "Group call can't be streamed"));
}
if (!group_call->is_joined) {
if (is_group_call_being_joined(input_group_call_id) || group_call->need_rejoin) {
group_call->after_join.push_back(PromiseCreator::lambda(
[actor_id = actor_id(this), group_call_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
send_closure(actor_id, &GroupCallManager::get_group_call_streams, group_call_id, std::move(promise));
}
}));
return;
}
return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING"));
}
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), input_group_call_id, audio_source = group_call->audio_source,
promise = std::move(promise)](Result<td_api::object_ptr<td_api::groupCallStreams>> &&result) mutable {
send_closure(actor_id, &GroupCallManager::finish_get_group_call_streams, input_group_call_id, audio_source,
std::move(result), std::move(promise));
});
td_->create_handler<GetGroupCallStreamChannelsQuery>(std::move(query_promise))
->send(input_group_call_id, group_call->stream_dc_id);
}
void GroupCallManager::finish_get_group_call_streams(InputGroupCallId input_group_call_id, int32 audio_source,
Result<td_api::object_ptr<td_api::groupCallStreams>> &&result,
Promise<td_api::object_ptr<td_api::groupCallStreams>> &&promise) {
if (!G()->close_flag() && result.is_error()) {
auto message = result.error().message();
if (message == "GROUPCALL_JOIN_MISSING" || message == "GROUPCALL_FORBIDDEN" || message == "GROUPCALL_INVALID") {
on_group_call_left(input_group_call_id, audio_source, message == "GROUPCALL_JOIN_MISSING");
}
}
promise.set_result(std::move(result));
}
void GroupCallManager::get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale,
int32 channel_id,
td_api::object_ptr<td_api::GroupCallVideoQuality> quality,
@ -4071,6 +4226,8 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
auto group_call = static_cast<const telegram_api::groupCall *>(group_call_ptr.get());
input_group_call_id = InputGroupCallId(group_call->id_, group_call->access_hash_);
call.is_active = true;
call.is_rtmp_stream = group_call->rtmp_stream_;
call.has_hidden_listeners = group_call->listeners_hidden_;
call.title = group_call->title_;
call.start_subscribed = group_call->schedule_start_subscribed_;
call.mute_new_participants = group_call->join_muted_;
@ -4193,6 +4350,14 @@ InputGroupCallId GroupCallManager::update_group_call(const tl_object_ptr<telegra
*group_call = std::move(call);
need_update = true;
} else {
if (call.is_rtmp_stream != group_call->is_rtmp_stream) {
group_call->is_rtmp_stream = call.is_rtmp_stream;
need_update = true;
}
if (call.has_hidden_listeners != group_call->has_hidden_listeners) {
group_call->has_hidden_listeners = call.has_hidden_listeners;
need_update = true;
}
if ((call.unmuted_video_count != group_call->unmuted_video_count ||
call.unmuted_video_limit != group_call->unmuted_video_limit) &&
call.can_enable_video_version >= group_call->can_enable_video_version) {
@ -4522,7 +4687,8 @@ bool GroupCallManager::set_group_call_participant_count(GroupCall *group_call, i
<< group_call->dialog_id;
}
count = known_participant_count;
} else if (group_call->loaded_all_participants && count > known_participant_count) {
} else if (group_call->loaded_all_participants && !group_call->has_hidden_listeners &&
count > known_participant_count) {
if (group_call->joined_date_asc) {
group_call->loaded_all_participants = false;
result = true;
@ -4662,10 +4828,10 @@ tl_object_ptr<td_api::groupCall> GroupCallManager::get_group_call_object(
bool is_video_recorded = get_group_call_is_video_recorded(group_call);
return td_api::make_object<td_api::groupCall>(
group_call->group_call_id.get(), get_group_call_title(group_call), scheduled_start_date, start_subscribed,
is_active, is_joined, group_call->need_rejoin, group_call->can_be_managed, group_call->participant_count,
group_call->loaded_all_participants, std::move(recent_speakers), is_my_video_enabled, is_my_video_paused,
can_enable_video, mute_new_participants, can_toggle_mute_new_participants, record_duration, is_video_recorded,
group_call->duration);
is_active, group_call->is_rtmp_stream, is_joined, group_call->need_rejoin, group_call->can_be_managed,
group_call->participant_count, group_call->has_hidden_listeners, group_call->loaded_all_participants,
std::move(recent_speakers), is_my_video_enabled, is_my_video_paused, can_enable_video, mute_new_participants,
can_toggle_mute_new_participants, record_duration, is_video_recorded, group_call->duration);
}
tl_object_ptr<td_api::updateGroupCall> GroupCallManager::get_update_group_call_object(

View File

@ -21,9 +21,9 @@
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <unordered_map>
#include <utility>
namespace td {
@ -53,7 +53,11 @@ class GroupCallManager final : public Actor {
void set_group_call_default_join_as(DialogId dialog_id, DialogId as_dialog_id, Promise<Unit> &&promise);
void create_voice_chat(DialogId dialog_id, string title, int32 start_date, Promise<GroupCallId> &&promise);
void create_voice_chat(DialogId dialog_id, string title, int32 start_date, bool is_rtmp_stream,
Promise<GroupCallId> &&promise);
void get_voice_chat_rtmp_stream_url(DialogId dialog_id, bool revoke,
Promise<td_api::object_ptr<td_api::rtmpUrl>> &&promise);
void get_group_call(GroupCallId group_call_id, Promise<td_api::object_ptr<td_api::groupCall>> &&promise);
@ -62,6 +66,9 @@ class GroupCallManager final : public Actor {
void reload_group_call(InputGroupCallId input_group_call_id,
Promise<td_api::object_ptr<td_api::groupCall>> &&promise);
void get_group_call_streams(GroupCallId group_call_id,
Promise<td_api::object_ptr<td_api::groupCallStreams>> &&promise);
void get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, int32 channel_id,
td_api::object_ptr<td_api::GroupCallVideoQuality> quality,
Promise<string> &&promise);
@ -198,6 +205,10 @@ class GroupCallManager final : public Actor {
void finish_get_group_call(InputGroupCallId input_group_call_id,
Result<tl_object_ptr<telegram_api::phone_groupCall>> &&result);
void finish_get_group_call_streams(InputGroupCallId input_group_call_id, int32 audio_source,
Result<td_api::object_ptr<td_api::groupCallStreams>> &&result,
Promise<td_api::object_ptr<td_api::groupCallStreams>> &&promise);
void finish_get_group_call_stream_segment(InputGroupCallId input_group_call_id, int32 audio_source,
Result<string> &&result, Promise<string> &&promise);
@ -385,21 +396,20 @@ class GroupCallManager final : public Actor {
vector<InputGroupCallId> input_group_call_ids_;
std::unordered_map<InputGroupCallId, unique_ptr<GroupCall>, InputGroupCallIdHash> group_calls_;
FlatHashMap<InputGroupCallId, unique_ptr<GroupCall>, InputGroupCallIdHash> group_calls_;
string pending_group_call_join_params_;
std::unordered_map<InputGroupCallId, unique_ptr<GroupCallParticipants>, InputGroupCallIdHash>
group_call_participants_;
std::unordered_map<DialogId, vector<InputGroupCallId>, DialogIdHash> participant_id_to_group_call_id_;
FlatHashMap<InputGroupCallId, unique_ptr<GroupCallParticipants>, InputGroupCallIdHash> group_call_participants_;
FlatHashMap<DialogId, vector<InputGroupCallId>, DialogIdHash> participant_id_to_group_call_id_;
std::unordered_map<GroupCallId, unique_ptr<GroupCallRecentSpeakers>, GroupCallIdHash> group_call_recent_speakers_;
FlatHashMap<GroupCallId, unique_ptr<GroupCallRecentSpeakers>, GroupCallIdHash> group_call_recent_speakers_;
std::unordered_map<InputGroupCallId, vector<Promise<td_api::object_ptr<td_api::groupCall>>>, InputGroupCallIdHash>
FlatHashMap<InputGroupCallId, vector<Promise<td_api::object_ptr<td_api::groupCall>>>, InputGroupCallIdHash>
load_group_call_queries_;
std::unordered_map<InputGroupCallId, unique_ptr<PendingJoinRequest>, InputGroupCallIdHash> pending_join_requests_;
std::unordered_map<InputGroupCallId, unique_ptr<PendingJoinRequest>, InputGroupCallIdHash>
FlatHashMap<InputGroupCallId, unique_ptr<PendingJoinRequest>, InputGroupCallIdHash> pending_join_requests_;
FlatHashMap<InputGroupCallId, unique_ptr<PendingJoinRequest>, InputGroupCallIdHash>
pending_join_presentation_requests_;
uint64 join_group_request_generation_ = 0;

View File

@ -26,10 +26,10 @@ struct GroupCallParticipant {
GroupCallVideoPayload presentation_payload;
int32 audio_source = 0;
int32 presentation_audio_source = 0;
int64 raise_hand_rating = 0;
int32 joined_date = 0;
int32 active_date = 0;
int32 volume_level = 10000;
int64 raise_hand_rating = 0;
bool is_volume_level_local = false;
bool server_is_muted_by_themselves = false;
bool server_is_muted_by_admin = false;

View File

@ -321,6 +321,11 @@ bool InlineQueriesManager::register_inline_message_content(
int64 query_id, const string &result_id, FileId file_id,
tl_object_ptr<telegram_api::BotInlineMessage> &&inline_message, int32 allowed_media_content_id, bool allow_invoice,
Photo *photo, Game *game) {
CHECK(query_id != 0);
if (result_id.empty()) {
return false;
}
InlineMessageContent content =
create_inline_message_content(td_, file_id, std::move(inline_message), allowed_media_content_id, photo, game);
if (content.message_content != nullptr) {
@ -842,6 +847,9 @@ uint64 InlineQueriesManager::send_inline_query(UserId bot_user_id, DialogId dial
query_hash = query_hash * 2023654985u + static_cast<uint64>(user_location.get_longitude() * 1e4);
}
query_hash &= 0x7FFFFFFFFFFFFFFF;
if (query_hash == 0) {
query_hash = 1;
}
auto it = inline_query_results_.find(query_hash);
if (it != inline_query_results_.end()) {
@ -1289,7 +1297,7 @@ string InlineQueriesManager::get_web_document_content_type(
void InlineQueriesManager::on_get_inline_query_results(DialogId dialog_id, UserId bot_user_id, uint64 query_hash,
tl_object_ptr<telegram_api::messages_botResults> &&results) {
LOG(INFO) << "Receive results for inline query " << query_hash;
if (results == nullptr) {
if (results == nullptr || results->query_id_ == 0) {
decrease_pending_request_count(query_hash);
return;
}

View File

@ -23,9 +23,9 @@
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <unordered_map>
#include <utility>
namespace td {
@ -143,12 +143,12 @@ class InlineQueriesManager final : public Actor {
};
MultiTimeout drop_inline_query_result_timeout_{"DropInlineQueryResultTimeout"};
std::unordered_map<uint64, InlineQueryResult> inline_query_results_; // query_hash -> result
FlatHashMap<uint64, InlineQueryResult> inline_query_results_; // query_hash -> result
std::unordered_map<int64, std::unordered_map<string, InlineMessageContent>>
FlatHashMap<int64, FlatHashMap<string, InlineMessageContent>>
inline_message_contents_; // query_id -> [result_id -> inline_message_content]
std::unordered_map<int64, UserId> query_id_to_bot_user_id_;
FlatHashMap<int64, UserId> query_id_to_bot_user_id_;
Td *td_;
ActorShared<> parent_;

View File

@ -54,8 +54,8 @@ InputDialogId::InputDialogId(const tl_object_ptr<telegram_api::InputPeer> &input
vector<InputDialogId> InputDialogId::get_input_dialog_ids(
const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
std::unordered_set<DialogId, DialogIdHash> *added_dialog_ids) {
std::unordered_set<DialogId, DialogIdHash> temp_added_dialog_ids;
FlatHashSet<DialogId, DialogIdHash> *added_dialog_ids) {
FlatHashSet<DialogId, DialogIdHash> temp_added_dialog_ids;
if (added_dialog_ids == nullptr) {
added_dialog_ids = &temp_added_dialog_ids;
}

View File

@ -10,10 +10,9 @@
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/StringBuilder.h"
#include <unordered_set>
namespace td {
class InputDialogId {
@ -28,9 +27,8 @@ class InputDialogId {
explicit InputDialogId(const tl_object_ptr<telegram_api::InputPeer> &input_peer);
static vector<InputDialogId> get_input_dialog_ids(
const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
std::unordered_set<DialogId, DialogIdHash> *added_dialog_ids = nullptr);
static vector<InputDialogId> get_input_dialog_ids(const vector<tl_object_ptr<telegram_api::InputPeer>> &input_peers,
FlatHashSet<DialogId, DialogIdHash> *added_dialog_ids = nullptr);
static vector<telegram_api::object_ptr<telegram_api::InputDialogPeer>> get_input_dialog_peers(
const vector<InputDialogId> &input_dialog_ids);

View File

@ -20,6 +20,7 @@
#include "td/utils/algorithm.h"
#include "td/utils/ExitGuard.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/SliceBuilder.h"
@ -28,7 +29,6 @@
#include <atomic>
#include <limits>
#include <map>
#include <unordered_set>
#include <utility>
namespace td {
@ -40,6 +40,16 @@ struct LanguagePackManager::PluralizedString {
string few_value_;
string many_value_;
string other_value_;
PluralizedString(string &&zero_value, string &&one_value, string &&two_value, string &&few_value, string &&many_value,
string &&other_value)
: zero_value_(std::move(zero_value))
, one_value_(std::move(one_value))
, two_value_(std::move(two_value))
, few_value_(std::move(few_value))
, many_value_(std::move(many_value))
, other_value_(std::move(other_value)) {
}
};
struct LanguagePackManager::Language {
@ -51,9 +61,9 @@ struct LanguagePackManager::Language {
bool was_loaded_full_ = false;
bool has_get_difference_query_ = false;
vector<Promise<Unit>> get_difference_queries_;
std::unordered_map<string, string> ordinary_strings_;
std::unordered_map<string, PluralizedString> pluralized_strings_;
std::unordered_set<string> deleted_strings_;
FlatHashMap<string, string> ordinary_strings_;
FlatHashMap<string, unique_ptr<PluralizedString>> pluralized_strings_;
FlatHashSet<string> deleted_strings_;
SqliteKeyValue kv_; // usages should be guarded by database_->mutex_
};
@ -84,15 +94,15 @@ struct LanguagePackManager::LanguagePack {
SqliteKeyValue pack_kv_; // usages should be guarded by database_->mutex_
std::map<string, LanguageInfo> custom_language_pack_infos_; // sorted by language_code
vector<std::pair<string, LanguageInfo>> server_language_pack_infos_; // sorted by server
std::unordered_map<string, LanguageInfo> all_server_language_pack_infos_;
std::unordered_map<string, unique_ptr<Language>> languages_;
FlatHashMap<string, unique_ptr<LanguageInfo>> all_server_language_pack_infos_;
FlatHashMap<string, unique_ptr<Language>> languages_;
};
struct LanguagePackManager::LanguageDatabase {
std::mutex mutex_;
string path_;
SqliteDb database_;
std::unordered_map<string, unique_ptr<LanguagePack>> language_packs_;
FlatHashMap<string, unique_ptr<LanguagePack>> language_packs_;
};
LanguagePackManager::~LanguagePackManager() = default;
@ -572,6 +582,10 @@ LanguagePackManager::Language *LanguagePackManager::add_language(LanguageDatabas
auto all_infos = full_split(lang.second, '\x00');
if (all_infos.size() % 11 == 0) {
for (size_t i = 0; i < all_infos.size(); i += 11) {
if (all_infos[i].empty()) {
LOG(ERROR) << "Have empty info about a language pack";
continue;
}
LanguageInfo info;
info.name_ = std::move(all_infos[i + 1]);
info.native_name_ = std::move(all_infos[i + 2]);
@ -584,7 +598,7 @@ LanguagePackManager::Language *LanguagePackManager::add_language(LanguageDatabas
info.total_string_count_ = to_integer<int32>(all_infos[i + 8]);
info.translated_string_count_ = to_integer<int32>(all_infos[i + 9]);
info.translation_url_ = std::move(all_infos[i + 10]);
pack->all_server_language_pack_infos_.emplace(all_infos[i], info);
pack->all_server_language_pack_infos_.emplace(all_infos[i], td::make_unique<LanguageInfo>(info));
pack->server_language_pack_infos_.emplace_back(std::move(all_infos[i]), std::move(info));
}
} else {
@ -679,7 +693,8 @@ void LanguagePackManager::load_language_string_unsafe(Language *language, const
auto all = full_split(Slice(value).substr(1), '\x00');
if (all.size() == 6) {
language->pluralized_strings_.emplace(
key, PluralizedString{all[0].str(), all[1].str(), all[2].str(), all[3].str(), all[4].str(), all[5].str()});
key, td::make_unique<PluralizedString>(all[0].str(), all[1].str(), all[2].str(), all[3].str(), all[4].str(),
all[5].str()));
return;
}
}
@ -771,17 +786,17 @@ td_api::object_ptr<td_api::LanguagePackStringValue> LanguagePackManager::get_lan
}
td_api::object_ptr<td_api::languagePackString> LanguagePackManager::get_language_pack_string_object(
const std::pair<string, string> &str) {
return td_api::make_object<td_api::languagePackString>(str.first, get_language_pack_string_value_object(str.second));
const string &key, const string &value) {
return td_api::make_object<td_api::languagePackString>(key, get_language_pack_string_value_object(value));
}
td_api::object_ptr<td_api::languagePackString> LanguagePackManager::get_language_pack_string_object(
const std::pair<string, PluralizedString> &str) {
return td_api::make_object<td_api::languagePackString>(str.first, get_language_pack_string_value_object(str.second));
const string &key, const PluralizedString &value) {
return td_api::make_object<td_api::languagePackString>(key, get_language_pack_string_value_object(value));
}
td_api::object_ptr<td_api::languagePackString> LanguagePackManager::get_language_pack_string_object(const string &str) {
return td_api::make_object<td_api::languagePackString>(str, get_language_pack_string_value_object());
td_api::object_ptr<td_api::languagePackString> LanguagePackManager::get_language_pack_string_object(const string &key) {
return td_api::make_object<td_api::languagePackString>(key, get_language_pack_string_value_object());
}
td_api::object_ptr<td_api::LanguagePackStringValue> LanguagePackManager::get_language_pack_string_value_object(
@ -793,7 +808,7 @@ td_api::object_ptr<td_api::LanguagePackStringValue> LanguagePackManager::get_lan
}
auto pluralized_it = language->pluralized_strings_.find(key);
if (pluralized_it != language->pluralized_strings_.end()) {
return get_language_pack_string_value_object(pluralized_it->second);
return get_language_pack_string_value_object(*pluralized_it->second);
}
LOG_IF(ERROR, !language->is_full_ && language->deleted_strings_.count(key) == 0) << "Have no string for key " << key;
return get_language_pack_string_value_object();
@ -812,10 +827,10 @@ td_api::object_ptr<td_api::languagePackStrings> LanguagePackManager::get_languag
vector<td_api::object_ptr<td_api::languagePackString>> strings;
if (keys.empty()) {
for (auto &str : language->ordinary_strings_) {
strings.push_back(get_language_pack_string_object(str));
strings.push_back(get_language_pack_string_object(str.first, str.second));
}
for (auto &str : language->pluralized_strings_) {
strings.push_back(get_language_pack_string_object(str));
strings.push_back(get_language_pack_string_object(str.first, *str.second));
}
} else {
for (auto &key : keys) {
@ -921,7 +936,7 @@ void LanguagePackManager::on_get_language_info(const string &language_pack,
std::lock_guard<std::mutex> lock(language->mutex_);
if (language_pack_info->base_language_pack_id_ != language->base_language_code_) {
language->base_language_code_ = language_pack_info->base_language_pack_id_;
if (language_pack_info->id_ == language_code_) {
if (language_pack == language_pack_ && language_pack_info->id_ == language_code_) {
base_language_code_ = language->base_language_code_;
was_updated_base_language_code = true;
}
@ -964,11 +979,11 @@ void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::la
string language_pack, bool only_local,
Promise<td_api::object_ptr<td_api::localizationTargetInfo>> promise) {
auto results = td_api::make_object<td_api::localizationTargetInfo>();
std::unordered_set<string> added_languages;
FlatHashSet<string> added_languages;
auto add_language_info = [&results, &added_languages](const string &language_code, const LanguageInfo &info,
bool is_installed) {
if (added_languages.insert(language_code).second) {
if (!language_code.empty() && added_languages.insert(language_code).second) {
results->language_packs_.push_back(get_language_pack_info_object(language_code, info));
results->language_packs_.back()->is_installed_ = is_installed;
}
@ -1014,7 +1029,7 @@ void LanguagePackManager::on_get_languages(vector<tl_object_ptr<telegram_api::la
std::lock_guard<std::mutex> pack_lock(pack->mutex_);
if (pack->server_language_pack_infos_ != all_server_infos) {
for (auto &info : all_server_infos) {
pack->all_server_language_pack_infos_[info.first] = info.second;
pack->all_server_language_pack_infos_[info.first] = td::make_unique<LanguageInfo>(info.second);
}
pack->server_language_pack_infos_ = std::move(all_server_infos);
@ -1058,7 +1073,8 @@ void LanguagePackManager::on_get_language(tl_object_ptr<telegram_api::langPackLa
}
}
}
pack->all_server_language_pack_infos_[lang_pack_language->lang_code_] = r_info.move_as_ok();
pack->all_server_language_pack_infos_[lang_pack_language->lang_code_] =
td::make_unique<LanguageInfo>(r_info.move_as_ok());
if (is_changed) {
save_server_language_pack_infos(pack);
@ -1367,6 +1383,10 @@ void LanguagePackManager::on_get_language_pack_strings(
switch (result->get_id()) {
case telegram_api::langPackString::ID: {
auto str = telegram_api::move_object_as<telegram_api::langPackString>(result);
if (!is_valid_key(str->key_)) {
LOG(ERROR) << "Receive invalid key \"" << str->key_ << '"';
break;
}
auto it = language->ordinary_strings_.find(str->key_);
if (it == language->ordinary_strings_.end()) {
key_count_delta++;
@ -1377,16 +1397,20 @@ void LanguagePackManager::on_get_language_pack_strings(
key_count_delta -= static_cast<int32>(language->pluralized_strings_.erase(str->key_));
language->deleted_strings_.erase(str->key_);
if (is_diff) {
strings.push_back(get_language_pack_string_object(*it));
strings.push_back(get_language_pack_string_object(it->first, it->second));
}
database_strings.emplace_back(std::move(str->key_), PSTRING() << '1' << it->second);
break;
}
case telegram_api::langPackStringPluralized::ID: {
auto str = telegram_api::move_object_as<telegram_api::langPackStringPluralized>(result);
PluralizedString value{std::move(str->zero_value_), std::move(str->one_value_),
std::move(str->two_value_), std::move(str->few_value_),
std::move(str->many_value_), std::move(str->other_value_)};
if (!is_valid_key(str->key_)) {
LOG(ERROR) << "Receive invalid key \"" << str->key_ << '"';
break;
}
auto value = td::make_unique<PluralizedString>(std::move(str->zero_value_), std::move(str->one_value_),
std::move(str->two_value_), std::move(str->few_value_),
std::move(str->many_value_), std::move(str->other_value_));
auto it = language->pluralized_strings_.find(str->key_);
if (it == language->pluralized_strings_.end()) {
key_count_delta++;
@ -1397,17 +1421,21 @@ void LanguagePackManager::on_get_language_pack_strings(
key_count_delta -= static_cast<int32>(language->ordinary_strings_.erase(str->key_));
language->deleted_strings_.erase(str->key_);
if (is_diff) {
strings.push_back(get_language_pack_string_object(*it));
strings.push_back(get_language_pack_string_object(it->first, *it->second));
}
database_strings.emplace_back(
std::move(str->key_), PSTRING()
<< '2' << it->second.zero_value_ << '\x00' << it->second.one_value_ << '\x00'
<< it->second.two_value_ << '\x00' << it->second.few_value_ << '\x00'
<< it->second.many_value_ << '\x00' << it->second.other_value_);
<< '2' << it->second->zero_value_ << '\x00' << it->second->one_value_
<< '\x00' << it->second->two_value_ << '\x00' << it->second->few_value_
<< '\x00' << it->second->many_value_ << '\x00' << it->second->other_value_);
break;
}
case telegram_api::langPackStringDeleted::ID: {
auto str = telegram_api::move_object_as<telegram_api::langPackStringDeleted>(result);
if (!is_valid_key(str->key_)) {
LOG(ERROR) << "Receive invalid key \"" << str->key_ << '"';
break;
}
key_count_delta -= static_cast<int32>(language->ordinary_strings_.erase(str->key_));
key_count_delta -= static_cast<int32>(language->pluralized_strings_.erase(str->key_));
language->deleted_strings_.insert(str->key_);
@ -1538,7 +1566,7 @@ void LanguagePackManager::add_custom_server_language(string language_code, Promi
return promise.set_error(Status::Error(400, "Language pack info not found"));
}
auto &info = pack->custom_language_pack_infos_[language_code];
info = it->second;
info = *it->second;
if (!pack->pack_kv_.empty()) {
pack->pack_kv_.set(language_code, get_language_info_string(info));
}
@ -1590,7 +1618,7 @@ Result<tl_object_ptr<telegram_api::LangPackString>> LanguagePackManager::convert
Result<LanguagePackManager::LanguageInfo> LanguagePackManager::get_language_info(
telegram_api::langPackLanguage *language) {
if (!check_language_code_name(language->lang_code_)) {
if (!check_language_code_name(language->lang_code_) || language->lang_code_.empty()) {
LOG(ERROR) << "Receive unsupported language pack ID " << language->lang_code_ << " from server";
return Status::Error(500, "Unsupported language pack ID");
}

View File

@ -15,6 +15,7 @@
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@ -100,7 +101,7 @@ class LanguagePackManager final : public NetQueryCallback {
vector<Promise<td_api::object_ptr<td_api::languagePackStrings>>> queries_;
};
std::unordered_map<string, std::unordered_map<string, PendingQueries>> get_all_language_pack_strings_queries_;
FlatHashMap<string, FlatHashMap<string, PendingQueries>> get_all_language_pack_strings_queries_;
static int32 manager_count_;
@ -125,11 +126,11 @@ class LanguagePackManager final : public NetQueryCallback {
const PluralizedString &value);
static td_api::object_ptr<td_api::LanguagePackStringValue> get_language_pack_string_value_object();
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(
const std::pair<string, string> &str);
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(
const std::pair<string, PluralizedString> &str);
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(const string &str);
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(const string &key,
const string &value);
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(const string &key,
const PluralizedString &value);
static td_api::object_ptr<td_api::languagePackString> get_language_pack_string_object(const string &key);
static td_api::object_ptr<td_api::LanguagePackStringValue> get_language_pack_string_value_object(
const Language *language, const string &key);

View File

@ -67,6 +67,18 @@ static bool is_valid_username(Slice username) {
return true;
}
static bool is_valid_phone_number(Slice phone_number) {
if (phone_number.empty() || phone_number.size() > 32) {
return false;
}
for (auto c : phone_number) {
if (!is_digit(c)) {
return false;
}
}
return true;
}
static string get_url_query_hash(bool is_tg, const HttpUrlQuery &url_query) {
const auto &path = url_query.path_;
if (is_tg) {
@ -80,6 +92,9 @@ static string get_url_query_hash(bool is_tg, const HttpUrlQuery &url_query) {
return path[1];
}
if (!path.empty() && path[0].size() >= 2 && (path[0][0] == ' ' || path[0][0] == '+')) {
if (is_valid_phone_number(Slice(path[0]).substr(1))) {
return string();
}
// /+<link>
return path[0].substr(1);
}
@ -209,6 +224,12 @@ class LinkManager::InternalLinkLanguage final : public InternalLink {
}
};
class LinkManager::InternalLinkLanguageSettings final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeLanguageSettings>();
}
};
class LinkManager::InternalLinkMessage final : public InternalLink {
string url_;
@ -259,6 +280,12 @@ class LinkManager::InternalLinkPassportDataRequest final : public InternalLink {
}
};
class LinkManager::InternalLinkPrivacyAndSecuritySettings final : public InternalLink {
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypePrivacyAndSecuritySettings>();
}
};
class LinkManager::InternalLinkProxy final : public InternalLink {
string server_;
int32 port_;
@ -363,6 +390,18 @@ class LinkManager::InternalLinkUnsupportedProxy final : public InternalLink {
}
};
class LinkManager::InternalLinkUserPhoneNumber final : public InternalLink {
string phone_number_;
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
return td_api::make_object<td_api::internalLinkTypeUserPhoneNumber>(phone_number_);
}
public:
explicit InternalLinkUserPhoneNumber(string phone_number) : phone_number_(std::move(phone_number)) {
}
};
class LinkManager::InternalLinkVoiceChat final : public InternalLink {
string dialog_username_;
string invite_hash_;
@ -803,6 +842,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
}
// resolve?domain=<username>
return td::make_unique<InternalLinkPublicDialog>(std::move(username));
} else if (is_valid_phone_number(get_arg("phone"))) {
// resolve?phone=12345
return td::make_unique<InternalLinkUserPhoneNumber>(get_arg("phone"));
}
} else if (path.size() == 1 && path[0] == "login") {
// login?code=123456
@ -829,6 +871,14 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
// settings/folders
return td::make_unique<InternalLinkFilterSettings>();
}
if (path.size() == 2 && path[1] == "language") {
// settings/language
return td::make_unique<InternalLinkLanguageSettings>();
}
if (path.size() == 2 && path[1] == "privacy") {
// settings/privacy
return td::make_unique<InternalLinkPrivacyAndSecuritySettings>();
}
if (path.size() == 2 && path[1] == "themes") {
// settings/themes
return td::make_unique<InternalLinkThemeSettings>();
@ -959,9 +1009,14 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
}
} else if (path[0][0] == ' ' || path[0][0] == '+') {
if (path[0].size() >= 2) {
// /+<link>
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
<< url_encode(get_url_query_hash(false, url_query)));
if (is_valid_phone_number(Slice(path[0]).substr(1))) {
// /+<phone_number>
return td::make_unique<InternalLinkUserPhoneNumber>(path[0].substr(1));
} else {
// /+<link>
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
<< url_encode(get_url_query_hash(false, url_query)));
}
}
} else if (path[0] == "addstickers") {
if (path.size() >= 2 && !path[1].empty()) {

View File

@ -92,9 +92,11 @@ class LinkManager final : public Actor {
class InternalLinkFilterSettings;
class InternalLinkGame;
class InternalLinkLanguage;
class InternalLinkLanguageSettings;
class InternalLinkMessage;
class InternalLinkMessageDraft;
class InternalLinkPassportDataRequest;
class InternalLinkPrivacyAndSecuritySettings;
class InternalLinkProxy;
class InternalLinkPublicDialog;
class InternalLinkQrCodeAuthentication;
@ -104,6 +106,7 @@ class LinkManager final : public Actor {
class InternalLinkThemeSettings;
class InternalLinkUnknownDeepLink;
class InternalLinkUnsupportedProxy;
class InternalLinkUserPhoneNumber;
class InternalLinkVoiceChat;
struct LinkInfo {

View File

@ -5500,7 +5500,7 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
switch (message_content->get_type()) {
case MessageContentType::Text: {
const auto *content = static_cast<const MessageText *>(message_content);
dependencies.web_page_ids.insert(content->web_page_id);
dependencies.add(content->web_page_id);
break;
}
case MessageContentType::Animation:
@ -5509,14 +5509,14 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::Contact: {
const auto *content = static_cast<const MessageContact *>(message_content);
dependencies.user_ids.insert(content->contact.get_user_id());
dependencies.add(content->contact.get_user_id());
break;
}
case MessageContentType::Document:
break;
case MessageContentType::Game: {
const auto *content = static_cast<const MessageGame *>(message_content);
dependencies.user_ids.insert(content->game.get_bot_user_id());
dependencies.add(content->game.get_bot_user_id());
break;
}
case MessageContentType::Invoice:
@ -5539,7 +5539,9 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::ChatCreate: {
const auto *content = static_cast<const MessageChatCreate *>(message_content);
dependencies.user_ids.insert(content->participant_user_ids.begin(), content->participant_user_ids.end());
for (auto &participant_user_id : content->participant_user_ids) {
dependencies.add(participant_user_id);
}
break;
}
case MessageContentType::ChatChangeTitle:
@ -5552,26 +5554,28 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::ChatAddUsers: {
const auto *content = static_cast<const MessageChatAddUsers *>(message_content);
dependencies.user_ids.insert(content->user_ids.begin(), content->user_ids.end());
for (auto &user_id : content->user_ids) {
dependencies.add(user_id);
}
break;
}
case MessageContentType::ChatJoinedByLink:
break;
case MessageContentType::ChatDeleteUser: {
const auto *content = static_cast<const MessageChatDeleteUser *>(message_content);
dependencies.user_ids.insert(content->user_id);
dependencies.add(content->user_id);
break;
}
case MessageContentType::ChatMigrateTo: {
const auto *content = static_cast<const MessageChatMigrateTo *>(message_content);
dependencies.channel_ids.insert(content->migrated_to_channel_id);
dependencies.add(content->migrated_to_channel_id);
break;
}
case MessageContentType::ChannelCreate:
break;
case MessageContentType::ChannelMigrateFrom: {
const auto *content = static_cast<const MessageChannelMigrateFrom *>(message_content);
dependencies.chat_ids.insert(content->migrated_from_chat_id);
dependencies.add(content->migrated_from_chat_id);
break;
}
case MessageContentType::PinMessage:
@ -5588,7 +5592,7 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::PaymentSuccessful: {
const auto *content = static_cast<const MessagePaymentSuccessful *>(message_content);
add_dialog_and_dependencies(dependencies, content->invoice_dialog_id);
dependencies.add_dialog_and_dependencies(content->invoice_dialog_id);
break;
}
case MessageContentType::ContactRegistered:
@ -5612,15 +5616,17 @@ void add_message_content_dependencies(Dependencies &dependencies, const MessageC
break;
case MessageContentType::ProximityAlertTriggered: {
const auto *content = static_cast<const MessageProximityAlertTriggered *>(message_content);
add_message_sender_dependencies(dependencies, content->traveler_dialog_id);
add_message_sender_dependencies(dependencies, content->watcher_dialog_id);
dependencies.add_message_sender_dependencies(content->traveler_dialog_id);
dependencies.add_message_sender_dependencies(content->watcher_dialog_id);
break;
}
case MessageContentType::GroupCall:
break;
case MessageContentType::InviteToGroupCall: {
const auto *content = static_cast<const MessageInviteToGroupCall *>(message_content);
dependencies.user_ids.insert(content->user_ids.begin(), content->user_ids.end());
for (auto &user_id : content->user_ids) {
dependencies.add(user_id);
}
break;
}
case MessageContentType::ChatSetTheme:

View File

@ -37,7 +37,7 @@
namespace td {
struct Dependencies;
class Dependencies;
class DialogAction;
class Game;
struct Photo;

View File

@ -341,4 +341,20 @@ bool can_have_message_content_caption(MessageContentType content_type) {
}
}
uint64 get_message_content_chain_id(MessageContentType content_type) {
switch (content_type) {
case MessageContentType::Animation:
case MessageContentType::Audio:
case MessageContentType::Document:
case MessageContentType::Photo:
case MessageContentType::Sticker:
case MessageContentType::Video:
case MessageContentType::VideoNote:
case MessageContentType::VoiceNote:
return 1;
default:
return 2;
}
}
} // namespace td

View File

@ -76,6 +76,8 @@ bool is_service_message_content(MessageContentType content_type);
bool can_have_message_content_caption(MessageContentType content_type);
uint64 get_message_content_chain_id(MessageContentType content_type);
struct MessageContentTypeHash {
std::size_t operator()(MessageContentType content_type) const {
return std::hash<int32>()(static_cast<int32>(content_type));

View File

@ -1270,9 +1270,14 @@ static Slice fix_url(Slice str) {
return full_url;
}
const std::unordered_set<Slice, SliceHash> &get_valid_short_usernames() {
static const std::unordered_set<Slice, SliceHash> valid_usernames{"gif", "wiki", "vid", "bing", "pic",
"bold", "imdb", "coub", "like", "vote"};
const FlatHashSet<Slice, SliceHash> &get_valid_short_usernames() {
static const FlatHashSet<Slice, SliceHash> valid_usernames = [] {
FlatHashSet<Slice, SliceHash> result;
for (auto username : {"gif", "wiki", "vid", "bing", "pic", "bold", "imdb", "coub", "like", "vote"}) {
result.insert(Slice(username));
}
return result;
}();
return valid_usernames;
}
@ -4141,7 +4146,7 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted
}
for (auto &entity : text->entities) {
if (entity.user_id.is_valid()) {
dependencies.user_ids.insert(entity.user_id);
dependencies.add(entity.user_id);
}
}
}

View File

@ -13,17 +13,17 @@
#include "td/telegram/UserId.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include <unordered_set>
#include <utility>
namespace td {
class ContactsManager;
struct Dependencies;
class Dependencies;
class MessageEntity {
public:
@ -131,7 +131,7 @@ inline bool operator!=(const FormattedText &lhs, const FormattedText &rhs) {
return !(lhs == rhs);
}
const std::unordered_set<Slice, SliceHash> &get_valid_short_usernames();
const FlatHashSet<Slice, SliceHash> &get_valid_short_usernames();
Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contacts_manager,
vector<tl_object_ptr<td_api::textEntity>> &&input_entities,

View File

@ -17,11 +17,11 @@
#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <algorithm>
#include <unordered_set>
#include <utility>
namespace td {
@ -52,7 +52,10 @@ class GetMessagesReactionsQuery final : public Td::ResultHandler {
LOG(INFO) << "Receive result for GetMessagesReactionsQuery: " << to_string(ptr);
if (ptr->get_id() == telegram_api::updates::ID) {
auto &updates = static_cast<telegram_api::updates *>(ptr.get())->updates_;
std::unordered_set<MessageId, MessageIdHash> skipped_message_ids(message_ids_.begin(), message_ids_.end());
FlatHashSet<MessageId, MessageIdHash> skipped_message_ids;
for (auto message_id : message_ids_) {
skipped_message_ids.insert(message_id);
}
for (const auto &update : updates) {
if (update->get_id() == telegram_api::updateMessageReactions::ID) {
auto update_message_reactions = static_cast<const telegram_api::updateMessageReactions *>(update.get());
@ -62,21 +65,22 @@ class GetMessagesReactionsQuery final : public Td::ResultHandler {
}
}
for (auto message_id : skipped_message_ids) {
td_->messages_manager_->on_update_message_reactions({dialog_id_, message_id}, nullptr);
td_->messages_manager_->update_message_reactions({dialog_id_, message_id}, nullptr);
}
}
td_->updates_manager_->on_get_updates(std::move(ptr), Promise<Unit>());
td_->messages_manager_->try_reload_message_reactions(dialog_id_, true);
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetMessagesReactionsQuery");
td_->messages_manager_->try_reload_message_reactions(dialog_id_, true);
}
};
class SendReactionQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
MessageId message_id_;
public:
explicit SendReactionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
@ -84,7 +88,6 @@ class SendReactionQuery final : public Td::ResultHandler {
void send(FullMessageId full_message_id, string reaction, bool is_big) {
dialog_id_ = full_message_id.get_dialog_id();
message_id_ = full_message_id.get_message_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
if (input_peer == nullptr) {
@ -100,8 +103,10 @@ class SendReactionQuery final : public Td::ResultHandler {
}
}
send_query(G()->net_query_creator().create(telegram_api::messages_sendReaction(
flags, false /*ignored*/, std::move(input_peer), message_id_.get_server_message_id().get(), reaction)));
send_query(G()->net_query_creator().create(
telegram_api::messages_sendReaction(flags, false /*ignored*/, std::move(input_peer),
full_message_id.get_message_id().get_server_message_id().get(), reaction),
{{dialog_id_}, {full_message_id}}));
}
void on_result(BufferSlice packet) final {
@ -152,8 +157,10 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
flags |= telegram_api::messages_getMessageReactionsList::OFFSET_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::messages_getMessageReactionsList(
flags, std::move(input_peer), message_id_.get_server_message_id().get(), reaction_, offset_, limit)));
send_query(G()->net_query_creator().create(
telegram_api::messages_getMessageReactionsList(
flags, std::move(input_peer), message_id_.get_server_message_id().get(), reaction_, offset_, limit),
{{full_message_id}}));
}
void on_result(BufferSlice packet) final {
@ -169,25 +176,37 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler {
td_->contacts_manager_->on_get_chats(std::move(ptr->chats_), "GetMessageReactionsListQuery");
int32 total_count = ptr->count_;
if (total_count < static_cast<int32>(ptr->reactions_.size())) {
auto received_reaction_count = static_cast<int32>(ptr->reactions_.size());
if (total_count < received_reaction_count) {
LOG(ERROR) << "Receive invalid total_count in " << to_string(ptr);
total_count = static_cast<int32>(ptr->reactions_.size());
total_count = received_reaction_count;
}
vector<td_api::object_ptr<td_api::addedReaction>> reactions;
for (auto &reaction : ptr->reactions_) {
FlatHashMap<string, vector<DialogId>> recent_reactions;
for (const auto &reaction : ptr->reactions_) {
DialogId dialog_id(reaction->peer_id_);
if (!dialog_id.is_valid() || (!reaction_.empty() && reaction_ != reaction->reaction_)) {
if (!dialog_id.is_valid() ||
(reaction_.empty() ? reaction->reaction_.empty() : reaction_ != reaction->reaction_)) {
LOG(ERROR) << "Receive unexpected " << to_string(reaction);
continue;
}
if (offset_.empty()) {
recent_reactions[reaction->reaction_].push_back(dialog_id);
}
auto message_sender = get_min_message_sender_object(td_, dialog_id, "GetMessageReactionsListQuery");
if (message_sender != nullptr) {
reactions.push_back(td_api::make_object<td_api::addedReaction>(reaction->reaction_, std::move(message_sender)));
}
}
if (offset_.empty()) {
td_->messages_manager_->on_get_message_reaction_list({dialog_id_, message_id_}, reaction_,
std::move(recent_reactions), total_count);
}
promise_.set_value(
td_api::make_object<td_api::addedReactions>(total_count, std::move(reactions), ptr->next_offset_));
}
@ -272,10 +291,11 @@ unique_ptr<MessageReactions> MessageReactions::get_message_reactions(
result->can_get_added_reactions_ = reactions->can_see_list_;
result->is_min_ = reactions->min_;
std::unordered_set<string> reaction_strings;
std::unordered_set<DialogId, DialogIdHash> recent_choosers;
FlatHashSet<string> reaction_strings;
FlatHashSet<DialogId, DialogIdHash> recent_choosers;
for (auto &reaction_count : reactions->results_) {
if (reaction_count->count_ <= 0 || reaction_count->count_ >= MessageReaction::MAX_CHOOSE_COUNT) {
if (reaction_count->count_ <= 0 || reaction_count->count_ >= MessageReaction::MAX_CHOOSE_COUNT ||
reaction_count->reaction_.empty()) {
LOG(ERROR) << "Receive reaction " << reaction_count->reaction_ << " with invalid count "
<< reaction_count->count_;
continue;
@ -368,10 +388,11 @@ void MessageReactions::update_from(const MessageReactions &old_reactions) {
}
}
}
unread_reactions_ = old_reactions.unread_reactions_;
}
}
void MessageReactions::sort_reactions(const std::unordered_map<string, size_t> &active_reaction_pos) {
void MessageReactions::sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos) {
std::sort(reactions_.begin(), reactions_.end(),
[&active_reaction_pos](const MessageReaction &lhs, const MessageReaction &rhs) {
if (lhs.get_choose_count() != rhs.get_choose_count()) {

View File

@ -17,9 +17,9 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/StringBuilder.h"
#include <unordered_map>
#include <utility>
namespace td {
@ -147,7 +147,7 @@ struct MessageReactions {
void update_from(const MessageReactions &old_reactions);
void sort_reactions(const std::unordered_map<string, size_t> &active_reaction_pos);
void sort_reactions(const FlatHashMap<string, size_t> &active_reaction_pos);
static bool need_update_message_reactions(const MessageReactions *old_reactions,
const MessageReactions *new_reactions);

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,6 @@
#include "td/telegram/secret_api.h"
#include "td/telegram/SecretChatId.h"
#include "td/telegram/SecretInputMedia.h"
#include "td/telegram/SequenceDispatcher.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -67,6 +66,8 @@
#include "td/utils/buffer.h"
#include "td/utils/ChangesProcessor.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Heap.h"
#include "td/utils/Hints.h"
#include "td/utils/logging.h"
@ -86,7 +87,7 @@
namespace td {
struct BinlogEvent;
struct Dependencies;
class Dependencies;
class DialogActionBar;
class DialogFilter;
class DraftMessage;
@ -160,6 +161,8 @@ class MessagesManager final : public Actor {
static DialogId get_message_dialog_id(const tl_object_ptr<telegram_api::Message> &message_ptr);
static FullMessageId get_full_message_id(const tl_object_ptr<telegram_api::Message> &message_ptr, bool is_scheduled);
tl_object_ptr<telegram_api::InputPeer> get_input_peer(DialogId dialog_id, AccessRights access_rights) const;
static tl_object_ptr<telegram_api::InputPeer> get_input_peer_force(DialogId dialog_id);
@ -227,6 +230,9 @@ class MessagesManager final : public Actor {
vector<tl_object_ptr<telegram_api::Message>> &&messages, Promise<Unit> &&promise);
void on_failed_messages_search(int64 random_id);
void on_get_outgoing_document_messages(vector<tl_object_ptr<telegram_api::Message>> &&messages,
Promise<td_api::object_ptr<td_api::foundMessages>> &&promise);
void on_get_scheduled_server_messages(DialogId dialog_id, uint32 generation,
vector<tl_object_ptr<telegram_api::Message>> &&messages, bool is_not_modified);
@ -339,12 +345,18 @@ class MessagesManager final : public Actor {
void on_update_message_forward_count(FullMessageId full_message_id, int32 forward_count);
void on_update_message_reactions(FullMessageId full_message_id,
tl_object_ptr<telegram_api::messageReactions> &&reactions);
tl_object_ptr<telegram_api::messageReactions> &&reactions, Promise<Unit> &&promise);
void update_message_reactions(FullMessageId full_message_id, unique_ptr<MessageReactions> &&reactions);
void try_reload_message_reactions(DialogId dialog_id, bool is_finished);
void on_get_message_reaction_list(FullMessageId full_message_id, const string &reaction,
FlatHashMap<string, vector<DialogId>> reactions, int32 total_count);
void on_update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count,
bool has_reply_info, tl_object_ptr<telegram_api::messageReplies> &&reply_info,
bool has_reactions,
tl_object_ptr<telegram_api::messageReactions> &&reactions);
bool has_reply_info,
tl_object_ptr<telegram_api::messageReplies> &&reply_info);
void on_update_live_location_viewed(FullMessageId full_message_id);
@ -416,10 +428,11 @@ class MessagesManager final : public Actor {
tl_object_ptr<td_api::messageSendOptions> &&options, tl_object_ptr<td_api::ReplyMarkup> &&reply_markup,
tl_object_ptr<td_api::InputMessageContent> &&input_message_content) TD_WARN_UNUSED_RESULT;
Result<vector<MessageId>> send_message_group(
Result<td_api::object_ptr<td_api::messages>> send_message_group(
DialogId dialog_id, MessageId top_thread_message_id, MessageId reply_to_message_id,
tl_object_ptr<td_api::messageSendOptions> &&options,
vector<tl_object_ptr<td_api::InputMessageContent>> &&input_message_contents) TD_WARN_UNUSED_RESULT;
vector<tl_object_ptr<td_api::InputMessageContent>> &&input_message_contents,
bool only_preview) TD_WARN_UNUSED_RESULT;
Result<MessageId> send_bot_start_message(UserId bot_user_id, DialogId dialog_id,
const string &parameter) TD_WARN_UNUSED_RESULT;
@ -683,6 +696,8 @@ class MessagesManager final : public Actor {
Status view_messages(DialogId dialog_id, MessageId top_thread_message_id, const vector<MessageId> &message_ids,
bool force_read) TD_WARN_UNUSED_RESULT;
void finish_get_message_views(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,
@ -756,6 +771,9 @@ class MessagesManager final : public Actor {
std::pair<int32, vector<FullMessageId>> search_call_messages(MessageId from_message_id, int32 limit, bool only_missed,
int64 &random_id, bool use_db, Promise<Unit> &&promise);
void search_outgoing_document_messages(const string &query, int32 limit,
Promise<td_api::object_ptr<td_api::foundMessages>> &&promise);
void search_dialog_recent_location_messages(DialogId dialog_id, int32 limit,
Promise<td_api::object_ptr<td_api::messages>> &&promise);
@ -918,7 +936,7 @@ class MessagesManager final : public Actor {
vector<tl_object_ptr<telegram_api::User>> users,
vector<tl_object_ptr<telegram_api::Chat>> chats);
FileSourceId get_message_file_source_id(FullMessageId full_message_id);
FileSourceId get_message_file_source_id(FullMessageId full_message_id, bool force = false);
struct MessagePushNotificationInfo {
NotificationGroupId group_id;
@ -976,9 +994,10 @@ class MessagesManager final : public Actor {
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
ActorOwn<MultiSequenceDispatcher> sequence_dispatcher_;
void add_message_file_to_downloads(FullMessageId full_message_id, FileId file_id, int32 priority,
Promise<td_api::object_ptr<td_api::file>> promise);
static uint64 get_sequence_dispatcher_id(DialogId dialog_id, MessageContentType message_content_type);
void get_message_file_search_text(FullMessageId full_message_id, string unique_file_id, Promise<string> promise);
private:
class PendingPtsUpdate {
@ -1129,6 +1148,9 @@ class MessagesManager final : public Actor {
bool have_next = false;
bool from_database = false;
bool has_get_message_views_query = false;
bool need_view_counter_increment = false;
DialogId real_forward_from_dialog_id; // for resend_message
MessageId real_forward_from_message_id; // for resend_message
@ -1145,6 +1167,7 @@ class MessagesManager final : public Actor {
MessageReplyInfo reply_info;
unique_ptr<MessageReactions> reactions;
unique_ptr<DraftMessage> thread_draft_message;
uint32 available_reactions_generation = 0;
int32 interaction_info_update_date = 0;
int32 legacy_layer = 0;
@ -1233,6 +1256,7 @@ class MessagesManager final : public Actor {
MessageId reply_markup_message_id;
DialogNotificationSettings notification_settings;
vector<string> available_reactions;
uint32 available_reactions_generation = 0;
MessageTtl message_ttl;
unique_ptr<DraftMessage> draft_message;
unique_ptr<DialogActionBar> action_bar;
@ -1341,22 +1365,22 @@ class MessagesManager final : public Actor {
// application start, used to guarantee that all assigned message identifiers
// are different
std::unordered_map<MessageId, std::set<MessageId>, MessageIdHash>
FlatHashMap<MessageId, std::set<MessageId>, MessageIdHash>
yet_unsent_thread_message_ids; // top_thread_message_id -> yet unsent message IDs
std::unordered_map<ScheduledServerMessageId, int32, ScheduledServerMessageIdHash> scheduled_message_date;
FlatHashMap<ScheduledServerMessageId, int32, ScheduledServerMessageIdHash> scheduled_message_date;
std::unordered_map<MessageId, MessageId, MessageIdHash> yet_unsent_message_id_to_persistent_message_id;
FlatHashMap<MessageId, MessageId, MessageIdHash> yet_unsent_message_id_to_persistent_message_id;
std::unordered_map<int32, MessageId> last_assigned_scheduled_message_id; // date -> message_id
FlatHashMap<int32, MessageId> last_assigned_scheduled_message_id; // date -> message_id
std::unordered_set<MessageId, MessageIdHash> deleted_message_ids;
std::unordered_set<ScheduledServerMessageId, ScheduledServerMessageIdHash> deleted_scheduled_server_message_ids;
FlatHashSet<MessageId, MessageIdHash> deleted_message_ids;
FlatHashSet<ScheduledServerMessageId, ScheduledServerMessageIdHash> deleted_scheduled_server_message_ids;
std::vector<std::pair<DialogId, MessageId>> pending_new_message_notifications;
std::vector<std::pair<DialogId, MessageId>> pending_new_mention_notifications;
vector<std::pair<DialogId, MessageId>> pending_new_message_notifications;
vector<std::pair<DialogId, MessageId>> pending_new_mention_notifications;
std::unordered_map<NotificationId, MessageId, NotificationIdHash> notification_id_to_message_id;
FlatHashMap<NotificationId, MessageId, NotificationIdHash> notification_id_to_message_id;
string client_data;
@ -1366,8 +1390,8 @@ class MessagesManager final : public Actor {
MessageId suffix_load_query_message_id_;
std::vector<std::pair<Promise<>, std::function<bool(const Message *)>>> suffix_load_queries_;
std::unordered_map<MessageId, int64, MessageIdHash> pending_viewed_live_locations; // message_id -> task_id
std::unordered_set<MessageId, MessageIdHash> pending_viewed_message_ids;
FlatHashMap<MessageId, int64, MessageIdHash> pending_viewed_live_locations; // message_id -> task_id
FlatHashSet<MessageId, MessageIdHash> pending_viewed_message_ids;
unique_ptr<Message> messages;
unique_ptr<Message> scheduled_messages;
@ -1444,7 +1468,7 @@ class MessagesManager final : public Actor {
vector<Promise<Unit>> load_list_queries_;
std::unordered_map<DialogId, int64, DialogIdHash> pinned_dialog_id_orders_;
FlatHashMap<DialogId, int64, DialogIdHash> pinned_dialog_id_orders_;
vector<DialogDate> pinned_dialogs_;
bool are_pinned_dialogs_inited_ = false;
@ -1763,7 +1787,8 @@ class MessagesManager final : public Actor {
static constexpr int32 MIN_READ_HISTORY_DELAY = 3; // seconds
static constexpr int32 MAX_SAVE_DIALOG_DELAY = 0; // seconds
static constexpr int32 LIVE_LOCATION_VIEW_PERIOD = 60; // seconds, server-side limit
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;
@ -1781,8 +1806,6 @@ class MessagesManager final : public Actor {
static constexpr bool DROP_SEND_MESSAGE_UPDATES = false;
static FullMessageId get_full_message_id(const tl_object_ptr<telegram_api::Message> &message_ptr, bool is_scheduled);
static int32 get_message_date(const tl_object_ptr<telegram_api::Message> &message_ptr);
static bool is_dialog_inited(const Dialog *d);
@ -1986,6 +2009,8 @@ class MessagesManager final : public Actor {
void on_media_message_ready_to_send(DialogId dialog_id, MessageId message_id, Promise<Message *> &&promise);
void send_secret_message(DialogId dialog_id, const Message *m, SecretInputMedia media);
void on_yet_unsent_media_queue_updated(DialogId dialog_id);
static void save_send_bot_start_message_log_event(UserId bot_user_id, DialogId dialog_id, const string &parameter,
@ -2104,7 +2129,7 @@ class MessagesManager final : public Actor {
void update_message_interaction_info(FullMessageId full_message_id, int32 view_count, int32 forward_count,
bool has_reply_info, tl_object_ptr<telegram_api::messageReplies> &&reply_info,
bool has_reactions, tl_object_ptr<telegram_api::messageReactions> &&reactions);
bool has_reactions, unique_ptr<MessageReactions> &&reactions);
bool is_active_message_reply_info(DialogId dialog_id, const MessageReplyInfo &info) const;
@ -2124,7 +2149,7 @@ class MessagesManager final : public Actor {
vector<td_api::object_ptr<td_api::unreadReaction>> get_unread_reactions_object(DialogId dialog_id,
const Message *m) const;
bool update_message_interaction_info(DialogId dialog_id, Message *m, int32 view_count, int32 forward_count,
bool update_message_interaction_info(Dialog *d, Message *m, int32 view_count, int32 forward_count,
bool has_reply_info, MessageReplyInfo &&reply_info, bool has_reactions,
unique_ptr<MessageReactions> &&reactions, const char *source);
@ -2179,6 +2204,8 @@ class MessagesManager final : public Actor {
void on_update_dialog_online_member_count_timeout(DialogId dialog_id);
void on_update_viewed_messages_timeout(DialogId dialog_id);
bool delete_newer_server_messages_at_the_end(Dialog *d, MessageId max_message_id);
template <class T, class It>
@ -2623,7 +2650,9 @@ class MessagesManager final : public Actor {
void set_dialog_available_reactions(Dialog *d, vector<string> &&available_reactions);
void update_dialog_message_reactions_visibility(Dialog *d);
void set_dialog_available_reactions_generation(Dialog *d, uint32 new_generation);
void hide_dialog_message_reactions(Dialog *d);
vector<string> get_active_reactions(const vector<string> &available_reactions) const;
@ -2634,6 +2663,14 @@ class MessagesManager final : public Actor {
vector<string> get_message_active_reactions(const Dialog *d, const Message *m) const;
static bool need_poll_dialog_message_reactions(const Dialog *d);
static bool need_poll_message_reactions(const Dialog *d, const Message *m);
void queue_message_reactions_reload(FullMessageId full_message_id);
void queue_message_reactions_reload(DialogId dialog_id, const vector<MessageId> &message_ids);
bool is_dialog_action_unneeded(DialogId dialog_id) const;
void on_send_dialog_action_timeout(DialogId dialog_id);
@ -2818,8 +2855,8 @@ class MessagesManager final : public Actor {
Message *on_get_message_from_database(Dialog *d, const MessagesDbDialogMessage &message, bool is_scheduled,
const char *source);
Message *on_get_message_from_database(Dialog *d, DialogId dialog_id, MessageId message_id, const BufferSlice &value,
bool is_scheduled, const char *source);
Message *on_get_message_from_database(Dialog *d, MessageId message_id, const BufferSlice &value, bool is_scheduled,
const char *source);
void get_dialog_message_by_date_from_server(const Dialog *d, int32 date, int64 random_id, bool after_database_search,
Promise<Unit> &&promise);
@ -3096,6 +3133,8 @@ class MessagesManager final : public Actor {
static void on_preload_folder_dialog_list_timeout_callback(void *messages_manager_ptr, int64 folder_id_int);
static void on_update_viewed_messages_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int);
void load_secret_thumbnail(FileId thumbnail_file_id);
void on_upload_media(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file,
@ -3169,7 +3208,7 @@ class MessagesManager final : public Actor {
string get_message_search_text(const Message *m) const;
unique_ptr<Message> parse_message(DialogId dialog_id, MessageId expected_message_id, const BufferSlice &value,
unique_ptr<Message> parse_message(Dialog *d, MessageId expected_message_id, const BufferSlice &value,
bool is_scheduled);
unique_ptr<Dialog> parse_dialog(DialogId dialog_id, const BufferSlice &value, const char *source);
@ -3275,20 +3314,20 @@ class MessagesManager final : public Actor {
double last_channel_pts_jump_warning_time_ = 0;
std::unordered_map<FileId, std::pair<FullMessageId, FileId>, FileIdHash>
FlatHashMap<FileId, std::pair<FullMessageId, FileId>, FileIdHash>
being_uploaded_files_; // file_id -> message, thumbnail_file_id
struct UploadedThumbnailInfo {
FullMessageId full_message_id;
FileId file_id; // original file file_id
tl_object_ptr<telegram_api::InputFile> input_file; // original file InputFile
};
std::unordered_map<FileId, UploadedThumbnailInfo, FileIdHash> being_uploaded_thumbnails_; // thumbnail_file_id -> ...
FlatHashMap<FileId, UploadedThumbnailInfo, FileIdHash> being_uploaded_thumbnails_; // thumbnail_file_id -> ...
struct UploadedSecretThumbnailInfo {
FullMessageId full_message_id;
FileId file_id; // original file file_id
tl_object_ptr<telegram_api::InputEncryptedFile> input_file; // original file InputEncryptedFile
};
std::unordered_map<FileId, UploadedSecretThumbnailInfo, FileIdHash>
FlatHashMap<FileId, UploadedSecretThumbnailInfo, FileIdHash>
being_loaded_secret_thumbnails_; // thumbnail_file_id -> ...
// TTL
@ -3327,12 +3366,11 @@ class MessagesManager final : public Actor {
bool ttl_db_has_query_;
Slot ttl_db_slot_;
std::unordered_map<int64, FullMessageId> being_sent_messages_; // message_random_id -> message
FlatHashMap<int64, FullMessageId> being_sent_messages_; // message_random_id -> message
std::unordered_map<FullMessageId, MessageId, FullMessageIdHash>
update_message_ids_; // new_message_id -> temporary_id
std::unordered_map<DialogId, std::unordered_map<ScheduledServerMessageId, MessageId, ScheduledServerMessageIdHash>,
DialogIdHash>
FlatHashMap<FullMessageId, MessageId, FullMessageIdHash> update_message_ids_; // new_message_id -> temporary_id
FlatHashMap<DialogId, FlatHashMap<ScheduledServerMessageId, MessageId, ScheduledServerMessageIdHash>,
DialogIdHash>
update_scheduled_message_ids_; // new_message_id -> temporary_id
const char *debug_add_message_to_dialog_fail_reason_ = "";
@ -3353,7 +3391,7 @@ class MessagesManager final : public Actor {
, promise(std::move(promise)) {
}
};
std::unordered_map<FileId, UploadedDialogPhotoInfo, FileIdHash> being_uploaded_dialog_photos_;
FlatHashMap<FileId, UploadedDialogPhotoInfo, FileIdHash> being_uploaded_dialog_photos_;
struct UploadedImportedMessagesInfo {
DialogId dialog_id;
@ -3369,7 +3407,7 @@ class MessagesManager final : public Actor {
, promise(std::move(promise)) {
}
};
std::unordered_map<FileId, unique_ptr<UploadedImportedMessagesInfo>, FileIdHash> being_uploaded_imported_messages_;
FlatHashMap<FileId, unique_ptr<UploadedImportedMessagesInfo>, FileIdHash> being_uploaded_imported_messages_;
struct UploadedImportedMessageAttachmentInfo {
DialogId dialog_id;
@ -3382,7 +3420,7 @@ class MessagesManager final : public Actor {
: dialog_id(dialog_id), import_id(import_id), is_reupload(is_reupload), promise(std::move(promise)) {
}
};
std::unordered_map<FileId, unique_ptr<UploadedImportedMessageAttachmentInfo>, FileIdHash>
FlatHashMap<FileId, unique_ptr<UploadedImportedMessageAttachmentInfo>, FileIdHash>
being_uploaded_imported_message_attachments_;
struct PendingMessageImport {
@ -3391,7 +3429,7 @@ class MessagesManager final : public Actor {
int64 import_id = 0;
Promise<Unit> promise;
};
std::unordered_map<int64, unique_ptr<PendingMessageImport>> pending_message_imports_;
FlatHashMap<int64, unique_ptr<PendingMessageImport>> pending_message_imports_;
struct PendingMessageGroupSend {
DialogId dialog_id;
@ -3400,25 +3438,24 @@ class MessagesManager final : public Actor {
vector<bool> is_finished;
vector<Status> results;
};
std::unordered_map<int64, PendingMessageGroupSend> pending_message_group_sends_; // media_album_id -> ...
FlatHashMap<int64, PendingMessageGroupSend> pending_message_group_sends_; // media_album_id -> ...
std::unordered_map<MessageId, DialogId, MessageIdHash> message_id_to_dialog_id_;
std::unordered_map<MessageId, DialogId, MessageIdHash> last_clear_history_message_id_to_dialog_id_;
FlatHashMap<MessageId, DialogId, MessageIdHash> message_id_to_dialog_id_;
FlatHashMap<MessageId, DialogId, MessageIdHash> last_clear_history_message_id_to_dialog_id_;
bool created_public_broadcasts_inited_ = false;
vector<ChannelId> created_public_broadcasts_;
std::unordered_map<int64, DialogId> created_dialogs_; // random_id -> dialog_id
std::unordered_map<DialogId, Promise<Unit>, DialogIdHash> pending_created_dialogs_; // dialog_id -> promise
FlatHashMap<int64, DialogId> created_dialogs_; // random_id -> dialog_id
FlatHashMap<DialogId, Promise<Unit>, DialogIdHash> pending_created_dialogs_; // dialog_id -> promise
bool running_get_difference_ = false; // true after before_get_difference and false after after_get_difference
std::unordered_map<DialogId, unique_ptr<Dialog>, DialogIdHash> dialogs_;
FlatHashMap<DialogId, unique_ptr<Dialog>, DialogIdHash> dialogs_;
std::unordered_set<DialogId, DialogIdHash>
loaded_dialogs_; // dialogs loaded from database, but not added to dialogs_
FlatHashSet<DialogId, DialogIdHash> loaded_dialogs_; // dialogs loaded from database, but not added to dialogs_
std::unordered_set<DialogId, DialogIdHash> postponed_chat_read_inbox_updates_;
FlatHashSet<DialogId, DialogIdHash> postponed_chat_read_inbox_updates_;
struct PendingGetMessageRequest {
MessageId message_id;
@ -3431,9 +3468,9 @@ class MessagesManager final : public Actor {
}
};
std::unordered_map<string, vector<Promise<Unit>>> search_public_dialogs_queries_;
std::unordered_map<string, vector<DialogId>> found_public_dialogs_; // TODO time bound cache
std::unordered_map<string, vector<DialogId>> found_on_server_dialogs_; // TODO time bound cache
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
struct CommonDialogs {
vector<DialogId> dialog_ids;
@ -3441,35 +3478,35 @@ class MessagesManager final : public Actor {
int32 total_count = 0;
bool is_outdated = false;
};
std::unordered_map<UserId, CommonDialogs, UserIdHash> found_common_dialogs_;
FlatHashMap<UserId, CommonDialogs, UserIdHash> found_common_dialogs_;
std::unordered_map<int64, FullMessageId> get_dialog_message_by_date_results_;
FlatHashMap<int64, FullMessageId> get_dialog_message_by_date_results_;
std::unordered_map<int64, td_api::object_ptr<td_api::messageCalendar>> found_dialog_message_calendars_;
std::unordered_map<int64, std::pair<int32, vector<MessageId>>>
found_dialog_messages_; // random_id -> [total_count, [message_id]...]
std::unordered_map<int64, DialogId> found_dialog_messages_dialog_id_; // random_id -> dialog_id
std::unordered_map<int64, std::pair<int32, vector<FullMessageId>>>
FlatHashMap<int64, td_api::object_ptr<td_api::messageCalendar>> found_dialog_message_calendars_;
FlatHashMap<int64, std::pair<int32, vector<MessageId>>>
found_dialog_messages_; // random_id -> [total_count, [message_id]...]
FlatHashMap<int64, DialogId> found_dialog_messages_dialog_id_; // random_id -> dialog_id
FlatHashMap<int64, std::pair<int32, vector<FullMessageId>>>
found_messages_; // random_id -> [total_count, [full_message_id]...]
std::unordered_map<int64, std::pair<int32, vector<FullMessageId>>>
FlatHashMap<int64, std::pair<int32, vector<FullMessageId>>>
found_call_messages_; // random_id -> [total_count, [full_message_id]...]
std::unordered_map<int64, FoundMessages> found_fts_messages_; // random_id -> FoundMessages
FlatHashMap<int64, FoundMessages> found_fts_messages_; // random_id -> FoundMessages
struct MessageEmbeddingCodes {
std::unordered_map<MessageId, string, MessageIdHash> embedding_codes_;
FlatHashMap<MessageId, string, MessageIdHash> embedding_codes_;
};
std::unordered_map<DialogId, MessageEmbeddingCodes, DialogIdHash> message_embedding_codes_[2];
FlatHashMap<DialogId, MessageEmbeddingCodes, DialogIdHash> message_embedding_codes_[2];
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_notification_settings_queries_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_notification_settings_queries_;
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_queries_;
std::unordered_map<DialogId, uint64, DialogIdHash> get_dialog_query_log_event_id_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> get_dialog_queries_;
FlatHashMap<DialogId, uint64, DialogIdHash> get_dialog_query_log_event_id_;
std::unordered_map<FullMessageId, int32, FullMessageIdHash> replied_by_yet_unsent_messages_;
FlatHashMap<FullMessageId, int32, FullMessageIdHash> replied_by_yet_unsent_messages_;
// full_message_id -> replies with media timestamps
std::unordered_map<FullMessageId, std::unordered_set<MessageId, MessageIdHash>, FullMessageIdHash>
FlatHashMap<FullMessageId, FlatHashSet<MessageId, MessageIdHash>, FullMessageIdHash>
replied_by_media_timestamp_messages_;
struct ActiveDialogAction {
@ -3487,13 +3524,13 @@ class MessagesManager final : public Actor {
}
};
std::unordered_map<DialogId, std::vector<ActiveDialogAction>, DialogIdHash> active_dialog_actions_;
FlatHashMap<DialogId, std::vector<ActiveDialogAction>, DialogIdHash> active_dialog_actions_;
ScopeNotificationSettings users_notification_settings_;
ScopeNotificationSettings chats_notification_settings_;
ScopeNotificationSettings channels_notification_settings_;
std::unordered_map<NotificationGroupId, DialogId, NotificationGroupIdHash> notification_group_id_to_dialog_id_;
FlatHashMap<NotificationGroupId, DialogId, NotificationGroupIdHash> notification_group_id_to_dialog_id_;
uint64 current_message_edit_generation_ = 0;
@ -3516,11 +3553,11 @@ class MessagesManager final : public Actor {
vector<RecommendedDialogFilter> recommended_dialog_filters_;
vector<Promise<Unit>> dialog_filter_reload_queries_;
std::unordered_map<DialogId, string, DialogIdHash> active_get_channel_differencies_;
std::unordered_map<DialogId, uint64, DialogIdHash> get_channel_difference_to_log_event_id_;
std::unordered_map<DialogId, int32, DialogIdHash> channel_get_difference_retry_timeouts_;
std::unordered_map<DialogId, std::multimap<int32, PendingPtsUpdate>, DialogIdHash> postponed_channel_updates_;
std::unordered_set<DialogId, DialogIdHash> is_channel_difference_finished_;
FlatHashMap<DialogId, string, DialogIdHash> active_get_channel_differencies_;
FlatHashMap<DialogId, uint64, DialogIdHash> get_channel_difference_to_log_event_id_;
FlatHashMap<DialogId, int32, DialogIdHash> channel_get_difference_retry_timeouts_;
FlatHashMap<DialogId, std::multimap<int32, PendingPtsUpdate>, DialogIdHash> postponed_channel_updates_;
FlatHashSet<DialogId, DialogIdHash> is_channel_difference_finished_;
MultiTimeout channel_get_difference_timeout_{"ChannelGetDifferenceTimeout"};
MultiTimeout channel_get_difference_retry_timeout_{"ChannelGetDifferenceRetryTimeout"};
@ -3535,25 +3572,26 @@ class MessagesManager final : public Actor {
MultiTimeout active_dialog_action_timeout_{"ActiveDialogActionTimeout"};
MultiTimeout update_dialog_online_member_count_timeout_{"UpdateDialogOnlineMemberCountTimeout"};
MultiTimeout preload_folder_dialog_list_timeout_{"PreloadFolderDialogListTimeout"};
MultiTimeout update_viewed_messages_timeout_{"UpdateViewedMessagesTimeout"};
Timeout reload_dialog_filters_timeout_;
Hints dialogs_hints_; // search dialogs by title and username
std::unordered_set<FullMessageId, FullMessageIdHash> active_live_location_full_message_ids_;
FlatHashSet<FullMessageId, FullMessageIdHash> active_live_location_full_message_ids_;
bool are_active_live_location_messages_loaded_ = false;
vector<Promise<Unit>> load_active_live_location_messages_queries_;
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> load_scheduled_messages_from_database_queries_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> load_scheduled_messages_from_database_queries_;
struct ResolvedUsername {
DialogId dialog_id;
double expires_at;
};
std::unordered_map<string, ResolvedUsername> resolved_usernames_;
std::unordered_map<string, DialogId> inaccessible_resolved_usernames_;
std::unordered_set<string> reload_voice_chat_on_search_usernames_;
FlatHashMap<string, ResolvedUsername> resolved_usernames_;
FlatHashMap<string, DialogId> inaccessible_resolved_usernames_;
FlatHashSet<string> reload_voice_chat_on_search_usernames_;
struct GetDialogsTask {
DialogListId dialog_list_id;
@ -3563,7 +3601,7 @@ class MessagesManager final : public Actor {
Promise<td_api::object_ptr<td_api::chats>> promise;
};
std::unordered_map<int64, GetDialogsTask> get_dialogs_tasks_;
FlatHashMap<int64, GetDialogsTask> get_dialogs_tasks_;
int64 current_get_dialogs_task_id_ = 0;
struct PendingOnGetDialogs {
@ -3575,27 +3613,26 @@ class MessagesManager final : public Actor {
};
vector<PendingOnGetDialogs> pending_on_get_dialogs_;
std::unordered_map<DialogId, PendingOnGetDialogs, DialogIdHash> pending_channel_on_get_dialogs_;
FlatHashMap<DialogId, PendingOnGetDialogs, DialogIdHash> pending_channel_on_get_dialogs_;
std::unordered_map<DialogId, vector<Promise<Unit>>, DialogIdHash> run_after_get_channel_difference_;
FlatHashMap<DialogId, vector<Promise<Unit>>, DialogIdHash> run_after_get_channel_difference_;
ChangesProcessor<unique_ptr<PendingSecretMessage>> pending_secret_messages_;
std::unordered_map<DialogId, vector<DialogId>, DialogIdHash>
pending_add_dialog_last_database_message_dependent_dialogs_;
std::unordered_map<DialogId, std::pair<int32, unique_ptr<Message>>, DialogIdHash>
FlatHashMap<DialogId, vector<DialogId>, DialogIdHash> pending_add_dialog_last_database_message_dependent_dialogs_;
FlatHashMap<DialogId, std::pair<int32, unique_ptr<Message>>, DialogIdHash>
pending_add_dialog_last_database_message_; // dialog -> dependency counter + message
std::unordered_map<DialogId, vector<DialogId>, DialogIdHash>
FlatHashMap<DialogId, vector<DialogId>, DialogIdHash>
pending_add_default_join_group_call_as_dialog_id_; // dialog_id -> dependent dialogs
std::unordered_map<DialogId, vector<std::pair<DialogId, bool>>, DialogIdHash>
FlatHashMap<DialogId, vector<std::pair<DialogId, bool>>, DialogIdHash>
pending_add_default_send_message_as_dialog_id_; // dialog_id -> [dependent dialog, need_drop]
struct MessageIds {
std::unordered_set<MessageId, MessageIdHash> message_ids;
FlatHashSet<MessageId, MessageIdHash> message_ids;
};
std::unordered_map<DialogId, MessageIds, DialogIdHash> dialog_bot_command_message_ids_;
FlatHashMap<DialogId, MessageIds, DialogIdHash> dialog_bot_command_message_ids_;
struct CallsDbState {
std::array<MessageId, 2> first_calls_database_message_id_by_index;
@ -3611,38 +3648,50 @@ class MessagesManager final : public Actor {
CallsDbState calls_db_state_;
int64 viewed_live_location_task_id_ = 0;
std::unordered_map<int64, FullMessageId> viewed_live_location_tasks_; // task_id -> task
FlatHashMap<int64, FullMessageId> viewed_live_location_tasks_; // task_id -> task
std::unordered_map<uint64, std::map<MessageId, Promise<Message *>>> yet_unsent_media_queues_;
FlatHashMap<uint64, std::map<MessageId, Promise<Message *>>> yet_unsent_media_queues_;
std::unordered_map<DialogId, NetQueryRef, DialogIdHash> set_typing_query_;
FlatHashMap<DialogId, NetQueryRef, DialogIdHash> set_typing_query_;
std::unordered_map<FullMessageId, FileSourceId, FullMessageIdHash> full_message_id_to_file_source_id_;
FlatHashMap<FullMessageId, FileSourceId, FullMessageIdHash> full_message_id_to_file_source_id_;
std::unordered_map<DialogId, int32, DialogIdHash> last_outgoing_forwarded_message_date_;
FlatHashMap<DialogId, int32, DialogIdHash> last_outgoing_forwarded_message_date_;
struct ViewedMessagesInfo {
FlatHashMap<MessageId, uint64, MessageIdHash> message_id_to_view_id;
std::map<uint64, MessageId> recently_viewed_messages;
uint64 current_view_id = 0;
};
FlatHashMap<DialogId, unique_ptr<ViewedMessagesInfo>, DialogIdHash> dialog_viewed_messages_;
struct OnlineMemberCountInfo {
int32 online_member_count = 0;
double updated_time = 0;
double update_time = 0;
bool is_update_sent = false;
};
FlatHashMap<DialogId, OnlineMemberCountInfo, DialogIdHash> dialog_online_member_counts_;
std::unordered_map<DialogId, OnlineMemberCountInfo, DialogIdHash> dialog_online_member_counts_;
struct ReactionsToReload {
FlatHashSet<MessageId, MessageIdHash> message_ids;
bool is_request_sent = false;
};
FlatHashMap<DialogId, ReactionsToReload, DialogIdHash> being_reloaded_reactions_;
std::unordered_map<DialogId, std::pair<bool, bool>, DialogIdHash> pending_dialog_group_call_updates_;
FlatHashMap<DialogId, std::pair<bool, bool>, DialogIdHash> pending_dialog_group_call_updates_;
std::unordered_map<string, int32> auth_notification_id_date_;
FlatHashMap<string, int32> auth_notification_id_date_;
std::unordered_map<DialogId, MessageId, DialogIdHash> previous_repaired_read_inbox_max_message_id_;
FlatHashMap<DialogId, MessageId, DialogIdHash> previous_repaired_read_inbox_max_message_id_;
struct PendingReaction {
int32 query_count = 0;
bool was_updated = false;
};
std::unordered_map<FullMessageId, PendingReaction, FullMessageIdHash> pending_reactions_;
FlatHashMap<FullMessageId, PendingReaction, FullMessageIdHash> pending_reactions_;
vector<string> active_reactions_;
std::unordered_map<string, size_t> active_reaction_pos_;
FlatHashMap<string, size_t> active_reaction_pos_;
uint32 scheduled_messages_sync_generation_ = 1;

View File

@ -61,8 +61,6 @@
#include <algorithm>
#include <iterator>
#include <limits>
#include <unordered_map>
#include <unordered_set>
namespace td {
@ -254,6 +252,9 @@ void NotificationManager::init() {
});
VLOG(notifications) << "Load call_notification_group_ids = " << call_notification_group_ids;
for (auto &group_id : call_notification_group_ids) {
if (!group_id.is_valid()) {
continue;
}
if (group_id.get() > current_notification_group_id_.get()) {
LOG(ERROR) << "Fix current notification group identifier from " << current_notification_group_id_ << " to "
<< group_id;
@ -282,7 +283,7 @@ void NotificationManager::init() {
for (size_t i = 0; i < ids.size(); i += 2) {
auto id = ids[i];
auto date = ids[i + 1];
if (date < min_date) {
if (date < min_date || id == 0) {
is_changed = true;
continue;
}
@ -1026,21 +1027,23 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
// and second addition, because we has kept the deletion
// calculate last state of all notifications
std::unordered_set<int32> added_notification_ids;
std::unordered_set<int32> edited_notification_ids;
std::unordered_set<int32> removed_notification_ids;
FlatHashSet<int32> added_notification_ids;
FlatHashSet<int32> edited_notification_ids;
FlatHashSet<int32> removed_notification_ids;
for (auto &update : updates) {
CHECK(update != nullptr);
if (update->get_id() == td_api::updateNotificationGroup::ID) {
auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
for (auto &notification : update_ptr->added_notifications_) {
auto notification_id = notification->id_;
CHECK(notification_id != 0);
bool is_inserted = added_notification_ids.insert(notification_id).second;
CHECK(is_inserted); // there must be no additions after addition
CHECK(edited_notification_ids.count(notification_id) == 0); // there must be no additions after edit
removed_notification_ids.erase(notification_id);
}
for (auto &notification_id : update_ptr->removed_notification_ids_) {
CHECK(notification_id != 0);
added_notification_ids.erase(notification_id);
edited_notification_ids.erase(notification_id);
if (!removed_notification_ids.insert(notification_id).second) {
@ -1055,6 +1058,7 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
CHECK(update->get_id() == td_api::updateNotification::ID);
auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
auto notification_id = update_ptr->notification_->id_;
CHECK(notification_id != 0);
CHECK(removed_notification_ids.count(notification_id) == 0); // there must be no edits of deleted notifications
added_notification_ids.erase(notification_id);
edited_notification_ids.insert(notification_id);
@ -1079,10 +1083,10 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
is_changed = false;
size_t cur_pos = 0;
std::unordered_map<int32, size_t> first_add_notification_pos;
std::unordered_map<int32, size_t> first_edit_notification_pos;
std::unordered_set<int32> can_be_deleted_notification_ids;
std::vector<int32> moved_deleted_notification_ids;
FlatHashMap<int32, size_t> first_add_notification_pos;
FlatHashMap<int32, size_t> first_edit_notification_pos;
FlatHashSet<int32> can_be_deleted_notification_ids;
vector<int32> moved_deleted_notification_ids;
size_t first_notification_group_pos = 0;
for (auto &update : updates) {
@ -1094,6 +1098,7 @@ void NotificationManager::flush_pending_updates(int32 group_id, const char *sour
for (auto &notification : update_ptr->added_notifications_) {
auto notification_id = notification->id_;
CHECK(notification_id != 0);
bool is_needed =
added_notification_ids.count(notification_id) != 0 || edited_notification_ids.count(notification_id) != 0;
if (!is_needed) {
@ -1777,7 +1782,7 @@ void NotificationManager::remove_added_notifications_from_pending_updates(
return;
}
std::unordered_set<int32> removed_notification_ids;
FlatHashSet<int32> removed_notification_ids;
for (auto &update : it->second) {
if (update == nullptr) {
continue;
@ -1791,6 +1796,7 @@ void NotificationManager::remove_added_notifications_from_pending_updates(
}
for (auto &notification : update_ptr->added_notifications_) {
if (is_removed(notification)) {
CHECK(notification->id_ != 0);
removed_notification_ids.insert(notification->id_);
VLOG(notifications) << "Remove " << NotificationId(notification->id_) << " in " << group_id;
notification = nullptr;
@ -1801,6 +1807,7 @@ void NotificationManager::remove_added_notifications_from_pending_updates(
CHECK(update->get_id() == td_api::updateNotification::ID);
auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
if (is_removed(update_ptr->notification_)) {
CHECK(update_ptr->notification_->id_ != 0);
removed_notification_ids.insert(update_ptr->notification_->id_);
VLOG(notifications) << "Remove " << NotificationId(update_ptr->notification_->id_) << " in " << group_id;
update = nullptr;
@ -2254,6 +2261,9 @@ NotificationGroupId NotificationManager::get_call_notification_group_id(DialogId
if (it != dialog_id_to_call_notification_group_id_.end()) {
return it->second;
}
if (!dialog_id.is_valid()) {
return {};
}
if (available_call_notification_group_ids_.empty()) {
// need to reserve new group_id for calls
@ -3004,9 +3014,12 @@ Status NotificationManager::process_push_notification_payload(string payload, bo
if (loc_key == "MESSAGE_ANNOUNCEMENT") {
if (announcement_message_text.empty()) {
return Status::Error("Have empty announcement message text");
return Status::Error("Receive empty announcement message text");
}
TRY_RESULT(announcement_id, get_json_object_int_field(custom, "announcement"));
if (announcement_id == 0) {
return Status::Error(200, "Receive unsupported announcement ID");
}
auto &date = announcement_id_date_[announcement_id];
auto now = G()->unix_time();
if (date >= now - ANNOUNCEMENT_ID_CACHE_TIME) {

View File

@ -24,6 +24,8 @@
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
@ -31,8 +33,6 @@
#include <functional>
#include <map>
#include <unordered_map>
#include <unordered_set>
namespace td {
@ -372,22 +372,22 @@ class NotificationManager final : public Actor {
bool is_binlog_processed_ = false;
bool running_get_difference_ = false;
std::unordered_set<int32> running_get_chat_difference_;
FlatHashSet<int32> running_get_chat_difference_;
NotificationGroups groups_;
std::unordered_map<NotificationGroupId, NotificationGroupKey, NotificationGroupIdHash> group_keys_;
FlatHashMap<NotificationGroupId, NotificationGroupKey, NotificationGroupIdHash> group_keys_;
std::unordered_map<int32, vector<td_api::object_ptr<td_api::Update>>> pending_updates_;
FlatHashMap<int32, vector<td_api::object_ptr<td_api::Update>>> pending_updates_;
MultiTimeout flush_pending_notifications_timeout_{"FlushPendingNotificationsTimeout"};
MultiTimeout flush_pending_updates_timeout_{"FlushPendingUpdatesTimeout"};
vector<NotificationGroupId> call_notification_group_ids_;
std::unordered_set<NotificationGroupId, NotificationGroupIdHash> available_call_notification_group_ids_;
std::unordered_map<DialogId, NotificationGroupId, DialogIdHash> dialog_id_to_call_notification_group_id_;
FlatHashSet<NotificationGroupId, NotificationGroupIdHash> available_call_notification_group_ids_;
FlatHashMap<DialogId, NotificationGroupId, DialogIdHash> dialog_id_to_call_notification_group_id_;
std::unordered_map<NotificationId, uint64, NotificationIdHash> temporary_notification_log_event_ids_;
std::unordered_map<NotificationId, uint64, NotificationIdHash> temporary_edit_notification_log_event_ids_;
FlatHashMap<NotificationId, uint64, NotificationIdHash> temporary_notification_log_event_ids_;
FlatHashMap<NotificationId, uint64, NotificationIdHash> temporary_edit_notification_log_event_ids_;
struct TemporaryNotification {
NotificationGroupId group_id;
NotificationId notification_id;
@ -396,17 +396,17 @@ class NotificationManager final : public Actor {
string sender_name;
bool is_outgoing;
};
std::unordered_map<FullMessageId, TemporaryNotification, FullMessageIdHash> temporary_notifications_;
std::unordered_map<NotificationId, FullMessageId, NotificationIdHash> temporary_notification_message_ids_;
std::unordered_map<NotificationId, vector<Promise<Unit>>, NotificationIdHash> push_notification_promises_;
FlatHashMap<FullMessageId, TemporaryNotification, FullMessageIdHash> temporary_notifications_;
FlatHashMap<NotificationId, FullMessageId, NotificationIdHash> temporary_notification_message_ids_;
FlatHashMap<NotificationId, vector<Promise<Unit>>, NotificationIdHash> push_notification_promises_;
struct ActiveCallNotification {
CallId call_id;
NotificationId notification_id;
};
std::unordered_map<DialogId, vector<ActiveCallNotification>, DialogIdHash> active_call_notifications_;
FlatHashMap<DialogId, vector<ActiveCallNotification>, DialogIdHash> active_call_notifications_;
std::unordered_map<int32, int32> announcement_id_date_;
FlatHashMap<int32, int32> announcement_id_date_;
Td *td_;
ActorShared<> parent_;

View File

@ -567,7 +567,7 @@ class GetBankCardInfoQuery final : public Td::ResultHandler {
}
void send(const string &bank_card_number) {
send_query(G()->net_query_creator().create(telegram_api::payments_getBankCardData(bank_card_number),
send_query(G()->net_query_creator().create(telegram_api::payments_getBankCardData(bank_card_number), {},
G()->get_webfile_dc_id()));
}

View File

@ -8,6 +8,7 @@
#include "td/telegram/AccessRights.h"
#include "td/telegram/AuthManager.h"
#include "td/telegram/ChainId.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/DialogId.h"
@ -17,10 +18,8 @@
#include "td/telegram/MemoryManager.h"
#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetActor.h"
#include "td/telegram/PollId.hpp"
#include "td/telegram/PollManager.hpp"
#include "td/telegram/SequenceDispatcher.h"
#include "td/telegram/StateManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/TdDb.h"
@ -46,7 +45,6 @@
#include <algorithm>
#include <limits>
#include <unordered_map>
namespace td {
@ -139,15 +137,16 @@ class GetPollVotersQuery final : public Td::ResultHandler {
}
};
class SetPollAnswerActor final : public NetActorOnce {
class SendVoteQuery final : public Td::ResultHandler {
Promise<tl_object_ptr<telegram_api::Updates>> promise_;
DialogId dialog_id_;
public:
explicit SetPollAnswerActor(Promise<tl_object_ptr<telegram_api::Updates>> &&promise) : promise_(std::move(promise)) {
explicit SendVoteQuery(Promise<tl_object_ptr<telegram_api::Updates>> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, vector<BufferSlice> &&options, uint64 generation, NetQueryRef *query_ref) {
void send(FullMessageId full_message_id, vector<BufferSlice> &&options, PollId poll_id, uint64 generation,
NetQueryRef *query_ref) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Read);
if (input_peer == nullptr) {
@ -157,11 +156,10 @@ class SetPollAnswerActor final : public NetActorOnce {
auto message_id = full_message_id.get_message_id().get_server_message_id().get();
auto query = G()->net_query_creator().create(
telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options)));
telegram_api::messages_sendVote(std::move(input_peer), message_id, std::move(options)),
{{poll_id}, {dialog_id_}});
*query_ref = query.get_weak();
auto sequence_id = -1;
send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_id);
send_query(std::move(query));
}
void on_result(BufferSlice packet) final {
@ -171,25 +169,25 @@ class SetPollAnswerActor final : public NetActorOnce {
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive sendVote result: " << to_string(result);
LOG(INFO) << "Receive result for SendVoteQuery: " << to_string(result);
promise_.set_value(std::move(result));
}
void on_error(Status status) final {
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetPollAnswerActor");
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SendVoteQuery");
promise_.set_error(std::move(status));
}
};
class StopPollActor final : public NetActorOnce {
class StopPollQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
DialogId dialog_id_;
public:
explicit StopPollActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
explicit StopPollQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup) {
void send(FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup, PollId poll_id) {
dialog_id_ = full_message_id.get_dialog_id();
auto input_peer = td_->messages_manager_->get_input_peer(dialog_id_, AccessRights::Edit);
if (input_peer == nullptr) {
@ -208,16 +206,11 @@ class StopPollActor final : public NetActorOnce {
poll->flags_ |= telegram_api::poll::CLOSED_MASK;
auto input_media = telegram_api::make_object<telegram_api::inputMediaPoll>(0, std::move(poll),
vector<BufferSlice>(), string(), Auto());
auto query = G()->net_query_creator().create(telegram_api::messages_editMessage(
flags, false /*ignored*/, std::move(input_peer), message_id, string(), std::move(input_media),
std::move(input_reply_markup), vector<tl_object_ptr<telegram_api::MessageEntity>>(), 0));
if (td_->auth_manager_->is_bot()) {
send_query(std::move(query));
} else {
auto sequence_id = -1;
send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
std::move(query), actor_shared(this), sequence_id);
}
send_query(G()->net_query_creator().create(
telegram_api::messages_editMessage(flags, false /*ignored*/, std::move(input_peer), message_id, string(),
std::move(input_media), std::move(input_reply_markup),
vector<tl_object_ptr<telegram_api::MessageEntity>>(), 0),
{{poll_id}, {dialog_id_}}));
}
void on_result(BufferSlice packet) final {
@ -227,7 +220,7 @@ class StopPollActor final : public NetActorOnce {
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for StopPoll: " << to_string(result);
LOG(INFO) << "Receive result for StopPollQuery: " << to_string(result);
td_->updates_manager_->on_get_updates(std::move(result), std::move(promise_));
}
@ -235,7 +228,7 @@ class StopPollActor final : public NetActorOnce {
if (!td_->auth_manager_->is_bot() && status.message() == "MESSAGE_NOT_MODIFIED") {
return promise_.set_value(Unit());
}
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "StopPollActor");
td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "StopPollQuery");
promise_.set_error(std::move(status));
}
};
@ -345,6 +338,7 @@ void PollManager::save_poll(const Poll *poll, PollId poll_id) {
}
void PollManager::on_load_poll_from_database(PollId poll_id, string value) {
CHECK(poll_id.is_valid());
loaded_from_database_polls_.insert(poll_id);
LOG(INFO) << "Successfully loaded " << poll_id << " of size " << value.size() << " from database";
@ -385,7 +379,7 @@ PollManager::Poll *PollManager::get_poll_force(PollId poll_id) {
if (!G()->parameters().use_message_db) {
return nullptr;
}
if (loaded_from_database_polls_.count(poll_id)) {
if (!poll_id.is_valid() || loaded_from_database_polls_.count(poll_id)) {
return nullptr;
}
@ -449,9 +443,9 @@ vector<int32> PollManager::get_vote_percentage(const vector<int32> &voter_counts
int32 pos = -1;
int32 count = 0;
};
std::unordered_map<int32, Option> options;
FlatHashMap<int32, Option> options;
for (size_t i = 0; i < result.size(); i++) {
auto &option = options[voter_counts[i]];
auto &option = options[voter_counts[i] + 1];
if (option.pos == -1) {
option.pos = narrow_cast<int32>(i);
}
@ -711,7 +705,7 @@ void PollManager::set_poll_answer(PollId poll_id, FullMessageId full_message_id,
return promise.set_error(Status::Error(400, "Can't revote in a quiz"));
}
std::unordered_map<size_t, int> affected_option_ids;
FlatHashMap<size_t, int> affected_option_ids;
vector<string> options;
for (auto &option_id : option_ids) {
auto index = static_cast<size_t>(option_id);
@ -720,19 +714,19 @@ void PollManager::set_poll_answer(PollId poll_id, FullMessageId full_message_id,
}
options.push_back(poll->options[index].data);
affected_option_ids[index]++;
affected_option_ids[index + 1]++;
}
for (size_t option_index = 0; option_index < poll->options.size(); option_index++) {
if (poll->options[option_index].is_chosen) {
if (poll->is_quiz) {
return promise.set_error(Status::Error(400, "Can't revote in a quiz"));
}
affected_option_ids[option_index]++;
affected_option_ids[option_index + 1]++;
}
}
for (auto it : affected_option_ids) {
for (const auto &it : affected_option_ids) {
if (it.second == 1) {
invalidate_poll_option_voters(poll, poll_id, it.first);
invalidate_poll_option_voters(poll, poll_id, it.first - 1);
}
}
@ -763,6 +757,14 @@ class PollManager::SetPollAnswerLogEvent {
void PollManager::do_set_poll_answer(PollId poll_id, FullMessageId full_message_id, vector<string> &&options,
uint64 log_event_id, Promise<Unit> &&promise) {
LOG(INFO) << "Set answer in " << poll_id << " from " << full_message_id;
if (!poll_id.is_valid() || !full_message_id.get_dialog_id().is_valid() ||
!full_message_id.get_message_id().is_valid()) {
CHECK(log_event_id != 0);
LOG(ERROR) << "Invalid SetPollAnswer log event";
binlog_erase(G()->td_db()->get_binlog(), log_event_id);
return;
}
auto &pending_answer = pending_answers_[poll_id];
if (!pending_answer.promises_.empty() && pending_answer.options_ == options) {
pending_answer.promises_.push_back(std::move(promise));
@ -823,8 +825,8 @@ void PollManager::do_set_poll_answer(PollId poll_id, FullMessageId full_message_
[poll_id, generation, actor_id = actor_id(this)](Result<tl_object_ptr<telegram_api::Updates>> &&result) {
send_closure(actor_id, &PollManager::on_set_poll_answer, poll_id, generation, std::move(result));
});
send_closure(td_->create_net_actor<SetPollAnswerActor>(std::move(query_promise)), &SetPollAnswerActor::send,
full_message_id, std::move(sent_options), generation, &pending_answer.query_ref_);
td_->create_handler<SendVoteQuery>(std::move(query_promise))
->send(full_message_id, std::move(sent_options), poll_id, generation, &pending_answer.query_ref_);
}
void PollManager::on_set_poll_answer(PollId poll_id, uint64 generation,
@ -1126,6 +1128,8 @@ class PollManager::StopPollLogEvent {
void PollManager::do_stop_poll(PollId poll_id, FullMessageId full_message_id, unique_ptr<ReplyMarkup> &&reply_markup,
uint64 log_event_id, Promise<Unit> &&promise) {
LOG(INFO) << "Stop " << poll_id << " from " << full_message_id;
CHECK(poll_id.is_valid());
if (log_event_id == 0 && G()->parameters().use_message_db && reply_markup == nullptr) {
StopPollLogEvent log_event{poll_id, full_message_id};
log_event_id =
@ -1136,8 +1140,7 @@ void PollManager::do_stop_poll(PollId poll_id, FullMessageId full_message_id, un
CHECK(is_inserted);
auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise));
send_closure(td_->create_net_actor<StopPollActor>(std::move(new_promise)), &StopPollActor::send, full_message_id,
std::move(reply_markup));
td_->create_handler<StopPollQuery>(std::move(new_promise))->send(full_message_id, std::move(reply_markup), poll_id);
}
void PollManager::stop_local_poll(PollId poll_id) {
@ -1353,7 +1356,7 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
return PollId();
}
if (poll_server != nullptr) {
std::unordered_set<Slice, SliceHash> option_data;
FlatHashSet<Slice, SliceHash> option_data;
for (auto &answer : poll_server->answers_) {
if (answer->option_.empty()) {
LOG(ERROR) << "Receive " << poll_id << " with an empty option data: " << to_string(poll_server);
@ -1694,8 +1697,8 @@ void PollManager::on_binlog_events(vector<BinlogEvent> &&events) {
auto dialog_id = log_event.full_message_id_.get_dialog_id();
Dependencies dependencies;
add_dialog_dependencies(dependencies, dialog_id); // do not load the dialog itself
resolve_dependencies_force(td_, dependencies, "SetPollAnswerLogEvent");
dependencies.add_dialog_dependencies(dialog_id); // do not load the dialog itself
dependencies.resolve_force(td_, "SetPollAnswerLogEvent");
do_set_poll_answer(log_event.poll_id_, log_event.full_message_id_, std::move(log_event.options_), event.id_,
Auto());
@ -1713,8 +1716,8 @@ void PollManager::on_binlog_events(vector<BinlogEvent> &&events) {
auto dialog_id = log_event.full_message_id_.get_dialog_id();
Dependencies dependencies;
add_dialog_dependencies(dependencies, dialog_id); // do not load the dialog itself
resolve_dependencies_force(td_, dependencies, "StopPollLogEvent");
dependencies.add_dialog_dependencies(dialog_id); // do not load the dialog itself
dependencies.resolve_force(td_, "StopPollLogEvent");
do_stop_poll(log_event.poll_id_, log_event.full_message_id_, nullptr, event.id_, Auto());
break;

View File

@ -21,10 +21,10 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Status.h"
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace td {
@ -209,9 +209,9 @@ class PollManager final : public Actor {
Td *td_;
ActorShared<> parent_;
std::unordered_map<PollId, unique_ptr<Poll>, PollIdHash> polls_;
FlatHashMap<PollId, unique_ptr<Poll>, PollIdHash> polls_;
std::unordered_map<PollId, std::unordered_set<FullMessageId, FullMessageIdHash>, PollIdHash> poll_messages_;
FlatHashMap<PollId, FlatHashSet<FullMessageId, FullMessageIdHash>, PollIdHash> poll_messages_;
struct PendingPollAnswer {
vector<string> options_;
@ -220,17 +220,17 @@ class PollManager final : public Actor {
uint64 log_event_id_ = 0;
NetQueryRef query_ref_;
};
std::unordered_map<PollId, PendingPollAnswer, PollIdHash> pending_answers_;
FlatHashMap<PollId, PendingPollAnswer, PollIdHash> pending_answers_;
std::unordered_map<PollId, vector<PollOptionVoters>, PollIdHash> poll_voters_;
FlatHashMap<PollId, vector<PollOptionVoters>, PollIdHash> poll_voters_;
int64 current_local_poll_id_ = 0;
uint64 current_generation_ = 0;
std::unordered_set<PollId, PollIdHash> loaded_from_database_polls_;
FlatHashSet<PollId, PollIdHash> loaded_from_database_polls_;
std::unordered_set<PollId, PollIdHash> being_closed_polls_;
FlatHashSet<PollId, PollIdHash> being_closed_polls_;
};
} // namespace td

View File

@ -19,6 +19,7 @@ QueryCombiner::QueryCombiner(Slice name, double min_delay) : next_query_time_(Ti
void QueryCombiner::add_query(int64 query_id, Promise<Promise<Unit>> &&send_query, Promise<Unit> &&promise) {
LOG(INFO) << "Add query " << query_id << " with" << (promise ? "" : "out") << " promise";
CHECK(query_id != 0);
auto &query = queries_[query_id];
if (promise) {
query.promises.push_back(std::move(promise));

View File

@ -10,11 +10,11 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <queue>
#include <unordered_map>
namespace td {
@ -39,7 +39,7 @@ class QueryCombiner final : public Actor {
std::queue<int64> delayed_queries_;
std::unordered_map<int64, QueryInfo> queries_;
FlatHashMap<int64, QueryInfo> queries_;
void do_send_query(int64 query_id, QueryInfo &query);

View File

@ -198,6 +198,9 @@ bool RecentDialogList::do_add_dialog(DialogId dialog_id) {
}
void RecentDialogList::remove_dialog(DialogId dialog_id) {
if (!dialog_id.is_valid()) {
return;
}
if (!is_loaded_) {
load_dialogs(Promise<Unit>());
}

View File

@ -12,8 +12,8 @@
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashSet.h"
#include <unordered_set>
#include <utility>
namespace td {
@ -38,7 +38,7 @@ class RecentDialogList final : public Actor {
const char *name_;
size_t max_size_;
vector<DialogId> dialog_ids_;
std::unordered_set<DialogId, DialogIdHash> removed_dialog_ids_;
FlatHashSet<DialogId, DialogIdHash> removed_dialog_ids_;
bool is_loaded_ = false;
vector<Promise<Unit>> load_list_queries_;

View File

@ -910,7 +910,7 @@ void add_reply_markup_dependencies(Dependencies &dependencies, const ReplyMarkup
for (auto &row : reply_markup->inline_keyboard) {
for (auto &button : row) {
if (button.user_id.is_valid()) {
dependencies.user_ids.insert(button.user_id);
dependencies.add(button.user_id);
}
}
}

View File

@ -16,7 +16,7 @@
namespace td {
struct Dependencies;
class Dependencies;
struct KeyboardButton {
// append only

View File

@ -35,6 +35,10 @@ Result<ReportReason> ReportReason::get_report_reason(td_api::object_ptr<td_api::
return ReportReason::Type::UnrelatedLocation;
case td_api::chatReportReasonFake::ID:
return ReportReason::Type::Fake;
case td_api::chatReportReasonIllegalDrugs::ID:
return ReportReason::Type::IllegalDrugs;
case td_api::chatReportReasonPersonalDetails::ID:
return ReportReason::Type::PersonalDetails;
case td_api::chatReportReasonCustom::ID:
return ReportReason::Type::Custom;
default:
@ -61,6 +65,10 @@ tl_object_ptr<telegram_api::ReportReason> ReportReason::get_input_report_reason(
return make_tl_object<telegram_api::inputReportReasonGeoIrrelevant>();
case ReportReason::Type::Fake:
return make_tl_object<telegram_api::inputReportReasonFake>();
case ReportReason::Type::IllegalDrugs:
return make_tl_object<telegram_api::inputReportReasonIllegalDrugs>();
case ReportReason::Type::PersonalDetails:
return make_tl_object<telegram_api::inputReportReasonPersonalDetails>();
case ReportReason::Type::Custom:
return make_tl_object<telegram_api::inputReportReasonOther>();
default:
@ -86,6 +94,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, const ReportReason &rep
return string_builder << "UnrelatedLocation";
case ReportReason::Type::Fake:
return string_builder << "Fake";
case ReportReason::Type::IllegalDrugs:
return string_builder << "IllegalDrugs";
case ReportReason::Type::PersonalDetails:
return string_builder << "PersonalDetails";
case ReportReason::Type::Custom:
return string_builder << "Custom";
default:

View File

@ -16,7 +16,18 @@
namespace td {
class ReportReason {
enum class Type : int32 { Spam, Violence, Pornography, ChildAbuse, Copyright, UnrelatedLocation, Fake, Custom };
enum class Type : int32 {
Spam,
Violence,
Pornography,
ChildAbuse,
Copyright,
UnrelatedLocation,
Fake,
IllegalDrugs,
PersonalDetails,
Custom
};
Type type_ = Type::Spam;
string message_;

View File

@ -70,7 +70,8 @@ SecretChatActor::SecretChatActor(int32 id, unique_ptr<Context> context, bool can
template <class T>
NetQueryPtr SecretChatActor::create_net_query(QueryType type, const T &function) {
return context_->net_query_creator().create(UniqueId::next(UniqueId::Type::Default, static_cast<uint8>(type)),
function, DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::On);
function, {}, DcId::main(), NetQuery::Type::Common,
NetQuery::AuthFlag::On);
}
void SecretChatActor::update_chat(telegram_api::object_ptr<telegram_api::EncryptedChat> chat) {

View File

@ -25,6 +25,7 @@
#include "td/utils/Slice.h"
#include "td/utils/SliceBuilder.h"
#include <limits>
#include <memory>
namespace td {
@ -954,8 +955,13 @@ void SecureManager::set_secure_value_errors(Td *td, tl_object_ptr<telegram_api::
void SecureManager::get_passport_authorization_form(UserId bot_user_id, string scope, string public_key, string nonce,
Promise<TdApiAuthorizationForm> promise) {
refcnt_++;
CHECK(max_authorization_form_id_ < std::numeric_limits<int32>::max());
auto authorization_form_id = ++max_authorization_form_id_;
auto &form = authorization_forms_[authorization_form_id];
auto &form_ptr = authorization_forms_[authorization_form_id];
if (form_ptr == nullptr) {
form_ptr = make_unique<AuthorizationForm>();
}
auto &form = *form_ptr;
form.bot_user_id = bot_user_id;
form.scope = scope;
form.public_key = public_key;
@ -976,7 +982,8 @@ void SecureManager::on_get_passport_authorization_form(
Result<telegram_api::object_ptr<telegram_api::account_authorizationForm>> r_authorization_form) {
auto it = authorization_forms_.find(authorization_form_id);
CHECK(it != authorization_forms_.end());
CHECK(it->second.is_received == false);
CHECK(it->second != nullptr);
CHECK(!it->second->is_received);
if (r_authorization_form.is_error()) {
authorization_forms_.erase(it);
return promise.set_error(r_authorization_form.move_as_error());
@ -1020,10 +1027,10 @@ void SecureManager::on_get_passport_authorization_form(
}
}
it->second.options = std::move(all_types);
it->second.values = std::move(authorization_form->values_);
it->second.errors = std::move(authorization_form->errors_);
it->second.is_received = true;
it->second->options = std::move(all_types);
it->second->values = std::move(authorization_form->values_);
it->second->errors = std::move(authorization_form->errors_);
it->second->is_received = true;
promise.set_value(td_api::make_object<td_api::passportAuthorizationForm>(
authorization_form_id, get_passport_required_elements_object(required_types),
@ -1036,16 +1043,17 @@ void SecureManager::get_passport_authorization_form_available_elements(int32 aut
if (it == authorization_forms_.end()) {
return promise.set_error(Status::Error(400, "Unknown authorization_form_id"));
}
if (!it->second.is_received) {
CHECK(it->second != nullptr);
if (!it->second->is_received) {
return promise.set_error(Status::Error(400, "Authorization form isn't received yet"));
}
refcnt_++;
send_closure(G()->password_manager(), &PasswordManager::get_secure_secret, password,
PromiseCreator::lambda([actor_id = actor_shared(this), authorization_form_id,
PromiseCreator::lambda([self = actor_shared(this), authorization_form_id,
promise = std::move(promise)](Result<secure_storage::Secret> r_secret) mutable {
send_closure(actor_id, &SecureManager::on_get_passport_authorization_form_secret,
authorization_form_id, std::move(promise), std::move(r_secret));
send_closure(self, &SecureManager::on_get_passport_authorization_form_secret, authorization_form_id,
std::move(promise), std::move(r_secret));
}));
}
@ -1056,8 +1064,9 @@ void SecureManager::on_get_passport_authorization_form_secret(int32 authorizatio
if (it == authorization_forms_.end()) {
return promise.set_error(Status::Error(400, "Authorization form has already been sent"));
}
CHECK(it->second.is_received);
if (it->second.is_decrypted) {
CHECK(it->second != nullptr);
CHECK(it->second->is_received);
if (it->second->is_decrypted) {
return promise.set_error(Status::Error(400, "Authorization form has already been decrypted"));
}
@ -1073,14 +1082,14 @@ void SecureManager::on_get_passport_authorization_form_secret(int32 authorizatio
}
auto secret = r_secret.move_as_ok();
it->second.is_decrypted = true;
it->second->is_decrypted = true;
auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
std::vector<TdApiSecureValue> values;
std::map<SecureValueType, SecureValueCredentials> all_credentials;
for (const auto &suitable_type : it->second.options) {
for (const auto &suitable_type : it->second->options) {
auto type = suitable_type.first;
for (auto &value : it->second.values) {
for (auto &value : it->second->values) {
if (value == nullptr) {
continue;
}
@ -1122,7 +1131,7 @@ void SecureManager::on_get_passport_authorization_form_secret(int32 authorizatio
};
vector<td_api::object_ptr<td_api::passportElementError>> errors;
for (auto &error_ptr : it->second.errors) {
for (auto &error_ptr : it->second->errors) {
CHECK(error_ptr != nullptr);
SecureValueType type = SecureValueType::None;
td_api::object_ptr<td_api::PassportElementErrorSource> source;
@ -1225,7 +1234,8 @@ void SecureManager::send_passport_authorization_form(int32 authorization_form_id
if (it == authorization_forms_.end()) {
return promise.set_error(Status::Error(400, "Unknown authorization_form_id"));
}
if (!it->second.is_received) {
CHECK(it->second != nullptr);
if (!it->second->is_received) {
return promise.set_error(Status::Error(400, "Authorization form isn't received yet"));
}
// there is no need to check for is_decrypted
@ -1247,8 +1257,8 @@ void SecureManager::send_passport_authorization_form(int32 authorization_form_id
for (auto &c : credentials) {
hashes.push_back(telegram_api::make_object<telegram_api::secureValueHash>(get_input_secure_value_type(c.type),
BufferSlice(c.hash)));
auto options_it = it->second.options.find(c.type);
if (options_it == it->second.options.end()) {
auto options_it = it->second->options.find(c.type);
if (options_it == it->second->options.end()) {
return promise.set_error(Status::Error(400, "Passport Element with the specified type was not requested"));
}
auto &options = options_it->second;
@ -1261,14 +1271,14 @@ void SecureManager::send_passport_authorization_form(int32 authorization_form_id
}
auto r_encrypted_credentials =
get_encrypted_credentials(credentials, it->second.nonce, it->second.public_key,
it->second.scope[0] == '{' && it->second.scope.back() == '}');
get_encrypted_credentials(credentials, it->second->nonce, it->second->public_key,
it->second->scope[0] == '{' && it->second->scope.back() == '}');
if (r_encrypted_credentials.is_error()) {
return promise.set_error(r_encrypted_credentials.move_as_error());
}
auto td_query = telegram_api::account_acceptAuthorization(
it->second.bot_user_id.get(), it->second.scope, it->second.public_key, std::move(hashes),
it->second->bot_user_id.get(), it->second->scope, it->second->public_key, std::move(hashes),
get_secure_credentials_encrypted_object(r_encrypted_credentials.move_as_ok()));
auto query = G()->net_query_creator().create(td_query);
auto new_promise =

View File

@ -18,10 +18,10 @@
#include "td/utils/common.h"
#include "td/utils/Container.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <map>
#include <unordered_map>
#include <utility>
namespace td {
@ -77,7 +77,7 @@ class SecureManager final : public NetQueryCallback {
vector<telegram_api::object_ptr<telegram_api::SecureValueError>> errors;
};
std::unordered_map<int32, AuthorizationForm> authorization_forms_;
FlatHashMap<int32, unique_ptr<AuthorizationForm>> authorization_forms_;
int32 max_authorization_form_id_{0};
void hangup() final;

View File

@ -8,6 +8,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/Td.h"
#include "td/actor/PromiseFuture.h"
@ -45,7 +46,7 @@ void SequenceDispatcher::send_with_callback(NetQueryPtr query, ActorShared<NetQu
cancel_timeout();
query->debug("Waiting at SequenceDispatcher");
auto query_weak_ref = query.get_weak();
data_.push_back(Data{State::Start, std::move(query_weak_ref), std::move(query), std::move(callback), 0, 0.0, 0.0});
data_.push_back(Data{State::Start, std::move(query_weak_ref), std::move(query), std::move(callback), 0, 0, 0});
loop();
}
@ -59,8 +60,7 @@ void SequenceDispatcher::check_timeout(Data &data) {
LOG(WARNING) << "Fail " << data.query_ << " to " << data.query_->source_ << " because total_timeout "
<< data.query_->total_timeout_ << " is greater than total_timeout_limit "
<< data.query_->total_timeout_limit_;
data.query_->set_error(Status::Error(
429, PSLICE() << "Too Many Requests: retry after " << static_cast<int32>(data.last_timeout_ + 0.999)));
data.query_->set_error(Status::Error(429, PSLICE() << "Too Many Requests: retry after " << data.last_timeout_));
data.state_ = State::Dummy;
try_resend_query(data, std::move(data.query_));
}
@ -74,6 +74,12 @@ void SequenceDispatcher::try_resend_query(Data &data, NetQueryPtr query) {
wait_cnt_++;
auto token = pos + id_offset_;
// TODO: if query is ok, use NetQueryCallback::on_result
if (data.callback_.empty()) {
do_finish(data);
send_closure(G()->td(), &Td::on_result, std::move(query));
loop();
return;
}
auto promise = PromiseCreator::lambda([&, self = actor_shared(this, token)](NetQueryPtr query) mutable {
if (!query.empty()) {
send_closure(std::move(self), &SequenceDispatcher::on_resend_ok, std::move(query));
@ -180,7 +186,6 @@ void SequenceDispatcher::loop() {
VLOG(net_query) << "Send " << data_[next_i_].query_;
data_[next_i_].query_->debug("send to Td::send_with_callback");
data_[next_i_].query_->set_session_rand(session_rand_);
G()->net_query_dispatcher().dispatch_with_callback(std::move(data_[next_i_].query_),
actor_shared(this, next_i_ + id_offset_));
data_[next_i_].state_ = State::Wait;
@ -247,12 +252,13 @@ void SequenceDispatcher::close_silent() {
stop();
}
/*** MultiSequenceDispatcher ***/
void MultiSequenceDispatcherOld::send_with_callback(NetQueryPtr query, ActorShared<NetQueryCallback> callback,
Span<uint64> chains) {
CHECK(all_of(chains, [](auto chain_id) { return chain_id != 0; }));
CHECK(!chains.empty());
auto sequence_id = chains[0];
void MultiSequenceDispatcherOld::send(NetQueryPtr query) {
auto callback = query->move_callback();
auto chain_ids = query->get_chain_ids();
query->set_in_sequence_dispatcher(true);
CHECK(all_of(chain_ids, [](auto chain_id) { return chain_id != 0; }));
CHECK(!chain_ids.empty());
auto sequence_id = chain_ids[0];
auto it_ok = dispatchers_.emplace(sequence_id, Data{0, ActorOwn<SequenceDispatcher>()});
auto &data = it_ok.first->second;
@ -280,19 +286,19 @@ void MultiSequenceDispatcherOld::ready_to_close() {
}
}
class MultiSequenceDispatcherNewImpl final : public MultiSequenceDispatcherNew {
class MultiSequenceDispatcherImpl final : public MultiSequenceDispatcher {
public:
void send_with_callback(NetQueryPtr query, ActorShared<NetQueryCallback> callback, Span<uint64> chains) final {
CHECK(all_of(chains, [](auto chain_id) { return chain_id != 0; }));
if (!chains.empty()) {
query->set_session_rand(static_cast<uint32>(chains[0] >> 10));
}
void send(NetQueryPtr query) final {
auto callback = query->move_callback();
auto chain_ids = query->get_chain_ids();
query->set_in_sequence_dispatcher(true);
CHECK(all_of(chain_ids, [](auto chain_id) { return chain_id != 0; }));
Node node;
node.net_query = std::move(query);
node.net_query->debug("Waiting at SequenceDispatcher");
node.net_query_ref = node.net_query.get_weak();
node.callback = std::move(callback);
scheduler_.create_task(chains, std::move(node));
scheduler_.create_task(chain_ids, std::move(node));
loop();
}
@ -300,51 +306,115 @@ class MultiSequenceDispatcherNewImpl final : public MultiSequenceDispatcherNew {
struct Node {
NetQueryRef net_query_ref;
NetQueryPtr net_query;
int32 total_timeout{0};
int32 last_timeout{0};
ActorShared<NetQueryCallback> callback;
friend StringBuilder &operator<<(StringBuilder &sb, const Node &node) {
return sb << node.net_query;
}
};
ChainScheduler<Node> scheduler_;
using TaskId = ChainScheduler<NetQueryPtr>::TaskId;
using ChainId = ChainScheduler<NetQueryPtr>::ChainId;
using TaskId = ChainScheduler<Node>::TaskId;
bool check_timeout(Node &node) {
auto &net_query = node.net_query;
if (net_query.empty() || net_query->is_ready()) {
return false;
}
net_query->total_timeout_ += node.total_timeout;
node.total_timeout = 0;
if (net_query->total_timeout_ > net_query->total_timeout_limit_) {
LOG(WARNING) << "Fail " << net_query << " to " << net_query->source_ << " because total_timeout "
<< net_query->total_timeout_ << " is greater than total_timeout_limit "
<< net_query->total_timeout_limit_;
net_query->set_error(Status::Error(429, PSLICE() << "Too Many Requests: retry after " << node.last_timeout));
return true;
}
return false;
}
void on_result(NetQueryPtr query) final {
auto task_id = TaskId(get_link_token());
auto &node = *scheduler_.get_task_extra(task_id);
if (query->last_timeout_ != 0) {
vector<TaskId> to_check_timeout;
auto tl_constructor = query->tl_constructor();
scheduler_.for_each_dependent(task_id, [&](TaskId child_task_id) {
auto &child_node = *scheduler_.get_task_extra(child_task_id);
if (child_node.net_query_ref->tl_constructor() == tl_constructor) {
child_node.total_timeout += query->last_timeout_;
child_node.last_timeout = query->last_timeout_;
to_check_timeout.push_back(child_task_id);
}
});
for (auto dependent_task_id : to_check_timeout) {
if (check_timeout(*scheduler_.get_task_extra(dependent_task_id))) {
scheduler_.pause_task(dependent_task_id);
try_resend(dependent_task_id);
}
}
}
if (query->is_error() && (query->error().code() == NetQuery::ResendInvokeAfter ||
(query->error().code() == 400 && (query->error().message() == "MSG_WAIT_FAILED" ||
query->error().message() == "MSG_WAIT_TIMEOUT")))) {
VLOG(net_query) << "Resend " << query;
query->resend();
return on_resend(std::move(query));
do_resend(task_id, node, std::move(query));
loop();
return;
}
auto promise = promise_send_closure(actor_shared(this, task_id), &MultiSequenceDispatcherNewImpl::on_resend);
send_closure(node.callback, &NetQueryCallback::on_result_resendable, std::move(query), std::move(promise));
node.net_query = std::move(query);
try_resend(task_id);
}
void on_resend(Result<NetQueryPtr> query) {
void try_resend(TaskId task_id) {
auto &node = *scheduler_.get_task_extra(task_id);
if (node.callback.empty()) {
auto query = std::move(node.net_query);
scheduler_.finish_task(task_id);
send_closure(G()->td(), &Td::on_result, std::move(query));
loop();
return;
}
auto promise = promise_send_closure(actor_shared(this, task_id), &MultiSequenceDispatcherImpl::on_resend);
send_closure(node.callback, &NetQueryCallback::on_result_resendable, std::move(node.net_query), std::move(promise));
}
void on_resend(Result<NetQueryPtr> r_query) {
auto task_id = TaskId(get_link_token());
auto &node = *scheduler_.get_task_extra(task_id);
if (query.is_error()) {
if (r_query.is_error()) {
scheduler_.finish_task(task_id);
} else {
node.net_query = query.move_as_ok();
node.net_query->debug("Waiting at SequenceDispatcher");
node.net_query_ref = node.net_query.get_weak();
scheduler_.reset_task(task_id);
do_resend(task_id, node, r_query.move_as_ok());
}
loop();
}
void do_resend(TaskId task_id, Node &node, NetQueryPtr &&query) {
node.net_query = std::move(query);
node.net_query->debug("Waiting at SequenceDispatcher");
node.net_query_ref = node.net_query.get_weak();
if (check_timeout(node)) {
scheduler_.pause_task(task_id);
try_resend(task_id);
} else {
scheduler_.reset_task(task_id);
}
}
void loop() final {
flush_pending_queries();
}
void tear_down() final {
// Leaves scheduler_ in an invalid state, but we are closing anyway
scheduler_.for_each([&](Node &node) {
scheduler_.for_each([](Node &node) {
if (node.net_query.empty()) {
return;
}
@ -371,16 +441,15 @@ class MultiSequenceDispatcherNewImpl final : public MultiSequenceDispatcherNew {
}
query->set_invoke_after(std::move(parents));
query->last_timeout_ = 0; // TODO: flood
VLOG(net_query) << "Send " << query;
query->debug("send to Td::send_with_callback");
query->last_timeout_ = 0;
query->debug("dispatch_with_callback");
G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, task.task_id));
}
}
};
ActorOwn<MultiSequenceDispatcherNew> MultiSequenceDispatcherNew::create(Slice name) {
return ActorOwn<MultiSequenceDispatcherNew>(create_actor<MultiSequenceDispatcherNewImpl>(name));
ActorOwn<MultiSequenceDispatcher> MultiSequenceDispatcher::create(Slice name) {
return ActorOwn<MultiSequenceDispatcher>(create_actor<MultiSequenceDispatcherImpl>(name));
}
} // namespace td

View File

@ -11,12 +11,11 @@
#include "td/actor/actor.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include <limits>
#include <unordered_map>
namespace td {
@ -42,8 +41,8 @@ class SequenceDispatcher final : public NetQueryCallback {
NetQueryPtr query_;
ActorShared<NetQueryCallback> callback_;
uint64 generation_;
double total_timeout_;
double last_timeout_;
int32 total_timeout_;
int32 last_timeout_;
};
ActorShared<Parent> parent_;
@ -77,7 +76,7 @@ class SequenceDispatcher final : public NetQueryCallback {
class MultiSequenceDispatcherOld final : public SequenceDispatcher::Parent {
public:
void send_with_callback(NetQueryPtr query, ActorShared<NetQueryCallback> callback, Span<uint64> chains);
void send(NetQueryPtr query);
static ActorOwn<MultiSequenceDispatcherOld> create(Slice name) {
return create_actor<MultiSequenceDispatcherOld>(name);
}
@ -87,19 +86,15 @@ class MultiSequenceDispatcherOld final : public SequenceDispatcher::Parent {
int32 cnt_;
ActorOwn<SequenceDispatcher> dispatcher_;
};
std::unordered_map<uint64, Data> dispatchers_;
FlatHashMap<uint64, Data> dispatchers_;
void on_result() final;
void ready_to_close() final;
};
using ChainId = uint64;
using ChainIds = vector<ChainId>;
class MultiSequenceDispatcherNew : public NetQueryCallback {
class MultiSequenceDispatcher : public NetQueryCallback {
public:
virtual void send_with_callback(NetQueryPtr query, ActorShared<NetQueryCallback> callback, Span<uint64> chains) = 0;
static ActorOwn<MultiSequenceDispatcherNew> create(Slice name);
virtual void send(NetQueryPtr query) = 0;
static ActorOwn<MultiSequenceDispatcher> create(Slice name);
};
using MultiSequenceDispatcher = MultiSequenceDispatcherOld;
} // namespace td

View File

@ -111,7 +111,7 @@ struct SponsoredMessageManager::SponsoredMessage {
struct SponsoredMessageManager::DialogSponsoredMessages {
vector<Promise<td_api::object_ptr<td_api::sponsoredMessage>>> promises;
vector<SponsoredMessage> messages;
std::unordered_map<int64, string> message_random_ids;
FlatHashMap<int64, string> message_random_ids;
};
SponsoredMessageManager::SponsoredMessageManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {

View File

@ -16,10 +16,9 @@
#include "td/actor/Timeout.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/Status.h"
#include <unordered_map>
namespace td {
class Td;
@ -58,7 +57,7 @@ class SponsoredMessageManager final : public Actor {
void on_get_dialog_sponsored_messages(
DialogId dialog_id, Result<telegram_api::object_ptr<telegram_api::messages_sponsoredMessages>> &&result);
std::unordered_map<DialogId, unique_ptr<DialogSponsoredMessages>, DialogIdHash> dialog_sponsored_messages_;
FlatHashMap<DialogId, unique_ptr<DialogSponsoredMessages>, DialogIdHash> dialog_sponsored_messages_;
MessageId current_sponsored_message_id_ = MessageId::max();

View File

@ -10,7 +10,7 @@
namespace td {
StickerFormat get_sticker_format(Slice mime_type) {
StickerFormat get_sticker_format_by_mime_type(Slice mime_type) {
if (mime_type == "application/x-tgsticker") {
return StickerFormat::Tgs;
}
@ -23,6 +23,19 @@ StickerFormat get_sticker_format(Slice mime_type) {
return StickerFormat::Unknown;
}
StickerFormat get_sticker_format_by_extension(Slice extension) {
if (extension == "tgs") {
return StickerFormat::Tgs;
}
if (extension == "webp") {
return StickerFormat::Webp;
}
if (extension == "webm") {
return StickerFormat::Webm;
}
return StickerFormat::Unknown;
}
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(
StickerFormat sticker_format, bool is_masks, td_api::object_ptr<td_api::maskPosition> mask_position) {
switch (sticker_format) {

View File

@ -17,7 +17,9 @@ namespace td {
// update store_sticker/store_sticker_set when this type changes
enum class StickerFormat : int32 { Unknown, Webp, Tgs, Webm };
StickerFormat get_sticker_format(Slice mime_type);
StickerFormat get_sticker_format_by_mime_type(Slice mime_type);
StickerFormat get_sticker_format_by_extension(Slice extension);
td_api::object_ptr<td_api::StickerType> get_sticker_type_object(StickerFormat sticker_format, bool is_masks,
td_api::object_ptr<td_api::maskPosition> mask_position);

View File

@ -65,7 +65,6 @@
#include <cmath>
#include <limits>
#include <type_traits>
#include <unordered_set>
namespace td {
@ -1015,9 +1014,11 @@ class CreateNewStickerSetQuery final : public Td::ResultHandler {
flags |= telegram_api::stickers_createStickerSet::SOFTWARE_MASK;
}
send_query(G()->net_query_creator().create(telegram_api::stickers_createStickerSet(
flags, false /*ignored*/, false /*ignored*/, false /*ignored*/, std::move(input_user), title, short_name,
nullptr, std::move(input_stickers), software)));
send_query(G()->net_query_creator().create(
telegram_api::stickers_createStickerSet(flags, false /*ignored*/, false /*ignored*/, false /*ignored*/,
std::move(input_user), title, short_name, nullptr,
std::move(input_stickers), software),
{{short_name}}));
}
void on_result(BufferSlice packet) final {
@ -1046,8 +1047,10 @@ class AddStickerToSetQuery final : public Td::ResultHandler {
}
void send(const string &short_name, tl_object_ptr<telegram_api::inputStickerSetItem> &&input_sticker) {
send_query(G()->net_query_creator().create(telegram_api::stickers_addStickerToSet(
make_tl_object<telegram_api::inputStickerSetShortName>(short_name), std::move(input_sticker))));
send_query(G()->net_query_creator().create(
telegram_api::stickers_addStickerToSet(make_tl_object<telegram_api::inputStickerSetShortName>(short_name),
std::move(input_sticker)),
{{short_name}}));
}
void on_result(BufferSlice packet) final {
@ -1076,8 +1079,10 @@ class SetStickerSetThumbnailQuery final : public Td::ResultHandler {
}
void send(const string &short_name, tl_object_ptr<telegram_api::InputDocument> &&input_document) {
send_query(G()->net_query_creator().create(telegram_api::stickers_setStickerSetThumb(
make_tl_object<telegram_api::inputStickerSetShortName>(short_name), std::move(input_document))));
send_query(G()->net_query_creator().create(
telegram_api::stickers_setStickerSetThumb(make_tl_object<telegram_api::inputStickerSetShortName>(short_name),
std::move(input_document)),
{{short_name}}));
}
void on_result(BufferSlice packet) final {
@ -1105,9 +1110,9 @@ class SetStickerPositionQuery final : public Td::ResultHandler {
explicit SetStickerPositionQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputDocument> &&input_document, int32 position) {
void send(const string &short_name, tl_object_ptr<telegram_api::inputDocument> &&input_document, int32 position) {
send_query(G()->net_query_creator().create(
telegram_api::stickers_changeStickerPosition(std::move(input_document), position)));
telegram_api::stickers_changeStickerPosition(std::move(input_document), position), {{short_name}}));
}
void on_result(BufferSlice packet) final {
@ -1135,8 +1140,9 @@ class DeleteStickerFromSetQuery final : public Td::ResultHandler {
explicit DeleteStickerFromSetQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(tl_object_ptr<telegram_api::inputDocument> &&input_document) {
send_query(G()->net_query_creator().create(telegram_api::stickers_removeStickerFromSet(std::move(input_document))));
void send(const string &short_name, tl_object_ptr<telegram_api::inputDocument> &&input_document) {
send_query(G()->net_query_creator().create(telegram_api::stickers_removeStickerFromSet(std::move(input_document)),
{{short_name}}));
}
void on_result(BufferSlice packet) final {
@ -1351,7 +1357,12 @@ void StickersManager::reload_reactions() {
}
StickersManager::SpecialStickerSet &StickersManager::add_special_sticker_set(const SpecialStickerSetType &type) {
auto &result = special_sticker_sets_[type];
CHECK(!type.is_empty());
auto &result_ptr = special_sticker_sets_[type];
if (result_ptr == nullptr) {
result_ptr = make_unique<SpecialStickerSet>();
}
auto &result = *result_ptr;
if (result.type_.is_empty()) {
result.type_ = type;
} else {
@ -1396,7 +1407,9 @@ void StickersManager::load_special_sticker_set_info_from_binlog(SpecialStickerSe
}
add_sticker_set(sticker_set.id_, sticker_set.access_hash_);
short_name_to_sticker_set_id_.emplace(sticker_set.short_name_, sticker_set.id_);
if (!sticker_set.short_name_.empty()) {
short_name_to_sticker_set_id_.emplace(sticker_set.short_name_, sticker_set.id_);
}
}
void StickersManager::load_special_sticker_set_by_type(SpecialStickerSetType type) {
@ -1879,7 +1892,7 @@ tl_object_ptr<td_api::DiceStickers> StickersManager::get_dice_stickers_object(co
return nullptr;
}
auto sticker_set_id = it->second.id_;
auto sticker_set_id = it->second->id_;
if (!sticker_set_id.is_valid()) {
return nullptr;
}
@ -1952,6 +1965,13 @@ PhotoFormat StickersManager::get_sticker_set_thumbnail_format(StickerFormat stic
}
}
double StickersManager::get_sticker_set_minithumbnail_zoom(const StickerSet *sticker_set) {
if (sticker_set->sticker_format == StickerFormat::Tgs) {
return 100.0 / 512.0;
}
return 1.0;
}
tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const {
const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
CHECK(sticker_set != nullptr);
@ -1974,7 +1994,8 @@ tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(Sticke
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
return make_tl_object<td_api::stickerSet>(
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2, 1.0),
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2,
get_sticker_set_minithumbnail_zoom(sticker_set)),
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
get_sticker_type_object(sticker_set->sticker_format, sticker_set->is_masks, nullptr), sticker_set->is_viewed,
std::move(stickers), std::move(emojis));
@ -2021,7 +2042,8 @@ tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_obje
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
return make_tl_object<td_api::stickerSetInfo>(
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3, 1.0),
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3,
get_sticker_set_minithumbnail_zoom(sticker_set)),
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
get_sticker_type_object(sticker_set->sticker_format, sticker_set->is_masks, nullptr), sticker_set->is_viewed,
sticker_set->was_loaded ? narrow_cast<int32>(sticker_set->sticker_ids.size()) : sticker_set->sticker_count,
@ -2106,7 +2128,7 @@ td_api::object_ptr<td_api::animatedEmoji> StickersManager::get_animated_emoji_ob
if (it == emoji_messages_.end()) {
return get_animated_emoji_object(get_animated_emoji_sticker(emoji), get_animated_emoji_sound_file_id(emoji));
} else {
return get_animated_emoji_object(it->second.animated_emoji_sticker, it->second.sound_file_id);
return get_animated_emoji_object(it->second->animated_emoji_sticker, it->second->sound_file_id);
}
}
@ -2244,7 +2266,7 @@ std::pair<int64, FileId> StickersManager::on_get_sticker_document(tl_object_ptr<
return {};
}
auto format = get_sticker_format(document->mime_type_);
auto format = get_sticker_format_by_mime_type(document->mime_type_);
if (format == StickerFormat::Unknown || (expected_format != StickerFormat::Unknown && format != expected_format)) {
LOG(ERROR) << "Expected sticker of the type " << expected_format << ", but received of the type " << format;
return {};
@ -2372,6 +2394,9 @@ StickerSetId StickersManager::add_sticker_set(tl_object_ptr<telegram_api::InputS
}
StickersManager::StickerSet *StickersManager::add_sticker_set(StickerSetId sticker_set_id, int64 access_hash) {
if (!sticker_set_id.is_valid()) {
return nullptr;
}
auto &s = sticker_sets_[sticker_set_id];
if (s == nullptr) {
s = make_unique<StickerSet>();
@ -2421,7 +2446,7 @@ FileId StickersManager::dup_sticker(FileId new_id, FileId old_id) {
const Sticker *old_sticker = get_sticker(old_id);
CHECK(old_sticker != nullptr);
auto &new_sticker = stickers_[new_id];
CHECK(!new_sticker);
CHECK(new_sticker == nullptr);
new_sticker = make_unique<Sticker>(*old_sticker);
new_sticker->file_id = new_id;
// there is no reason to dup m_thumbnail
@ -2593,6 +2618,21 @@ void StickersManager::add_sticker_thumbnail(Sticker *s, PhotoSize thumbnail) {
void StickersManager::create_sticker(FileId file_id, string minithumbnail, PhotoSize thumbnail, Dimensions dimensions,
tl_object_ptr<telegram_api::documentAttributeSticker> sticker,
StickerFormat format, MultiPromiseActor *load_data_multipromise_ptr) {
if (format == StickerFormat::Unknown && sticker == nullptr) {
auto old_sticker = get_sticker(file_id);
if (old_sticker != nullptr) {
format = old_sticker->format;
} else {
// guess format by file extension
auto file_view = td_->file_manager_->get_file_view(file_id);
auto suggested_path = file_view.suggested_path();
const PathView path_view(suggested_path);
format = get_sticker_format_by_extension(path_view.extension());
if (format == StickerFormat::Unknown) {
format = StickerFormat::Webp;
}
}
}
if (is_sticker_format_vector(format) && dimensions.width == 0) {
dimensions.width = 512;
dimensions.height = 512;
@ -2764,15 +2804,6 @@ tl_object_ptr<telegram_api::InputMedia> StickersManager::get_input_media(
flags |= telegram_api::inputMediaUploadedDocument::THUMB_MASK;
}
auto mime_type = get_sticker_format_mime_type(s->format);
if (s->format == StickerFormat::Unknown && !s->set_id.is_valid()) {
auto suggested_path = file_view.suggested_path();
const PathView path_view(suggested_path);
if (path_view.extension() == "tgs") {
mime_type = "application/x-tgsticker";
} else if (path_view.extension() == "webm") {
mime_type = "video/webm";
}
}
return make_tl_object<telegram_api::inputMediaUploadedDocument>(
flags, false /*ignored*/, false /*ignored*/, std::move(input_file), std::move(input_thumbnail), mime_type,
std::move(attributes), vector<tl_object_ptr<telegram_api::InputDocument>>(), 0);
@ -2788,6 +2819,9 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
CHECK(set != nullptr);
StickerSetId set_id{set->id_};
StickerSet *s = add_sticker_set(set_id, set->access_hash_);
if (s == nullptr) {
return {};
}
bool is_installed = (set->flags_ & telegram_api::stickerSet::INSTALLED_DATE_MASK) != 0;
bool is_archived = set->archived_;
@ -2900,7 +2934,10 @@ StickerSetId StickersManager::on_get_sticker_set(tl_object_ptr<telegram_api::sti
LOG_IF(ERROR, s->is_masks != is_masks) << "Masks type of " << set_id << "/" << s->short_name << " has changed from "
<< s->is_masks << " to " << is_masks << " from " << source;
}
short_name_to_sticker_set_id_.emplace(clean_username(s->short_name), set_id);
auto cleaned_username = clean_username(s->short_name);
if (!cleaned_username.empty()) {
short_name_to_sticker_set_id_.emplace(cleaned_username, set_id);
}
on_update_sticker_set(s, is_installed, is_archived, is_changed);
@ -3018,19 +3055,19 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
vector<tl_object_ptr<telegram_api::stickerPack>> packs = std::move(set->packs_);
vector<tl_object_ptr<telegram_api::Document>> documents = std::move(set->documents_);
std::unordered_map<int64, FileId> document_id_to_sticker_id;
FlatHashMap<int64, FileId> document_id_to_sticker_id;
s->sticker_ids.clear();
bool is_bot = td_->auth_manager_->is_bot();
for (auto &document_ptr : documents) {
auto sticker_id = on_get_sticker_document(std::move(document_ptr), s->sticker_format);
if (!sticker_id.second.is_valid()) {
if (!sticker_id.second.is_valid() || sticker_id.first == 0) {
continue;
}
s->sticker_ids.push_back(sticker_id.second);
if (!is_bot) {
document_id_to_sticker_id.insert(sticker_id);
document_id_to_sticker_id.emplace(sticker_id.first, sticker_id.second);
}
}
if (static_cast<int32>(s->sticker_ids.size()) != s->sticker_count) {
@ -3043,6 +3080,12 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
s->emoji_stickers_map_.clear();
s->sticker_emojis_map_.clear();
for (auto &pack : packs) {
auto cleaned_emoji = remove_emoji_modifiers(pack->emoticon_).str();
if (cleaned_emoji.empty()) {
LOG(ERROR) << "Receive empty emoji in " << set_id << "/" << s->short_name << " from " << source;
continue;
}
vector<FileId> stickers;
stickers.reserve(pack->documents_.size());
for (int64 document_id : pack->documents_) {
@ -3056,7 +3099,8 @@ StickerSetId StickersManager::on_get_messages_sticker_set(StickerSetId sticker_s
stickers.push_back(it->second);
s->sticker_emojis_map_[it->second].push_back(pack->emoticon_);
}
auto &sticker_ids = s->emoji_stickers_map_[remove_emoji_modifiers(pack->emoticon_).str()];
auto &sticker_ids = s->emoji_stickers_map_[cleaned_emoji];
for (auto sticker_id : stickers) {
if (!td::contains(sticker_ids, sticker_id)) {
sticker_ids.push_back(sticker_id);
@ -3179,11 +3223,20 @@ void StickersManager::load_reactions() {
auto status = log_event_parse(reactions_, reactions);
if (status.is_error()) {
LOG(ERROR) << "Can't load available reactions: " << status;
reactions_ = {};
return reload_reactions();
}
for (auto &reaction : reactions_.reactions_) {
if (!reaction.is_valid()) {
LOG(ERROR) << "Loaded invalid reaction";
reactions_ = {};
return reload_reactions();
}
}
LOG(INFO) << "Successfully loaded " << reactions_.reactions_.size() << " available reactions";
send_closure(G()->td(), &Td::send_update, get_update_reactions_object());
LOG(INFO) << "Successfully sent updateReactions";
update_active_reactions();
}
@ -3236,9 +3289,8 @@ void StickersManager::on_get_available_reactions(
on_get_sticker_document(std::move(available_reaction->around_animation_), StickerFormat::Tgs).second;
reaction.center_animation_ =
on_get_sticker_document(std::move(available_reaction->center_icon_), StickerFormat::Tgs).second;
if (!reaction.static_icon_.is_valid() || !reaction.appear_animation_.is_valid() ||
!reaction.select_animation_.is_valid() || !reaction.activate_animation_.is_valid() ||
!reaction.effect_animation_.is_valid()) {
if (!reaction.is_valid()) {
LOG(ERROR) << "Receive invalid reaction " << reaction.reaction_;
continue;
}
@ -3267,8 +3319,10 @@ void StickersManager::on_get_installed_sticker_sets(bool is_masks,
CHECK(constructor_id == telegram_api::messages_allStickers::ID);
auto stickers = move_tl_object_as<telegram_api::messages_allStickers>(stickers_ptr);
std::unordered_set<StickerSetId, StickerSetIdHash> uninstalled_sticker_sets(
installed_sticker_set_ids_[is_masks].begin(), installed_sticker_set_ids_[is_masks].end());
FlatHashSet<StickerSetId, StickerSetIdHash> uninstalled_sticker_sets;
for (auto &sticker_set_id : installed_sticker_set_ids_[is_masks]) {
uninstalled_sticker_sets.insert(sticker_set_id);
}
vector<StickerSetId> sets_to_load;
vector<StickerSetId> installed_sticker_set_ids;
@ -4072,7 +4126,8 @@ void StickersManager::load_sticker_sets(vector<StickerSetId> &&sticker_set_ids,
return;
}
auto load_request_id = current_sticker_set_load_request_++;
CHECK(current_sticker_set_load_request_ < std::numeric_limits<uint32>::max());
auto load_request_id = ++current_sticker_set_load_request_;
StickerSetLoadRequest &load_request = sticker_set_load_requests_[load_request_id];
load_request.promise = std::move(promise);
load_request.left_queries = sticker_set_ids.size();
@ -4106,7 +4161,8 @@ void StickersManager::load_sticker_sets_without_stickers(vector<StickerSetId> &&
return;
}
auto load_request_id = current_sticker_set_load_request_++;
CHECK(current_sticker_set_load_request_ < std::numeric_limits<uint32>::max());
auto load_request_id = ++current_sticker_set_load_request_;
StickerSetLoadRequest &load_request = sticker_set_load_requests_[load_request_id];
load_request.promise = std::move(promise);
load_request.left_queries = sticker_set_ids.size();
@ -4355,8 +4411,11 @@ void StickersManager::on_update_emoji_sounds() {
FullRemoteFileLocation(FileType::VoiceNote, id, access_hash, dc_id, std::move(file_reference)),
FileLocationSource::FromServer, DialogId(), 0, expected_size, std::move(suggested_file_name));
CHECK(file_id.is_valid());
emoji_sounds_.emplace(remove_fitzpatrick_modifier(sounds[i]).str(), file_id);
new_file_ids.push_back(file_id);
auto cleaned_emoji = remove_fitzpatrick_modifier(sounds[i]).str();
if (!cleaned_emoji.empty()) {
emoji_sounds_.emplace(cleaned_emoji, file_id);
new_file_ids.push_back(file_id);
}
}
td_->file_manager_->change_files_source(get_app_config_file_source_id(), old_file_ids, new_file_ids);
@ -4397,11 +4456,11 @@ void StickersManager::try_update_animated_emoji_messages() {
for (auto &it : emoji_messages_) {
auto new_animated_sticker = get_animated_emoji_sticker(sticker_set, it.first);
auto new_sound_file_id = get_animated_emoji_sound_file_id(it.first);
if (new_animated_sticker != it.second.animated_emoji_sticker ||
(new_animated_sticker.first.is_valid() && new_sound_file_id != it.second.sound_file_id)) {
it.second.animated_emoji_sticker = new_animated_sticker;
it.second.sound_file_id = new_sound_file_id;
for (const auto &full_message_id : it.second.full_message_ids) {
if (new_animated_sticker != it.second->animated_emoji_sticker ||
(new_animated_sticker.first.is_valid() && new_sound_file_id != it.second->sound_file_id)) {
it.second->animated_emoji_sticker = new_animated_sticker;
it.second->sound_file_id = new_sound_file_id;
for (const auto &full_message_id : it.second->full_message_ids) {
full_message_ids.push_back(full_message_id);
}
}
@ -4476,7 +4535,11 @@ void StickersManager::register_emoji(const string &emoji, FullMessageId full_mes
}
LOG(INFO) << "Register emoji " << emoji << " from " << full_message_id << " from " << source;
auto &emoji_messages = emoji_messages_[emoji];
auto &emoji_messages_ptr = emoji_messages_[emoji];
if (emoji_messages_ptr == nullptr) {
emoji_messages_ptr = make_unique<EmojiMessages>();
}
auto &emoji_messages = *emoji_messages_ptr;
if (emoji_messages.full_message_ids.empty()) {
emoji_messages.animated_emoji_sticker = get_animated_emoji_sticker(emoji);
emoji_messages.sound_file_id = get_animated_emoji_sound_file_id(emoji);
@ -4494,7 +4557,7 @@ void StickersManager::unregister_emoji(const string &emoji, FullMessageId full_m
LOG(INFO) << "Unregister emoji " << emoji << " from " << full_message_id << " from " << source;
auto it = emoji_messages_.find(emoji);
CHECK(it != emoji_messages_.end());
auto &full_message_ids = it->second.full_message_ids;
auto &full_message_ids = it->second->full_message_ids;
auto is_deleted = full_message_ids.erase(full_message_id) > 0;
LOG_CHECK(is_deleted) << source << ' ' << emoji << ' ' << full_message_id;
@ -4843,7 +4906,7 @@ void StickersManager::schedule_update_animated_emoji_clicked(const StickerSet *s
}
auto all_sticker_ids = get_animated_emoji_click_stickers(sticker_set, emoji);
std::unordered_map<int, FileId> sticker_ids;
FlatHashMap<int, FileId> sticker_ids;
for (auto sticker_id : all_sticker_ids) {
auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
if (it != sticker_set->sticker_emojis_map_.end()) {
@ -4860,6 +4923,9 @@ void StickersManager::schedule_update_animated_emoji_clicked(const StickerSet *s
auto start_time = max(now, next_update_animated_emoji_clicked_time_);
for (const auto &click : clicks) {
auto index = click.first;
if (index <= 0) {
return;
}
auto sticker_id = sticker_ids[index];
if (!sticker_id.is_valid()) {
LOG(INFO) << "Failed to find sticker for " << emoji << " with index " << index;
@ -4935,8 +5001,12 @@ void StickersManager::read_featured_sticker_sets(void *td_void) {
auto td = static_cast<Td *>(td_void);
auto &set_ids = td->stickers_manager_->pending_viewed_featured_sticker_set_ids_;
td->create_handler<ReadFeaturedStickerSetsQuery>()->send(vector<StickerSetId>(set_ids.begin(), set_ids.end()));
vector<StickerSetId> sticker_set_ids;
for (auto sticker_set_id : set_ids) {
sticker_set_ids.push_back(sticker_set_id);
}
set_ids.clear();
td->create_handler<ReadFeaturedStickerSetsQuery>()->send(std::move(sticker_set_ids));
}
std::pair<int32, vector<StickerSetId>> StickersManager::get_archived_sticker_sets(bool is_masks,
@ -5176,9 +5246,12 @@ void StickersManager::on_get_featured_sticker_sets(
// the count will be fixed in on_load_old_featured_sticker_sets_finished
}
std::unordered_set<StickerSetId, StickerSetIdHash> unread_sticker_set_ids;
FlatHashSet<StickerSetId, StickerSetIdHash> unread_sticker_set_ids;
for (auto &unread_sticker_set_id : featured_stickers->unread_) {
unread_sticker_set_ids.insert(StickerSetId(unread_sticker_set_id));
StickerSetId sticker_set_id(unread_sticker_set_id);
if (sticker_set_id.is_valid()) {
unread_sticker_set_ids.insert(sticker_set_id);
}
}
vector<StickerSetId> featured_sticker_set_ids;
@ -5461,6 +5534,7 @@ void StickersManager::send_get_attached_stickers_query(FileId file_id, Promise<U
void StickersManager::on_get_attached_sticker_sets(
FileId file_id, vector<tl_object_ptr<telegram_api::StickerSetCovered>> &&sticker_sets) {
CHECK(file_id.is_valid());
vector<StickerSetId> &sticker_set_ids = attached_sticker_sets_[file_id];
sticker_set_ids.clear();
for (auto &sticker_set_covered : sticker_sets) {
@ -5489,8 +5563,11 @@ int StickersManager::apply_installed_sticker_sets_order(bool is_masks, const vec
return 0;
}
std::unordered_set<StickerSetId, StickerSetIdHash> valid_set_ids(current_sticker_set_ids.begin(),
current_sticker_set_ids.end());
FlatHashSet<StickerSetId, StickerSetIdHash> valid_set_ids;
for (auto sticker_set_id : current_sticker_set_ids) {
valid_set_ids.insert(sticker_set_id);
}
vector<StickerSetId> new_sticker_set_ids;
for (auto sticker_set_id : sticker_set_ids) {
auto it = valid_set_ids.find(sticker_set_id);
@ -5779,7 +5856,7 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri
file_ids.reserve(stickers.size());
vector<FileId> local_file_ids;
vector<FileId> url_file_ids;
std::unordered_set<int32> sticker_formats;
FlatHashSet<int32> sticker_formats;
StickerFormat sticker_format = StickerFormat::Unknown;
for (auto &sticker : stickers) {
auto r_file_id = prepare_input_sticker(sticker.get());
@ -5850,6 +5927,7 @@ void StickersManager::upload_sticker_file(UserId user_id, FileId file_id, Promis
upload_file_id = td_->documents_manager_->dup_document(td_->file_manager_->dup_file_id(file_id), file_id);
}
CHECK(upload_file_id.is_valid());
being_uploaded_files_[upload_file_id] = {user_id, std::move(promise)};
LOG(INFO) << "Ask to upload sticker file " << upload_file_id;
td_->file_manager_->upload(upload_file_id, upload_sticker_file_callback_, 2, 0);
@ -6154,6 +6232,18 @@ void StickersManager::on_sticker_set_thumbnail_uploaded(int64 random_id, Result<
->send(pending_set_sticker_set_thumbnail->short_name, file_view.main_remote_location().as_input_document());
}
string StickersManager::get_sticker_set_short_name(FileId sticker_id) const {
string sticker_set_short_name;
const Sticker *s = get_sticker(sticker_id);
if (s != nullptr && s->set_id.is_valid()) {
const StickerSet *sticker_set = get_sticker_set(s->set_id);
if (sticker_set != nullptr) {
return sticker_set->short_name;
}
}
return string();
}
void StickersManager::set_sticker_position_in_set(const tl_object_ptr<td_api::InputFile> &sticker, int32 position,
Promise<Unit> &&promise) {
if (position < 0) {
@ -6173,7 +6263,7 @@ void StickersManager::set_sticker_position_in_set(const tl_object_ptr<td_api::In
}
td_->create_handler<SetStickerPositionQuery>(std::move(promise))
->send(file_view.main_remote_location().as_input_document(), position);
->send(get_sticker_set_short_name(file_id), file_view.main_remote_location().as_input_document(), position);
}
void StickersManager::remove_sticker_from_set(const tl_object_ptr<td_api::InputFile> &sticker,
@ -6191,7 +6281,7 @@ void StickersManager::remove_sticker_from_set(const tl_object_ptr<td_api::InputF
}
td_->create_handler<DeleteStickerFromSetQuery>(std::move(promise))
->send(file_view.main_remote_location().as_input_document());
->send(get_sticker_set_short_name(file_id), file_view.main_remote_location().as_input_document());
}
vector<FileId> StickersManager::get_attached_sticker_file_ids(const vector<int32> &int_file_ids) {
@ -7107,6 +7197,9 @@ int32 StickersManager::get_emoji_language_code_version(const string &language_co
if (it != emoji_language_code_versions_.end()) {
return it->second;
}
if (language_code.empty()) {
return 0;
}
auto &result = emoji_language_code_versions_[language_code];
result = to_integer<int32>(
G()->td_db()->get_sqlite_sync_pmc()->get(get_emoji_language_code_version_database_key(language_code)));
@ -7122,6 +7215,9 @@ double StickersManager::get_emoji_language_code_last_difference_time(const strin
if (it != emoji_language_code_last_difference_times_.end()) {
return it->second;
}
if (language_code.empty()) {
return Time::now_cached() - G()->unix_time();
}
auto &result = emoji_language_code_last_difference_times_[language_code];
auto old_unix_time = to_integer<int32>(G()->td_db()->get_sqlite_sync_pmc()->get(
get_emoji_language_code_last_difference_time_database_key(language_code)));
@ -7261,6 +7357,13 @@ vector<string> StickersManager::get_emoji_language_codes(const vector<string> &i
auto it = emoji_language_codes_.find(key);
if (it == emoji_language_codes_.end()) {
it = emoji_language_codes_.emplace(key, full_split(G()->td_db()->get_sqlite_sync_pmc()->get(key), '$')).first;
td::remove_if(it->second, [](const string &language_code) {
if (language_code.empty() || language_code.find('$') != string::npos) {
LOG(ERROR) << "Loaded language_code \"" << language_code << '"';
return true;
}
return false;
});
}
if (it->second.empty()) {
load_language_codes(std::move(language_codes), std::move(key), std::move(promise));
@ -7376,6 +7479,7 @@ void StickersManager::on_get_emoji_keywords(
void StickersManager::load_emoji_keywords_difference(const string &language_code) {
LOG(INFO) << "Load emoji keywords difference for language " << language_code;
CHECK(!language_code.empty());
emoji_language_code_last_difference_times_[language_code] =
Time::now_cached() + 1e9; // prevent simultaneous requests
int32 from_version = get_emoji_language_code_version(language_code);
@ -7416,7 +7520,7 @@ void StickersManager::on_get_emoji_keywords_difference(
keywords->version_ = version;
}
version = keywords->version_;
std::unordered_map<string, string> key_values;
FlatHashMap<string, string> key_values;
key_values.emplace(get_emoji_language_code_version_database_key(language_code), to_string(version));
key_values.emplace(get_emoji_language_code_last_difference_time_database_key(language_code),
to_string(G()->unix_time()));
@ -7505,6 +7609,7 @@ vector<string> StickersManager::search_emojis(const string &text, bool exact_mat
vector<string> languages_to_load;
for (auto &language_code : language_codes) {
CHECK(!language_code.empty());
auto version = get_emoji_language_code_version(language_code);
if (version == 0) {
languages_to_load.push_back(language_code);

View File

@ -25,6 +25,8 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/FlatHashMap.h"
#include "td/utils/FlatHashSet.h"
#include "td/utils/Hints.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@ -32,7 +34,6 @@
#include <memory>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace td {
@ -376,8 +377,8 @@ class StickersManager final : public Actor {
PhotoSize thumbnail;
vector<FileId> sticker_ids;
std::unordered_map<string, vector<FileId>> emoji_stickers_map_; // emoji -> stickers
std::unordered_map<FileId, vector<string>, FileIdHash> sticker_emojis_map_; // sticker -> emojis
FlatHashMap<string, vector<FileId>> emoji_stickers_map_; // emoji -> stickers
FlatHashMap<FileId, vector<string>, FileIdHash> sticker_emojis_map_; // sticker -> emojis
bool is_installed = false;
bool is_archived = false;
@ -453,6 +454,11 @@ class StickersManager final : public Actor {
FileId around_animation_;
FileId center_animation_;
bool is_valid() const {
return static_icon_.is_valid() && appear_animation_.is_valid() && select_animation_.is_valid() &&
activate_animation_.is_valid() && effect_animation_.is_valid() && !reaction_.empty();
}
template <class StorerT>
void store(StorerT &storer) const;
@ -481,6 +487,8 @@ class StickersManager final : public Actor {
StickerSetId sticker_set_id,
int64 document_id, double zoom);
static double get_sticker_set_minithumbnail_zoom(const StickerSet *sticker_set);
static tl_object_ptr<td_api::MaskPoint> get_mask_point_object(int32 point);
tl_object_ptr<td_api::stickerSetInfo> get_sticker_set_info_object(StickerSetId sticker_set_id,
@ -506,6 +514,8 @@ class StickersManager final : public Actor {
StickerSetId on_get_input_sticker_set(FileId sticker_file_id, tl_object_ptr<telegram_api::InputStickerSet> &&set_ptr,
MultiPromiseActor *load_data_multipromise_ptr = nullptr);
string get_sticker_set_short_name(FileId sticker_id) const;
void on_resolve_sticker_set_short_name(FileId sticker_file_id, const string &short_name);
int apply_installed_sticker_sets_order(bool is_masks, const vector<StickerSetId> &sticker_set_ids);
@ -758,9 +768,9 @@ class StickersManager final : public Actor {
bool is_inited_ = false;
std::unordered_map<FileId, unique_ptr<Sticker>, FileIdHash> stickers_; // file_id -> Sticker
std::unordered_map<StickerSetId, unique_ptr<StickerSet>, StickerSetIdHash> sticker_sets_; // id -> StickerSet
std::unordered_map<string, StickerSetId> short_name_to_sticker_set_id_;
FlatHashMap<FileId, unique_ptr<Sticker>, FileIdHash> stickers_; // file_id -> Sticker
FlatHashMap<StickerSetId, unique_ptr<StickerSet>, StickerSetIdHash> sticker_sets_; // id -> StickerSet
FlatHashMap<string, StickerSetId> short_name_to_sticker_set_id_;
vector<StickerSetId> installed_sticker_set_ids_[2];
vector<StickerSetId> featured_sticker_set_ids_;
@ -808,7 +818,7 @@ class StickersManager final : public Actor {
vector<StickerSetId> archived_sticker_set_ids_[2];
int32 total_archived_sticker_set_count_[2] = {-1, -1};
std::unordered_map<FileId, vector<StickerSetId>, FileIdHash> attached_sticker_sets_;
FlatHashMap<FileId, vector<StickerSetId>, FileIdHash> attached_sticker_sets_;
Hints installed_sticker_sets_hints_[2]; // search installed sticker sets by their title and name
@ -817,19 +827,19 @@ class StickersManager final : public Actor {
int32 cache_time_ = 300;
double next_reload_time_ = 0;
};
std::unordered_map<string, FoundStickers> found_stickers_;
std::unordered_map<string, vector<Promise<Unit>>> search_stickers_queries_;
FlatHashMap<string, FoundStickers> found_stickers_;
FlatHashMap<string, vector<Promise<Unit>>> search_stickers_queries_;
std::unordered_map<string, vector<StickerSetId>> found_sticker_sets_;
std::unordered_map<string, vector<Promise<Unit>>> search_sticker_sets_queries_;
std::unordered_set<StickerSetId, StickerSetIdHash> pending_viewed_featured_sticker_set_ids_;
FlatHashSet<StickerSetId, StickerSetIdHash> pending_viewed_featured_sticker_set_ids_;
Timeout pending_featured_sticker_set_views_timeout_;
int32 recent_stickers_limit_ = 200;
int32 favorite_stickers_limit_ = 5;
std::unordered_map<SpecialStickerSetType, SpecialStickerSet, SpecialStickerSetTypeHash> special_sticker_sets_;
FlatHashMap<SpecialStickerSetType, unique_ptr<SpecialStickerSet>, SpecialStickerSetTypeHash> special_sticker_sets_;
struct StickerSetLoadRequest {
Promise<Unit> promise;
@ -837,14 +847,14 @@ class StickersManager final : public Actor {
size_t left_queries = 0;
};
std::unordered_map<uint32, StickerSetLoadRequest> sticker_set_load_requests_;
FlatHashMap<uint32, StickerSetLoadRequest> sticker_set_load_requests_;
uint32 current_sticker_set_load_request_ = 0;
std::unordered_map<int64, unique_ptr<PendingNewStickerSet>> pending_new_sticker_sets_;
FlatHashMap<int64, unique_ptr<PendingNewStickerSet>> pending_new_sticker_sets_;
std::unordered_map<int64, unique_ptr<PendingAddStickerToSet>> pending_add_sticker_to_sets_;
FlatHashMap<int64, unique_ptr<PendingAddStickerToSet>> pending_add_sticker_to_sets_;
std::unordered_map<int64, unique_ptr<PendingSetStickerSetThumbnail>> pending_set_sticker_set_thumbnails_;
FlatHashMap<int64, unique_ptr<PendingSetStickerSetThumbnail>> pending_set_sticker_set_thumbnails_;
vector<Promise<Unit>> pending_get_animated_emoji_queries_;
@ -866,26 +876,26 @@ class StickersManager final : public Actor {
std::shared_ptr<UploadStickerFileCallback> upload_sticker_file_callback_;
std::unordered_map<FileId, std::pair<UserId, Promise<Unit>>, FileIdHash> being_uploaded_files_;
FlatHashMap<FileId, std::pair<UserId, Promise<Unit>>, FileIdHash> being_uploaded_files_;
Reactions reactions_;
std::unordered_map<string, vector<string>> emoji_language_codes_;
std::unordered_map<string, int32> emoji_language_code_versions_;
std::unordered_map<string, double> emoji_language_code_last_difference_times_;
std::unordered_set<string> reloaded_emoji_keywords_;
std::unordered_map<string, vector<Promise<Unit>>> load_emoji_keywords_queries_;
std::unordered_map<string, vector<Promise<Unit>>> load_language_codes_queries_;
std::unordered_map<int64, string> emoji_suggestions_urls_;
FlatHashMap<string, vector<string>> emoji_language_codes_;
FlatHashMap<string, int32> emoji_language_code_versions_;
FlatHashMap<string, double> emoji_language_code_last_difference_times_;
FlatHashSet<string> reloaded_emoji_keywords_;
FlatHashMap<string, vector<Promise<Unit>>> load_emoji_keywords_queries_;
FlatHashMap<string, vector<Promise<Unit>>> load_language_codes_queries_;
FlatHashMap<int64, string> emoji_suggestions_urls_;
std::unordered_map<string, std::unordered_set<FullMessageId, FullMessageIdHash>> dice_messages_;
FlatHashMap<string, FlatHashSet<FullMessageId, FullMessageIdHash>> dice_messages_;
struct EmojiMessages {
std::unordered_set<FullMessageId, FullMessageIdHash> full_message_ids;
FlatHashSet<FullMessageId, FullMessageIdHash> full_message_ids;
std::pair<FileId, int> animated_emoji_sticker;
FileId sound_file_id;
};
std::unordered_map<string, EmojiMessages> emoji_messages_;
FlatHashMap<string, unique_ptr<EmojiMessages>> emoji_messages_;
string dice_emojis_str_;
vector<string> dice_emojis_;
@ -894,7 +904,7 @@ class StickersManager final : public Actor {
vector<std::pair<int32, int32>> dice_success_values_;
string emoji_sounds_str_;
std::unordered_map<string, FileId> emoji_sounds_;
FlatHashMap<string, FileId> emoji_sounds_;
double animated_emoji_zoom_ = 0.0;

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