Merge remote-tracking branch 'td/master'
This commit is contained in:
commit
232ce6e4d3
@ -544,6 +544,7 @@ set(TDLIB_SOURCE
|
|||||||
td/telegram/MessageId.h
|
td/telegram/MessageId.h
|
||||||
td/telegram/MessageLinkInfo.h
|
td/telegram/MessageLinkInfo.h
|
||||||
td/telegram/MessageReplyInfo.h
|
td/telegram/MessageReplyInfo.h
|
||||||
|
td/telegram/MessageThreadInfo.h
|
||||||
td/telegram/MessagesDb.h
|
td/telegram/MessagesDb.h
|
||||||
td/telegram/MessageSearchFilter.h
|
td/telegram/MessageSearchFilter.h
|
||||||
td/telegram/MessagesManager.h
|
td/telegram/MessagesManager.h
|
||||||
|
@ -203,11 +203,9 @@ For C++ projects that use CMake, the best approach is to build `TDLib` as part o
|
|||||||
There are several libraries that you could use in your CMake project:
|
There are several libraries that you could use in your CMake project:
|
||||||
|
|
||||||
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a JSON interface. This has a simple C interface, so it can be easily used with any programming language that is able to execute C functions.
|
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a JSON interface. This has a simple C interface, so it can be easily used with any programming language that is able to execute C functions.
|
||||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) and [td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) documentation for more information.
|
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for more information.
|
||||||
* Td::TdStatic — static library with C++ interface for general usage.
|
* Td::TdStatic — static library with C++ interface for general usage.
|
||||||
See [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) and [Log](https://core.telegram.org/tdlib/docs/classtd_1_1_log.html) documentation for more information.
|
See [ClientManager](https://core.telegram.org/tdlib/docs/classtd_1_1_client_manager.html) and [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) documentation for more information.
|
||||||
* Td::TdCoreStatic — static library with low-level C++ interface intended mostly for internal usage.
|
|
||||||
See [ClientActor](https://core.telegram.org/tdlib/docs/classtd_1_1_client_actor.html) and [Log](https://core.telegram.org/tdlib/docs/classtd_1_1_log.html) documentation for more information.
|
|
||||||
|
|
||||||
For example, part of your CMakeLists.txt may look like this:
|
For example, part of your CMakeLists.txt may look like this:
|
||||||
```
|
```
|
||||||
@ -245,7 +243,7 @@ git checkout td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h
|
|||||||
## Using from other programming languages
|
## Using from other programming languages
|
||||||
`TDLib` provides efficient native C++, Java, and .NET interfaces.
|
`TDLib` provides efficient native C++, Java, and .NET interfaces.
|
||||||
But for most use cases we suggest to use the JSON interface, which can be easily used with any programming language that is able to execute C functions.
|
But for most use cases we suggest to use the JSON interface, which can be easily used with any programming language that is able to execute C functions.
|
||||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) and [td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) documentation for detailed JSON interface description,
|
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for detailed JSON interface description,
|
||||||
the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of
|
the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of
|
||||||
all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "td/utils/port/thread.h"
|
#include "td/utils/port/thread.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
#include "td/utils/SliceBuilder.h"
|
#include "td/utils/SliceBuilder.h"
|
||||||
|
#include "td/utils/ThreadSafeCounter.h"
|
||||||
|
|
||||||
#include "td/telegram/telegram_api.h"
|
#include "td/telegram/telegram_api.h"
|
||||||
#include "td/telegram/telegram_api.hpp"
|
#include "td/telegram/telegram_api.hpp"
|
||||||
@ -32,34 +33,32 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace td {
|
|
||||||
|
|
||||||
class F {
|
class F {
|
||||||
uint32 ∑
|
td::uint32 ∑
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit F(uint32 &sum) : sum(sum) {
|
explicit F(td::uint32 &sum) : sum(sum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void operator()(const T &x) const {
|
void operator()(const T &x) const {
|
||||||
sum += static_cast<uint32>(x.get_id());
|
sum += static_cast<td::uint32>(x.get_id());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BENCH(Call, "TL Call") {
|
BENCH(Call, "TL Call") {
|
||||||
tl_object_ptr<telegram_api::Function> x = make_tl_object<telegram_api::account_getWallPapers>(0);
|
td::tl_object_ptr<td::telegram_api::Function> x = td::make_tl_object<td::telegram_api::account_getWallPapers>(0);
|
||||||
uint32 res = 0;
|
td::uint32 res = 0;
|
||||||
F f(res);
|
F f(res);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
downcast_call(*x, f);
|
downcast_call(*x, f);
|
||||||
}
|
}
|
||||||
do_not_optimize_away(res);
|
td::do_not_optimize_away(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !TD_EVENTFD_UNSUPPORTED
|
#if !TD_EVENTFD_UNSUPPORTED
|
||||||
BENCH(EventFd, "EventFd") {
|
BENCH(EventFd, "EventFd") {
|
||||||
EventFd fd;
|
td::EventFd fd;
|
||||||
fd.init();
|
fd.init();
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
fd.release();
|
fd.release();
|
||||||
@ -76,15 +75,15 @@ BENCH(NewInt, "new int + delete") {
|
|||||||
res += reinterpret_cast<std::uintptr_t>(x);
|
res += reinterpret_cast<std::uintptr_t>(x);
|
||||||
delete x;
|
delete x;
|
||||||
}
|
}
|
||||||
do_not_optimize_away(res);
|
td::do_not_optimize_away(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(NewObj, "new struct, then delete") {
|
BENCH(NewObj, "new struct, then delete") {
|
||||||
struct A {
|
struct A {
|
||||||
int32 a = 0;
|
td::int32 a = 0;
|
||||||
int32 b = 0;
|
td::int32 b = 0;
|
||||||
int32 c = 0;
|
td::int32 c = 0;
|
||||||
int32 d = 0;
|
td::int32 d = 0;
|
||||||
};
|
};
|
||||||
std::uintptr_t res = 0;
|
std::uintptr_t res = 0;
|
||||||
A **ptr = new A *[n];
|
A **ptr = new A *[n];
|
||||||
@ -96,14 +95,14 @@ BENCH(NewObj, "new struct, then delete") {
|
|||||||
delete ptr[i];
|
delete ptr[i];
|
||||||
}
|
}
|
||||||
delete[] ptr;
|
delete[] ptr;
|
||||||
do_not_optimize_away(res);
|
td::do_not_optimize_away(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !TD_THREAD_UNSUPPORTED
|
#if !TD_THREAD_UNSUPPORTED
|
||||||
BENCH(ThreadNew, "new struct, then delete in several threads") {
|
BENCH(ThreadNew, "new struct, then delete in several threads") {
|
||||||
td::NewObjBench a, b;
|
NewObjBench a, b;
|
||||||
thread ta([&] { a.run(n / 2); });
|
td::thread ta([&] { a.run(n / 2); });
|
||||||
thread tb([&] { b.run(n - n / 2); });
|
td::thread tb([&] { b.run(n - n / 2); });
|
||||||
ta.join();
|
ta.join();
|
||||||
tb.join();
|
tb.join();
|
||||||
}
|
}
|
||||||
@ -113,17 +112,17 @@ BENCH(ThreadNew, "new struct, then delete in several threads") {
|
|||||||
BENCH(Time, "Clocks::monotonic") {
|
BENCH(Time, "Clocks::monotonic") {
|
||||||
double res = 0;
|
double res = 0;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
res += Clocks::monotonic();
|
res += td::Clocks::monotonic();
|
||||||
}
|
}
|
||||||
do_not_optimize_away(res);
|
td::do_not_optimize_away(res);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
#if !TD_WINDOWS
|
#if !TD_WINDOWS
|
||||||
class PipeBench final : public Benchmark {
|
class PipeBench final : public td::Benchmark {
|
||||||
public:
|
public:
|
||||||
int p[2];
|
int p[2];
|
||||||
|
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return "pipe write + read int32";
|
return "pipe write + read int32";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +141,7 @@ class PipeBench final : public Benchmark {
|
|||||||
CHECK(read_len == sizeof(val));
|
CHECK(read_len == sizeof(val));
|
||||||
res += val;
|
res += val;
|
||||||
}
|
}
|
||||||
do_not_optimize_away(res);
|
td::do_not_optimize_away(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tear_down() final {
|
void tear_down() final {
|
||||||
@ -153,11 +152,11 @@ class PipeBench final : public Benchmark {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||||
class SemBench final : public Benchmark {
|
class SemBench final : public td::Benchmark {
|
||||||
sem_t sem;
|
sem_t sem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return "sem post + wait";
|
return "sem post + wait";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,12 +179,12 @@ class SemBench final : public Benchmark {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !TD_WINDOWS
|
#if !TD_WINDOWS
|
||||||
class UtimeBench final : public Benchmark {
|
class UtimeBench final : public td::Benchmark {
|
||||||
public:
|
public:
|
||||||
void start_up() final {
|
void start_up() final {
|
||||||
FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok().close();
|
td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok().close();
|
||||||
}
|
}
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return "utime";
|
return "utime";
|
||||||
}
|
}
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
@ -203,62 +202,62 @@ class UtimeBench final : public Benchmark {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
BENCH(Pwrite, "pwrite") {
|
BENCH(Pwrite, "pwrite") {
|
||||||
auto fd = FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok();
|
auto fd = td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok();
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
fd.pwrite("a", 0).ok();
|
fd.pwrite("a", 0).ok();
|
||||||
}
|
}
|
||||||
fd.close();
|
fd.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateFileBench final : public Benchmark {
|
class CreateFileBench final : public td::Benchmark {
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return "create_file";
|
return "create_file";
|
||||||
}
|
}
|
||||||
void start_up() final {
|
void start_up() final {
|
||||||
mkdir("A").ensure();
|
td::mkdir("A").ensure();
|
||||||
}
|
}
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
FileFd::open(PSLICE() << "A/" << i, FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
|
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void tear_down() final {
|
void tear_down() final {
|
||||||
td::walk_path("A/", [&](CSlice path, auto type) {
|
td::walk_path("A/", [&](td::CSlice path, auto type) {
|
||||||
if (type == td::WalkPath::Type::ExitDir) {
|
if (type == td::WalkPath::Type::ExitDir) {
|
||||||
rmdir(path).ignore();
|
td::rmdir(path).ignore();
|
||||||
} else if (type == td::WalkPath::Type::NotDir) {
|
} else if (type == td::WalkPath::Type::NotDir) {
|
||||||
unlink(path).ignore();
|
td::unlink(path).ignore();
|
||||||
}
|
}
|
||||||
}).ignore();
|
}).ignore();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class WalkPathBench final : public Benchmark {
|
class WalkPathBench final : public td::Benchmark {
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return "walk_path";
|
return "walk_path";
|
||||||
}
|
}
|
||||||
void start_up_n(int n) final {
|
void start_up_n(int n) final {
|
||||||
mkdir("A").ensure();
|
td::mkdir("A").ensure();
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
FileFd::open(PSLICE() << "A/" << i, FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
|
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
td::walk_path("A/", [&](CSlice path, auto type) {
|
td::walk_path("A/", [&](td::CSlice path, auto type) {
|
||||||
if (type == td::WalkPath::Type::EnterDir) {
|
if (type == td::WalkPath::Type::EnterDir) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stat(path).ok();
|
td::stat(path).ok();
|
||||||
cnt++;
|
cnt++;
|
||||||
}).ignore();
|
}).ignore();
|
||||||
}
|
}
|
||||||
void tear_down() final {
|
void tear_down() final {
|
||||||
td::walk_path("A/", [&](CSlice path, auto type) {
|
td::walk_path("A/", [&](td::CSlice path, auto type) {
|
||||||
if (type == td::WalkPath::Type::ExitDir) {
|
if (type == td::WalkPath::Type::ExitDir) {
|
||||||
rmdir(path).ignore();
|
td::rmdir(path).ignore();
|
||||||
} else if (type == td::WalkPath::Type::NotDir) {
|
} else if (type == td::WalkPath::Type::NotDir) {
|
||||||
unlink(path).ignore();
|
td::unlink(path).ignore();
|
||||||
}
|
}
|
||||||
}).ignore();
|
}).ignore();
|
||||||
}
|
}
|
||||||
@ -266,14 +265,14 @@ class WalkPathBench final : public Benchmark {
|
|||||||
|
|
||||||
#if !TD_THREAD_UNSUPPORTED
|
#if !TD_THREAD_UNSUPPORTED
|
||||||
template <int ThreadN = 2>
|
template <int ThreadN = 2>
|
||||||
class AtomicReleaseIncBench final : public Benchmark {
|
class AtomicReleaseIncBench final : public td::Benchmark {
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return PSTRING() << "AtomicReleaseInc" << ThreadN;
|
return PSTRING() << "AtomicReleaseInc" << ThreadN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic<uint64> a_;
|
static std::atomic<td::uint64> a_;
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
std::vector<thread> threads;
|
td::vector<td::thread> threads;
|
||||||
for (int i = 0; i < ThreadN; i++) {
|
for (int i = 0; i < ThreadN; i++) {
|
||||||
threads.emplace_back([&] {
|
threads.emplace_back([&] {
|
||||||
for (int i = 0; i < n / ThreadN; i++) {
|
for (int i = 0; i < n / ThreadN; i++) {
|
||||||
@ -287,17 +286,17 @@ class AtomicReleaseIncBench final : public Benchmark {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int ThreadN>
|
template <int ThreadN>
|
||||||
std::atomic<uint64> AtomicReleaseIncBench<ThreadN>::a_;
|
std::atomic<td::uint64> AtomicReleaseIncBench<ThreadN>::a_;
|
||||||
|
|
||||||
template <int ThreadN = 2>
|
template <int ThreadN = 2>
|
||||||
class AtomicReleaseCasIncBench final : public Benchmark {
|
class AtomicReleaseCasIncBench final : public td::Benchmark {
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
|
return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic<uint64> a_;
|
static std::atomic<td::uint64> a_;
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
std::vector<thread> threads;
|
td::vector<td::thread> threads;
|
||||||
for (int i = 0; i < ThreadN; i++) {
|
for (int i = 0; i < ThreadN; i++) {
|
||||||
threads.emplace_back([&] {
|
threads.emplace_back([&] {
|
||||||
for (int i = 0; i < n / ThreadN; i++) {
|
for (int i = 0; i < n / ThreadN; i++) {
|
||||||
@ -313,16 +312,16 @@ class AtomicReleaseCasIncBench final : public Benchmark {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int ThreadN>
|
template <int ThreadN>
|
||||||
std::atomic<uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
|
std::atomic<td::uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
|
||||||
|
|
||||||
template <int ThreadN = 2>
|
template <int ThreadN>
|
||||||
class RwMutexReadBench final : public Benchmark {
|
class RwMutexReadBench final : public td::Benchmark {
|
||||||
string get_description() const final {
|
td::string get_description() const final {
|
||||||
return PSTRING() << "RwMutexRead" << ThreadN;
|
return PSTRING() << "RwMutexRead" << ThreadN;
|
||||||
}
|
}
|
||||||
RwMutex mutex_;
|
td::RwMutex mutex_;
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
std::vector<thread> threads;
|
td::vector<td::thread> threads;
|
||||||
for (int i = 0; i < ThreadN; i++) {
|
for (int i = 0; i < ThreadN; i++) {
|
||||||
threads.emplace_back([&] {
|
threads.emplace_back([&] {
|
||||||
for (int i = 0; i < n / ThreadN; i++) {
|
for (int i = 0; i < n / ThreadN; i++) {
|
||||||
@ -335,14 +334,15 @@ class RwMutexReadBench final : public Benchmark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <int ThreadN = 2>
|
|
||||||
class RwMutexWriteBench final : public Benchmark {
|
template <int ThreadN>
|
||||||
string get_description() const final {
|
class RwMutexWriteBench final : public td::Benchmark {
|
||||||
|
td::string get_description() const final {
|
||||||
return PSTRING() << "RwMutexWrite" << ThreadN;
|
return PSTRING() << "RwMutexWrite" << ThreadN;
|
||||||
}
|
}
|
||||||
RwMutex mutex_;
|
td::RwMutex mutex_;
|
||||||
void run(int n) final {
|
void run(int n) final {
|
||||||
std::vector<thread> threads;
|
td::vector<td::thread> threads;
|
||||||
for (int i = 0; i < ThreadN; i++) {
|
for (int i = 0; i < ThreadN; i++) {
|
||||||
threads.emplace_back([&] {
|
threads.emplace_back([&] {
|
||||||
for (int i = 0; i < n / ThreadN; i++) {
|
for (int i = 0; i < n / ThreadN; i++) {
|
||||||
@ -355,41 +355,107 @@ class RwMutexWriteBench final : public Benchmark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ThreadSafeCounterBench final : public td::Benchmark {
|
||||||
|
static td::ThreadSafeCounter counter_;
|
||||||
|
int thread_count_;
|
||||||
|
|
||||||
|
td::string get_description() const final {
|
||||||
|
return PSTRING() << "ThreadSafeCounter" << thread_count_;
|
||||||
|
}
|
||||||
|
void run(int n) final {
|
||||||
|
counter_.clear();
|
||||||
|
td::vector<td::thread> threads;
|
||||||
|
for (int i = 0; i < thread_count_; i++) {
|
||||||
|
threads.emplace_back([n] {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
counter_.add(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (auto &thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
CHECK(counter_.sum() == n * thread_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ThreadSafeCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
td::ThreadSafeCounter ThreadSafeCounterBench::counter_;
|
||||||
|
|
||||||
|
template <bool StrictOrder>
|
||||||
|
class AtomicCounterBench final : public td::Benchmark {
|
||||||
|
static std::atomic<td::int64> counter_;
|
||||||
|
int thread_count_;
|
||||||
|
|
||||||
|
td::string get_description() const final {
|
||||||
|
return PSTRING() << "AtomicCounter" << thread_count_;
|
||||||
|
}
|
||||||
|
void run(int n) final {
|
||||||
|
counter_.store(0);
|
||||||
|
td::vector<td::thread> threads;
|
||||||
|
for (int i = 0; i < thread_count_; i++) {
|
||||||
|
threads.emplace_back([n] {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
counter_.fetch_add(1, StrictOrder ? std::memory_order_seq_cst : std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (auto &thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
CHECK(counter_.load() == n * thread_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AtomicCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <bool StrictOrder>
|
||||||
|
std::atomic<td::int64> AtomicCounterBench<StrictOrder>::counter_;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
} // namespace td
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||||
#if !TD_THREAD_UNSUPPORTED
|
#if !TD_THREAD_UNSUPPORTED
|
||||||
td::bench(td::AtomicReleaseIncBench<1>());
|
for (int i = 1; i <= 16; i *= 2) {
|
||||||
td::bench(td::AtomicReleaseIncBench<2>());
|
td::bench(ThreadSafeCounterBench(i));
|
||||||
td::bench(td::AtomicReleaseCasIncBench<1>());
|
td::bench(AtomicCounterBench<false>(i));
|
||||||
td::bench(td::AtomicReleaseCasIncBench<2>());
|
td::bench(AtomicCounterBench<true>(i));
|
||||||
td::bench(td::RwMutexWriteBench<1>());
|
}
|
||||||
td::bench(td::RwMutexReadBench<1>());
|
|
||||||
td::bench(td::RwMutexWriteBench<>());
|
td::bench(AtomicReleaseIncBench<1>());
|
||||||
td::bench(td::RwMutexReadBench<>());
|
td::bench(AtomicReleaseIncBench<2>());
|
||||||
|
td::bench(AtomicReleaseCasIncBench<1>());
|
||||||
|
td::bench(AtomicReleaseCasIncBench<2>());
|
||||||
|
td::bench(RwMutexWriteBench<1>());
|
||||||
|
td::bench(RwMutexReadBench<1>());
|
||||||
|
td::bench(RwMutexWriteBench<2>());
|
||||||
|
td::bench(RwMutexReadBench<2>());
|
||||||
#endif
|
#endif
|
||||||
#if !TD_WINDOWS
|
#if !TD_WINDOWS
|
||||||
td::bench(td::UtimeBench());
|
td::bench(UtimeBench());
|
||||||
#endif
|
#endif
|
||||||
td::bench(td::WalkPathBench());
|
td::bench(WalkPathBench());
|
||||||
td::bench(td::CreateFileBench());
|
td::bench(CreateFileBench());
|
||||||
td::bench(td::PwriteBench());
|
td::bench(PwriteBench());
|
||||||
|
|
||||||
td::bench(td::CallBench());
|
td::bench(CallBench());
|
||||||
#if !TD_THREAD_UNSUPPORTED
|
#if !TD_THREAD_UNSUPPORTED
|
||||||
td::bench(td::ThreadNewBench());
|
td::bench(ThreadNewBench());
|
||||||
#endif
|
#endif
|
||||||
#if !TD_EVENTFD_UNSUPPORTED
|
#if !TD_EVENTFD_UNSUPPORTED
|
||||||
td::bench(td::EventFdBench());
|
td::bench(EventFdBench());
|
||||||
#endif
|
#endif
|
||||||
td::bench(td::NewObjBench());
|
td::bench(NewObjBench());
|
||||||
td::bench(td::NewIntBench());
|
td::bench(NewIntBench());
|
||||||
#if !TD_WINDOWS
|
#if !TD_WINDOWS
|
||||||
td::bench(td::PipeBench());
|
td::bench(PipeBench());
|
||||||
#endif
|
#endif
|
||||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||||
td::bench(td::SemBench());
|
td::bench(SemBench());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -626,7 +626,7 @@ function onOptionsChanged() {
|
|||||||
cmake = 'cmake3';
|
cmake = 'cmake3';
|
||||||
packages += ' gperf';
|
packages += ' gperf';
|
||||||
} else {
|
} else {
|
||||||
commands.push(sudo + 'dnf --enablerepo=PowerTools install gperf');
|
commands.push(sudo + 'dnf --enablerepo=powertools install gperf');
|
||||||
}
|
}
|
||||||
packages += ' ' + cmake;
|
packages += ' ' + cmake;
|
||||||
if (target === 'JNI') {
|
if (target === 'JNI') {
|
||||||
|
@ -7,5 +7,5 @@ Then you can run the example:
|
|||||||
python tdjson_example.py
|
python tdjson_example.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html),
|
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
|
||||||
[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||||
|
@ -11,5 +11,5 @@ cmake --build . --target install
|
|||||||
|
|
||||||
Then you can open and build the example with the latest Xcode.
|
Then you can open and build the example with the latest Xcode.
|
||||||
|
|
||||||
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html),
|
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
|
||||||
[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.
|
||||||
|
@ -759,6 +759,8 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
|
|||||||
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
|
//@can_be_deleted_for_all_users True, if the message can be deleted for all users
|
||||||
//@can_get_statistics True, if the message statistics are available
|
//@can_get_statistics True, if the message statistics are available
|
||||||
//@can_get_message_thread True, if the message thread info is available
|
//@can_get_message_thread True, if the message thread info is available
|
||||||
|
//@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
|
||||||
|
//@has_timestamped_media True, if media timestamp entities refers to a media in this message as opposed to a media in the replied message
|
||||||
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
|
//@is_channel_post True, if the message is a channel post. All messages to channels are channel posts, all other messages are not channel posts
|
||||||
//@contains_unread_mention True, if the message contains an unread mention for the current user
|
//@contains_unread_mention True, if the message contains an unread mention for the current user
|
||||||
//@date Point in time (Unix timestamp) when the message was sent
|
//@date Point in time (Unix timestamp) when the message was sent
|
||||||
@ -776,7 +778,7 @@ messageSendingStateFailed error_code:int32 error_message:string can_retry:Bool r
|
|||||||
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
|
//@restriction_reason If non-empty, contains a human-readable description of the reason why access to this message must be restricted
|
||||||
//@content Content of the message
|
//@content Content of the message
|
||||||
//@reply_markup Reply markup for the message; may be null
|
//@reply_markup Reply markup for the message; may be null
|
||||||
message id:int53 sender: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_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_statistics:Bool can_get_message_thread:Bool is_channel_post:Bool contains_unread_mention:Bool date:int32 edit_date:int32 forward_info:messageForwardInfo interaction_info:messageInteractionInfo reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 author_signature:string media_album_id:int64 restriction_reason:string content:MessageContent reply_markup:ReplyMarkup = Message;
|
message id:int53 sender: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_deleted_only_for_self:Bool can_be_deleted_for_all_users:Bool can_get_statistics:Bool can_get_message_thread: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 reply_in_chat_id:int53 reply_to_message_id:int53 message_thread_id:int53 ttl:int32 ttl_expires_in:double via_bot_user_id:int32 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 count of messages found @messages List of messages; messages may be null
|
||||||
messages total_count:int32 messages:vector<message> = Messages;
|
messages total_count:int32 messages:vector<message> = Messages;
|
||||||
@ -1257,7 +1259,8 @@ pageBlockMap location:location zoom:int32 width:int32 height:int32 caption:pageB
|
|||||||
//@version Version of the instant view, currently can be 1 or 2
|
//@version Version of the instant view, currently can be 1 or 2
|
||||||
//@is_rtl True, if the instant view must be shown from right to left
|
//@is_rtl True, if the instant view must be shown from right to left
|
||||||
//@is_full True, if the instant view contains the full page. A network request might be needed to get the full web page instant view
|
//@is_full True, if the instant view contains the full page. A network request might be needed to get the full web page instant view
|
||||||
webPageInstantView page_blocks:vector<PageBlock> view_count:int32 version:int32 is_rtl:Bool is_full:Bool = WebPageInstantView;
|
//@feedback_link An internal link to be opened to leave feedback about the instant view
|
||||||
|
webPageInstantView page_blocks:vector<PageBlock> view_count:int32 version:int32 is_rtl:Bool is_full:Bool feedback_link:InternalLinkType = WebPageInstantView;
|
||||||
|
|
||||||
|
|
||||||
//@description Describes a web page preview
|
//@description Describes a web page preview
|
||||||
@ -1852,6 +1855,9 @@ textEntityTypeTextUrl url:string = TextEntityType;
|
|||||||
//@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user
|
//@description A text shows instead of a raw mention of the user (e.g., when the user has no username) @user_id Identifier of the mentioned user
|
||||||
textEntityTypeMentionName user_id:int32 = TextEntityType;
|
textEntityTypeMentionName user_id:int32 = TextEntityType;
|
||||||
|
|
||||||
|
//@description A media timestamp @media_timestamp Timestamp from which a video/audio/video note/voice note playing should start, in seconds. The media can be in the content or the web page preview of the current message, or in the same places in the replied message
|
||||||
|
textEntityTypeMediaTimestamp media_timestamp:int32 = TextEntityType;
|
||||||
|
|
||||||
|
|
||||||
//@description A thumbnail to be sent along with a file; must be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
|
//@description A thumbnail to be sent along with a file; must be in JPEG or WEBP format for stickers, and less than 200 KB in size @thumbnail Thumbnail file to send. Sending thumbnails by file_id is currently not supported
|
||||||
//@width Thumbnail width, usually shouldn't exceed 320. Use 0 if unknown @height Thumbnail height, usually shouldn't exceed 320. Use 0 if unknown
|
//@width Thumbnail width, usually shouldn't exceed 320. Use 0 if unknown @height Thumbnail height, usually shouldn't exceed 320. Use 0 if unknown
|
||||||
@ -3052,27 +3058,27 @@ internalLinkTypeBotStartInGroup bot_username:string start_parameter:string = Int
|
|||||||
//@description The link is a link to the change phone number section of the app
|
//@description The link is a link to the change phone number section of the app
|
||||||
internalLinkTypeChangePhoneNumber = InternalLinkType;
|
internalLinkTypeChangePhoneNumber = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a chat invite link. Call checkChatInviteLink to process the link
|
//@description The link is a chat invite link. Call checkChatInviteLink with the given invite link to process the link @invite_link Internal representation of the invite link
|
||||||
internalLinkTypeChatInvite = InternalLinkType;
|
internalLinkTypeChatInvite invite_link:string = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a link to the filter settings section of the app
|
//@description The link is a link to the filter settings section of the app
|
||||||
internalLinkTypeFilterSettings = InternalLinkType;
|
internalLinkTypeFilterSettings = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a link to a game. Call searchPublicChat with the given bot username, check that the user is a bot, ask the current user to select a group to send the game, and then call sendMessage with inputMessageGame
|
//@description The link is a link to a game. Call searchPublicChat with the given bot username, check that the user is a bot, ask the current user to select a chat to send the game, and then call sendMessage with inputMessageGame
|
||||||
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
|
//@bot_username Username of the bot that owns the game @game_short_name Short name of the game
|
||||||
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
|
internalLinkTypeGame bot_username:string game_short_name:string = InternalLinkType;
|
||||||
|
|
||||||
//@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
|
//@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;
|
internalLinkTypeLanguagePack language_pack_id:string = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a link to a Telegram message. Call getMessageLinkInfo to process the link
|
//@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 = InternalLinkType;
|
internalLinkTypeMessage url:string = InternalLinkType;
|
||||||
|
|
||||||
//@description The link contains a message draft text. A share screen needs to be shown to the user, then the chosen chat should be open and the text should be added to the input field
|
//@description The link contains a message draft text. A share screen needs to be shown to the user, then the chosen chat should be open and the text should be added to the input field
|
||||||
//@text Message draft text @contains_link True, if the first line of the text contains a link. If true, the input field needs to be focused and the text after the link should be selected
|
//@text Message draft text @contains_link True, if the first line of the text contains a link. If true, the input field needs to be focused and the text after the link should be selected
|
||||||
internalLinkTypeMessageDraft text:formattedText contains_link:Bool = InternalLinkType;
|
internalLinkTypeMessageDraft text:formattedText contains_link:Bool = InternalLinkType;
|
||||||
|
|
||||||
//@description The link contains a request of Telegram passport data. Call getPassportAuthorizationForm to process the link if the link was received outside of the app, otherwise ignore it
|
//@description The link contains a request of Telegram passport data. Call getPassportAuthorizationForm with the given parameters to process the link if the link was received from outside of the app, otherwise ignore it
|
||||||
//@bot_user_id User identifier of the service's bot @scope Telegram Passport element types requested by the service @public_key Service's public key @nonce Unique request identifier provided by the service
|
//@bot_user_id User identifier of the service's bot @scope Telegram Passport element types requested by the service @public_key Service's public key @nonce Unique request identifier provided by the service
|
||||||
//@callback_url An HTTP URL to open once the request is finished or canceled with the parameter tg_passport=success or tg_passport=cancel respectively. If empty, then the link tgbot{bot_user_id}://passport/success or tgbot{bot_user_id}://passport/cancel needs to be opened instead
|
//@callback_url An HTTP URL to open once the request is finished or canceled with the parameter tg_passport=success or tg_passport=cancel respectively. If empty, then the link tgbot{bot_user_id}://passport/success or tgbot{bot_user_id}://passport/cancel needs to be opened instead
|
||||||
internalLinkTypePassportDataRequest bot_user_id:int32 scope:string public_key:string nonce:string callback_url:string = InternalLinkType;
|
internalLinkTypePassportDataRequest bot_user_id:int32 scope:string public_key:string nonce:string callback_url:string = InternalLinkType;
|
||||||
@ -3081,7 +3087,7 @@ internalLinkTypePassportDataRequest bot_user_id:int32 scope:string public_key:st
|
|||||||
//@hash Hash value from the link @phone_number Phone number value from the link
|
//@hash Hash value from the link @phone_number Phone number value from the link
|
||||||
internalLinkTypePhoneNumberConfirmation hash:string phone_number:string = InternalLinkType;
|
internalLinkTypePhoneNumberConfirmation hash:string phone_number:string = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a link to a proxy. Call addProxy to process the link and add the proxy
|
//@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
|
//@server Proxy server IP address @port Proxy server port @type Type of the proxy
|
||||||
internalLinkTypeProxy server:string port:int32 type:ProxyType = InternalLinkType;
|
internalLinkTypeProxy server:string port:int32 type:ProxyType = InternalLinkType;
|
||||||
|
|
||||||
@ -3104,8 +3110,8 @@ internalLinkTypeTheme theme_name:string = InternalLinkType;
|
|||||||
//@description The link is a link to the theme settings section of the app
|
//@description The link is a link to the theme settings section of the app
|
||||||
internalLinkTypeThemeSettings = InternalLinkType;
|
internalLinkTypeThemeSettings = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is an unknown tg: link. Call getDeepLinkInfo to process the link
|
//@description The link is an unknown tg: link. Call getDeepLinkInfo to process the link @link Link to be passed to getDeepLinkInfo
|
||||||
internalLinkTypeUnknownDeepLink = InternalLinkType;
|
internalLinkTypeUnknownDeepLink link:string = InternalLinkType;
|
||||||
|
|
||||||
//@description The link is a link to a voice chat. Call searchPublicChat with the given chat username, and then joinGoupCall with the given invite hash to process the link
|
//@description The link is a link to a voice chat. Call searchPublicChat with the given chat username, and then joinGoupCall with the given invite hash to process the link
|
||||||
//@chat_username Username of the chat with the voice chat @invite_hash If non-empty, invite hash to be used to join the voice chat without being muted by administrators
|
//@chat_username Username of the chat with the voice chat @invite_hash If non-empty, invite hash to be used to join the voice chat without being muted by administrators
|
||||||
@ -3119,7 +3125,7 @@ messageLink link:string is_public:Bool = MessageLink;
|
|||||||
//@is_public True, if the link is a public link for a message in a chat
|
//@is_public True, if the link is a public link for a message in a chat
|
||||||
//@chat_id If found, identifier of the chat to which the message belongs, 0 otherwise
|
//@chat_id If found, identifier of the chat to which the message belongs, 0 otherwise
|
||||||
//@message If found, the linked message; may be null
|
//@message If found, the linked message; may be null
|
||||||
//@media_timestamp Timestamp from which the video/audio/video note/voice note playing should start, in seconds; 0 if not specified. The media can be in the message content or in its link preview
|
//@media_timestamp Timestamp from which the video/audio/video note/voice note playing should start, in seconds; 0 if not specified. The media can be in the message content or in its web page preview
|
||||||
//@for_album True, if the whole media album to which the message belongs is linked
|
//@for_album True, if the whole media album to which the message belongs is linked
|
||||||
//@for_comment True, if the message is linked as a channel post comment or from a message thread
|
//@for_comment True, if the message is linked as a channel post comment or from a message thread
|
||||||
messageLinkInfo is_public:Bool chat_id:int53 message:message media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLinkInfo;
|
messageLinkInfo is_public:Bool chat_id:int53 message:message media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLinkInfo;
|
||||||
@ -3245,7 +3251,7 @@ networkStatistics since_date:int32 entries:vector<NetworkStatisticsEntry> = Netw
|
|||||||
//@use_less_data_for_calls True, if "use less data for calls" option needs to be enabled
|
//@use_less_data_for_calls True, if "use less data for calls" option needs to be enabled
|
||||||
autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int32 max_other_file_size:int32 video_upload_bitrate:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings;
|
autoDownloadSettings is_auto_download_enabled:Bool max_photo_file_size:int32 max_video_file_size:int32 max_other_file_size:int32 video_upload_bitrate:int32 preload_large_videos:Bool preload_next_audio:Bool use_less_data_for_calls:Bool = AutoDownloadSettings;
|
||||||
|
|
||||||
//@description Contains auto-download settings presets for the user
|
//@description Contains auto-download settings presets for the current user
|
||||||
//@low Preset with lowest settings; supposed to be used by default when roaming
|
//@low Preset with lowest settings; supposed to be used by default when roaming
|
||||||
//@medium Preset with medium settings; supposed to be used by default when using mobile data
|
//@medium Preset with medium settings; supposed to be used by default when using mobile data
|
||||||
//@high Preset with highest settings; supposed to be used by default when connected on Wi-Fi
|
//@high Preset with highest settings; supposed to be used by default when connected on Wi-Fi
|
||||||
@ -3926,7 +3932,7 @@ setDatabaseEncryptionKey new_encryption_key:bytes = Ok;
|
|||||||
//@description Returns the current state of 2-step verification
|
//@description Returns the current state of 2-step verification
|
||||||
getPasswordState = PasswordState;
|
getPasswordState = PasswordState;
|
||||||
|
|
||||||
//@description Changes the password for the user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed
|
//@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 should 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 if the recovery email address should be changed @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;
|
setPassword old_password:string new_password:string new_hint:string set_recovery_email_address:Bool new_recovery_email_address:string = PasswordState;
|
||||||
|
|
||||||
@ -4027,10 +4033,10 @@ getChannelDifference channel_difference_id:int64 = Ok;
|
|||||||
getRemoteFile remote_file_id:string file_type:FileType = File;
|
getRemoteFile remote_file_id:string file_type:FileType = File;
|
||||||
|
|
||||||
//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (chat.position.order, chat.id) in descending order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1).
|
//@description Returns an ordered list of chats in a chat list. Chats are sorted by the pair (chat.position.order, chat.id) in descending order. (For example, to get a list of chats from the beginning, the offset_order should be equal to a biggest signed 64-bit number 9223372036854775807 == 2^63 - 1).
|
||||||
//-For optimal performance the number of returned chats is chosen by the library
|
//-For optimal performance, the number of returned chats is chosen by TDLib
|
||||||
//@chat_list The chat list in which to return chats
|
//@chat_list The chat list in which to return chats
|
||||||
//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from
|
//@offset_order Chat order to return chats from @offset_chat_id Chat identifier to return chats from
|
||||||
//@limit The maximum number of chats to be returned. It is possible that fewer chats than the limit are returned even if the end of the list is not reached
|
//@limit The maximum number of chats to be returned. For optimal performance, the number of returned chats is chosen by TDLib and can be smaller than the specified limit, even if the end of the list is not reached
|
||||||
getChats chat_list:ChatList offset_order:int64 offset_chat_id:int53 limit:int32 = Chats;
|
getChats chat_list:ChatList offset_order:int64 offset_chat_id:int53 limit:int32 = Chats;
|
||||||
|
|
||||||
//@description Searches a public chat by its username. Currently only private chats, supergroups and channels can be public. Returns the chat if found; otherwise an error is returned @username Username to be resolved
|
//@description Searches a public chat by its username. Currently only private chats, supergroups and channels can be public. Returns the chat if found; otherwise an error is returned @username Username to be resolved
|
||||||
@ -4039,7 +4045,7 @@ searchPublicChat username:string = Chat;
|
|||||||
//@description Searches public chats by looking for specified query in their username and title. Currently only private chats, supergroups and channels can be public. Returns a meaningful number of results. Returns nothing if the length of the searched username prefix is less than 5. Excludes private chats with contacts and chats from the chat list from the results @query Query to search for
|
//@description Searches public chats by looking for specified query in their username and title. Currently only private chats, supergroups and channels can be public. Returns a meaningful number of results. Returns nothing if the length of the searched username prefix is less than 5. Excludes private chats with contacts and chats from the chat list from the results @query Query to search for
|
||||||
searchPublicChats query:string = Chats;
|
searchPublicChats query:string = Chats;
|
||||||
|
|
||||||
//@description Searches for the specified query in the title and username of already known chats, this is an offline request. Returns chats in the order seen in the main chat list @query Query to search for. If the query is empty, returns up to 20 recently found chats @limit The maximum number of chats to be returned
|
//@description Searches for the specified query in the title and username of already known chats, this is an offline request. Returns chats in the order seen in the main chat list @query Query to search for. If the query is empty, returns up to 50 recently found chats @limit The maximum number of chats to be returned
|
||||||
searchChats query:string limit:int32 = Chats;
|
searchChats query:string limit:int32 = Chats;
|
||||||
|
|
||||||
//@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the main chat list @query Query to search for @limit The maximum number of chats to be returned
|
//@description Searches for the specified query in the title and username of already known chats via request to the server. Returns chats in the order seen in the main chat list @query Query to search for @limit The maximum number of chats to be returned
|
||||||
@ -4084,21 +4090,21 @@ getGroupsInCommon user_id:int32 offset_chat_id:int53 limit:int32 = Chats;
|
|||||||
|
|
||||||
|
|
||||||
//@description Returns messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id).
|
//@description Returns messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id).
|
||||||
//-For optimal performance the number of returned messages is chosen by the library. This is an offline request if only_local is true
|
//-For optimal performance, the number of returned messages is chosen by TDLib. This is an offline request if only_local is true
|
||||||
//@chat_id Chat identifier
|
//@chat_id Chat identifier
|
||||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
//@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
|
//@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. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
//@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 If true, returns only messages that are available locally without sending network requests
|
||||||
getChatHistory chat_id:int53 from_message_id:int53 offset:int32 limit:int32 only_local:Bool = Messages;
|
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.
|
//@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.
|
||||||
//-The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). For optimal performance the number of returned messages is chosen by the library
|
//-The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
|
||||||
//@chat_id Chat identifier
|
//@chat_id Chat identifier
|
||||||
//@message_id Message identifier, which thread history needs to be returned
|
//@message_id Message identifier, which thread history needs to be returned
|
||||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
//@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
|
//@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. Fewer messages may be returned than specified by the limit, even if the end of the message thread history has not been reached
|
//@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
|
||||||
getMessageThreadHistory chat_id:int53 message_id:int53 from_message_id:int53 offset:int32 limit:int32 = Messages;
|
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
|
//@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
|
||||||
@ -4109,41 +4115,41 @@ deleteChatHistory chat_id:int53 remove_from_chat_list:Bool revoke:Bool = Ok;
|
|||||||
deleteChat chat_id:int53 = Ok;
|
deleteChat chat_id:int53 = Ok;
|
||||||
|
|
||||||
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
|
//@description Searches for messages with given words in the chat. Returns the results in reverse chronological order, i.e. in order of decreasing message_id. Cannot be used in secret chats with a non-empty query
|
||||||
//-(searchSecretMessages should be used instead), or without an enabled message database. For optimal performance the number of returned messages is chosen by the library
|
//-(searchSecretMessages should be used instead), or without an enabled message database. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||||
//@chat_id Identifier of the chat in which to search messages
|
//@chat_id Identifier of the chat in which to search messages
|
||||||
//@query Query to search for
|
//@query Query to search for
|
||||||
//@sender If not null, only messages sent by the specified sender will be returned. Not supported in secret chats
|
//@sender If not null, only messages sent by the specified sender will be returned. Not supported in secret chats
|
||||||
//@from_message_id Identifier of the message starting from which history must be fetched; use 0 to get results from the last message
|
//@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 to get the specified message and some newer messages
|
//@offset Specify 0 to get results from exactly the from_message_id or a negative offset to get the specified message and 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 -offset. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
//@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 -offset. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||||
//@filter Filter for message content in the search results
|
//@filter Filter for message content in the search results
|
||||||
//@message_thread_id If not 0, only messages in the specified thread will be returned; supergroups only
|
//@message_thread_id If not 0, only messages in the specified thread will be returned; supergroups only
|
||||||
searchChatMessages chat_id:int53 query:string sender:MessageSender from_message_id:int53 offset:int32 limit:int32 filter:SearchMessagesFilter message_thread_id:int53 = Messages;
|
searchChatMessages chat_id:int53 query:string sender:MessageSender from_message_id:int53 offset:int32 limit:int32 filter:SearchMessagesFilter message_thread_id:int53 = Messages;
|
||||||
|
|
||||||
//@description Searches for messages in all chats except secret chats. Returns the results in reverse chronological order (i.e., in order of decreasing (date, chat_id, message_id)).
|
//@description Searches for messages in all chats except secret chats. Returns the results in reverse chronological order (i.e., in order of decreasing (date, chat_id, message_id)).
|
||||||
//-For optimal performance the number of returned messages is chosen by the library
|
//-For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||||
//@chat_list Chat list in which to search messages; pass null to search in all chats regardless of their chat list. Only Main and Archive chat lists are supported
|
//@chat_list Chat list in which to search messages; pass null to search in all chats regardless of their chat list. Only Main and Archive chat lists are supported
|
||||||
//@query Query to search for
|
//@query Query to search for
|
||||||
//@offset_date The date of the message starting from which the results should be fetched. Use 0 or any date in the future to get results from the last message
|
//@offset_date The date of the message starting from which the results should be fetched. Use 0 or any date in the future to get results from the last message
|
||||||
//@offset_chat_id The chat identifier of the last found message, or 0 for the first request
|
//@offset_chat_id The chat identifier of the last found message, or 0 for the first request
|
||||||
//@offset_message_id The message identifier of the last found message, or 0 for the first request
|
//@offset_message_id The message identifier of the last found message, or 0 for the first request
|
||||||
//@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
//@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
|
||||||
//@filter Filter for message content in the search results; searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention, searchMessagesFilterUnreadMention, searchMessagesFilterFailedToSend and searchMessagesFilterPinned are unsupported in this function
|
//@filter Filter for message content in the search results; searchMessagesFilterCall, searchMessagesFilterMissedCall, searchMessagesFilterMention, searchMessagesFilterUnreadMention, searchMessagesFilterFailedToSend and searchMessagesFilterPinned are unsupported in this function
|
||||||
//@min_date If not 0, the minimum date of the messages to return
|
//@min_date If not 0, the minimum date of the messages to return
|
||||||
//@max_date If not 0, the maximum date of the messages to return
|
//@max_date If not 0, the maximum date of the messages to return
|
||||||
searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:int53 offset_message_id:int53 limit:int32 filter:SearchMessagesFilter min_date:int32 max_date:int32 = Messages;
|
searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:int53 offset_message_id:int53 limit:int32 filter:SearchMessagesFilter min_date:int32 max_date:int32 = Messages;
|
||||||
|
|
||||||
//@description Searches for messages in secret chats. Returns the results in reverse chronological order. For optimal performance the number of returned messages is chosen by the library
|
//@description Searches for messages in secret chats. Returns the results in reverse chronological order. For optimal performance, the number of returned messages is chosen by TDLib
|
||||||
//@chat_id Identifier of the chat in which to search. Specify 0 to search in all secret chats
|
//@chat_id Identifier of the chat in which to search. Specify 0 to search in all secret chats
|
||||||
//@query Query to search for. If empty, searchChatMessages should be used instead
|
//@query Query to search for. If empty, searchChatMessages should be used instead
|
||||||
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
||||||
//@limit The maximum number of messages to be returned; up to 100. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached
|
//@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
|
||||||
//@filter A filter for message content in the search results
|
//@filter A filter for message content in the search results
|
||||||
searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter:SearchMessagesFilter = FoundMessages;
|
searchSecretMessages chat_id:int53 query:string offset:string limit:int32 filter:SearchMessagesFilter = FoundMessages;
|
||||||
|
|
||||||
//@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 the library
|
//@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
|
//@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. Fewer messages may be returned than specified by the limit, even if the end of the message history has not been reached @only_missed If true, returns only messages with missed 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 If true, returns only messages with missed calls
|
||||||
searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages;
|
searchCallMessages from_message_id:int53 limit:int32 only_missed:Bool = Messages;
|
||||||
|
|
||||||
//@description Deletes all call messages @revoke Pass true to delete the messages for all users
|
//@description Deletes all call messages @revoke Pass true to delete the messages for all users
|
||||||
@ -4164,11 +4170,11 @@ getChatMessageCount chat_id:int53 filter:SearchMessagesFilter return_local:Bool
|
|||||||
//@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
|
//@description Returns all scheduled messages in a chat. The messages are returned in a reverse chronological order (i.e., in order of decreasing message_id) @chat_id Chat identifier
|
||||||
getChatScheduledMessages chat_id:int53 = Messages;
|
getChatScheduledMessages chat_id:int53 = Messages;
|
||||||
|
|
||||||
//@description Returns forwarded copies of a channel message to different public channels. For optimal performance the number of returned messages is chosen by the library
|
//@description Returns forwarded copies of a channel message to different public channels. For optimal performance, the number of returned messages is chosen by TDLib
|
||||||
//@chat_id Chat identifier of the message
|
//@chat_id Chat identifier of the message
|
||||||
//@message_id Message identifier
|
//@message_id Message identifier
|
||||||
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
//@offset Offset of the first entry to return as received from the previous request; use empty string to get first chunk of results
|
||||||
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. Fewer messages may be returned than specified by the limit, even if the end of the list has not been reached
|
//@limit The maximum number of messages to be returned; must be positive and can't be greater than 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||||
getMessagePublicForwards chat_id:int53 message_id:int53 offset:string limit:int32 = FoundMessages;
|
getMessagePublicForwards chat_id:int53 message_id:int53 offset:string limit:int32 = FoundMessages;
|
||||||
|
|
||||||
|
|
||||||
@ -4179,10 +4185,10 @@ removeNotification notification_group_id:int32 notification_id:int32 = Ok;
|
|||||||
removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = Ok;
|
removeNotificationGroup notification_group_id:int32 max_notification_id:int32 = Ok;
|
||||||
|
|
||||||
|
|
||||||
//@description Returns an HTTPS link to a message in a chat. Available only for already sent messages in supergroups and channels. This is an offline request
|
//@description Returns an HTTPS link to a message in a chat. Available only for already sent messages in supergroups and channels, or if message.can_get_media_timestamp_links and a media timestamp link is generated. This is an offline request
|
||||||
//@chat_id Identifier of the chat to which the message belongs
|
//@chat_id Identifier of the chat to which the message belongs
|
||||||
//@message_id Identifier of the message
|
//@message_id Identifier of the message
|
||||||
//@media_timestamp If not 0, timestamp from which the video/audio/video note/voice note playing should start, in seconds. The media can be in the message content or in its link preview
|
//@media_timestamp If not 0, timestamp from which the video/audio/video note/voice note playing should start, in seconds. The media can be in the message content or in its web page preview
|
||||||
//@for_album Pass true to create a link for the whole media album
|
//@for_album Pass true to create a link for the whole media album
|
||||||
//@for_comment Pass true to create a link to the message as a channel post comment, or from a message thread
|
//@for_comment Pass true to create a link to the message as a channel post comment, or from a message thread
|
||||||
getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLink;
|
getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bool for_comment:Bool = MessageLink;
|
||||||
@ -4193,7 +4199,7 @@ getMessageLink chat_id:int53 message_id:int53 media_timestamp:int32 for_album:Bo
|
|||||||
//@for_album Pass true to return an HTML code for embedding of the whole media album
|
//@for_album Pass true to return an HTML code for embedding of the whole media album
|
||||||
getMessageEmbeddingCode chat_id:int53 message_id:int53 for_album:Bool = Text;
|
getMessageEmbeddingCode chat_id:int53 message_id:int53 for_album:Bool = Text;
|
||||||
|
|
||||||
//@description Returns information about a public or private message link @url The message link
|
//@description Returns information about a public or private message link. Can be called for any internal link of the type internalLinkTypeMessage @url The message link
|
||||||
getMessageLinkInfo url:string = MessageLinkInfo;
|
getMessageLinkInfo url:string = MessageLinkInfo;
|
||||||
|
|
||||||
|
|
||||||
@ -4341,11 +4347,11 @@ getJsonString json_value:JsonValue = Text;
|
|||||||
//@option_ids 0-based identifiers of answer options, chosen by the user. User can choose more than 1 answer option only is the poll allows multiple answers
|
//@option_ids 0-based identifiers of answer options, chosen by the user. User can choose more than 1 answer option only is the poll allows multiple answers
|
||||||
setPollAnswer chat_id:int53 message_id:int53 option_ids:vector<int32> = Ok;
|
setPollAnswer chat_id:int53 message_id:int53 option_ids:vector<int32> = Ok;
|
||||||
|
|
||||||
//@description Returns users voted for the specified option in a non-anonymous polls. For the optimal performance the number of returned users is chosen by the library
|
//@description Returns users voted for the specified option in a non-anonymous polls. For optimal performance, the number of returned users is chosen by TDLib
|
||||||
//@chat_id Identifier of the chat to which the poll belongs @message_id Identifier of the message containing the poll
|
//@chat_id Identifier of the chat to which the poll belongs @message_id Identifier of the message containing the poll
|
||||||
//@option_id 0-based identifier of the answer option
|
//@option_id 0-based identifier of the answer option
|
||||||
//@offset Number of users to skip in the result; must be non-negative
|
//@offset Number of users to skip in the result; must be non-negative
|
||||||
//@limit The maximum number of users to be returned; must be positive and can't be greater than 50. Fewer users may be returned than specified by the limit, even if the end of the voter list has not been reached
|
//@limit The maximum number of users to be returned; must be positive and can't be greater than 50. For optimal performance, the number of returned users is chosen by TDLib and can be smaller than the specified limit, even if the end of the voter list has not been reached
|
||||||
getPollVoters chat_id:int53 message_id:int53 option_id:int32 offset:int32 limit:int32 = Users;
|
getPollVoters chat_id:int53 message_id:int53 option_id:int32 offset:int32 limit:int32 = Users;
|
||||||
|
|
||||||
//@description Stops a poll. A poll in a message can be stopped when the message has can_be_edited flag set
|
//@description Stops a poll. A poll in a message can be stopped when the message has can_be_edited flag set
|
||||||
@ -4435,7 +4441,7 @@ openMessageContent chat_id:int53 message_id:int53 = Ok;
|
|||||||
//@description Returns information about the type of an internal link. Returns a 404 error if the link is not internal. Can be called before authorization @link The link
|
//@description Returns information about the type of an internal link. Returns a 404 error if the link is not internal. Can be called before authorization @link The link
|
||||||
getInternalLinkType link:string = InternalLinkType;
|
getInternalLinkType link:string = InternalLinkType;
|
||||||
|
|
||||||
//@description Returns information about an action to be done when the current user clicks an external link. Don't use this method for links from secret chats if link preview is disabled in secret chats @link The link
|
//@description Returns information about an action to be done when the current user clicks an external link. Don't use this method for links from secret chats if web page preview is disabled in secret chats @link The link
|
||||||
getExternalLinkInfo link:string = LoginUrlInfo;
|
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
|
//@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
|
||||||
@ -4588,7 +4594,7 @@ setChatMemberStatus chat_id:int53 member_id:MessageSender status:ChatMemberStatu
|
|||||||
//@chat_id Chat identifier
|
//@chat_id Chat identifier
|
||||||
//@member_id Member identifier
|
//@member_id Member identifier
|
||||||
//@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Ignored in basic groups
|
//@banned_until_date Point in time (Unix timestamp) when the user will be unbanned; 0 if never. If the user is banned for more than 366 days or for less than 30 seconds from the current time, the user is considered to be banned forever. Ignored in basic groups
|
||||||
//@revoke_messages Pass true to delete all messages in the chat for the user. Always true for supergroups and channels
|
//@revoke_messages Pass true to delete all messages in the chat for the user that is being removed. Always true for supergroups and channels
|
||||||
banChatMember chat_id:int53 member_id:MessageSender banned_until_date:int32 revoke_messages:Bool = Ok;
|
banChatMember chat_id:int53 member_id:MessageSender banned_until_date:int32 revoke_messages:Bool = Ok;
|
||||||
|
|
||||||
//@description Checks whether the current session can be used to transfer a chat ownership to another user
|
//@description Checks whether the current session can be used to transfer a chat ownership to another user
|
||||||
@ -4945,9 +4951,9 @@ getInstalledStickerSets is_masks:Bool = StickerSets;
|
|||||||
//@description Returns a list of archived sticker sets @is_masks Pass true to return mask stickers sets; pass false to return ordinary sticker sets @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return
|
//@description Returns a list of archived sticker sets @is_masks Pass true to return mask stickers sets; pass false to return ordinary sticker sets @offset_sticker_set_id Identifier of the sticker set from which to return the result @limit The maximum number of sticker sets to return
|
||||||
getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = StickerSets;
|
getArchivedStickerSets is_masks:Bool offset_sticker_set_id:int64 limit:int32 = StickerSets;
|
||||||
|
|
||||||
//@description Returns a list of trending sticker sets. For the optimal performance the number of returned sticker sets is chosen by the library
|
//@description Returns a list of trending sticker sets. For optimal performance, the number of returned sticker sets is chosen by TDLib
|
||||||
//@offset The offset from which to return the sticker sets; must be non-negative
|
//@offset The offset from which to return the sticker sets; must be non-negative
|
||||||
//@limit The maximum number of sticker sets to be returned; must be non-negative. Fewer sticker sets may be returned than specified by the limit, even if the end of the list has not been reached
|
//@limit The maximum number of sticker sets to be returned; must be non-negative. For optimal performance, the number of returned sticker sets is chosen by TDLib and can be smaller than the specified limit, even if the end of the list has not been reached
|
||||||
getTrendingStickerSets offset:int32 limit:int32 = StickerSets;
|
getTrendingStickerSets offset:int32 limit:int32 = StickerSets;
|
||||||
|
|
||||||
//@description Returns a list of sticker sets attached to a file. Currently only photos and videos can have attached sticker sets @file_id File identifier
|
//@description Returns a list of sticker sets attached to a file. Currently only photos and videos can have attached sticker sets @file_id File identifier
|
||||||
@ -5042,7 +5048,7 @@ setProfilePhoto photo:InputChatPhoto = Ok;
|
|||||||
//@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete
|
//@description Deletes a profile photo @profile_photo_id Identifier of the profile photo to delete
|
||||||
deleteProfilePhoto profile_photo_id:int64 = Ok;
|
deleteProfilePhoto profile_photo_id:int64 = Ok;
|
||||||
|
|
||||||
//@description Changes the first and last name of the current user @first_name The new value of the first name for the user; 1-64 characters @last_name The new value of the optional last name for the user; 0-64 characters
|
//@description Changes the first and last name of the current user @first_name The new value of the first name for the current user; 1-64 characters @last_name The new value of the optional last name for the current user; 0-64 characters
|
||||||
setName first_name:string last_name:string = Ok;
|
setName first_name:string last_name:string = Ok;
|
||||||
|
|
||||||
//@description Changes the bio of the current user @bio The new value of the user bio; 0-70 characters without line feeds
|
//@description Changes the bio of the current user @bio The new value of the user bio; 0-70 characters without line feeds
|
||||||
@ -5058,7 +5064,7 @@ setLocation location:location = Ok;
|
|||||||
//@phone_number The new phone number of the user in international format @settings Settings for the authentication of the user's phone number
|
//@phone_number The new phone number of the user in international format @settings Settings for the authentication of the user's phone number
|
||||||
changePhoneNumber phone_number:string settings:phoneNumberAuthenticationSettings = AuthenticationCodeInfo;
|
changePhoneNumber phone_number:string settings:phoneNumberAuthenticationSettings = AuthenticationCodeInfo;
|
||||||
|
|
||||||
//@description Re-sends the authentication code sent to confirm a new phone number for the user. Works only if the previously received authenticationCodeInfo next_code_type was not null and the server-specified timeout has passed
|
//@description Re-sends the authentication code sent to confirm a new phone number for the current user. Works only if the previously received authenticationCodeInfo next_code_type was not null and the server-specified timeout has passed
|
||||||
resendChangePhoneNumberCode = AuthenticationCodeInfo;
|
resendChangePhoneNumberCode = AuthenticationCodeInfo;
|
||||||
|
|
||||||
//@description Checks the authentication code sent to confirm a new phone number of the user @code Verification code received by SMS, phone call or flash call
|
//@description Checks the authentication code sent to confirm a new phone number of the user @code Verification code received by SMS, phone call or flash call
|
||||||
|
@ -1485,7 +1485,8 @@ void ConfigManager::process_app_config(tl_object_ptr<telegram_api::JSONValue> &c
|
|||||||
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
|
for (auto &key_value : static_cast<telegram_api::jsonObject *>(config.get())->value_) {
|
||||||
Slice key = key_value->key_;
|
Slice key = key_value->key_;
|
||||||
telegram_api::JSONValue *value = key_value->value_.get();
|
telegram_api::JSONValue *value = key_value->value_.get();
|
||||||
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config") {
|
if (key == "test" || key == "wallet_enabled" || key == "wallet_blockchain_name" || key == "wallet_config" ||
|
||||||
|
key == "stickers_emoji_cache_time") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (key == "ignore_restriction_reasons") {
|
if (key == "ignore_restriction_reasons") {
|
||||||
|
@ -9540,7 +9540,9 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s
|
|||||||
|
|
||||||
Dependencies dependencies;
|
Dependencies dependencies;
|
||||||
dependencies.channel_ids.insert(channel_id);
|
dependencies.channel_ids.insert(channel_id);
|
||||||
add_dialog_and_dependencies(dependencies, DialogId(channel_full->linked_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.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->bot_user_ids.begin(), channel_full->bot_user_ids.end());
|
||||||
dependencies.user_ids.insert(channel_full->invite_link.get_creator_user_id());
|
dependencies.user_ids.insert(channel_full->invite_link.get_creator_user_id());
|
||||||
|
@ -240,8 +240,9 @@ void CountryInfoManager::do_get_phone_number_info(string phone_number_prefix, st
|
|||||||
result += pattern[current_pattern_pos++];
|
result += pattern[current_pattern_pos++];
|
||||||
}
|
}
|
||||||
if (current_pattern_pos == pattern.size()) {
|
if (current_pattern_pos == pattern.size()) {
|
||||||
result += c;
|
result += ' ';
|
||||||
} else if (pattern[current_pattern_pos] == 'X') {
|
}
|
||||||
|
if (current_pattern_pos >= pattern.size() || pattern[current_pattern_pos] == 'X') {
|
||||||
result += c;
|
result += c;
|
||||||
current_pattern_pos++;
|
current_pattern_pos++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,13 +45,13 @@ unique_ptr<DraftMessage> get_draft_message(ContactsManager *contacts_manager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto entities = get_message_entities(contacts_manager, std::move(draft->entities_), "draftMessage");
|
auto entities = get_message_entities(contacts_manager, std::move(draft->entities_), "draftMessage");
|
||||||
auto status = fix_formatted_text(draft->message_, entities, true, true, true, true);
|
auto status = fix_formatted_text(draft->message_, entities, true, true, true, true, true);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
LOG(ERROR) << "Receive error " << status << " while parsing draft " << draft->message_;
|
LOG(ERROR) << "Receive error " << status << " while parsing draft " << draft->message_;
|
||||||
if (!clean_input_string(draft->message_)) {
|
if (!clean_input_string(draft->message_)) {
|
||||||
draft->message_.clear();
|
draft->message_.clear();
|
||||||
}
|
}
|
||||||
entities = find_entities(draft->message_, false);
|
entities = find_entities(draft->message_, false, true);
|
||||||
}
|
}
|
||||||
result->input_message_text.text = FormattedText{std::move(draft->message_), std::move(entities)};
|
result->input_message_text.text = FormattedText{std::move(draft->message_), std::move(entities)};
|
||||||
result->input_message_text.disable_web_page_preview = (flags & telegram_api::draftMessage::NO_WEBPAGE_MASK) != 0;
|
result->input_message_text.disable_web_page_preview = (flags & telegram_api::draftMessage::NO_WEBPAGE_MASK) != 0;
|
||||||
|
@ -93,7 +93,7 @@ const FormattedText &Game::get_text() const {
|
|||||||
|
|
||||||
tl_object_ptr<td_api::game> Game::get_game_object(Td *td, bool skip_bot_commands) const {
|
tl_object_ptr<td_api::game> Game::get_game_object(Td *td, bool skip_bot_commands) const {
|
||||||
return make_tl_object<td_api::game>(
|
return make_tl_object<td_api::game>(
|
||||||
id_, short_name_, title_, get_formatted_text_object(text_, skip_bot_commands), description_,
|
id_, short_name_, title_, get_formatted_text_object(text_, skip_bot_commands, -1), description_,
|
||||||
get_photo_object(td->file_manager_.get(), photo_),
|
get_photo_object(td->file_manager_.get(), photo_),
|
||||||
td->animations_manager_->get_animation_object(animation_file_id_, "get_game_object"));
|
td->animations_manager_->get_animation_object(animation_file_id_, "get_game_object"));
|
||||||
}
|
}
|
||||||
|
@ -39,22 +39,24 @@ Result<InputMessageText> process_input_message_text(const ContactsManager *conta
|
|||||||
}
|
}
|
||||||
|
|
||||||
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_)));
|
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(input_message_text->text_->entities_)));
|
||||||
auto need_skip_commands = need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot);
|
auto need_skip_bot_commands = need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot);
|
||||||
bool parse_markdown = G()->shared_config().get_option_boolean("always_parse_markdown");
|
bool parse_markdown = G()->shared_config().get_option_boolean("always_parse_markdown");
|
||||||
TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, parse_markdown,
|
TRY_STATUS(fix_formatted_text(input_message_text->text_->text_, entities, for_draft, parse_markdown,
|
||||||
need_skip_commands, for_draft));
|
need_skip_bot_commands, is_bot || for_draft || parse_markdown, for_draft));
|
||||||
InputMessageText result{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)},
|
InputMessageText result{FormattedText{std::move(input_message_text->text_->text_), std::move(entities)},
|
||||||
input_message_text->disable_web_page_preview_, input_message_text->clear_draft_};
|
input_message_text->disable_web_page_preview_, input_message_text->clear_draft_};
|
||||||
if (G()->shared_config().get_option_boolean("always_parse_markdown")) {
|
if (parse_markdown) {
|
||||||
result.text = parse_markdown_v3(std::move(result.text));
|
result.text = parse_markdown_v3(std::move(result.text));
|
||||||
fix_formatted_text(result.text.text, result.text.entities, for_draft, false, need_skip_commands, for_draft)
|
fix_formatted_text(result.text.text, result.text.entities, for_draft, false, need_skip_bot_commands,
|
||||||
|
is_bot || for_draft, for_draft)
|
||||||
.ensure();
|
.ensure();
|
||||||
}
|
}
|
||||||
return std::move(result);
|
return std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used only for draft
|
||||||
td_api::object_ptr<td_api::inputMessageText> get_input_message_text_object(const InputMessageText &input_message_text) {
|
td_api::object_ptr<td_api::inputMessageText> get_input_message_text_object(const InputMessageText &input_message_text) {
|
||||||
return td_api::make_object<td_api::inputMessageText>(get_formatted_text_object(input_message_text.text, false),
|
return td_api::make_object<td_api::inputMessageText>(get_formatted_text_object(input_message_text.text, false, -1),
|
||||||
input_message_text.disable_web_page_preview,
|
input_message_text.disable_web_page_preview,
|
||||||
input_message_text.clear_draft);
|
input_message_text.clear_draft);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "td/telegram/MessageEntity.h"
|
#include "td/telegram/MessageEntity.h"
|
||||||
#include "td/telegram/MessageId.h"
|
#include "td/telegram/MessageId.h"
|
||||||
#include "td/telegram/MessagesManager.h"
|
#include "td/telegram/MessagesManager.h"
|
||||||
|
#include "td/telegram/misc.h"
|
||||||
#include "td/telegram/ServerMessageId.h"
|
#include "td/telegram/ServerMessageId.h"
|
||||||
#include "td/telegram/Td.h"
|
#include "td/telegram/Td.h"
|
||||||
#include "td/telegram/TdDb.h"
|
#include "td/telegram/TdDb.h"
|
||||||
@ -64,6 +65,26 @@ static bool is_valid_username(Slice username) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string get_url_query_hash(bool is_tg, const HttpUrlQuery &url_query) {
|
||||||
|
const auto &path = url_query.path_;
|
||||||
|
if (is_tg) {
|
||||||
|
if (path.size() == 1 && path[0] == "join" && !url_query.get_arg("invite").empty()) {
|
||||||
|
// join?invite=abcdef
|
||||||
|
return url_query.get_arg("invite").str();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (path.size() >= 2 && path[0] == "joinchat" && !path[1].empty()) {
|
||||||
|
// /joinchat/<link>
|
||||||
|
return path[1];
|
||||||
|
}
|
||||||
|
if (path.size() >= 1 && path[0].size() >= 2 && (path[0][0] == ' ' || path[0][0] == '+')) {
|
||||||
|
// /+<link>
|
||||||
|
return path[0].substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
class LinkManager::InternalLinkActiveSessions final : public InternalLink {
|
class LinkManager::InternalLinkActiveSessions final : public InternalLink {
|
||||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||||
return td_api::make_object<td_api::internalLinkTypeActiveSessions>();
|
return td_api::make_object<td_api::internalLinkTypeActiveSessions>();
|
||||||
@ -143,8 +164,14 @@ class LinkManager::InternalLinkConfirmPhone final : public InternalLink {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class LinkManager::InternalLinkDialogInvite final : public InternalLink {
|
class LinkManager::InternalLinkDialogInvite final : public InternalLink {
|
||||||
|
string url_;
|
||||||
|
|
||||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||||
return td_api::make_object<td_api::internalLinkTypeChatInvite>();
|
return td_api::make_object<td_api::internalLinkTypeChatInvite>(url_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit InternalLinkDialogInvite(string url) : url_(std::move(url)) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -181,8 +208,14 @@ class LinkManager::InternalLinkLanguage final : public InternalLink {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class LinkManager::InternalLinkMessage final : public InternalLink {
|
class LinkManager::InternalLinkMessage final : public InternalLink {
|
||||||
|
string url_;
|
||||||
|
|
||||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||||
return td_api::make_object<td_api::internalLinkTypeMessage>();
|
return td_api::make_object<td_api::internalLinkTypeMessage>(url_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit InternalLinkMessage(string url) : url_(std::move(url)) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -191,7 +224,7 @@ class LinkManager::InternalLinkMessageDraft final : public InternalLink {
|
|||||||
bool contains_link_ = false;
|
bool contains_link_ = false;
|
||||||
|
|
||||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||||
return td_api::make_object<td_api::internalLinkTypeMessageDraft>(get_formatted_text_object(text_, true),
|
return td_api::make_object<td_api::internalLinkTypeMessageDraft>(get_formatted_text_object(text_, true, -1),
|
||||||
contains_link_);
|
contains_link_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,8 +343,14 @@ class LinkManager::InternalLinkThemeSettings final : public InternalLink {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class LinkManager::InternalLinkUnknownDeepLink final : public InternalLink {
|
class LinkManager::InternalLinkUnknownDeepLink final : public InternalLink {
|
||||||
|
string link_;
|
||||||
|
|
||||||
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
td_api::object_ptr<td_api::InternalLinkType> get_internal_link_type_object() const final {
|
||||||
return td_api::make_object<td_api::internalLinkTypeUnknownDeepLink>();
|
return td_api::make_object<td_api::internalLinkTypeUnknownDeepLink>(link_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit InternalLinkUnknownDeepLink(string link) : link_(std::move(link)) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -329,6 +368,55 @@ class LinkManager::InternalLinkVoiceChat final : public InternalLink {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GetDeepLinkInfoQuery final : public Td::ResultHandler {
|
||||||
|
Promise<td_api::object_ptr<td_api::deepLinkInfo>> promise_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GetDeepLinkInfoQuery(Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise)
|
||||||
|
: promise_(std::move(promise)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(Slice link) {
|
||||||
|
send_query(G()->net_query_creator().create_unauth(telegram_api::help_getDeepLinkInfo(link.str())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_result(uint64 id, BufferSlice packet) final {
|
||||||
|
auto result_ptr = fetch_result<telegram_api::help_getDeepLinkInfo>(packet);
|
||||||
|
if (result_ptr.is_error()) {
|
||||||
|
return on_error(id, result_ptr.move_as_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = result_ptr.move_as_ok();
|
||||||
|
switch (result->get_id()) {
|
||||||
|
case telegram_api::help_deepLinkInfoEmpty::ID:
|
||||||
|
return promise_.set_value(nullptr);
|
||||||
|
case telegram_api::help_deepLinkInfo::ID: {
|
||||||
|
auto info = telegram_api::move_object_as<telegram_api::help_deepLinkInfo>(result);
|
||||||
|
bool need_update = (info->flags_ & telegram_api::help_deepLinkInfo::UPDATE_APP_MASK) != 0;
|
||||||
|
|
||||||
|
auto entities = get_message_entities(nullptr, std::move(info->entities_), "GetDeepLinkInfoQuery");
|
||||||
|
auto status = fix_formatted_text(info->message_, entities, true, true, true, true, true);
|
||||||
|
if (status.is_error()) {
|
||||||
|
LOG(ERROR) << "Receive error " << status << " while parsing deep link info " << info->message_;
|
||||||
|
if (!clean_input_string(info->message_)) {
|
||||||
|
info->message_.clear();
|
||||||
|
}
|
||||||
|
entities = find_entities(info->message_, true, true);
|
||||||
|
}
|
||||||
|
FormattedText text{std::move(info->message_), std::move(entities)};
|
||||||
|
return promise_.set_value(
|
||||||
|
td_api::make_object<td_api::deepLinkInfo>(get_formatted_text_object(text, true, -1), need_update));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(uint64 id, Status status) final {
|
||||||
|
promise_.set_error(std::move(status));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class RequestUrlAuthQuery final : public Td::ResultHandler {
|
class RequestUrlAuthQuery final : public Td::ResultHandler {
|
||||||
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> promise_;
|
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> promise_;
|
||||||
string url_;
|
string url_;
|
||||||
@ -637,6 +725,13 @@ struct CopyArg {
|
|||||||
StringBuilder &operator<<(StringBuilder &string_builder, const CopyArg ©_arg) {
|
StringBuilder &operator<<(StringBuilder &string_builder, const CopyArg ©_arg) {
|
||||||
auto arg = copy_arg.url_query_->get_arg(copy_arg.name_);
|
auto arg = copy_arg.url_query_->get_arg(copy_arg.name_);
|
||||||
if (arg.empty()) {
|
if (arg.empty()) {
|
||||||
|
for (const auto &query_arg : copy_arg.url_query_->args_) {
|
||||||
|
if (query_arg.first == copy_arg.name_) {
|
||||||
|
char c = *copy_arg.is_first_ ? '?' : '&';
|
||||||
|
*copy_arg.is_first_ = false;
|
||||||
|
return string_builder << c << copy_arg.name_;
|
||||||
|
}
|
||||||
|
}
|
||||||
return string_builder;
|
return string_builder;
|
||||||
}
|
}
|
||||||
char c = *copy_arg.is_first_ ? '?' : '&';
|
char c = *copy_arg.is_first_ ? '?' : '&';
|
||||||
@ -666,8 +761,10 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
if (path.size() == 1 && path[0] == "resolve") {
|
if (path.size() == 1 && path[0] == "resolve") {
|
||||||
if (is_valid_username(get_arg("domain"))) {
|
if (is_valid_username(get_arg("domain"))) {
|
||||||
if (has_arg("post")) {
|
if (has_arg("post")) {
|
||||||
// resolve?domain=<username>&post=12345&single
|
// resolve?domain=<username>&post=12345&single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
|
||||||
return td::make_unique<InternalLinkMessage>();
|
return td::make_unique<InternalLinkMessage>(PSTRING() << "tg:resolve" << copy_arg("domain") << copy_arg("post")
|
||||||
|
<< copy_arg("single") << copy_arg("thread")
|
||||||
|
<< copy_arg("comment") << copy_arg("t"));
|
||||||
}
|
}
|
||||||
auto username = get_arg("domain");
|
auto username = get_arg("domain");
|
||||||
for (auto &arg : url_query.args_) {
|
for (auto &arg : url_query.args_) {
|
||||||
@ -694,7 +791,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
}
|
}
|
||||||
if (username == "telegrampassport") {
|
if (username == "telegrampassport") {
|
||||||
// resolve?domain=telegrampassport&bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
|
// resolve?domain=telegrampassport&bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
|
||||||
return get_internal_link_passport(url_query.args_);
|
return get_internal_link_passport(query, url_query.args_);
|
||||||
}
|
}
|
||||||
// resolve?domain=<username>
|
// resolve?domain=<username>
|
||||||
return td::make_unique<InternalLinkPublicDialog>(std::move(username));
|
return td::make_unique<InternalLinkPublicDialog>(std::move(username));
|
||||||
@ -710,7 +807,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
}
|
}
|
||||||
} else if (path.size() == 1 && path[0] == "passport") {
|
} else if (path.size() == 1 && path[0] == "passport") {
|
||||||
// passport?bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
|
// passport?bot_id=<bot_user_id>&scope=<scope>&public_key=<public_key>&nonce=<nonce>
|
||||||
return get_internal_link_passport(url_query.args_);
|
return get_internal_link_passport(query, url_query.args_);
|
||||||
} else if (path.size() >= 1 && path[0] == "settings") {
|
} else if (path.size() >= 1 && path[0] == "settings") {
|
||||||
if (path.size() == 2 && path[1] == "change_number") {
|
if (path.size() == 2 && path[1] == "change_number") {
|
||||||
// settings/change_number
|
// settings/change_number
|
||||||
@ -733,7 +830,8 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
} else if (path.size() == 1 && path[0] == "join") {
|
} else if (path.size() == 1 && path[0] == "join") {
|
||||||
// join?invite=<hash>
|
// join?invite=<hash>
|
||||||
if (has_arg("invite")) {
|
if (has_arg("invite")) {
|
||||||
return td::make_unique<InternalLinkDialogInvite>();
|
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
|
||||||
|
<< url_encode(get_url_query_hash(true, url_query)));
|
||||||
}
|
}
|
||||||
} else if (path.size() == 1 && path[0] == "addstickers") {
|
} else if (path.size() == 1 && path[0] == "addstickers") {
|
||||||
// addstickers?set=<name>
|
// addstickers?set=<name>
|
||||||
@ -774,9 +872,11 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (path.size() == 1 && path[0] == "privatepost") {
|
} else if (path.size() == 1 && path[0] == "privatepost") {
|
||||||
// privatepost?channel=123456789&msg_id=12345
|
// privatepost?channel=123456789&msg_id=12345&single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
|
||||||
if (has_arg("channel") && has_arg("msg_id")) {
|
if (has_arg("channel") && has_arg("msg_id")) {
|
||||||
return td::make_unique<InternalLinkMessage>();
|
return td::make_unique<InternalLinkMessage>(
|
||||||
|
PSTRING() << "tg:privatepost" << copy_arg("channel") << copy_arg("msg_id") << copy_arg("single")
|
||||||
|
<< copy_arg("thread") << copy_arg("comment") << copy_arg("t"));
|
||||||
}
|
}
|
||||||
} else if (path.size() == 1 && path[0] == "bg") {
|
} else if (path.size() == 1 && path[0] == "bg") {
|
||||||
// bg?color=<color>
|
// bg?color=<color>
|
||||||
@ -801,7 +901,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
|
|||||||
return get_internal_link_message_draft(get_arg("url"), get_arg("text"));
|
return get_internal_link_message_draft(get_arg("url"), get_arg("text"));
|
||||||
}
|
}
|
||||||
if (!path.empty() && !path[0].empty()) {
|
if (!path.empty() && !path[0].empty()) {
|
||||||
return td::make_unique<InternalLinkUnknownDeepLink>();
|
return td::make_unique<InternalLinkUnknownDeepLink>(PSTRING() << "tg://" << query);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -828,8 +928,12 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
|
|||||||
|
|
||||||
if (path[0] == "c") {
|
if (path[0] == "c") {
|
||||||
if (path.size() >= 3 && to_integer<int64>(path[1]) > 0 && to_integer<int64>(path[2]) > 0) {
|
if (path.size() >= 3 && to_integer<int64>(path[1]) > 0 && to_integer<int64>(path[2]) > 0) {
|
||||||
// /c/123456789/12345
|
// /c/123456789/12345?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
|
||||||
return td::make_unique<InternalLinkMessage>();
|
is_first_arg = false;
|
||||||
|
return td::make_unique<InternalLinkMessage>(PSTRING()
|
||||||
|
<< "tg:privatepost?channel=" << to_integer<int64>(path[1])
|
||||||
|
<< "&msg_id=" << to_integer<int64>(path[2]) << copy_arg("single")
|
||||||
|
<< copy_arg("thread") << copy_arg("comment") << copy_arg("t"));
|
||||||
}
|
}
|
||||||
} else if (path[0] == "login") {
|
} else if (path[0] == "login") {
|
||||||
if (path.size() >= 2 && !path[1].empty()) {
|
if (path.size() >= 2 && !path[1].empty()) {
|
||||||
@ -839,12 +943,14 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
|
|||||||
} else if (path[0] == "joinchat") {
|
} else if (path[0] == "joinchat") {
|
||||||
if (path.size() >= 2 && !path[1].empty()) {
|
if (path.size() >= 2 && !path[1].empty()) {
|
||||||
// /joinchat/<link>
|
// /joinchat/<link>
|
||||||
return td::make_unique<InternalLinkDialogInvite>();
|
return td::make_unique<InternalLinkDialogInvite>(PSTRING() << "tg:join?invite="
|
||||||
|
<< url_encode(get_url_query_hash(false, url_query)));
|
||||||
}
|
}
|
||||||
} else if (path[0][0] == ' ' || path[0][0] == '+') {
|
} else if (path[0][0] == ' ' || path[0][0] == '+') {
|
||||||
if (path[0].size() >= 2) {
|
if (path[0].size() >= 2) {
|
||||||
// /+<link>
|
// /+<link>
|
||||||
return td::make_unique<InternalLinkDialogInvite>();
|
return td::make_unique<InternalLinkDialogInvite>(
|
||||||
|
PSTRING() << "tg:join?invite=" + url_encode(get_url_query_hash(false, url_query)));
|
||||||
}
|
}
|
||||||
} else if (path[0] == "addstickers") {
|
} else if (path[0] == "addstickers") {
|
||||||
if (path.size() >= 2 && !path[1].empty()) {
|
if (path.size() >= 2 && !path[1].empty()) {
|
||||||
@ -903,8 +1009,11 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
|
|||||||
}
|
}
|
||||||
} else if (is_valid_username(path[0])) {
|
} else if (is_valid_username(path[0])) {
|
||||||
if (path.size() >= 2 && to_integer<int64>(path[1]) > 0) {
|
if (path.size() >= 2 && to_integer<int64>(path[1]) > 0) {
|
||||||
// /<username>/12345?single&thread=<thread_id>&comment=<message_id>
|
// /<username>/12345?single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
|
||||||
return td::make_unique<InternalLinkMessage>();
|
is_first_arg = false;
|
||||||
|
return td::make_unique<InternalLinkMessage>(
|
||||||
|
PSTRING() << "tg:resolve?domain=" << url_encode(path[0]) << "&post=" << to_integer<int64>(path[1])
|
||||||
|
<< copy_arg("single") << copy_arg("thread") << copy_arg("comment") << copy_arg("t"));
|
||||||
}
|
}
|
||||||
auto username = path[0];
|
auto username = path[0];
|
||||||
for (auto &arg : url_query.args_) {
|
for (auto &arg : url_query.args_) {
|
||||||
@ -955,7 +1064,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_message_dra
|
|||||||
} else {
|
} else {
|
||||||
full_text.text = url.str();
|
full_text.text = url.str();
|
||||||
}
|
}
|
||||||
if (fix_formatted_text(full_text.text, full_text.entities, false, false, false, true).is_error()) {
|
if (fix_formatted_text(full_text.text, full_text.entities, false, false, false, true, true).is_error()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (full_text.text[0] == '@') {
|
if (full_text.text[0] == '@') {
|
||||||
@ -968,7 +1077,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_message_dra
|
|||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_passport(
|
unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_passport(
|
||||||
const vector<std::pair<string, string>> &args) {
|
Slice query, const vector<std::pair<string, string>> &args) {
|
||||||
auto get_arg = [&args](Slice key) {
|
auto get_arg = [&args](Slice key) {
|
||||||
for (auto &arg : args) {
|
for (auto &arg : args) {
|
||||||
if (arg.first == key) {
|
if (arg.first == key) {
|
||||||
@ -988,7 +1097,7 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_passport(
|
|||||||
auto callback_url = get_arg("callback_url");
|
auto callback_url = get_arg("callback_url");
|
||||||
|
|
||||||
if (!bot_user_id.is_valid() || scope.empty() || public_key.empty() || nonce.empty()) {
|
if (!bot_user_id.is_valid() || scope.empty() || public_key.empty() || nonce.empty()) {
|
||||||
return td::make_unique<InternalLinkUnknownDeepLink>();
|
return td::make_unique<InternalLinkUnknownDeepLink>(PSTRING() << "tg://" << query);
|
||||||
}
|
}
|
||||||
return td::make_unique<InternalLinkPassportDataRequest>(bot_user_id, scope.str(), public_key.str(), nonce.str(),
|
return td::make_unique<InternalLinkPassportDataRequest>(bot_user_id, scope.str(), public_key.str(), nonce.str(),
|
||||||
callback_url.str());
|
callback_url.str());
|
||||||
@ -1008,6 +1117,22 @@ void LinkManager::update_autologin_domains(string autologin_token, vector<string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LinkManager::get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise) {
|
||||||
|
Slice link_scheme("tg:");
|
||||||
|
if (begins_with(link, link_scheme)) {
|
||||||
|
link.remove_prefix(link_scheme.size());
|
||||||
|
if (begins_with(link, "//")) {
|
||||||
|
link.remove_prefix(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < link.size() && link[pos] != '/' && link[pos] != '?' && link[pos] != '#') {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
link.truncate(pos);
|
||||||
|
td_->create_handler<GetDeepLinkInfoQuery>(std::move(promise))->send(link);
|
||||||
|
}
|
||||||
|
|
||||||
void LinkManager::get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise) {
|
void LinkManager::get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise) {
|
||||||
auto default_result = td_api::make_object<td_api::loginUrlInfoOpen>(link, false);
|
auto default_result = td_api::make_object<td_api::loginUrlInfoOpen>(link, false);
|
||||||
if (G()->close_flag()) {
|
if (G()->close_flag()) {
|
||||||
@ -1091,24 +1216,7 @@ string LinkManager::get_dialog_invite_link_hash(Slice invite_link) {
|
|||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
const auto url_query = parse_url_query(link_info.query_);
|
const auto url_query = parse_url_query(link_info.query_);
|
||||||
const auto &path = url_query.path_;
|
return get_url_query_hash(link_info.is_tg_, url_query);
|
||||||
|
|
||||||
if (link_info.is_tg_) {
|
|
||||||
if (path.size() == 1 && path[0] == "join" && !url_query.get_arg("invite").empty()) {
|
|
||||||
// join?invite=abcdef
|
|
||||||
return url_query.get_arg("invite").str();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (path.size() >= 2 && path[0] == "joinchat" && !path[1].empty()) {
|
|
||||||
// /joinchat/<link>
|
|
||||||
return path[1];
|
|
||||||
}
|
|
||||||
if (path.size() >= 1 && path[0].size() >= 2 && (path[0][0] == ' ' || path[0][0] == '+')) {
|
|
||||||
// /+<link>
|
|
||||||
return path[0].substr(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
|
Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
|
||||||
@ -1129,8 +1237,8 @@ Result<MessageLinkInfo> LinkManager::get_message_link_info(Slice url) {
|
|||||||
bool is_single = false;
|
bool is_single = false;
|
||||||
bool for_comment = false;
|
bool for_comment = false;
|
||||||
if (link_info.is_tg_) {
|
if (link_info.is_tg_) {
|
||||||
// resolve?domain=username&post=12345&single
|
// resolve?domain=username&post=12345&single&t=123&comment=12&thread=21
|
||||||
// privatepost?channel=123456789&msg_id=12345
|
// privatepost?channel=123456789&msg_id=12345&single&t=123&comment=12&thread=21
|
||||||
|
|
||||||
bool is_resolve = false;
|
bool is_resolve = false;
|
||||||
if (begins_with(url, "resolve")) {
|
if (begins_with(url, "resolve")) {
|
||||||
|
@ -54,6 +54,8 @@ class LinkManager final : public Actor {
|
|||||||
void update_autologin_domains(string autologin_token, vector<string> autologin_domains,
|
void update_autologin_domains(string autologin_token, vector<string> autologin_domains,
|
||||||
vector<string> url_auth_domains);
|
vector<string> url_auth_domains);
|
||||||
|
|
||||||
|
void get_deep_link_info(Slice link, Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise);
|
||||||
|
|
||||||
void get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise);
|
void get_external_link_info(string &&link, Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise);
|
||||||
|
|
||||||
void get_login_url_info(FullMessageId full_message_id, int32 button_id,
|
void get_login_url_info(FullMessageId full_message_id, int32 button_id,
|
||||||
@ -110,7 +112,8 @@ class LinkManager final : public Actor {
|
|||||||
|
|
||||||
static unique_ptr<InternalLink> parse_t_me_link_query(Slice query);
|
static unique_ptr<InternalLink> parse_t_me_link_query(Slice query);
|
||||||
|
|
||||||
static unique_ptr<InternalLink> get_internal_link_passport(const vector<std::pair<string, string>> &args);
|
static unique_ptr<InternalLink> get_internal_link_passport(Slice query,
|
||||||
|
const vector<std::pair<string, string>> &args);
|
||||||
|
|
||||||
static unique_ptr<InternalLink> get_internal_link_message_draft(Slice url, Slice text);
|
static unique_ptr<InternalLink> get_internal_link_message_draft(Slice url, Slice text);
|
||||||
|
|
||||||
|
@ -1030,7 +1030,7 @@ static void parse_caption(FormattedText &caption, ParserT &parser) {
|
|||||||
if (!check_utf8(caption.text)) {
|
if (!check_utf8(caption.text)) {
|
||||||
caption.text.clear();
|
caption.text.clear();
|
||||||
}
|
}
|
||||||
caption.entities = find_entities(caption.text, false);
|
caption.entities = find_entities(caption.text, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1461,7 +1461,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
|
|||||||
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageText>(bot_inline_message);
|
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageText>(bot_inline_message);
|
||||||
auto entities = get_message_entities(td->contacts_manager_.get(), std::move(inline_message->entities_),
|
auto entities = get_message_entities(td->contacts_manager_.get(), std::move(inline_message->entities_),
|
||||||
"botInlineMessageText");
|
"botInlineMessageText");
|
||||||
auto status = fix_formatted_text(inline_message->message_, entities, false, true, true, false);
|
auto status = fix_formatted_text(inline_message->message_, entities, false, true, true, false, false);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
LOG(ERROR) << "Receive error " << status << " while parsing botInlineMessageText " << inline_message->message_;
|
LOG(ERROR) << "Receive error " << status << " while parsing botInlineMessageText " << inline_message->message_;
|
||||||
break;
|
break;
|
||||||
@ -1525,7 +1525,7 @@ InlineMessageContent create_inline_message_content(Td *td, FileId file_id,
|
|||||||
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageMediaAuto>(bot_inline_message);
|
auto inline_message = move_tl_object_as<telegram_api::botInlineMessageMediaAuto>(bot_inline_message);
|
||||||
auto caption =
|
auto caption =
|
||||||
get_message_text(td->contacts_manager_.get(), inline_message->message_, std::move(inline_message->entities_),
|
get_message_text(td->contacts_manager_.get(), inline_message->message_, std::move(inline_message->entities_),
|
||||||
true, 0, false, "create_inline_message_content");
|
true, false, 0, false, "create_inline_message_content");
|
||||||
if (allowed_media_content_id == td_api::inputMessageAnimation::ID) {
|
if (allowed_media_content_id == td_api::inputMessageAnimation::ID) {
|
||||||
result.message_content = make_unique<MessageAnimation>(file_id, std::move(caption));
|
result.message_content = make_unique<MessageAnimation>(file_id, std::move(caption));
|
||||||
} else if (allowed_media_content_id == td_api::inputMessageAudio::ID) {
|
} else if (allowed_media_content_id == td_api::inputMessageAudio::ID) {
|
||||||
@ -2911,12 +2911,14 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
|||||||
case MessageContentType::Text: {
|
case MessageContentType::Text: {
|
||||||
auto old_ = static_cast<const MessageText *>(old_content);
|
auto old_ = static_cast<const MessageText *>(old_content);
|
||||||
auto new_ = static_cast<const MessageText *>(new_content);
|
auto new_ = static_cast<const MessageText *>(new_content);
|
||||||
|
auto get_content_object = [td, dialog_id](const MessageContent *content) {
|
||||||
|
return to_string(
|
||||||
|
get_message_content_object(content, td, dialog_id, -1, false, false, std::numeric_limits<int32>::max()));
|
||||||
|
};
|
||||||
if (old_->text.text != new_->text.text) {
|
if (old_->text.text != new_->text.text) {
|
||||||
if (need_message_changed_warning && need_message_text_changed_warning(old_, new_)) {
|
if (need_message_changed_warning && need_message_text_changed_warning(old_, new_)) {
|
||||||
LOG(ERROR) << "Message text has changed from "
|
LOG(ERROR) << "Message text has changed in " << get_content_object(old_content) << ". New content is "
|
||||||
<< to_string(get_message_content_object(old_content, td, dialog_id, -1, false, false))
|
<< get_content_object(new_content);
|
||||||
<< ". New content is "
|
|
||||||
<< to_string(get_message_content_object(new_content, td, dialog_id, -1, false, false));
|
|
||||||
}
|
}
|
||||||
need_update = true;
|
need_update = true;
|
||||||
}
|
}
|
||||||
@ -2925,10 +2927,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
|||||||
if (need_message_changed_warning && need_message_text_changed_warning(old_, new_) &&
|
if (need_message_changed_warning && need_message_text_changed_warning(old_, new_) &&
|
||||||
old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT &&
|
old_->text.entities.size() <= MAX_CUSTOM_ENTITIES_COUNT &&
|
||||||
need_message_entities_changed_warning(old_->text.entities, new_->text.entities)) {
|
need_message_entities_changed_warning(old_->text.entities, new_->text.entities)) {
|
||||||
LOG(WARNING) << "Entities has changed from "
|
LOG(WARNING) << "Entities has changed in " << get_content_object(old_content) << ". New content is "
|
||||||
<< to_string(get_message_content_object(old_content, td, dialog_id, -1, false, false))
|
<< get_content_object(new_content);
|
||||||
<< ". New content is "
|
|
||||||
<< to_string(get_message_content_object(new_content, td, dialog_id, -1, false, false));
|
|
||||||
}
|
}
|
||||||
need_update = true;
|
need_update = true;
|
||||||
}
|
}
|
||||||
@ -3825,14 +3825,14 @@ unique_ptr<MessageContent> get_secret_message_content(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto entities = get_message_entities(std::move(secret_entities));
|
auto entities = get_message_entities(std::move(secret_entities));
|
||||||
auto status = fix_formatted_text(message_text, entities, true, false, true, false);
|
auto status = fix_formatted_text(message_text, entities, true, false, true, td->auth_manager_->is_bot(), false);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
LOG(WARNING) << "Receive error " << status << " while parsing secret message \"" << message_text
|
LOG(WARNING) << "Receive error " << status << " while parsing secret message \"" << message_text
|
||||||
<< "\" with entities " << format::as_array(entities);
|
<< "\" with entities " << format::as_array(entities);
|
||||||
if (!clean_input_string(message_text)) {
|
if (!clean_input_string(message_text)) {
|
||||||
message_text.clear();
|
message_text.clear();
|
||||||
}
|
}
|
||||||
entities = find_entities(message_text, true);
|
entities = find_entities(message_text, true, td->auth_manager_->is_bot());
|
||||||
}
|
}
|
||||||
|
|
||||||
// support of old layer and old constructions
|
// support of old layer and old constructions
|
||||||
@ -4649,19 +4649,21 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
|
|||||||
|
|
||||||
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
||||||
DialogId dialog_id, int32 message_date,
|
DialogId dialog_id, int32 message_date,
|
||||||
bool is_content_secret, bool skip_bot_commands) {
|
bool is_content_secret, bool skip_bot_commands,
|
||||||
|
int32 max_media_timestamp) {
|
||||||
CHECK(content != nullptr);
|
CHECK(content != nullptr);
|
||||||
switch (content->get_type()) {
|
switch (content->get_type()) {
|
||||||
case MessageContentType::Animation: {
|
case MessageContentType::Animation: {
|
||||||
const MessageAnimation *m = static_cast<const MessageAnimation *>(content);
|
const MessageAnimation *m = static_cast<const MessageAnimation *>(content);
|
||||||
return make_tl_object<td_api::messageAnimation>(
|
return make_tl_object<td_api::messageAnimation>(
|
||||||
td->animations_manager_->get_animation_object(m->file_id, "get_message_content_object"),
|
td->animations_manager_->get_animation_object(m->file_id, "get_message_content_object"),
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands), is_content_secret);
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||||
}
|
}
|
||||||
case MessageContentType::Audio: {
|
case MessageContentType::Audio: {
|
||||||
const MessageAudio *m = static_cast<const MessageAudio *>(content);
|
const MessageAudio *m = static_cast<const MessageAudio *>(content);
|
||||||
return make_tl_object<td_api::messageAudio>(td->audios_manager_->get_audio_object(m->file_id),
|
return make_tl_object<td_api::messageAudio>(
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands));
|
td->audios_manager_->get_audio_object(m->file_id),
|
||||||
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp));
|
||||||
}
|
}
|
||||||
case MessageContentType::Contact: {
|
case MessageContentType::Contact: {
|
||||||
const MessageContact *m = static_cast<const MessageContact *>(content);
|
const MessageContact *m = static_cast<const MessageContact *>(content);
|
||||||
@ -4671,7 +4673,7 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
const MessageDocument *m = static_cast<const MessageDocument *>(content);
|
const MessageDocument *m = static_cast<const MessageDocument *>(content);
|
||||||
return make_tl_object<td_api::messageDocument>(
|
return make_tl_object<td_api::messageDocument>(
|
||||||
td->documents_manager_->get_document_object(m->file_id, PhotoFormat::Jpeg),
|
td->documents_manager_->get_document_object(m->file_id, PhotoFormat::Jpeg),
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands));
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp));
|
||||||
}
|
}
|
||||||
case MessageContentType::Game: {
|
case MessageContentType::Game: {
|
||||||
const MessageGame *m = static_cast<const MessageGame *>(content);
|
const MessageGame *m = static_cast<const MessageGame *>(content);
|
||||||
@ -4696,9 +4698,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
}
|
}
|
||||||
case MessageContentType::Photo: {
|
case MessageContentType::Photo: {
|
||||||
const MessagePhoto *m = static_cast<const MessagePhoto *>(content);
|
const MessagePhoto *m = static_cast<const MessagePhoto *>(content);
|
||||||
return make_tl_object<td_api::messagePhoto>(get_photo_object(td->file_manager_.get(), m->photo),
|
return make_tl_object<td_api::messagePhoto>(
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
get_photo_object(td->file_manager_.get(), m->photo),
|
||||||
is_content_secret);
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||||
}
|
}
|
||||||
case MessageContentType::Sticker: {
|
case MessageContentType::Sticker: {
|
||||||
const MessageSticker *m = static_cast<const MessageSticker *>(content);
|
const MessageSticker *m = static_cast<const MessageSticker *>(content);
|
||||||
@ -4706,7 +4708,8 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
}
|
}
|
||||||
case MessageContentType::Text: {
|
case MessageContentType::Text: {
|
||||||
const MessageText *m = static_cast<const MessageText *>(content);
|
const MessageText *m = static_cast<const MessageText *>(content);
|
||||||
return make_tl_object<td_api::messageText>(get_formatted_text_object(m->text, skip_bot_commands),
|
return make_tl_object<td_api::messageText>(
|
||||||
|
get_formatted_text_object(m->text, skip_bot_commands, max_media_timestamp),
|
||||||
td->web_pages_manager_->get_web_page_object(m->web_page_id));
|
td->web_pages_manager_->get_web_page_object(m->web_page_id));
|
||||||
}
|
}
|
||||||
case MessageContentType::Unsupported:
|
case MessageContentType::Unsupported:
|
||||||
@ -4717,9 +4720,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
}
|
}
|
||||||
case MessageContentType::Video: {
|
case MessageContentType::Video: {
|
||||||
const MessageVideo *m = static_cast<const MessageVideo *>(content);
|
const MessageVideo *m = static_cast<const MessageVideo *>(content);
|
||||||
return make_tl_object<td_api::messageVideo>(td->videos_manager_->get_video_object(m->file_id),
|
return make_tl_object<td_api::messageVideo>(
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
td->videos_manager_->get_video_object(m->file_id),
|
||||||
is_content_secret);
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), is_content_secret);
|
||||||
}
|
}
|
||||||
case MessageContentType::VideoNote: {
|
case MessageContentType::VideoNote: {
|
||||||
const MessageVideoNote *m = static_cast<const MessageVideoNote *>(content);
|
const MessageVideoNote *m = static_cast<const MessageVideoNote *>(content);
|
||||||
@ -4728,9 +4731,9 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
}
|
}
|
||||||
case MessageContentType::VoiceNote: {
|
case MessageContentType::VoiceNote: {
|
||||||
const MessageVoiceNote *m = static_cast<const MessageVoiceNote *>(content);
|
const MessageVoiceNote *m = static_cast<const MessageVoiceNote *>(content);
|
||||||
return make_tl_object<td_api::messageVoiceNote>(td->voice_notes_manager_->get_voice_note_object(m->file_id),
|
return make_tl_object<td_api::messageVoiceNote>(
|
||||||
get_formatted_text_object(m->caption, skip_bot_commands),
|
td->voice_notes_manager_->get_voice_note_object(m->file_id),
|
||||||
m->is_listened);
|
get_formatted_text_object(m->caption, skip_bot_commands, max_media_timestamp), m->is_listened);
|
||||||
}
|
}
|
||||||
case MessageContentType::ChatCreate: {
|
case MessageContentType::ChatCreate: {
|
||||||
const MessageChatCreate *m = static_cast<const MessageChatCreate *>(content);
|
const MessageChatCreate *m = static_cast<const MessageChatCreate *>(content);
|
||||||
@ -4879,6 +4882,10 @@ tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageCo
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FormattedText *get_message_content_text_mutable(MessageContent *content) {
|
||||||
|
return const_cast<FormattedText *>(get_message_content_text(content));
|
||||||
|
}
|
||||||
|
|
||||||
const FormattedText *get_message_content_text(const MessageContent *content) {
|
const FormattedText *get_message_content_text(const MessageContent *content) {
|
||||||
switch (content->get_type()) {
|
switch (content->get_type()) {
|
||||||
case MessageContentType::Text:
|
case MessageContentType::Text:
|
||||||
@ -4920,10 +4927,6 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td)
|
|||||||
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
|
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
|
||||||
return td->audios_manager_->get_audio_duration(audio_file_id);
|
return td->audios_manager_->get_audio_duration(audio_file_id);
|
||||||
}
|
}
|
||||||
case MessageContentType::Text: {
|
|
||||||
auto web_page_id = static_cast<const MessageText *>(content)->web_page_id;
|
|
||||||
return td->web_pages_manager_->get_web_page_duration(web_page_id);
|
|
||||||
}
|
|
||||||
case MessageContentType::Video: {
|
case MessageContentType::Video: {
|
||||||
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
|
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
|
||||||
return td->videos_manager_->get_video_duration(video_file_id);
|
return td->videos_manager_->get_video_duration(video_file_id);
|
||||||
@ -4941,6 +4944,34 @@ int32 get_message_content_duration(const MessageContent *content, const Td *td)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 get_message_content_media_duration(const MessageContent *content, const Td *td) {
|
||||||
|
CHECK(content != nullptr);
|
||||||
|
switch (content->get_type()) {
|
||||||
|
case MessageContentType::Audio: {
|
||||||
|
auto audio_file_id = static_cast<const MessageAudio *>(content)->file_id;
|
||||||
|
return td->audios_manager_->get_audio_duration(audio_file_id);
|
||||||
|
}
|
||||||
|
case MessageContentType::Text: {
|
||||||
|
auto web_page_id = static_cast<const MessageText *>(content)->web_page_id;
|
||||||
|
return td->web_pages_manager_->get_web_page_media_duration(web_page_id);
|
||||||
|
}
|
||||||
|
case MessageContentType::Video: {
|
||||||
|
auto video_file_id = static_cast<const MessageVideo *>(content)->file_id;
|
||||||
|
return td->videos_manager_->get_video_duration(video_file_id);
|
||||||
|
}
|
||||||
|
case MessageContentType::VideoNote: {
|
||||||
|
auto video_note_file_id = static_cast<const MessageVideoNote *>(content)->file_id;
|
||||||
|
return td->video_notes_manager_->get_video_note_duration(video_note_file_id);
|
||||||
|
}
|
||||||
|
case MessageContentType::VoiceNote: {
|
||||||
|
auto voice_file_id = static_cast<const MessageVoiceNote *>(content)->file_id;
|
||||||
|
return td->voice_notes_manager_->get_voice_note_duration(voice_file_id);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileId get_message_content_upload_file_id(const MessageContent *content) {
|
FileId get_message_content_upload_file_id(const MessageContent *content) {
|
||||||
switch (content->get_type()) {
|
switch (content->get_type()) {
|
||||||
case MessageContentType::Animation:
|
case MessageContentType::Animation:
|
||||||
|
@ -198,7 +198,10 @@ unique_ptr<MessageContent> get_action_message_content(Td *td, tl_object_ptr<tele
|
|||||||
|
|
||||||
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
tl_object_ptr<td_api::MessageContent> get_message_content_object(const MessageContent *content, Td *td,
|
||||||
DialogId dialog_id, int32 message_date,
|
DialogId dialog_id, int32 message_date,
|
||||||
bool is_content_secret, bool skip_bot_commands);
|
bool is_content_secret, bool skip_bot_commands,
|
||||||
|
int32 max_media_timestamp);
|
||||||
|
|
||||||
|
FormattedText *get_message_content_text_mutable(MessageContent *content);
|
||||||
|
|
||||||
const FormattedText *get_message_content_text(const MessageContent *content);
|
const FormattedText *get_message_content_text(const MessageContent *content);
|
||||||
|
|
||||||
@ -206,6 +209,8 @@ const FormattedText *get_message_content_caption(const MessageContent *content);
|
|||||||
|
|
||||||
int32 get_message_content_duration(const MessageContent *content, const Td *td);
|
int32 get_message_content_duration(const MessageContent *content, const Td *td);
|
||||||
|
|
||||||
|
int32 get_message_content_media_duration(const MessageContent *content, const Td *td);
|
||||||
|
|
||||||
FileId get_message_content_upload_file_id(const MessageContent *content);
|
FileId get_message_content_upload_file_id(const MessageContent *content);
|
||||||
|
|
||||||
FileId get_message_content_any_file_id(const MessageContent *content);
|
FileId get_message_content_any_file_id(const MessageContent *content);
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
int MessageEntity::get_type_priority(Type type) {
|
int MessageEntity::get_type_priority(Type type) {
|
||||||
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50};
|
static const int types[] = {50, 50, 50, 50, 50, 90, 91, 20, 11, 10, 49, 49, 50, 50, 92, 93, 0, 50, 50};
|
||||||
static_assert(sizeof(types) / sizeof(types[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
|
static_assert(sizeof(types) / sizeof(types[0]) == static_cast<size_t>(MessageEntity::Type::Size), "");
|
||||||
return types[static_cast<int32>(type)];
|
return types[static_cast<int32>(type)];
|
||||||
}
|
}
|
||||||
@ -72,6 +72,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
|
|||||||
return string_builder << "PhoneNumber";
|
return string_builder << "PhoneNumber";
|
||||||
case MessageEntity::Type::BankCardNumber:
|
case MessageEntity::Type::BankCardNumber:
|
||||||
return string_builder << "BankCardNumber";
|
return string_builder << "BankCardNumber";
|
||||||
|
case MessageEntity::Type::MediaTimestamp:
|
||||||
|
return string_builder << "MediaTimestamp";
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return string_builder << "Impossible";
|
return string_builder << "Impossible";
|
||||||
@ -81,6 +83,9 @@ StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity::Ty
|
|||||||
StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity) {
|
StringBuilder &operator<<(StringBuilder &string_builder, const MessageEntity &message_entity) {
|
||||||
string_builder << '[' << message_entity.type << ", offset = " << message_entity.offset
|
string_builder << '[' << message_entity.type << ", offset = " << message_entity.offset
|
||||||
<< ", length = " << message_entity.length;
|
<< ", length = " << message_entity.length;
|
||||||
|
if (message_entity.media_timestamp >= 0) {
|
||||||
|
string_builder << ", media_timestamp = \"" << message_entity.media_timestamp << "\"";
|
||||||
|
}
|
||||||
if (!message_entity.argument.empty()) {
|
if (!message_entity.argument.empty()) {
|
||||||
string_builder << ", argument = \"" << message_entity.argument << "\"";
|
string_builder << ", argument = \"" << message_entity.argument << "\"";
|
||||||
}
|
}
|
||||||
@ -130,6 +135,8 @@ tl_object_ptr<td_api::TextEntityType> MessageEntity::get_text_entity_type_object
|
|||||||
return make_tl_object<td_api::textEntityTypePhoneNumber>();
|
return make_tl_object<td_api::textEntityTypePhoneNumber>();
|
||||||
case MessageEntity::Type::BankCardNumber:
|
case MessageEntity::Type::BankCardNumber:
|
||||||
return make_tl_object<td_api::textEntityTypeBankCardNumber>();
|
return make_tl_object<td_api::textEntityTypeBankCardNumber>();
|
||||||
|
case MessageEntity::Type::MediaTimestamp:
|
||||||
|
return make_tl_object<td_api::textEntityTypeMediaTimestamp>(media_timestamp);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -141,7 +148,7 @@ tl_object_ptr<td_api::textEntity> MessageEntity::get_text_entity_object() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
||||||
bool skip_bot_commands) {
|
bool skip_bot_commands, int32 max_media_timestamp) {
|
||||||
vector<tl_object_ptr<td_api::textEntity>> result;
|
vector<tl_object_ptr<td_api::textEntity>> result;
|
||||||
result.reserve(entities.size());
|
result.reserve(entities.size());
|
||||||
|
|
||||||
@ -149,6 +156,9 @@ vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<
|
|||||||
if (skip_bot_commands && entity.type == MessageEntity::Type::BotCommand) {
|
if (skip_bot_commands && entity.type == MessageEntity::Type::BotCommand) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (entity.type == MessageEntity::Type::MediaTimestamp && max_media_timestamp < entity.media_timestamp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto entity_object = entity.get_text_entity_object();
|
auto entity_object = entity.get_text_entity_object();
|
||||||
if (entity_object->type_ != nullptr) {
|
if (entity_object->type_ != nullptr) {
|
||||||
result.push_back(std::move(entity_object));
|
result.push_back(std::move(entity_object));
|
||||||
@ -162,9 +172,10 @@ StringBuilder &operator<<(StringBuilder &string_builder, const FormattedText &te
|
|||||||
return string_builder << '"' << text.text << "\" with entities " << text.entities;
|
return string_builder << '"' << text.text << "\" with entities " << text.entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands) {
|
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
|
||||||
return td_api::make_object<td_api::formattedText>(text.text,
|
int32 max_media_timestamp) {
|
||||||
get_text_entities_object(text.entities, skip_bot_commands));
|
return td_api::make_object<td_api::formattedText>(
|
||||||
|
text.text, get_text_entities_object(text.entities, skip_bot_commands, max_media_timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_word_character(uint32 code) {
|
static bool is_word_character(uint32 code) {
|
||||||
@ -430,6 +441,57 @@ static vector<Slice> match_cashtags(Slice str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static vector<Slice> match_media_timestamps(Slice str) {
|
||||||
|
vector<Slice> result;
|
||||||
|
const unsigned char *begin = str.ubegin();
|
||||||
|
const unsigned char *end = str.uend();
|
||||||
|
const unsigned char *ptr = begin;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ptr = static_cast<const unsigned char *>(std::memchr(ptr, ':', narrow_cast<int32>(end - ptr)));
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto media_timestamp_begin = ptr;
|
||||||
|
while (media_timestamp_begin != begin &&
|
||||||
|
(media_timestamp_begin[-1] == ':' || is_digit(media_timestamp_begin[-1]))) {
|
||||||
|
media_timestamp_begin--;
|
||||||
|
}
|
||||||
|
auto media_timestamp_end = ptr;
|
||||||
|
while (media_timestamp_end + 1 != end && (media_timestamp_end[1] == ':' || is_digit(media_timestamp_end[1]))) {
|
||||||
|
media_timestamp_end++;
|
||||||
|
}
|
||||||
|
media_timestamp_end++;
|
||||||
|
|
||||||
|
if (media_timestamp_begin != ptr && media_timestamp_end != ptr + 1 && is_digit(ptr[1])) {
|
||||||
|
ptr = media_timestamp_end;
|
||||||
|
|
||||||
|
if (media_timestamp_begin != begin) {
|
||||||
|
uint32 prev;
|
||||||
|
next_utf8_unsafe(prev_utf8_unsafe(media_timestamp_begin), &prev, "match_media_timestamps 1");
|
||||||
|
|
||||||
|
if (is_word_character(prev)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (media_timestamp_end != end) {
|
||||||
|
uint32 next;
|
||||||
|
next_utf8_unsafe(media_timestamp_end, &next, "match_media_timestamps 2");
|
||||||
|
|
||||||
|
if (is_word_character(next)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.emplace_back(media_timestamp_begin, media_timestamp_end);
|
||||||
|
} else {
|
||||||
|
ptr = media_timestamp_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static vector<Slice> match_bank_card_numbers(Slice str) {
|
static vector<Slice> match_bank_card_numbers(Slice str) {
|
||||||
vector<Slice> result;
|
vector<Slice> result;
|
||||||
const unsigned char *begin = str.ubegin();
|
const unsigned char *begin = str.ubegin();
|
||||||
@ -1247,6 +1309,42 @@ vector<std::pair<Slice, bool>> find_urls(Slice str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<std::pair<Slice, int32>> find_media_timestamps(Slice str) {
|
||||||
|
vector<std::pair<Slice, int32>> result;
|
||||||
|
for (auto media_timestamp : match_media_timestamps(str)) {
|
||||||
|
vector<Slice> parts = full_split(media_timestamp, ':');
|
||||||
|
CHECK(parts.size() >= 2);
|
||||||
|
if (parts.size() > 3 || parts.back().size() != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto seconds = to_integer<int32>(parts.back());
|
||||||
|
if (seconds >= 60) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (parts.size() == 2) {
|
||||||
|
if (parts[0].size() > 4 || parts[0].empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto minutes = to_integer<int32>(parts[0]);
|
||||||
|
result.emplace_back(media_timestamp, minutes * 60 + seconds);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (parts[0].size() > 2 || parts[1].size() > 2 || parts[0].empty() || parts[1].empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto minutes = to_integer<int32>(parts[1]);
|
||||||
|
if (minutes >= 60) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto hours = to_integer<int32>(parts[0]);
|
||||||
|
result.emplace_back(media_timestamp, hours * 3600 + minutes * 60 + seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int32 text_length(Slice text) {
|
static int32 text_length(Slice text) {
|
||||||
return narrow_cast<int32>(utf8_utf16_length(text));
|
return narrow_cast<int32>(utf8_utf16_length(text));
|
||||||
}
|
}
|
||||||
@ -1291,7 +1389,8 @@ static constexpr int32 get_continuous_entities_mask() {
|
|||||||
get_entity_type_mask(MessageEntity::Type::EmailAddress) | get_entity_type_mask(MessageEntity::Type::TextUrl) |
|
get_entity_type_mask(MessageEntity::Type::EmailAddress) | get_entity_type_mask(MessageEntity::Type::TextUrl) |
|
||||||
get_entity_type_mask(MessageEntity::Type::MentionName) | get_entity_type_mask(MessageEntity::Type::Cashtag) |
|
get_entity_type_mask(MessageEntity::Type::MentionName) | get_entity_type_mask(MessageEntity::Type::Cashtag) |
|
||||||
get_entity_type_mask(MessageEntity::Type::PhoneNumber) |
|
get_entity_type_mask(MessageEntity::Type::PhoneNumber) |
|
||||||
get_entity_type_mask(MessageEntity::Type::BankCardNumber);
|
get_entity_type_mask(MessageEntity::Type::BankCardNumber) |
|
||||||
|
get_entity_type_mask(MessageEntity::Type::MediaTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int32 get_pre_entities_mask() {
|
static constexpr int32 get_pre_entities_mask() {
|
||||||
@ -1440,7 +1539,7 @@ static void remove_entities_intersecting_blockquote(vector<MessageEntity> &entit
|
|||||||
while (blockquote_it != blockquote_entities.end() &&
|
while (blockquote_it != blockquote_entities.end() &&
|
||||||
(blockquote_it->type != MessageEntity::Type::BlockQuote ||
|
(blockquote_it->type != MessageEntity::Type::BlockQuote ||
|
||||||
blockquote_it->offset + blockquote_it->length <= entities[i].offset)) {
|
blockquote_it->offset + blockquote_it->length <= entities[i].offset)) {
|
||||||
blockquote_it++;
|
++blockquote_it;
|
||||||
}
|
}
|
||||||
if (blockquote_it != blockquote_entities.end() &&
|
if (blockquote_it != blockquote_entities.end() &&
|
||||||
(blockquote_it->offset + blockquote_it->length < entities[i].offset + entities[i].length ||
|
(blockquote_it->offset + blockquote_it->length < entities[i].offset + entities[i].length ||
|
||||||
@ -1456,7 +1555,52 @@ static void remove_entities_intersecting_blockquote(vector<MessageEntity> &entit
|
|||||||
entities.erase(entities.begin() + left_entities, entities.end());
|
entities.erase(entities.begin() + left_entities, entities.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands) {
|
// keeps only non-intersecting entities
|
||||||
|
// fixes entity offsets from UTF-8 to UTF-16 offsets
|
||||||
|
static void fix_entity_offsets(Slice text, vector<MessageEntity> &entities) {
|
||||||
|
if (entities.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_entities(entities);
|
||||||
|
|
||||||
|
remove_intersecting_entities(entities);
|
||||||
|
|
||||||
|
const unsigned char *begin = text.ubegin();
|
||||||
|
const unsigned char *ptr = begin;
|
||||||
|
const unsigned char *end = text.uend();
|
||||||
|
|
||||||
|
int32 utf16_pos = 0;
|
||||||
|
for (auto &entity : entities) {
|
||||||
|
int cnt = 2;
|
||||||
|
auto entity_begin = entity.offset;
|
||||||
|
auto entity_end = entity.offset + entity.length;
|
||||||
|
|
||||||
|
int32 pos = static_cast<int32>(ptr - begin);
|
||||||
|
if (entity_begin == pos) {
|
||||||
|
cnt--;
|
||||||
|
entity.offset = utf16_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ptr != end && cnt > 0) {
|
||||||
|
unsigned char c = ptr[0];
|
||||||
|
utf16_pos += 1 + (c >= 0xf0);
|
||||||
|
ptr = next_utf8_unsafe(ptr, nullptr, "fix_entity_offsets");
|
||||||
|
|
||||||
|
pos = static_cast<int32>(ptr - begin);
|
||||||
|
if (entity_begin == pos) {
|
||||||
|
cnt--;
|
||||||
|
entity.offset = utf16_pos;
|
||||||
|
} else if (entity_end == pos) {
|
||||||
|
cnt--;
|
||||||
|
entity.length = utf16_pos - entity.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(cnt == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) {
|
||||||
vector<MessageEntity> entities;
|
vector<MessageEntity> entities;
|
||||||
|
|
||||||
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
|
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
|
||||||
@ -1485,47 +1629,31 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands) {
|
|||||||
entities.emplace_back(type, offset, length);
|
entities.emplace_back(type, offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entities.empty()) {
|
if (!skip_media_timestamps) {
|
||||||
|
auto media_timestamps = find_media_timestamps(text);
|
||||||
|
for (auto &entity : media_timestamps) {
|
||||||
|
auto offset = narrow_cast<int32>(entity.first.begin() - text.begin());
|
||||||
|
auto length = narrow_cast<int32>(entity.first.size());
|
||||||
|
entities.emplace_back(MessageEntity::Type::MediaTimestamp, offset, length, entity.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fix_entity_offsets(text, entities);
|
||||||
|
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_entities(entities);
|
static vector<MessageEntity> find_media_timestamp_entities(Slice text) {
|
||||||
|
vector<MessageEntity> entities;
|
||||||
|
|
||||||
remove_intersecting_entities(entities);
|
auto media_timestamps = find_media_timestamps(text);
|
||||||
|
for (auto &entity : media_timestamps) {
|
||||||
// fix offsets to UTF-16 offsets
|
auto offset = narrow_cast<int32>(entity.first.begin() - text.begin());
|
||||||
const unsigned char *begin = text.ubegin();
|
auto length = narrow_cast<int32>(entity.first.size());
|
||||||
const unsigned char *ptr = begin;
|
entities.emplace_back(MessageEntity::Type::MediaTimestamp, offset, length, entity.second);
|
||||||
const unsigned char *end = text.uend();
|
|
||||||
|
|
||||||
int32 utf16_pos = 0;
|
|
||||||
for (auto &entity : entities) {
|
|
||||||
int cnt = 2;
|
|
||||||
auto entity_begin = entity.offset;
|
|
||||||
auto entity_end = entity.offset + entity.length;
|
|
||||||
|
|
||||||
int32 pos = static_cast<int32>(ptr - begin);
|
|
||||||
if (entity_begin == pos) {
|
|
||||||
cnt--;
|
|
||||||
entity.offset = utf16_pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ptr != end && cnt > 0) {
|
fix_entity_offsets(text, entities);
|
||||||
unsigned char c = ptr[0];
|
|
||||||
utf16_pos += 1 + (c >= 0xf0);
|
|
||||||
ptr = next_utf8_unsafe(ptr, nullptr, "find_entities");
|
|
||||||
|
|
||||||
pos = static_cast<int32>(ptr - begin);
|
|
||||||
if (entity_begin == pos) {
|
|
||||||
cnt--;
|
|
||||||
entity.offset = utf16_pos;
|
|
||||||
} else if (entity_end == pos) {
|
|
||||||
cnt--;
|
|
||||||
entity.length = utf16_pos - entity.offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CHECK(cnt == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
@ -1546,17 +1674,17 @@ static vector<MessageEntity> merge_entities(vector<MessageEntity> old_entities,
|
|||||||
for (auto &old_entity : old_entities) {
|
for (auto &old_entity : old_entities) {
|
||||||
while (new_it != new_end && new_it->offset + new_it->length <= old_entity.offset) {
|
while (new_it != new_end && new_it->offset + new_it->length <= old_entity.offset) {
|
||||||
result.push_back(std::move(*new_it));
|
result.push_back(std::move(*new_it));
|
||||||
new_it++;
|
++new_it;
|
||||||
}
|
}
|
||||||
auto old_entity_end = old_entity.offset + old_entity.length;
|
auto old_entity_end = old_entity.offset + old_entity.length;
|
||||||
result.push_back(std::move(old_entity));
|
result.push_back(std::move(old_entity));
|
||||||
while (new_it != new_end && new_it->offset < old_entity_end) {
|
while (new_it != new_end && new_it->offset < old_entity_end) {
|
||||||
new_it++;
|
++new_it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (new_it != new_end) {
|
while (new_it != new_end) {
|
||||||
result.push_back(std::move(*new_it));
|
result.push_back(std::move(*new_it));
|
||||||
new_it++;
|
++new_it;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -1616,6 +1744,8 @@ string get_first_url(Slice text, const vector<MessageEntity> &entities) {
|
|||||||
break;
|
break;
|
||||||
case MessageEntity::Type::BankCardNumber:
|
case MessageEntity::Type::BankCardNumber:
|
||||||
break;
|
break;
|
||||||
|
case MessageEntity::Type::MediaTimestamp:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -2239,7 +2369,7 @@ static vector<MessageEntity> find_splittable_entities_v3(Slice text, const vecto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto found_entities = find_entities(text, false);
|
auto found_entities = find_entities(text, false, true);
|
||||||
td::remove_if(found_entities, [](const auto &entity) {
|
td::remove_if(found_entities, [](const auto &entity) {
|
||||||
return entity.type == MessageEntity::Type::EmailAddress || entity.type == MessageEntity::Type::Url;
|
return entity.type == MessageEntity::Type::EmailAddress || entity.type == MessageEntity::Type::Url;
|
||||||
});
|
});
|
||||||
@ -3030,6 +3160,8 @@ vector<tl_object_ptr<secret_api::MessageEntity>> get_input_secret_message_entiti
|
|||||||
break;
|
break;
|
||||||
case MessageEntity::Type::MentionName:
|
case MessageEntity::Type::MentionName:
|
||||||
break;
|
break;
|
||||||
|
case MessageEntity::Type::MediaTimestamp:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -3121,6 +3253,15 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
|
|||||||
entities.emplace_back(entity->offset_, entity->length_, user_id);
|
entities.emplace_back(entity->offset_, entity->length_, user_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case td_api::textEntityTypeMediaTimestamp::ID: {
|
||||||
|
auto entity_media_timestamp = static_cast<td_api::textEntityTypeMediaTimestamp *>(entity->type_.get());
|
||||||
|
if (entity_media_timestamp->media_timestamp_ < 0) {
|
||||||
|
return Status::Error(400, "Invalid media timestamp specified");
|
||||||
|
}
|
||||||
|
entities.emplace_back(MessageEntity::Type::MediaTimestamp, entity->offset_, entity->length_,
|
||||||
|
entity_media_timestamp->media_timestamp_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -3516,6 +3657,19 @@ static Result<string> clean_input_string_with_entities(const string &text, vecto
|
|||||||
// entities must be sorted by offset and length, but not necessary by type
|
// entities must be sorted by offset and length, but not necessary by type
|
||||||
// returns {last_non_whitespace_pos, last_non_whitespace_utf16_offset}
|
// returns {last_non_whitespace_pos, last_non_whitespace_utf16_offset}
|
||||||
static std::pair<size_t, int32> remove_invalid_entities(const string &text, vector<MessageEntity> &entities) {
|
static std::pair<size_t, int32> remove_invalid_entities(const string &text, vector<MessageEntity> &entities) {
|
||||||
|
if (entities.empty()) {
|
||||||
|
// fast path
|
||||||
|
for (size_t pos = 0; pos < text.size(); pos++) {
|
||||||
|
auto back_pos = text.size() - pos - 1;
|
||||||
|
auto c = text[back_pos];
|
||||||
|
if (c != '\n' && c != ' ') {
|
||||||
|
return {back_pos, 0 /*unused*/};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {text.size(), -1};
|
||||||
|
}
|
||||||
|
|
||||||
// check_is_sorted(entities);
|
// check_is_sorted(entities);
|
||||||
vector<MessageEntity *> nested_entities_stack;
|
vector<MessageEntity *> nested_entities_stack;
|
||||||
size_t current_entity = 0;
|
size_t current_entity = 0;
|
||||||
@ -3759,7 +3913,7 @@ static void merge_new_entities(vector<MessageEntity> &entities, vector<MessageEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
||||||
bool skip_bot_commands, bool for_draft) {
|
bool skip_bot_commands, bool skip_media_timestamps, bool for_draft) {
|
||||||
string result;
|
string result;
|
||||||
if (entities.empty()) {
|
if (entities.empty()) {
|
||||||
// fast path
|
// fast path
|
||||||
@ -3867,7 +4021,9 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skip_new_entities) {
|
if (!skip_new_entities) {
|
||||||
merge_new_entities(entities, find_entities(text, skip_bot_commands));
|
merge_new_entities(entities, find_entities(text, skip_bot_commands, skip_media_timestamps));
|
||||||
|
} else if (!skip_media_timestamps) {
|
||||||
|
merge_new_entities(entities, find_media_timestamp_entities(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
// new whitespace-only entities could be added after splitting of entities
|
// new whitespace-only entities could be added after splitting of entities
|
||||||
@ -3880,11 +4036,12 @@ Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool al
|
|||||||
|
|
||||||
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
||||||
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
||||||
bool skip_new_entities, int32 send_date, bool from_album, const char *source) {
|
bool skip_new_entities, bool skip_media_timestamps, int32 send_date, bool from_album,
|
||||||
|
const char *source) {
|
||||||
auto entities = get_message_entities(contacts_manager, std::move(server_entities), source);
|
auto entities = get_message_entities(contacts_manager, std::move(server_entities), source);
|
||||||
auto debug_message_text = message_text;
|
auto debug_message_text = message_text;
|
||||||
auto debug_entities = entities;
|
auto debug_entities = entities;
|
||||||
auto status = fix_formatted_text(message_text, entities, true, skip_new_entities, true, false);
|
auto status = fix_formatted_text(message_text, entities, true, skip_new_entities, true, skip_media_timestamps, false);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
// message entities in media albums can be wrong because of a long time ago fixed server-side bug
|
// message entities in media albums can be wrong because of a long time ago fixed server-side bug
|
||||||
if (!from_album && (send_date == 0 || send_date > 1600340000)) { // approximate fix date
|
if (!from_album && (send_date == 0 || send_date > 1600340000)) { // approximate fix date
|
||||||
@ -3895,7 +4052,7 @@ FormattedText get_message_text(const ContactsManager *contacts_manager, string m
|
|||||||
if (!clean_input_string(message_text)) {
|
if (!clean_input_string(message_text)) {
|
||||||
message_text.clear();
|
message_text.clear();
|
||||||
}
|
}
|
||||||
entities = find_entities(message_text, false);
|
entities = find_entities(message_text, false, skip_media_timestamps);
|
||||||
}
|
}
|
||||||
return FormattedText{std::move(message_text), std::move(entities)};
|
return FormattedText{std::move(message_text), std::move(entities)};
|
||||||
}
|
}
|
||||||
@ -3939,7 +4096,7 @@ Result<FormattedText> process_input_caption(const ContactsManager *contacts_mana
|
|||||||
}
|
}
|
||||||
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(caption->entities_)));
|
TRY_RESULT(entities, get_message_entities(contacts_manager, std::move(caption->entities_)));
|
||||||
TRY_STATUS(fix_formatted_text(caption->text_, entities, true, false,
|
TRY_STATUS(fix_formatted_text(caption->text_, entities, true, false,
|
||||||
need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot), false));
|
need_always_skip_bot_commands(contacts_manager, dialog_id, is_bot), is_bot, false));
|
||||||
return FormattedText{std::move(caption->text_), std::move(entities)};
|
return FormattedText{std::move(caption->text_), std::move(entities)};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3954,6 +4111,19 @@ void add_formatted_text_dependencies(Dependencies &dependencies, const Formatted
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_media_timestamps(const FormattedText *text, int32 min_media_timestamp, int32 max_media_timestamp) {
|
||||||
|
if (text == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto &entity : text->entities) {
|
||||||
|
if (entity.type == MessageEntity::Type::MediaTimestamp && min_media_timestamp <= entity.media_timestamp &&
|
||||||
|
entity.media_timestamp <= max_media_timestamp) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool has_bot_commands(const FormattedText *text) {
|
bool has_bot_commands(const FormattedText *text) {
|
||||||
if (text == nullptr) {
|
if (text == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -48,28 +48,34 @@ class MessageEntity {
|
|||||||
Strikethrough,
|
Strikethrough,
|
||||||
BlockQuote,
|
BlockQuote,
|
||||||
BankCardNumber,
|
BankCardNumber,
|
||||||
|
MediaTimestamp,
|
||||||
Size
|
Size
|
||||||
};
|
};
|
||||||
Type type;
|
Type type = Type::Size;
|
||||||
int32 offset;
|
int32 offset = -1;
|
||||||
int32 length;
|
int32 length = -1;
|
||||||
|
int32 media_timestamp = -1;
|
||||||
string argument;
|
string argument;
|
||||||
UserId user_id;
|
UserId user_id;
|
||||||
|
|
||||||
MessageEntity() = default;
|
MessageEntity() = default;
|
||||||
|
|
||||||
MessageEntity(Type type, int32 offset, int32 length, string argument = "")
|
MessageEntity(Type type, int32 offset, int32 length, string argument = "")
|
||||||
: type(type), offset(offset), length(length), argument(std::move(argument)), user_id() {
|
: type(type), offset(offset), length(length), argument(std::move(argument)) {
|
||||||
}
|
}
|
||||||
MessageEntity(int32 offset, int32 length, UserId user_id)
|
MessageEntity(int32 offset, int32 length, UserId user_id)
|
||||||
: type(Type::MentionName), offset(offset), length(length), argument(), user_id(user_id) {
|
: type(Type::MentionName), offset(offset), length(length), user_id(user_id) {
|
||||||
|
}
|
||||||
|
MessageEntity(Type type, int32 offset, int32 length, int32 media_timestamp)
|
||||||
|
: type(type), offset(offset), length(length), media_timestamp(media_timestamp) {
|
||||||
|
CHECK(type == Type::MediaTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
tl_object_ptr<td_api::textEntity> get_text_entity_object() const;
|
tl_object_ptr<td_api::textEntity> get_text_entity_object() const;
|
||||||
|
|
||||||
bool operator==(const MessageEntity &other) const {
|
bool operator==(const MessageEntity &other) const {
|
||||||
return offset == other.offset && length == other.length && type == other.type && argument == other.argument &&
|
return offset == other.offset && length == other.length && type == other.type &&
|
||||||
user_id == other.user_id;
|
media_timestamp == other.media_timestamp && argument == other.argument && user_id == other.user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const MessageEntity &other) const {
|
bool operator<(const MessageEntity &other) const {
|
||||||
@ -132,11 +138,12 @@ Result<vector<MessageEntity>> get_message_entities(const ContactsManager *contac
|
|||||||
bool allow_all = false);
|
bool allow_all = false);
|
||||||
|
|
||||||
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<MessageEntity> &entities,
|
||||||
bool skip_bot_commands);
|
bool skip_bot_commands, int32 max_media_timestamp);
|
||||||
|
|
||||||
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands);
|
td_api::object_ptr<td_api::formattedText> get_formatted_text_object(const FormattedText &text, bool skip_bot_commands,
|
||||||
|
int32 max_media_timestamp);
|
||||||
|
|
||||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands);
|
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
|
||||||
|
|
||||||
vector<Slice> find_mentions(Slice str);
|
vector<Slice> find_mentions(Slice str);
|
||||||
vector<Slice> find_bot_commands(Slice str);
|
vector<Slice> find_bot_commands(Slice str);
|
||||||
@ -146,6 +153,7 @@ vector<Slice> find_bank_card_numbers(Slice str);
|
|||||||
vector<Slice> find_tg_urls(Slice str);
|
vector<Slice> find_tg_urls(Slice str);
|
||||||
bool is_email_address(Slice str);
|
bool is_email_address(Slice str);
|
||||||
vector<std::pair<Slice, bool>> find_urls(Slice str); // slice + is_email_address
|
vector<std::pair<Slice, bool>> find_urls(Slice str); // slice + is_email_address
|
||||||
|
vector<std::pair<Slice, int32>> find_media_timestamps(Slice str); // slice + media_timestamp
|
||||||
|
|
||||||
string get_first_url(Slice text, const vector<MessageEntity> &entities);
|
string get_first_url(Slice text, const vector<MessageEntity> &entities);
|
||||||
|
|
||||||
@ -178,11 +186,12 @@ vector<MessageEntity> get_message_entities(vector<tl_object_ptr<secret_api::Mess
|
|||||||
|
|
||||||
// like clean_input_string but also validates entities
|
// like clean_input_string but also validates entities
|
||||||
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
Status fix_formatted_text(string &text, vector<MessageEntity> &entities, bool allow_empty, bool skip_new_entities,
|
||||||
bool skip_bot_commands, bool for_draft) TD_WARN_UNUSED_RESULT;
|
bool skip_bot_commands, bool skip_media_timestamps, bool for_draft) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
FormattedText get_message_text(const ContactsManager *contacts_manager, string message_text,
|
||||||
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
vector<tl_object_ptr<telegram_api::MessageEntity>> &&server_entities,
|
||||||
bool skip_new_entities, int32 send_date, bool from_album, const char *source);
|
bool skip_new_entities, bool skip_media_timestamps, int32 send_date, bool from_album,
|
||||||
|
const char *source);
|
||||||
|
|
||||||
td_api::object_ptr<td_api::formattedText> extract_input_caption(
|
td_api::object_ptr<td_api::formattedText> extract_input_caption(
|
||||||
tl_object_ptr<td_api::InputMessageContent> &input_message_content);
|
tl_object_ptr<td_api::InputMessageContent> &input_message_content);
|
||||||
@ -192,6 +201,8 @@ Result<FormattedText> process_input_caption(const ContactsManager *contacts_mana
|
|||||||
|
|
||||||
void add_formatted_text_dependencies(Dependencies &dependencies, const FormattedText *text);
|
void add_formatted_text_dependencies(Dependencies &dependencies, const FormattedText *text);
|
||||||
|
|
||||||
|
bool has_media_timestamps(const FormattedText *text, int32 min_media_timestamp, int32 max_media_timestamp);
|
||||||
|
|
||||||
bool has_bot_commands(const FormattedText *text);
|
bool has_bot_commands(const FormattedText *text);
|
||||||
|
|
||||||
bool need_always_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot);
|
bool need_always_skip_bot_commands(const ContactsManager *contacts_manager, DialogId dialog_id, bool is_bot);
|
||||||
|
@ -24,6 +24,9 @@ void MessageEntity::store(StorerT &storer) const {
|
|||||||
if (type == Type::MentionName) {
|
if (type == Type::MentionName) {
|
||||||
store(user_id, storer);
|
store(user_id, storer);
|
||||||
}
|
}
|
||||||
|
if (type == Type::MediaTimestamp) {
|
||||||
|
store(media_timestamp, storer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ParserT>
|
template <class ParserT>
|
||||||
@ -38,6 +41,9 @@ void MessageEntity::parse(ParserT &parser) {
|
|||||||
if (type == Type::MentionName) {
|
if (type == Type::MentionName) {
|
||||||
parse(user_id, parser);
|
parse(user_id, parser);
|
||||||
}
|
}
|
||||||
|
if (type == Type::MediaTimestamp) {
|
||||||
|
parse(media_timestamp, parser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class StorerT>
|
template <class StorerT>
|
||||||
|
21
td/telegram/MessageThreadInfo.h
Normal file
21
td/telegram/MessageThreadInfo.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/telegram/DialogId.h"
|
||||||
|
#include "td/telegram/MessageId.h"
|
||||||
|
|
||||||
|
#include "td/utils/common.h"
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
|
||||||
|
struct MessageThreadInfo {
|
||||||
|
DialogId dialog_id;
|
||||||
|
vector<MessageId> message_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace td
|
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,7 @@
|
|||||||
#include "td/telegram/MessageId.h"
|
#include "td/telegram/MessageId.h"
|
||||||
#include "td/telegram/MessageLinkInfo.h"
|
#include "td/telegram/MessageLinkInfo.h"
|
||||||
#include "td/telegram/MessageReplyInfo.h"
|
#include "td/telegram/MessageReplyInfo.h"
|
||||||
|
#include "td/telegram/MessageThreadInfo.h"
|
||||||
#include "td/telegram/MessagesDb.h"
|
#include "td/telegram/MessagesDb.h"
|
||||||
#include "td/telegram/MessageSearchFilter.h"
|
#include "td/telegram/MessageSearchFilter.h"
|
||||||
#include "td/telegram/MessageTtlSetting.h"
|
#include "td/telegram/MessageTtlSetting.h"
|
||||||
@ -573,17 +574,13 @@ class MessagesManager final : public Actor {
|
|||||||
void get_messages_from_server(vector<FullMessageId> &&message_ids, Promise<Unit> &&promise, const char *source,
|
void get_messages_from_server(vector<FullMessageId> &&message_ids, Promise<Unit> &&promise, const char *source,
|
||||||
tl_object_ptr<telegram_api::InputMessage> input_message = nullptr);
|
tl_object_ptr<telegram_api::InputMessage> input_message = nullptr);
|
||||||
|
|
||||||
struct MessageThreadInfo {
|
|
||||||
DialogId dialog_id;
|
|
||||||
vector<MessageId> message_ids;
|
|
||||||
};
|
|
||||||
void get_message_thread(DialogId dialog_id, MessageId message_id, Promise<MessageThreadInfo> &&promise);
|
void get_message_thread(DialogId dialog_id, MessageId message_id, Promise<MessageThreadInfo> &&promise);
|
||||||
|
|
||||||
td_api::object_ptr<td_api::messageThreadInfo> get_message_thread_info_object(const MessageThreadInfo &info);
|
td_api::object_ptr<td_api::messageThreadInfo> get_message_thread_info_object(const MessageThreadInfo &info);
|
||||||
|
|
||||||
void process_discussion_message(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
void process_discussion_message(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
||||||
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
||||||
MessageId expected_message_id, Promise<vector<FullMessageId>> promise);
|
MessageId expected_message_id, Promise<MessageThreadInfo> promise);
|
||||||
|
|
||||||
bool is_message_edited_recently(FullMessageId full_message_id, int32 seconds);
|
bool is_message_edited_recently(FullMessageId full_message_id, int32 seconds);
|
||||||
|
|
||||||
@ -1044,6 +1041,7 @@ class MessagesManager final : public Actor {
|
|||||||
bool is_mention_notification_disabled = false;
|
bool is_mention_notification_disabled = false;
|
||||||
bool is_from_scheduled = false;
|
bool is_from_scheduled = false;
|
||||||
bool is_pinned = false;
|
bool is_pinned = false;
|
||||||
|
bool are_media_timestamp_entities_found = false;
|
||||||
|
|
||||||
bool is_copy = false; // for send_message
|
bool is_copy = false; // for send_message
|
||||||
bool from_background = false; // for send_message
|
bool from_background = false; // for send_message
|
||||||
@ -1065,6 +1063,9 @@ class MessagesManager final : public Actor {
|
|||||||
NotificationId notification_id;
|
NotificationId notification_id;
|
||||||
NotificationId removed_notification_id;
|
NotificationId removed_notification_id;
|
||||||
|
|
||||||
|
int32 max_reply_media_timestamp = -1;
|
||||||
|
int32 max_own_media_timestamp = -2; // to update replied messages on the first load
|
||||||
|
|
||||||
int32 view_count = 0;
|
int32 view_count = 0;
|
||||||
int32 forward_count = 0;
|
int32 forward_count = 0;
|
||||||
MessageReplyInfo reply_info;
|
MessageReplyInfo reply_info;
|
||||||
@ -1101,6 +1102,7 @@ class MessagesManager final : public Actor {
|
|||||||
unique_ptr<Message> right;
|
unique_ptr<Message> right;
|
||||||
|
|
||||||
mutable int32 last_access_date = 0;
|
mutable int32 last_access_date = 0;
|
||||||
|
mutable bool is_update_sent = false; // whether the message is known to the app
|
||||||
|
|
||||||
mutable uint64 send_message_log_event_id = 0;
|
mutable uint64 send_message_log_event_id = 0;
|
||||||
|
|
||||||
@ -1648,9 +1650,9 @@ class MessagesManager final : public Actor {
|
|||||||
static constexpr int32 MAX_SEARCH_MESSAGES = 100; // server side limit
|
static constexpr int32 MAX_SEARCH_MESSAGES = 100; // server side limit
|
||||||
static constexpr int32 MIN_SEARCH_PUBLIC_DIALOG_PREFIX_LEN = 4; // server side limit
|
static constexpr int32 MIN_SEARCH_PUBLIC_DIALOG_PREFIX_LEN = 4; // server side limit
|
||||||
static constexpr int32 MIN_CHANNEL_DIFFERENCE = 1;
|
static constexpr int32 MIN_CHANNEL_DIFFERENCE = 1;
|
||||||
static constexpr int32 MAX_CHANNEL_DIFFERENCE = 1000; // server side limit
|
static constexpr int32 MAX_CHANNEL_DIFFERENCE = 100;
|
||||||
static constexpr int32 MAX_BOT_CHANNEL_DIFFERENCE = 1000000; // server side limit
|
static constexpr int32 MAX_BOT_CHANNEL_DIFFERENCE = 100000; // server side limit
|
||||||
static constexpr int32 MAX_RECENTLY_FOUND_DIALOGS = 30; // some reasonable value
|
static constexpr int32 MAX_RECENTLY_FOUND_DIALOGS = 50; // some reasonable value
|
||||||
static constexpr size_t MAX_TITLE_LENGTH = 128; // server side limit for chat title
|
static constexpr size_t MAX_TITLE_LENGTH = 128; // server side limit for chat title
|
||||||
static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat description
|
static constexpr size_t MAX_DESCRIPTION_LENGTH = 255; // server side limit for chat description
|
||||||
static constexpr size_t MAX_DIALOG_FILTER_TITLE_LENGTH = 12; // server side limit for dialog filter title
|
static constexpr size_t MAX_DIALOG_FILTER_TITLE_LENGTH = 12; // server side limit for dialog filter title
|
||||||
@ -1784,6 +1786,8 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
Status can_pin_messages(DialogId dialog_id) const;
|
Status can_pin_messages(DialogId dialog_id) const;
|
||||||
|
|
||||||
|
static Status can_get_media_timestamp_link(DialogId dialog_id, const Message *m);
|
||||||
|
|
||||||
void cancel_edit_message_media(DialogId dialog_id, Message *m, Slice error_message);
|
void cancel_edit_message_media(DialogId dialog_id, Message *m, Slice error_message);
|
||||||
|
|
||||||
void on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id,
|
void on_message_media_edited(DialogId dialog_id, MessageId message_id, FileId file_id, FileId thumbnail_file_id,
|
||||||
@ -2142,13 +2146,26 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
void attach_message_to_next(Dialog *d, MessageId message_id, const char *source);
|
void attach_message_to_next(Dialog *d, MessageId message_id, const char *source);
|
||||||
|
|
||||||
bool update_message(Dialog *d, Message *old_message, unique_ptr<Message> new_message, bool *need_update_dialog_pos);
|
bool update_message(Dialog *d, Message *old_message, unique_ptr<Message> new_message, bool *need_update_dialog_pos,
|
||||||
|
bool is_message_in_dialog);
|
||||||
|
|
||||||
static bool need_message_changed_warning(const Message *old_message);
|
static bool need_message_changed_warning(const Message *old_message);
|
||||||
|
|
||||||
bool update_message_content(DialogId dialog_id, Message *old_message, unique_ptr<MessageContent> new_content,
|
bool update_message_content(DialogId dialog_id, Message *old_message, unique_ptr<MessageContent> new_content,
|
||||||
bool need_send_update_message_content, bool need_merge_files, bool is_message_in_dialog);
|
bool need_send_update_message_content, bool need_merge_files, bool is_message_in_dialog);
|
||||||
|
|
||||||
|
void update_message_max_reply_media_timestamp(const Dialog *d, Message *m, bool need_send_update_message_content);
|
||||||
|
|
||||||
|
void update_message_max_own_media_timestamp(const Dialog *d, Message *m);
|
||||||
|
|
||||||
|
void update_message_max_reply_media_timestamp_in_replied_messages(DialogId dialog_id, MessageId reply_to_message_id);
|
||||||
|
|
||||||
|
void register_message_reply(const Dialog *d, const Message *m);
|
||||||
|
|
||||||
|
void reregister_message_reply(const Dialog *d, const Message *m);
|
||||||
|
|
||||||
|
void unregister_message_reply(const Dialog *d, const Message *m);
|
||||||
|
|
||||||
void send_update_new_message(const Dialog *d, const Message *m);
|
void send_update_new_message(const Dialog *d, const Message *m);
|
||||||
|
|
||||||
static bool is_from_mention_notification_group(const Dialog *d, const Message *m);
|
static bool is_from_mention_notification_group(const Dialog *d, const Message *m);
|
||||||
@ -2205,7 +2222,9 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
void send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const;
|
void send_update_message_send_succeeded(Dialog *d, MessageId old_message_id, const Message *m) const;
|
||||||
|
|
||||||
void send_update_message_content(DialogId dialog_id, const Message *m, const char *source);
|
void send_update_message_content(DialogId dialog_id, Message *m, bool is_message_in_dialog, const char *source);
|
||||||
|
|
||||||
|
void send_update_message_content(const Dialog *d, Message *m, bool is_message_in_dialog, const char *source);
|
||||||
|
|
||||||
void send_update_message_content_impl(DialogId dialog_id, const Message *m, const char *source) const;
|
void send_update_message_content_impl(DialogId dialog_id, const Message *m, const char *source) const;
|
||||||
|
|
||||||
@ -2608,6 +2627,10 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
bool has_message_sender_user_id(DialogId dialog_id, const Message *m) const;
|
bool has_message_sender_user_id(DialogId dialog_id, const Message *m) const;
|
||||||
|
|
||||||
|
int32 get_message_own_max_media_timestamp(const Message *m) const;
|
||||||
|
|
||||||
|
static int32 get_message_max_media_timestamp(const Message *m);
|
||||||
|
|
||||||
static bool get_message_disable_web_page_preview(const Message *m);
|
static bool get_message_disable_web_page_preview(const Message *m);
|
||||||
|
|
||||||
static int32 get_message_flags(const Message *m);
|
static int32 get_message_flags(const Message *m);
|
||||||
@ -2656,9 +2679,9 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
void process_discussion_message_impl(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
void process_discussion_message_impl(telegram_api::object_ptr<telegram_api::messages_discussionMessage> &&result,
|
||||||
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
DialogId dialog_id, MessageId message_id, DialogId expected_dialog_id,
|
||||||
MessageId expected_message_id, Promise<vector<FullMessageId>> promise);
|
MessageId expected_message_id, Promise<MessageThreadInfo> promise);
|
||||||
|
|
||||||
void on_get_discussion_message(DialogId dialog_id, MessageId message_id, vector<FullMessageId> full_message_ids,
|
void on_get_discussion_message(DialogId dialog_id, MessageId message_id, MessageThreadInfo &&message_thread_info,
|
||||||
Promise<MessageThreadInfo> &&promise);
|
Promise<MessageThreadInfo> &&promise);
|
||||||
|
|
||||||
static MessageId get_first_database_message_id_by_index(const Dialog *d, MessageSearchFilter filter);
|
static MessageId get_first_database_message_id_by_index(const Dialog *d, MessageSearchFilter filter);
|
||||||
@ -3207,10 +3230,10 @@ class MessagesManager final : public Actor {
|
|||||||
std::unordered_map<int64, FoundMessages> found_fts_messages_; // random_id -> FoundMessages
|
std::unordered_map<int64, FoundMessages> found_fts_messages_; // random_id -> FoundMessages
|
||||||
std::unordered_map<int64, FoundMessages> found_message_public_forwards_; // random_id -> FoundMessages
|
std::unordered_map<int64, FoundMessages> found_message_public_forwards_; // random_id -> FoundMessages
|
||||||
|
|
||||||
struct PublicMessageLinks {
|
struct MessageEmbeddingCodes {
|
||||||
std::unordered_map<MessageId, std::pair<string, string>, MessageIdHash> links_;
|
std::unordered_map<MessageId, string, MessageIdHash> embedding_codes_;
|
||||||
};
|
};
|
||||||
std::unordered_map<DialogId, PublicMessageLinks, DialogIdHash> public_message_links_[2];
|
std::unordered_map<DialogId, MessageEmbeddingCodes, DialogIdHash> message_embedding_codes_[2];
|
||||||
|
|
||||||
std::unordered_map<int64, tl_object_ptr<td_api::chatEvents>> chat_events_; // random_id -> chat events
|
std::unordered_map<int64, tl_object_ptr<td_api::chatEvents>> chat_events_; // random_id -> chat events
|
||||||
|
|
||||||
@ -3223,6 +3246,10 @@ class MessagesManager final : public Actor {
|
|||||||
|
|
||||||
std::unordered_map<FullMessageId, int32, FullMessageIdHash> replied_by_yet_unsent_messages_;
|
std::unordered_map<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>
|
||||||
|
replied_by_media_timestamp_messages_;
|
||||||
|
|
||||||
struct ActiveDialogAction {
|
struct ActiveDialogAction {
|
||||||
MessageId top_thread_message_id;
|
MessageId top_thread_message_id;
|
||||||
UserId user_id;
|
UserId user_id;
|
||||||
|
@ -109,9 +109,7 @@ void parse(PhotoSizeSource::FullLegacy &source, ParserT &parser) {
|
|||||||
parse(source.volume_id, parser);
|
parse(source.volume_id, parser);
|
||||||
parse(source.secret, parser);
|
parse(source.secret, parser);
|
||||||
parse(source.local_id, parser);
|
parse(source.local_id, parser);
|
||||||
if (source.local_id < 0) {
|
// source.local_id can be negative in secret chat thumbnails
|
||||||
parser.set_error("Wrong local_id");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class StorerT>
|
template <class StorerT>
|
||||||
|
@ -575,7 +575,7 @@ td_api::object_ptr<td_api::poll> PollManager::get_poll_object(PollId poll_id, co
|
|||||||
auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id;
|
auto correct_option_id = is_local_poll_id(poll_id) ? -1 : poll->correct_option_id;
|
||||||
poll_type = td_api::make_object<td_api::pollTypeQuiz>(
|
poll_type = td_api::make_object<td_api::pollTypeQuiz>(
|
||||||
correct_option_id,
|
correct_option_id,
|
||||||
get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation, true));
|
get_formatted_text_object(is_local_poll_id(poll_id) ? FormattedText() : poll->explanation, true, -1));
|
||||||
} else {
|
} else {
|
||||||
poll_type = td_api::make_object<td_api::pollTypeRegular>(poll->allow_multiple_answers);
|
poll_type = td_api::make_object<td_api::pollTypeRegular>(poll->allow_multiple_answers);
|
||||||
}
|
}
|
||||||
@ -1581,12 +1581,12 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptr<telegram_api::poll
|
|||||||
|
|
||||||
auto entities =
|
auto entities =
|
||||||
get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll");
|
get_message_entities(td_->contacts_manager_.get(), std::move(poll_results->solution_entities_), "on_get_poll");
|
||||||
auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, false);
|
auto status = fix_formatted_text(poll_results->solution_, entities, true, true, true, true, false);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
if (!clean_input_string(poll_results->solution_)) {
|
if (!clean_input_string(poll_results->solution_)) {
|
||||||
poll_results->solution_.clear();
|
poll_results->solution_.clear();
|
||||||
}
|
}
|
||||||
entities = find_entities(poll_results->solution_, true);
|
entities = find_entities(poll_results->solution_, true, true);
|
||||||
}
|
}
|
||||||
FormattedText explanation{std::move(poll_results->solution_), std::move(entities)};
|
FormattedText explanation{std::move(poll_results->solution_), std::move(entities)};
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include "td/utils/format.h"
|
#include "td/utils/format.h"
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/misc.h"
|
#include "td/utils/misc.h"
|
||||||
#include "td/utils/overloaded.h"
|
|
||||||
#include "td/utils/Random.h"
|
#include "td/utils/Random.h"
|
||||||
#include "td/utils/ScopeGuard.h"
|
#include "td/utils/ScopeGuard.h"
|
||||||
#include "td/utils/SliceBuilder.h"
|
#include "td/utils/SliceBuilder.h"
|
||||||
|
@ -441,8 +441,8 @@ void SetSecureValue::start_up() {
|
|||||||
for (auto it = secure_value_.files.begin(); it != secure_value_.files.end();) {
|
for (auto it = secure_value_.files.begin(); it != secure_value_.files.end();) {
|
||||||
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
||||||
bool is_duplicate = false;
|
bool is_duplicate = false;
|
||||||
for (auto pit = secure_value_.files.begin(); pit != it; pit++) {
|
for (auto other_it = secure_value_.files.begin(); other_it != it; ++other_it) {
|
||||||
if (file_id == file_manager->get_file_view(pit->file_id).file_id()) {
|
if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) {
|
||||||
is_duplicate = true;
|
is_duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -458,8 +458,8 @@ void SetSecureValue::start_up() {
|
|||||||
for (auto it = secure_value_.translations.begin(); it != secure_value_.translations.end();) {
|
for (auto it = secure_value_.translations.begin(); it != secure_value_.translations.end();) {
|
||||||
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
auto file_id = file_manager->get_file_view(it->file_id).file_id();
|
||||||
bool is_duplicate = file_id == front_side_file_id || file_id == reverse_side_file_id || file_id == selfie_file_id;
|
bool is_duplicate = file_id == front_side_file_id || file_id == reverse_side_file_id || file_id == selfie_file_id;
|
||||||
for (auto pit = secure_value_.translations.begin(); pit != it; pit++) {
|
for (auto other_it = secure_value_.translations.begin(); other_it != it; ++other_it) {
|
||||||
if (file_id == file_manager->get_file_view(pit->file_id).file_id()) {
|
if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) {
|
||||||
is_duplicate = true;
|
is_duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -182,10 +182,6 @@ struct EncryptedValue {
|
|||||||
BufferSlice data;
|
BufferSlice data;
|
||||||
ValueHash hash;
|
ValueHash hash;
|
||||||
};
|
};
|
||||||
struct EncryptedFile {
|
|
||||||
std::string path;
|
|
||||||
ValueHash hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<EncryptedValue> encrypt_value(const Secret &secret, Slice data);
|
Result<EncryptedValue> encrypt_value(const Secret &secret, Slice data);
|
||||||
Result<ValueHash> encrypt_file(const Secret &secret, std::string src, std::string dest);
|
Result<ValueHash> encrypt_file(const Secret &secret, std::string src, std::string dest);
|
||||||
|
@ -136,8 +136,8 @@ void update_suggested_actions(vector<SuggestedAction> &suggested_actions,
|
|||||||
} else if (old_it == suggested_actions.end() || *new_it < *old_it) {
|
} else if (old_it == suggested_actions.end() || *new_it < *old_it) {
|
||||||
added_actions.push_back(*new_it++);
|
added_actions.push_back(*new_it++);
|
||||||
} else {
|
} else {
|
||||||
old_it++;
|
++old_it;
|
||||||
new_it++;
|
++new_it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CHECK(!added_actions.empty() || !removed_actions.empty());
|
CHECK(!added_actions.empty() || !removed_actions.empty());
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
#include "td/telegram/MessageLinkInfo.h"
|
#include "td/telegram/MessageLinkInfo.h"
|
||||||
#include "td/telegram/MessageSearchFilter.h"
|
#include "td/telegram/MessageSearchFilter.h"
|
||||||
#include "td/telegram/MessagesManager.h"
|
#include "td/telegram/MessagesManager.h"
|
||||||
|
#include "td/telegram/MessageThreadInfo.h"
|
||||||
#include "td/telegram/misc.h"
|
#include "td/telegram/misc.h"
|
||||||
#include "td/telegram/net/ConnectionCreator.h"
|
#include "td/telegram/net/ConnectionCreator.h"
|
||||||
#include "td/telegram/net/DcId.h"
|
#include "td/telegram/net/DcId.h"
|
||||||
@ -440,67 +441,6 @@ class GetInviteTextQuery final : public Td::ResultHandler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetDeepLinkInfoQuery final : public Td::ResultHandler {
|
|
||||||
Promise<td_api::object_ptr<td_api::deepLinkInfo>> promise_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit GetDeepLinkInfoQuery(Promise<td_api::object_ptr<td_api::deepLinkInfo>> &&promise)
|
|
||||||
: promise_(std::move(promise)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void send(Slice link) {
|
|
||||||
Slice link_scheme("tg:");
|
|
||||||
if (begins_with(link, link_scheme)) {
|
|
||||||
link.remove_prefix(link_scheme.size());
|
|
||||||
if (begins_with(link, "//")) {
|
|
||||||
link.remove_prefix(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t pos = 0;
|
|
||||||
while (pos < link.size() && link[pos] != '/' && link[pos] != '?' && link[pos] != '#') {
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
link.truncate(pos);
|
|
||||||
send_query(G()->net_query_creator().create_unauth(telegram_api::help_getDeepLinkInfo(link.str())));
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_result(uint64 id, BufferSlice packet) final {
|
|
||||||
auto result_ptr = fetch_result<telegram_api::help_getDeepLinkInfo>(packet);
|
|
||||||
if (result_ptr.is_error()) {
|
|
||||||
return on_error(id, result_ptr.move_as_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = result_ptr.move_as_ok();
|
|
||||||
switch (result->get_id()) {
|
|
||||||
case telegram_api::help_deepLinkInfoEmpty::ID:
|
|
||||||
return promise_.set_value(nullptr);
|
|
||||||
case telegram_api::help_deepLinkInfo::ID: {
|
|
||||||
auto info = telegram_api::move_object_as<telegram_api::help_deepLinkInfo>(result);
|
|
||||||
bool need_update = (info->flags_ & telegram_api::help_deepLinkInfo::UPDATE_APP_MASK) != 0;
|
|
||||||
|
|
||||||
auto entities = get_message_entities(nullptr, std::move(info->entities_), "GetDeepLinkInfoQuery");
|
|
||||||
auto status = fix_formatted_text(info->message_, entities, true, true, true, true);
|
|
||||||
if (status.is_error()) {
|
|
||||||
LOG(ERROR) << "Receive error " << status << " while parsing deep link info " << info->message_;
|
|
||||||
if (!clean_input_string(info->message_)) {
|
|
||||||
info->message_.clear();
|
|
||||||
}
|
|
||||||
entities = find_entities(info->message_, true);
|
|
||||||
}
|
|
||||||
FormattedText text{std::move(info->message_), std::move(entities)};
|
|
||||||
return promise_.set_value(
|
|
||||||
td_api::make_object<td_api::deepLinkInfo>(get_formatted_text_object(text, true), need_update));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_error(uint64 id, Status status) final {
|
|
||||||
promise_.set_error(std::move(status));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SaveAppLogQuery final : public Td::ResultHandler {
|
class SaveAppLogQuery final : public Td::ResultHandler {
|
||||||
Promise<Unit> promise_;
|
Promise<Unit> promise_;
|
||||||
|
|
||||||
@ -1067,13 +1007,13 @@ class GetRepliedMessageRequest final : public RequestOnceActor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetMessageThreadRequest final : public RequestActor<MessagesManager::MessageThreadInfo> {
|
class GetMessageThreadRequest final : public RequestActor<MessageThreadInfo> {
|
||||||
DialogId dialog_id_;
|
DialogId dialog_id_;
|
||||||
MessageId message_id_;
|
MessageId message_id_;
|
||||||
|
|
||||||
MessagesManager::MessageThreadInfo message_thread_info_;
|
MessageThreadInfo message_thread_info_;
|
||||||
|
|
||||||
void do_run(Promise<MessagesManager::MessageThreadInfo> &&promise) final {
|
void do_run(Promise<MessageThreadInfo> &&promise) final {
|
||||||
if (get_tries() < 2) {
|
if (get_tries() < 2) {
|
||||||
promise.set_value(std::move(message_thread_info_));
|
promise.set_value(std::move(message_thread_info_));
|
||||||
return;
|
return;
|
||||||
@ -1081,7 +1021,7 @@ class GetMessageThreadRequest final : public RequestActor<MessagesManager::Messa
|
|||||||
td->messages_manager_->get_message_thread(dialog_id_, message_id_, std::move(promise));
|
td->messages_manager_->get_message_thread(dialog_id_, message_id_, std::move(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_set_result(MessagesManager::MessageThreadInfo &&result) final {
|
void do_set_result(MessageThreadInfo &&result) final {
|
||||||
message_thread_info_ = std::move(result);
|
message_thread_info_ = std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4042,6 +3982,7 @@ void Td::close_impl(bool destroy_flag) {
|
|||||||
close_flag_ = 1;
|
close_flag_ = 1;
|
||||||
G()->set_close_flag();
|
G()->set_close_flag();
|
||||||
send_closure(auth_manager_actor_, &AuthManager::on_closing, destroy_flag);
|
send_closure(auth_manager_actor_, &AuthManager::on_closing, destroy_flag);
|
||||||
|
updates_manager_->timeout_expired();
|
||||||
|
|
||||||
// wait till all request_actors will stop.
|
// wait till all request_actors will stop.
|
||||||
request_actors_.clear();
|
request_actors_.clear();
|
||||||
@ -8347,7 +8288,7 @@ void Td::on_request(uint64 id, const td_api::getApplicationDownloadLink &request
|
|||||||
void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) {
|
void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) {
|
||||||
CLEAN_INPUT_STRING(request.link_);
|
CLEAN_INPUT_STRING(request.link_);
|
||||||
CREATE_REQUEST_PROMISE();
|
CREATE_REQUEST_PROMISE();
|
||||||
create_handler<GetDeepLinkInfoQuery>(std::move(promise))->send(request.link_);
|
link_manager_->get_deep_link_info(request.link_, std::move(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Td::on_request(uint64 id, const td_api::getApplicationConfig &request) {
|
void Td::on_request(uint64 id, const td_api::getApplicationConfig &request) {
|
||||||
@ -8510,8 +8451,9 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getTextEn
|
|||||||
if (!check_utf8(request.text_)) {
|
if (!check_utf8(request.text_)) {
|
||||||
return make_error(400, "Text must be encoded in UTF-8");
|
return make_error(400, "Text must be encoded in UTF-8");
|
||||||
}
|
}
|
||||||
auto text_entities = find_entities(request.text_, false);
|
auto text_entities = find_entities(request.text_, false, false);
|
||||||
return make_tl_object<td_api::textEntities>(get_text_entities_object(text_entities, false));
|
return make_tl_object<td_api::textEntities>(
|
||||||
|
get_text_entities_object(text_entities, false, std::numeric_limits<int32>::max()));
|
||||||
}
|
}
|
||||||
|
|
||||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseTextEntities &request) {
|
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseTextEntities &request) {
|
||||||
@ -8546,7 +8488,7 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseTextEntiti
|
|||||||
}
|
}
|
||||||
|
|
||||||
return make_tl_object<td_api::formattedText>(std::move(request.text_),
|
return make_tl_object<td_api::formattedText>(std::move(request.text_),
|
||||||
get_text_entities_object(r_entities.ok(), false));
|
get_text_entities_object(r_entities.ok(), false, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &request) {
|
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &request) {
|
||||||
@ -8559,14 +8501,14 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &
|
|||||||
return make_error(400, r_entities.error().message());
|
return make_error(400, r_entities.error().message());
|
||||||
}
|
}
|
||||||
auto entities = r_entities.move_as_ok();
|
auto entities = r_entities.move_as_ok();
|
||||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true);
|
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true, true);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
return make_error(400, status.error().message());
|
return make_error(400, status.error().message());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parsed_text = parse_markdown_v3({std::move(request.text_->text_), std::move(entities)});
|
auto parsed_text = parse_markdown_v3({std::move(request.text_->text_), std::move(entities)});
|
||||||
fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).ensure();
|
fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).ensure();
|
||||||
return get_formatted_text_object(parsed_text, true);
|
return get_formatted_text_object(parsed_text, false, std::numeric_limits<int32>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText &request) {
|
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText &request) {
|
||||||
@ -8579,12 +8521,13 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText
|
|||||||
return make_error(400, r_entities.error().message());
|
return make_error(400, r_entities.error().message());
|
||||||
}
|
}
|
||||||
auto entities = r_entities.move_as_ok();
|
auto entities = r_entities.move_as_ok();
|
||||||
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true);
|
auto status = fix_formatted_text(request.text_->text_, entities, true, true, true, true, true);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
return make_error(400, status.error().message());
|
return make_error(400, status.error().message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_formatted_text_object(get_markdown_v3({std::move(request.text_->text_), std::move(entities)}), true);
|
return get_formatted_text_object(get_markdown_v3({std::move(request.text_->text_), std::move(entities)}), false,
|
||||||
|
std::numeric_limits<int32>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getFileMimeType &request) {
|
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getFileMimeType &request) {
|
||||||
|
@ -95,12 +95,12 @@ TermsOfService::TermsOfService(telegram_api::object_ptr<telegram_api::help_terms
|
|||||||
|
|
||||||
id_ = std::move(terms->id_->data_);
|
id_ = std::move(terms->id_->data_);
|
||||||
auto entities = get_message_entities(nullptr, std::move(terms->entities_), "TermsOfService");
|
auto entities = get_message_entities(nullptr, std::move(terms->entities_), "TermsOfService");
|
||||||
auto status = fix_formatted_text(terms->text_, entities, true, true, true, false);
|
auto status = fix_formatted_text(terms->text_, entities, true, true, true, true, false);
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
if (!clean_input_string(terms->text_)) {
|
if (!clean_input_string(terms->text_)) {
|
||||||
terms->text_.clear();
|
terms->text_.clear();
|
||||||
}
|
}
|
||||||
entities = find_entities(terms->text_, true);
|
entities = find_entities(terms->text_, true, true);
|
||||||
}
|
}
|
||||||
if (terms->text_.empty()) {
|
if (terms->text_.empty()) {
|
||||||
id_.clear();
|
id_.clear();
|
||||||
|
@ -42,7 +42,7 @@ class TermsOfService {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return td_api::make_object<td_api::termsOfService>(get_formatted_text_object(text_, true), min_user_age_,
|
return td_api::make_object<td_api::termsOfService>(get_formatted_text_object(text_, true, -1), min_user_age_,
|
||||||
show_popup_);
|
show_popup_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -95,7 +95,7 @@ class UpdatesManager final : public Actor {
|
|||||||
void on_get_updates(tl_object_ptr<telegram_api::Updates> &&updates_ptr, Promise<Unit> &&promise);
|
void on_get_updates(tl_object_ptr<telegram_api::Updates> &&updates_ptr, Promise<Unit> &&promise);
|
||||||
|
|
||||||
void add_pending_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 new_pts, int32 pts_count,
|
void add_pending_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 new_pts, int32 pts_count,
|
||||||
Promise<Unit> &&promise, const char *source);
|
double receive_time, Promise<Unit> &&promise, const char *source);
|
||||||
|
|
||||||
static std::unordered_set<int64> get_sent_messages_random_ids(const telegram_api::Updates *updates_ptr);
|
static std::unordered_set<int64> get_sent_messages_random_ids(const telegram_api::Updates *updates_ptr);
|
||||||
|
|
||||||
@ -122,9 +122,13 @@ class UpdatesManager final : public Actor {
|
|||||||
return running_get_difference_;
|
return running_get_difference_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void timeout_expired() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int32 FORCED_GET_DIFFERENCE_PTS_DIFF = 100000;
|
static constexpr int32 FORCED_GET_DIFFERENCE_PTS_DIFF = 100000;
|
||||||
|
static constexpr int32 GAP_TIMEOUT_UPDATE_COUNT = 20;
|
||||||
static const double MAX_UNFILLED_GAP_TIME;
|
static const double MAX_UNFILLED_GAP_TIME;
|
||||||
|
static const double MAX_PTS_SAVE_DELAY;
|
||||||
static constexpr bool DROP_PTS_UPDATES = false;
|
static constexpr bool DROP_PTS_UPDATES = false;
|
||||||
|
|
||||||
friend class OnUpdate;
|
friend class OnUpdate;
|
||||||
@ -134,10 +138,16 @@ class UpdatesManager final : public Actor {
|
|||||||
tl_object_ptr<telegram_api::Update> update;
|
tl_object_ptr<telegram_api::Update> update;
|
||||||
int32 pts;
|
int32 pts;
|
||||||
int32 pts_count;
|
int32 pts_count;
|
||||||
|
double receive_time;
|
||||||
Promise<Unit> promise;
|
Promise<Unit> promise;
|
||||||
|
|
||||||
PendingPtsUpdate(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count, Promise<Unit> &&promise)
|
PendingPtsUpdate(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count, double receive_time,
|
||||||
: update(std::move(update)), pts(pts), pts_count(pts_count), promise(std::move(promise)) {
|
Promise<Unit> &&promise)
|
||||||
|
: update(std::move(update))
|
||||||
|
, pts(pts)
|
||||||
|
, pts_count(pts_count)
|
||||||
|
, receive_time(receive_time)
|
||||||
|
, promise(std::move(promise)) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,17 +156,24 @@ class UpdatesManager final : public Actor {
|
|||||||
int32 seq_begin;
|
int32 seq_begin;
|
||||||
int32 seq_end;
|
int32 seq_end;
|
||||||
int32 date;
|
int32 date;
|
||||||
|
double receive_time;
|
||||||
vector<tl_object_ptr<telegram_api::Update>> updates;
|
vector<tl_object_ptr<telegram_api::Update>> updates;
|
||||||
Promise<Unit> promise;
|
Promise<Unit> promise;
|
||||||
|
|
||||||
PendingSeqUpdates(int32 seq_begin, int32 seq_end, int32 date, vector<tl_object_ptr<telegram_api::Update>> &&updates,
|
PendingSeqUpdates(int32 seq_begin, int32 seq_end, int32 date, double receive_time,
|
||||||
Promise<Unit> &&promise)
|
vector<tl_object_ptr<telegram_api::Update>> &&updates, Promise<Unit> &&promise)
|
||||||
: seq_begin(seq_begin), seq_end(seq_end), date(date), updates(std::move(updates)), promise(std::move(promise)) {
|
: seq_begin(seq_begin)
|
||||||
|
, seq_end(seq_end)
|
||||||
|
, date(date)
|
||||||
|
, receive_time(receive_time)
|
||||||
|
, updates(std::move(updates))
|
||||||
|
, promise(std::move(promise)) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PendingQtsUpdate {
|
class PendingQtsUpdate {
|
||||||
public:
|
public:
|
||||||
|
double receive_time;
|
||||||
tl_object_ptr<telegram_api::Update> update;
|
tl_object_ptr<telegram_api::Update> update;
|
||||||
vector<Promise<Unit>> promises;
|
vector<Promise<Unit>> promises;
|
||||||
};
|
};
|
||||||
@ -170,6 +187,11 @@ class UpdatesManager final : public Actor {
|
|||||||
int32 seq_ = 0;
|
int32 seq_ = 0;
|
||||||
string date_source_ = "nowhere";
|
string date_source_ = "nowhere";
|
||||||
|
|
||||||
|
double last_pts_save_time_ = 0;
|
||||||
|
double last_qts_save_time_ = 0;
|
||||||
|
int32 pending_pts_ = 0;
|
||||||
|
int32 pending_qts_ = 0;
|
||||||
|
|
||||||
int32 short_update_date_ = 0;
|
int32 short_update_date_ = 0;
|
||||||
|
|
||||||
int32 accumulated_pts_count_ = 0;
|
int32 accumulated_pts_count_ = 0;
|
||||||
@ -243,13 +265,13 @@ class UpdatesManager final : public Actor {
|
|||||||
void add_pending_qts_update(tl_object_ptr<telegram_api::Update> &&update, int32 qts, Promise<Unit> &&promise);
|
void add_pending_qts_update(tl_object_ptr<telegram_api::Update> &&update, int32 qts, Promise<Unit> &&promise);
|
||||||
|
|
||||||
void on_pending_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, int32 seq_begin, int32 seq_end,
|
void on_pending_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, int32 seq_begin, int32 seq_end,
|
||||||
int32 date, Promise<Unit> &&promise, const char *source);
|
int32 date, double receive_time, Promise<Unit> &&promise, const char *source);
|
||||||
|
|
||||||
void process_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, bool force_apply,
|
void process_updates(vector<tl_object_ptr<telegram_api::Update>> &&updates, bool force_apply,
|
||||||
Promise<Unit> &&promise);
|
Promise<Unit> &&promise);
|
||||||
|
|
||||||
void postpone_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count,
|
void postpone_pts_update(tl_object_ptr<telegram_api::Update> &&update, int32 pts, int32 pts_count,
|
||||||
Promise<Unit> &&promise);
|
double receive_time, Promise<Unit> &&promise);
|
||||||
|
|
||||||
void process_pts_update(tl_object_ptr<telegram_api::Update> &&update);
|
void process_pts_update(tl_object_ptr<telegram_api::Update> &&update);
|
||||||
|
|
||||||
@ -258,14 +280,18 @@ class UpdatesManager final : public Actor {
|
|||||||
|
|
||||||
void process_qts_update(tl_object_ptr<telegram_api::Update> &&update_ptr, int32 qts, Promise<Unit> &&promise);
|
void process_qts_update(tl_object_ptr<telegram_api::Update> &&update_ptr, int32 qts, Promise<Unit> &&promise);
|
||||||
|
|
||||||
|
void process_all_pending_pts_updates();
|
||||||
|
|
||||||
|
void drop_all_pending_pts_updates();
|
||||||
|
|
||||||
|
void process_postponed_pts_updates();
|
||||||
|
|
||||||
void process_pending_pts_updates();
|
void process_pending_pts_updates();
|
||||||
|
|
||||||
void process_pending_seq_updates();
|
void process_pending_seq_updates();
|
||||||
|
|
||||||
void process_pending_qts_updates();
|
void process_pending_qts_updates();
|
||||||
|
|
||||||
void drop_pending_pts_updates();
|
|
||||||
|
|
||||||
static void fill_pts_gap(void *td);
|
static void fill_pts_gap(void *td);
|
||||||
|
|
||||||
static void fill_seq_gap(void *td);
|
static void fill_seq_gap(void *td);
|
||||||
@ -292,8 +318,6 @@ class UpdatesManager final : public Actor {
|
|||||||
|
|
||||||
void after_get_difference();
|
void after_get_difference();
|
||||||
|
|
||||||
int32 get_min_pending_pts() const;
|
|
||||||
|
|
||||||
static bool have_update_pts_changed(const vector<tl_object_ptr<telegram_api::Update>> &updates);
|
static bool have_update_pts_changed(const vector<tl_object_ptr<telegram_api::Update>> &updates);
|
||||||
|
|
||||||
static bool check_pts_update_dialog_id(DialogId dialog_id);
|
static bool check_pts_update_dialog_id(DialogId dialog_id);
|
||||||
|
@ -825,7 +825,7 @@ int64 WebPagesManager::get_web_page_preview(td_api::object_ptr<td_api::formatted
|
|||||||
}
|
}
|
||||||
auto entities = r_entities.move_as_ok();
|
auto entities = r_entities.move_as_ok();
|
||||||
|
|
||||||
auto result = fix_formatted_text(text->text_, entities, true, false, true, false);
|
auto result = fix_formatted_text(text->text_, entities, true, false, true, true, false);
|
||||||
if (result.is_error() || text->text_.empty()) {
|
if (result.is_error() || text->text_.empty()) {
|
||||||
promise.set_value(Unit());
|
promise.set_value(Unit());
|
||||||
return 0;
|
return 0;
|
||||||
@ -1225,7 +1225,7 @@ tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId we
|
|||||||
|
|
||||||
FormattedText description;
|
FormattedText description;
|
||||||
description.text = web_page->description;
|
description.text = web_page->description;
|
||||||
description.entities = find_entities(web_page->description, true);
|
description.entities = find_entities(web_page->description, true, false);
|
||||||
|
|
||||||
auto r_url = parse_url(web_page->display_url);
|
auto r_url = parse_url(web_page->display_url);
|
||||||
if (r_url.is_ok()) {
|
if (r_url.is_ok()) {
|
||||||
@ -1294,11 +1294,12 @@ tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId we
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto duration = get_web_page_media_duration(web_page);
|
||||||
return make_tl_object<td_api::webPage>(
|
return make_tl_object<td_api::webPage>(
|
||||||
web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title,
|
web_page->url, web_page->display_url, web_page->type, web_page->site_name, web_page->title,
|
||||||
get_formatted_text_object(description, true), get_photo_object(td_->file_manager_.get(), web_page->photo),
|
get_formatted_text_object(description, true, duration == 0 ? std::numeric_limits<int32>::max() : duration),
|
||||||
web_page->embed_url, web_page->embed_type, web_page->embed_dimensions.width, web_page->embed_dimensions.height,
|
get_photo_object(td_->file_manager_.get(), web_page->photo), web_page->embed_url, web_page->embed_type,
|
||||||
web_page->duration, web_page->author,
|
web_page->embed_dimensions.width, web_page->embed_dimensions.height, web_page->duration, web_page->author,
|
||||||
web_page->document.type == Document::Type::Animation
|
web_page->document.type == Document::Type::Animation
|
||||||
? td_->animations_manager_->get_animation_object(web_page->document.file_id, "get_web_page_object")
|
? td_->animations_manager_->get_animation_object(web_page->document.file_id, "get_web_page_object")
|
||||||
: nullptr,
|
: nullptr,
|
||||||
@ -1325,11 +1326,11 @@ tl_object_ptr<td_api::webPage> WebPagesManager::get_web_page_object(WebPageId we
|
|||||||
|
|
||||||
tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_view_object(
|
tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_view_object(
|
||||||
WebPageId web_page_id) const {
|
WebPageId web_page_id) const {
|
||||||
return get_web_page_instant_view_object(get_web_page_instant_view(web_page_id));
|
return get_web_page_instant_view_object(web_page_id, get_web_page_instant_view(web_page_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_view_object(
|
tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_view_object(
|
||||||
const WebPageInstantView *web_page_instant_view) const {
|
WebPageId web_page_id, const WebPageInstantView *web_page_instant_view) const {
|
||||||
if (web_page_instant_view == nullptr) {
|
if (web_page_instant_view == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -1337,10 +1338,12 @@ tl_object_ptr<td_api::webPageInstantView> WebPagesManager::get_web_page_instant_
|
|||||||
LOG(ERROR) << "Trying to get not loaded web page instant view";
|
LOG(ERROR) << "Trying to get not loaded web page instant view";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
auto feedback_link =
|
||||||
|
td_api::make_object<td_api::internalLinkTypeBotStart>("previews", PSTRING() << "webpage" << web_page_id.get());
|
||||||
return td_api::make_object<td_api::webPageInstantView>(
|
return td_api::make_object<td_api::webPageInstantView>(
|
||||||
get_page_block_objects(web_page_instant_view->page_blocks, td_, web_page_instant_view->url),
|
get_page_block_objects(web_page_instant_view->page_blocks, td_, web_page_instant_view->url),
|
||||||
web_page_instant_view->view_count, web_page_instant_view->is_v2 ? 2 : 1, web_page_instant_view->is_rtl,
|
web_page_instant_view->view_count, web_page_instant_view->is_v2 ? 2 : 1, web_page_instant_view->is_rtl,
|
||||||
web_page_instant_view->is_full);
|
web_page_instant_view->is_full, std::move(feedback_link));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebPagesManager::on_web_page_changed(WebPageId web_page_id, bool have_web_page) {
|
void WebPagesManager::on_web_page_changed(WebPageId web_page_id, bool have_web_page) {
|
||||||
@ -1527,7 +1530,7 @@ void WebPagesManager::on_get_web_page_instant_view(WebPage *web_page, tl_object_
|
|||||||
web_page->instant_view.is_loaded = true;
|
web_page->instant_view.is_loaded = true;
|
||||||
|
|
||||||
LOG(DEBUG) << "Receive web page instant view: "
|
LOG(DEBUG) << "Receive web page instant view: "
|
||||||
<< to_string(get_web_page_instant_view_object(&web_page->instant_view));
|
<< to_string(get_web_page_instant_view_object(WebPageId(), &web_page->instant_view));
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebPagesManager::WebPageLogEvent {
|
class WebPagesManager::WebPageLogEvent {
|
||||||
@ -1751,14 +1754,24 @@ string WebPagesManager::get_web_page_search_text(WebPageId web_page_id) const {
|
|||||||
return PSTRING() << web_page->title + " " + web_page->description;
|
return PSTRING() << web_page->title + " " + web_page->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 WebPagesManager::get_web_page_duration(WebPageId web_page_id) const {
|
int32 WebPagesManager::get_web_page_media_duration(WebPageId web_page_id) const {
|
||||||
const WebPage *web_page = get_web_page(web_page_id);
|
const WebPage *web_page = get_web_page(web_page_id);
|
||||||
if (web_page == nullptr) {
|
if (web_page == nullptr) {
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
return get_web_page_media_duration(web_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 WebPagesManager::get_web_page_media_duration(const WebPage *web_page) {
|
||||||
|
if (web_page->document.type == Document::Type::Audio || web_page->document.type == Document::Type::Video ||
|
||||||
|
web_page->document.type == Document::Type::VideoNote || web_page->document.type == Document::Type::VoiceNote ||
|
||||||
|
web_page->embed_type == "iframe") {
|
||||||
return web_page->duration;
|
return web_page->duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
vector<FileId> WebPagesManager::get_web_page_file_ids(const WebPage *web_page) const {
|
vector<FileId> WebPagesManager::get_web_page_file_ids(const WebPage *web_page) const {
|
||||||
if (web_page == nullptr) {
|
if (web_page == nullptr) {
|
||||||
return vector<FileId>();
|
return vector<FileId>();
|
||||||
|
@ -91,7 +91,7 @@ class WebPagesManager final : public Actor {
|
|||||||
|
|
||||||
string get_web_page_search_text(WebPageId web_page_id) const;
|
string get_web_page_search_text(WebPageId web_page_id) const;
|
||||||
|
|
||||||
int32 get_web_page_duration(WebPageId web_page_id) const;
|
int32 get_web_page_media_duration(WebPageId web_page_id) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int32 WEBPAGE_FLAG_HAS_TYPE = 1 << 0;
|
static constexpr int32 WEBPAGE_FLAG_HAS_TYPE = 1 << 0;
|
||||||
@ -130,7 +130,7 @@ class WebPagesManager final : public Actor {
|
|||||||
WebPageId get_web_page_instant_view(WebPageId web_page_id, bool force_full, Promise<Unit> &&promise);
|
WebPageId get_web_page_instant_view(WebPageId web_page_id, bool force_full, Promise<Unit> &&promise);
|
||||||
|
|
||||||
tl_object_ptr<td_api::webPageInstantView> get_web_page_instant_view_object(
|
tl_object_ptr<td_api::webPageInstantView> get_web_page_instant_view_object(
|
||||||
const WebPageInstantView *web_page_instant_view) const;
|
WebPageId web_page_id, const WebPageInstantView *web_page_instant_view) const;
|
||||||
|
|
||||||
static void on_pending_web_page_timeout_callback(void *web_pages_manager_ptr, int64 web_page_id);
|
static void on_pending_web_page_timeout_callback(void *web_pages_manager_ptr, int64 web_page_id);
|
||||||
void on_pending_web_page_timeout(WebPageId web_page_id);
|
void on_pending_web_page_timeout(WebPageId web_page_id);
|
||||||
@ -174,6 +174,8 @@ class WebPagesManager final : public Actor {
|
|||||||
|
|
||||||
void tear_down() final;
|
void tear_down() final;
|
||||||
|
|
||||||
|
static int32 get_web_page_media_duration(const WebPage *web_page);
|
||||||
|
|
||||||
FileSourceId get_web_page_file_source_id(WebPage *web_page);
|
FileSourceId get_web_page_file_source_id(WebPage *web_page);
|
||||||
|
|
||||||
vector<FileId> get_web_page_file_ids(const WebPage *web_page) const;
|
vector<FileId> get_web_page_file_ids(const WebPage *web_page) const;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "td/utils/port/Clocks.h"
|
#include "td/utils/port/Clocks.h"
|
||||||
#include "td/utils/port/FileFd.h"
|
#include "td/utils/port/FileFd.h"
|
||||||
#include "td/utils/port/path.h"
|
#include "td/utils/port/path.h"
|
||||||
|
#include "td/utils/port/Stat.h"
|
||||||
#include "td/utils/Random.h"
|
#include "td/utils/Random.h"
|
||||||
#include "td/utils/SliceBuilder.h"
|
#include "td/utils/SliceBuilder.h"
|
||||||
#include "td/utils/StringBuilder.h"
|
#include "td/utils/StringBuilder.h"
|
||||||
|
@ -35,6 +35,9 @@ class Timeout final : public Actor {
|
|||||||
bool has_timeout() const {
|
bool has_timeout() const {
|
||||||
return Actor::has_timeout();
|
return Actor::has_timeout();
|
||||||
}
|
}
|
||||||
|
double get_timeout() const {
|
||||||
|
return Actor::get_timeout();
|
||||||
|
}
|
||||||
void set_timeout_in(double timeout) {
|
void set_timeout_in(double timeout) {
|
||||||
Actor::set_timeout_in(timeout);
|
Actor::set_timeout_in(timeout);
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,7 @@ class Actor : public ObserverBase {
|
|||||||
void stop();
|
void stop();
|
||||||
void do_stop();
|
void do_stop();
|
||||||
bool has_timeout() const;
|
bool has_timeout() const;
|
||||||
|
double get_timeout() const;
|
||||||
void set_timeout_in(double timeout_in);
|
void set_timeout_in(double timeout_in);
|
||||||
void set_timeout_at(double timeout_at);
|
void set_timeout_at(double timeout_at);
|
||||||
void cancel_timeout();
|
void cancel_timeout();
|
||||||
|
@ -54,6 +54,9 @@ inline void Actor::do_stop() {
|
|||||||
inline bool Actor::has_timeout() const {
|
inline bool Actor::has_timeout() const {
|
||||||
return Scheduler::instance()->has_actor_timeout(this);
|
return Scheduler::instance()->has_actor_timeout(this);
|
||||||
}
|
}
|
||||||
|
inline double Actor::get_timeout() const {
|
||||||
|
return Scheduler::instance()->get_actor_timeout(this);
|
||||||
|
}
|
||||||
inline void Actor::set_timeout_in(double timeout_in) {
|
inline void Actor::set_timeout_in(double timeout_in) {
|
||||||
Scheduler::instance()->set_actor_timeout_in(this, timeout_in);
|
Scheduler::instance()->set_actor_timeout_in(this, timeout_in);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ class Scheduler {
|
|||||||
void finish_migrate_actor(Actor *actor);
|
void finish_migrate_actor(Actor *actor);
|
||||||
|
|
||||||
bool has_actor_timeout(const Actor *actor) const;
|
bool has_actor_timeout(const Actor *actor) const;
|
||||||
|
double get_actor_timeout(const Actor *actor) const;
|
||||||
void set_actor_timeout_in(Actor *actor, double timeout);
|
void set_actor_timeout_in(Actor *actor, double timeout);
|
||||||
void set_actor_timeout_at(Actor *actor, double timeout_at);
|
void set_actor_timeout_at(Actor *actor, double timeout_at);
|
||||||
void cancel_actor_timeout(Actor *actor);
|
void cancel_actor_timeout(Actor *actor);
|
||||||
@ -176,6 +177,7 @@ class Scheduler {
|
|||||||
void start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id);
|
void start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id);
|
||||||
|
|
||||||
bool has_actor_timeout(const ActorInfo *actor_info) const;
|
bool has_actor_timeout(const ActorInfo *actor_info) const;
|
||||||
|
double get_actor_timeout(const ActorInfo *actor_info) const;
|
||||||
void set_actor_timeout_in(ActorInfo *actor_info, double timeout);
|
void set_actor_timeout_in(ActorInfo *actor_info, double timeout);
|
||||||
void set_actor_timeout_at(ActorInfo *actor_info, double timeout_at);
|
void set_actor_timeout_at(ActorInfo *actor_info, double timeout_at);
|
||||||
void cancel_actor_timeout(ActorInfo *actor_info);
|
void cancel_actor_timeout(ActorInfo *actor_info);
|
||||||
|
@ -392,6 +392,7 @@ void Scheduler::do_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id) {
|
|||||||
void Scheduler::start_migrate_actor(Actor *actor, int32 dest_sched_id) {
|
void Scheduler::start_migrate_actor(Actor *actor, int32 dest_sched_id) {
|
||||||
start_migrate_actor(actor->get_info(), dest_sched_id);
|
start_migrate_actor(actor->get_info(), dest_sched_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id) {
|
void Scheduler::start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id) {
|
||||||
VLOG(actor) << "Start migrate actor: " << tag("name", actor_info) << tag("ptr", actor_info)
|
VLOG(actor) << "Start migrate actor: " << tag("name", actor_info) << tag("ptr", actor_info)
|
||||||
<< tag("actor_count", actor_count_);
|
<< tag("actor_count", actor_count_);
|
||||||
@ -406,6 +407,11 @@ void Scheduler::start_migrate_actor(ActorInfo *actor_info, int32 dest_sched_id)
|
|||||||
cancel_actor_timeout(actor_info);
|
cancel_actor_timeout(actor_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Scheduler::get_actor_timeout(const ActorInfo *actor_info) const {
|
||||||
|
const HeapNode *heap_node = actor_info->get_heap_node();
|
||||||
|
return heap_node->in_heap() ? timeout_queue_.get_key(heap_node) - Time::now() : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
void Scheduler::set_actor_timeout_in(ActorInfo *actor_info, double timeout) {
|
void Scheduler::set_actor_timeout_in(ActorInfo *actor_info, double timeout) {
|
||||||
if (timeout > 1e10) {
|
if (timeout > 1e10) {
|
||||||
timeout = 1e10;
|
timeout = 1e10;
|
||||||
|
@ -302,6 +302,9 @@ inline void Scheduler::finish_migrate_actor(Actor *actor) {
|
|||||||
inline bool Scheduler::has_actor_timeout(const Actor *actor) const {
|
inline bool Scheduler::has_actor_timeout(const Actor *actor) const {
|
||||||
return has_actor_timeout(actor->get_info());
|
return has_actor_timeout(actor->get_info());
|
||||||
}
|
}
|
||||||
|
inline double Scheduler::get_actor_timeout(const Actor *actor) const {
|
||||||
|
return get_actor_timeout(actor->get_info());
|
||||||
|
}
|
||||||
inline void Scheduler::set_actor_timeout_in(Actor *actor, double timeout) {
|
inline void Scheduler::set_actor_timeout_in(Actor *actor, double timeout) {
|
||||||
set_actor_timeout_in(actor->get_info(), timeout);
|
set_actor_timeout_in(actor->get_info(), timeout);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ struct HeapNode {
|
|||||||
void remove() {
|
void remove() {
|
||||||
pos_ = -1;
|
pos_ = -1;
|
||||||
}
|
}
|
||||||
int pos_ = -1;
|
int32 pos_ = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class KeyT, int K = 4>
|
template <class KeyT, int K = 4>
|
||||||
@ -37,6 +37,12 @@ class KHeap {
|
|||||||
return array_[0].key_;
|
return array_[0].key_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyT get_key(const HeapNode *node) const {
|
||||||
|
size_t pos = static_cast<size_t>(node->pos_);
|
||||||
|
CHECK(pos < array_.size());
|
||||||
|
return array_[pos].key_;
|
||||||
|
}
|
||||||
|
|
||||||
const HeapNode *top() const {
|
const HeapNode *top() const {
|
||||||
return array_[0].node_;
|
return array_[0].node_;
|
||||||
}
|
}
|
||||||
@ -45,19 +51,19 @@ class KHeap {
|
|||||||
CHECK(!empty());
|
CHECK(!empty());
|
||||||
HeapNode *result = array_[0].node_;
|
HeapNode *result = array_[0].node_;
|
||||||
result->remove();
|
result->remove();
|
||||||
erase(0);
|
erase(static_cast<size_t>(0));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(KeyT key, HeapNode *node) {
|
void insert(KeyT key, HeapNode *node) {
|
||||||
CHECK(!node->in_heap());
|
CHECK(!node->in_heap());
|
||||||
array_.push_back({key, node});
|
array_.push_back({key, node});
|
||||||
fix_up(static_cast<int>(array_.size()) - 1);
|
fix_up(array_.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix(KeyT key, HeapNode *node) {
|
void fix(KeyT key, HeapNode *node) {
|
||||||
CHECK(node->in_heap());
|
size_t pos = static_cast<size_t>(node->pos_);
|
||||||
int pos = node->pos_;
|
CHECK(pos < array_.size());
|
||||||
KeyT old_key = array_[pos].key_;
|
KeyT old_key = array_[pos].key_;
|
||||||
array_[pos].key_ = key;
|
array_[pos].key_ = key;
|
||||||
if (key < old_key) {
|
if (key < old_key) {
|
||||||
@ -68,9 +74,9 @@ class KHeap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void erase(HeapNode *node) {
|
void erase(HeapNode *node) {
|
||||||
CHECK(node->in_heap());
|
size_t pos = static_cast<size_t>(node->pos_);
|
||||||
int pos = node->pos_;
|
|
||||||
node->remove();
|
node->remove();
|
||||||
|
CHECK(pos < array_.size());
|
||||||
erase(pos);
|
erase(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,34 +109,34 @@ class KHeap {
|
|||||||
};
|
};
|
||||||
vector<Item> array_;
|
vector<Item> array_;
|
||||||
|
|
||||||
void fix_up(int pos) {
|
void fix_up(size_t pos) {
|
||||||
auto item = array_[pos];
|
auto item = array_[pos];
|
||||||
|
|
||||||
while (pos) {
|
while (pos) {
|
||||||
int parent_pos = (pos - 1) / K;
|
auto parent_pos = (pos - 1) / K;
|
||||||
auto parent_item = array_[parent_pos];
|
auto parent_item = array_[parent_pos];
|
||||||
|
|
||||||
if (parent_item.key_ < item.key_) {
|
if (parent_item.key_ < item.key_) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent_item.node_->pos_ = pos;
|
parent_item.node_->pos_ = static_cast<int32>(pos);
|
||||||
array_[pos] = parent_item;
|
array_[pos] = parent_item;
|
||||||
pos = parent_pos;
|
pos = parent_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.node_->pos_ = pos;
|
item.node_->pos_ = static_cast<int32>(pos);
|
||||||
array_[pos] = item;
|
array_[pos] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_down(int pos) {
|
void fix_down(size_t pos) {
|
||||||
auto item = array_[pos];
|
auto item = array_[pos];
|
||||||
while (true) {
|
while (true) {
|
||||||
int left_pos = pos * K + 1;
|
auto left_pos = pos * K + 1;
|
||||||
int right_pos = min(left_pos + K, static_cast<int>(array_.size()));
|
auto right_pos = min(left_pos + K, array_.size());
|
||||||
int next_pos = pos;
|
auto next_pos = pos;
|
||||||
KeyT next_key = item.key_;
|
KeyT next_key = item.key_;
|
||||||
for (int i = left_pos; i < right_pos; i++) {
|
for (auto i = left_pos; i < right_pos; i++) {
|
||||||
KeyT i_key = array_[i].key_;
|
KeyT i_key = array_[i].key_;
|
||||||
if (i_key < next_key) {
|
if (i_key < next_key) {
|
||||||
next_key = i_key;
|
next_key = i_key;
|
||||||
@ -141,21 +147,24 @@ class KHeap {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
array_[pos] = array_[next_pos];
|
array_[pos] = array_[next_pos];
|
||||||
array_[pos].node_->pos_ = pos;
|
array_[pos].node_->pos_ = static_cast<int32>(pos);
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.node_->pos_ = pos;
|
item.node_->pos_ = static_cast<int32>(pos);
|
||||||
array_[pos] = item;
|
array_[pos] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
void erase(int pos) {
|
void erase(size_t pos) {
|
||||||
array_[pos] = array_.back();
|
array_[pos] = array_.back();
|
||||||
array_.pop_back();
|
array_.pop_back();
|
||||||
if (pos < static_cast<int>(array_.size())) {
|
if (pos < array_.size()) {
|
||||||
fix_down(pos);
|
fix_down(pos);
|
||||||
fix_up(pos);
|
fix_up(pos);
|
||||||
}
|
}
|
||||||
|
if (array_.capacity() > 50 && array_.size() < array_.capacity() / 4) {
|
||||||
|
array_.shrink_to_fit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,6 +53,10 @@ class ThreadSafeCounter {
|
|||||||
return counter_.sum(0);
|
return counter_.sum(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
counter_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThreadSafeMultiCounter<1> counter_;
|
ThreadSafeMultiCounter<1> counter_;
|
||||||
};
|
};
|
||||||
|
192
test/link.cpp
192
test/link.cpp
@ -113,8 +113,8 @@ TEST(Link, parse_internal_link) {
|
|||||||
auto change_phone_number = [] {
|
auto change_phone_number = [] {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeChangePhoneNumber>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeChangePhoneNumber>();
|
||||||
};
|
};
|
||||||
auto chat_invite = [] {
|
auto chat_invite = [](td::string hash) {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeChatInvite>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeChatInvite>("tg:join?invite=" + hash);
|
||||||
};
|
};
|
||||||
auto filter_settings = [] {
|
auto filter_settings = [] {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeFilterSettings>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeFilterSettings>();
|
||||||
@ -125,8 +125,8 @@ TEST(Link, parse_internal_link) {
|
|||||||
auto language_pack = [](td::string language_pack_name) {
|
auto language_pack = [](td::string language_pack_name) {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeLanguagePack>(language_pack_name);
|
return td::td_api::make_object<td::td_api::internalLinkTypeLanguagePack>(language_pack_name);
|
||||||
};
|
};
|
||||||
auto message = [] {
|
auto message = [](td::string url) {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeMessage>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeMessage>(url);
|
||||||
};
|
};
|
||||||
auto message_draft = [](td::string text, bool contains_url) {
|
auto message_draft = [](td::string text, bool contains_url) {
|
||||||
auto formatted_text = td::td_api::make_object<td::td_api::formattedText>();
|
auto formatted_text = td::td_api::make_object<td::td_api::formattedText>();
|
||||||
@ -167,66 +167,80 @@ TEST(Link, parse_internal_link) {
|
|||||||
auto theme_settings = [] {
|
auto theme_settings = [] {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeThemeSettings>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeThemeSettings>();
|
||||||
};
|
};
|
||||||
auto unknown_deep_link = [] {
|
auto unknown_deep_link = [](td::string link) {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeUnknownDeepLink>();
|
return td::td_api::make_object<td::td_api::internalLinkTypeUnknownDeepLink>(link);
|
||||||
};
|
};
|
||||||
auto voice_chat = [](td::string chat_username, td::string invite_hash) {
|
auto voice_chat = [](td::string chat_username, td::string invite_hash) {
|
||||||
return td::td_api::make_object<td::td_api::internalLinkTypeVoiceChat>(chat_username, invite_hash);
|
return td::td_api::make_object<td::td_api::internalLinkTypeVoiceChat>(chat_username, invite_hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_internal_link("t.me/levlam/1", message());
|
parse_internal_link("t.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("telegram.me/levlam/1", message());
|
parse_internal_link("telegram.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("telegram.dog/levlam/1", message());
|
parse_internal_link("telegram.dog/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("www.t.me/levlam/1", message());
|
parse_internal_link("www.t.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("www%2etelegram.me/levlam/1", message());
|
parse_internal_link("www%2etelegram.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("www%2Etelegram.dog/levlam/1", message());
|
parse_internal_link("www%2Etelegram.dog/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("www%252Etelegram.dog/levlam/1", nullptr);
|
parse_internal_link("www%252Etelegram.dog/levlam/1", nullptr);
|
||||||
parse_internal_link("www.t.me/s/s/s/s/s/joinchat/1", chat_invite());
|
parse_internal_link("www.t.me/s/s/s/s/s/joinchat/1", chat_invite("1"));
|
||||||
parse_internal_link("www.t.me/s/%73/%73/s/%73/joinchat/1", chat_invite());
|
parse_internal_link("www.t.me/s/%73/%73/s/%73/joinchat/1", chat_invite("1"));
|
||||||
parse_internal_link("http://t.me/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/joinchat/1", chat_invite());
|
parse_internal_link("http://t.me/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/s/joinchat/1", chat_invite("1"));
|
||||||
parse_internal_link("http://t.me/levlam/1", message());
|
parse_internal_link("http://t.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("https://t.me/levlam/1", message());
|
parse_internal_link("https://t.me/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("hTtp://www.t.me:443/levlam/1", message());
|
parse_internal_link("hTtp://www.t.me:443/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("httPs://t.me:80/levlam/1", message());
|
parse_internal_link("httPs://t.me:80/levlam/1", message("tg:resolve?domain=levlam&post=1"));
|
||||||
parse_internal_link("https://t.me:200/levlam/1", nullptr);
|
parse_internal_link("https://t.me:200/levlam/1", nullptr);
|
||||||
parse_internal_link("http:t.me/levlam/1", nullptr);
|
parse_internal_link("http:t.me/levlam/1", nullptr);
|
||||||
parse_internal_link("t.dog/levlam/1", nullptr);
|
parse_internal_link("t.dog/levlam/1", nullptr);
|
||||||
parse_internal_link("t.m/levlam/1", nullptr);
|
parse_internal_link("t.m/levlam/1", nullptr);
|
||||||
parse_internal_link("t.men/levlam/1", nullptr);
|
parse_internal_link("t.men/levlam/1", nullptr);
|
||||||
|
|
||||||
parse_internal_link("tg:resolve?domain=username&post=12345&single", message());
|
parse_internal_link("tg:resolve?domain=username&post=12345&single",
|
||||||
parse_internal_link("TG://resolve?domain=username&post=12345&single&voicechat=aasd", message());
|
message("tg:resolve?domain=username&post=12345&single"));
|
||||||
|
parse_internal_link("tg:resolve?domain=user%31name&post=%312345&single&comment=456&t=789&single&thread=123%20%31",
|
||||||
|
message("tg:resolve?domain=user1name&post=12345&single&thread=123%201&comment=456&t=789"));
|
||||||
|
parse_internal_link("TG://resolve?domain=username&post=12345&single&voicechat=aasd",
|
||||||
|
message("tg:resolve?domain=username&post=12345&single"));
|
||||||
parse_internal_link("TG://test@resolve?domain=username&post=12345&single", nullptr);
|
parse_internal_link("TG://test@resolve?domain=username&post=12345&single", nullptr);
|
||||||
parse_internal_link("tg:resolve:80?domain=username&post=12345&single", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username&post=12345&single", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username&post=12345&single", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username&post=12345&single", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username&post=12345&single", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username&post=12345&single", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=&post=12345&single", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=&post=12345&single",
|
||||||
|
unknown_deep_link("tg://resolve?domain=&post=12345&single"));
|
||||||
parse_internal_link("tg:resolve?domain=telegram&post=&single", public_chat("telegram"));
|
parse_internal_link("tg:resolve?domain=telegram&post=&single", public_chat("telegram"));
|
||||||
|
|
||||||
parse_internal_link("t.me/username/12345?single", message());
|
parse_internal_link("t.me/username/12345?single", message("tg:resolve?domain=username&post=12345&single"));
|
||||||
parse_internal_link("t.me/username/12345?asdasd", message());
|
parse_internal_link("t.me/username/12345?asdasd", message("tg:resolve?domain=username&post=12345"));
|
||||||
parse_internal_link("t.me/username/12345", message());
|
parse_internal_link("t.me/username/12345", message("tg:resolve?domain=username&post=12345"));
|
||||||
parse_internal_link("t.me/username/12345/", message());
|
parse_internal_link("t.me/username/12345/", message("tg:resolve?domain=username&post=12345"));
|
||||||
parse_internal_link("t.me/username/12345#asdasd", message());
|
parse_internal_link("t.me/username/12345#asdasd", message("tg:resolve?domain=username&post=12345"));
|
||||||
parse_internal_link("t.me/username/12345//?voicechat=&single", message());
|
parse_internal_link("t.me/username/12345//?voicechat=&single",
|
||||||
parse_internal_link("t.me/username/12345/asdasd//asd/asd/asd/?single", message());
|
message("tg:resolve?domain=username&post=12345&single"));
|
||||||
parse_internal_link("t.me/username/1asdasdas/asdasd//asd/asd/asd/?single", message());
|
parse_internal_link("t.me/username/12345/asdasd//asd/asd/asd/?single",
|
||||||
|
message("tg:resolve?domain=username&post=12345&single"));
|
||||||
|
parse_internal_link("t.me/username/1asdasdas/asdasd//asd/asd/asd/?single",
|
||||||
|
message("tg:resolve?domain=username&post=1&single"));
|
||||||
parse_internal_link("t.me/username/asd", public_chat("username"));
|
parse_internal_link("t.me/username/asd", public_chat("username"));
|
||||||
parse_internal_link("t.me/username/0", public_chat("username"));
|
parse_internal_link("t.me/username/0", public_chat("username"));
|
||||||
parse_internal_link("t.me/username/-12345", public_chat("username"));
|
parse_internal_link("t.me/username/-12345", public_chat("username"));
|
||||||
parse_internal_link("t.me//12345?single", nullptr);
|
parse_internal_link("t.me//12345?single", nullptr);
|
||||||
parse_internal_link("https://telegram.dog/telegram/?single", public_chat("telegram"));
|
parse_internal_link("https://telegram.dog/telegram/?single", public_chat("telegram"));
|
||||||
|
|
||||||
parse_internal_link("tg:privatepost?domain=username/12345&single", unknown_deep_link());
|
parse_internal_link("tg:privatepost?domain=username/12345&single",
|
||||||
parse_internal_link("tg:privatepost?channel=username/12345&single", unknown_deep_link());
|
unknown_deep_link("tg://privatepost?domain=username/12345&single"));
|
||||||
parse_internal_link("tg:privatepost?channel=username&msg_id=12345", message());
|
parse_internal_link("tg:privatepost?channel=username/12345&single",
|
||||||
|
unknown_deep_link("tg://privatepost?channel=username/12345&single"));
|
||||||
|
parse_internal_link("tg:privatepost?channel=username&msg_id=12345",
|
||||||
|
message("tg:privatepost?channel=username&msg_id=12345"));
|
||||||
|
|
||||||
parse_internal_link("t.me/c/12345?single", nullptr);
|
parse_internal_link("t.me/c/12345?single", nullptr);
|
||||||
parse_internal_link("t.me/c/1/c?single", nullptr);
|
parse_internal_link("t.me/c/1/c?single", nullptr);
|
||||||
parse_internal_link("t.me/c/c/1?single", nullptr);
|
parse_internal_link("t.me/c/c/1?single", nullptr);
|
||||||
parse_internal_link("t.me/c//1?single", nullptr);
|
parse_internal_link("t.me/c//1?single", nullptr);
|
||||||
parse_internal_link("t.me/c/12345/123?single", message());
|
parse_internal_link("t.me/c/12345/123", message("tg:privatepost?channel=12345&msg_id=123"));
|
||||||
parse_internal_link("t.me/c/12345/123/asd/asd////?single", message());
|
parse_internal_link("t.me/c/12345/123?single", message("tg:privatepost?channel=12345&msg_id=123&single"));
|
||||||
|
parse_internal_link("t.me/c/12345/123/asd/asd////?single", message("tg:privatepost?channel=12345&msg_id=123&single"));
|
||||||
|
parse_internal_link("t.me/c/%312345/%3123?comment=456&t=789&single&thread=123%20%31",
|
||||||
|
message("tg:privatepost?channel=12345&msg_id=123&single&thread=123%201&comment=456&t=789"));
|
||||||
|
|
||||||
parse_internal_link("tg:bg?color=111111#asdasd", background("111111"));
|
parse_internal_link("tg:bg?color=111111#asdasd", background("111111"));
|
||||||
parse_internal_link("tg:bg?color=11111%31", background("111111"));
|
parse_internal_link("tg:bg?color=11111%31", background("111111"));
|
||||||
@ -244,7 +258,8 @@ TEST(Link, parse_internal_link) {
|
|||||||
background("test?mode=12&intensity=2&bg_color=3&rotation=4"));
|
background("test?mode=12&intensity=2&bg_color=3&rotation=4"));
|
||||||
parse_internal_link("tg:bg?mode=12&&slug=test&intensity=2&bg_color=3",
|
parse_internal_link("tg:bg?mode=12&&slug=test&intensity=2&bg_color=3",
|
||||||
background("test?mode=12&intensity=2&bg_color=3"));
|
background("test?mode=12&intensity=2&bg_color=3"));
|
||||||
parse_internal_link("tg:bg?mode=12&intensity=2&bg_color=3", unknown_deep_link());
|
parse_internal_link("tg:bg?mode=12&intensity=2&bg_color=3",
|
||||||
|
unknown_deep_link("tg://bg?mode=12&intensity=2&bg_color=3"));
|
||||||
|
|
||||||
parse_internal_link("tg:bg?color=111111#asdasd", background("111111"));
|
parse_internal_link("tg:bg?color=111111#asdasd", background("111111"));
|
||||||
parse_internal_link("tg:bg?color=11111%31", background("111111"));
|
parse_internal_link("tg:bg?color=11111%31", background("111111"));
|
||||||
@ -262,7 +277,8 @@ TEST(Link, parse_internal_link) {
|
|||||||
background("test?mode=12&intensity=2&bg_color=3&rotation=4"));
|
background("test?mode=12&intensity=2&bg_color=3&rotation=4"));
|
||||||
parse_internal_link("tg:bg?mode=12&&slug=test&intensity=2&bg_color=3",
|
parse_internal_link("tg:bg?mode=12&&slug=test&intensity=2&bg_color=3",
|
||||||
background("test?mode=12&intensity=2&bg_color=3"));
|
background("test?mode=12&intensity=2&bg_color=3"));
|
||||||
parse_internal_link("tg:bg?mode=12&intensity=2&bg_color=3", unknown_deep_link());
|
parse_internal_link("tg:bg?mode=12&intensity=2&bg_color=3",
|
||||||
|
unknown_deep_link("tg://bg?mode=12&intensity=2&bg_color=3"));
|
||||||
|
|
||||||
parse_internal_link("%54.me/bg/111111#asdasd", background("111111"));
|
parse_internal_link("%54.me/bg/111111#asdasd", background("111111"));
|
||||||
parse_internal_link("t.me/bg/11111%31", background("111111"));
|
parse_internal_link("t.me/bg/11111%31", background("111111"));
|
||||||
@ -318,8 +334,8 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("https://t.me/msg?url=@&text=@", message_draft(" @\n@", true));
|
parse_internal_link("https://t.me/msg?url=@&text=@", message_draft(" @\n@", true));
|
||||||
parse_internal_link("https://t.me/msg?url=%FF&text=1", nullptr);
|
parse_internal_link("https://t.me/msg?url=%FF&text=1", nullptr);
|
||||||
|
|
||||||
parse_internal_link("tg:login?codec=12345", unknown_deep_link());
|
parse_internal_link("tg:login?codec=12345", unknown_deep_link("tg://login?codec=12345"));
|
||||||
parse_internal_link("tg:login", unknown_deep_link());
|
parse_internal_link("tg:login", unknown_deep_link("tg://login"));
|
||||||
parse_internal_link("tg:login?code=abacaba", authentication_code("abacaba"));
|
parse_internal_link("tg:login?code=abacaba", authentication_code("abacaba"));
|
||||||
parse_internal_link("tg:login?code=123456", authentication_code("123456"));
|
parse_internal_link("tg:login?code=123456", authentication_code("123456"));
|
||||||
|
|
||||||
@ -338,7 +354,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("t.me/login/123456/123123/12/31/a/s//21w/?asdas#test", authentication_code("123456"));
|
parse_internal_link("t.me/login/123456/123123/12/31/a/s//21w/?asdas#test", authentication_code("123456"));
|
||||||
|
|
||||||
parse_internal_link("tg:login?token=abacaba", qr_code_authentication());
|
parse_internal_link("tg:login?token=abacaba", qr_code_authentication());
|
||||||
parse_internal_link("tg:login?token=", unknown_deep_link());
|
parse_internal_link("tg:login?token=", unknown_deep_link("tg://login?token="));
|
||||||
|
|
||||||
parse_internal_link("t.me/joinchat?invite=abcdef", nullptr);
|
parse_internal_link("t.me/joinchat?invite=abcdef", nullptr);
|
||||||
parse_internal_link("t.me/joinchat", nullptr);
|
parse_internal_link("t.me/joinchat", nullptr);
|
||||||
@ -347,30 +363,34 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("t.me/joinchat?/abcdef", nullptr);
|
parse_internal_link("t.me/joinchat?/abcdef", nullptr);
|
||||||
parse_internal_link("t.me/joinchat/?abcdef", nullptr);
|
parse_internal_link("t.me/joinchat/?abcdef", nullptr);
|
||||||
parse_internal_link("t.me/joinchat/#abcdef", nullptr);
|
parse_internal_link("t.me/joinchat/#abcdef", nullptr);
|
||||||
parse_internal_link("t.me/joinchat/abacaba", chat_invite());
|
parse_internal_link("t.me/joinchat/abacaba", chat_invite("abacaba"));
|
||||||
parse_internal_link("t.me/joinchat/aba%20aba", chat_invite());
|
parse_internal_link("t.me/joinchat/aba%20aba", chat_invite("aba%20aba"));
|
||||||
parse_internal_link("t.me/joinchat/123456a", chat_invite());
|
parse_internal_link("t.me/joinchat/aba%30aba", chat_invite("aba0aba"));
|
||||||
parse_internal_link("t.me/joinchat/12345678901", chat_invite());
|
parse_internal_link("t.me/joinchat/123456a", chat_invite("123456a"));
|
||||||
parse_internal_link("t.me/joinchat/123456", chat_invite());
|
parse_internal_link("t.me/joinchat/12345678901", chat_invite("12345678901"));
|
||||||
parse_internal_link("t.me/joinchat/123456/123123/12/31/a/s//21w/?asdas#test", chat_invite());
|
parse_internal_link("t.me/joinchat/123456", chat_invite("123456"));
|
||||||
|
parse_internal_link("t.me/joinchat/123456/123123/12/31/a/s//21w/?asdas#test", chat_invite("123456"));
|
||||||
|
|
||||||
parse_internal_link("t.me/+?invite=abcdef", nullptr);
|
parse_internal_link("t.me/+?invite=abcdef", nullptr);
|
||||||
parse_internal_link("t.me/+a", chat_invite());
|
parse_internal_link("t.me/+a", chat_invite("a"));
|
||||||
parse_internal_link("t.me/+", nullptr);
|
parse_internal_link("t.me/+", nullptr);
|
||||||
parse_internal_link("t.me/+/abcdef", nullptr);
|
parse_internal_link("t.me/+/abcdef", nullptr);
|
||||||
parse_internal_link("t.me/ ?/abcdef", nullptr);
|
parse_internal_link("t.me/ ?/abcdef", nullptr);
|
||||||
parse_internal_link("t.me/+?abcdef", nullptr);
|
parse_internal_link("t.me/+?abcdef", nullptr);
|
||||||
parse_internal_link("t.me/+#abcdef", nullptr);
|
parse_internal_link("t.me/+#abcdef", nullptr);
|
||||||
parse_internal_link("t.me/ abacaba", chat_invite());
|
parse_internal_link("t.me/ abacaba", chat_invite("abacaba"));
|
||||||
parse_internal_link("t.me/+aba%20aba", chat_invite());
|
parse_internal_link("t.me/+aba%20aba", chat_invite("aba%20aba"));
|
||||||
parse_internal_link("t.me/+123456a", chat_invite());
|
parse_internal_link("t.me/+aba%30aba", chat_invite("aba0aba"));
|
||||||
parse_internal_link("t.me/%2012345678901", chat_invite());
|
parse_internal_link("t.me/+123456a", chat_invite("123456a"));
|
||||||
parse_internal_link("t.me/+123456", chat_invite());
|
parse_internal_link("t.me/%2012345678901", chat_invite("12345678901"));
|
||||||
parse_internal_link("t.me/ 123456/123123/12/31/a/s//21w/?asdas#test", chat_invite());
|
parse_internal_link("t.me/+123456", chat_invite("123456"));
|
||||||
|
parse_internal_link("t.me/ 123456/123123/12/31/a/s//21w/?asdas#test", chat_invite("123456"));
|
||||||
parse_internal_link("t.me/ /123456/123123/12/31/a/s//21w/?asdas#test", nullptr);
|
parse_internal_link("t.me/ /123456/123123/12/31/a/s//21w/?asdas#test", nullptr);
|
||||||
|
|
||||||
parse_internal_link("tg:join?invite=abcdef", chat_invite());
|
parse_internal_link("tg:join?invite=abcdef", chat_invite("abcdef"));
|
||||||
parse_internal_link("tg:join?invite=", unknown_deep_link());
|
parse_internal_link("tg:join?invite=abc%20def", chat_invite("abc%20def"));
|
||||||
|
parse_internal_link("tg://join?invite=abc%30def", chat_invite("abc0def"));
|
||||||
|
parse_internal_link("tg:join?invite=", unknown_deep_link("tg://join?invite="));
|
||||||
|
|
||||||
parse_internal_link("t.me/addstickers?set=abcdef", nullptr);
|
parse_internal_link("t.me/addstickers?set=abcdef", nullptr);
|
||||||
parse_internal_link("t.me/addstickers", nullptr);
|
parse_internal_link("t.me/addstickers", nullptr);
|
||||||
@ -388,7 +408,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
|
|
||||||
parse_internal_link("tg:addstickers?set=abcdef", sticker_set("abcdef"));
|
parse_internal_link("tg:addstickers?set=abcdef", sticker_set("abcdef"));
|
||||||
parse_internal_link("tg:addstickers?set=abc%30ef", sticker_set("abc0ef"));
|
parse_internal_link("tg:addstickers?set=abc%30ef", sticker_set("abc0ef"));
|
||||||
parse_internal_link("tg://addstickers?set=", unknown_deep_link());
|
parse_internal_link("tg://addstickers?set=", unknown_deep_link("tg://addstickers?set="));
|
||||||
|
|
||||||
parse_internal_link("t.me/confirmphone?hash=abc%30ef&phone=", nullptr);
|
parse_internal_link("t.me/confirmphone?hash=abc%30ef&phone=", nullptr);
|
||||||
parse_internal_link("t.me/confirmphone/123456/123123/12/31/a/s//21w/?hash=abc%30ef&phone=123456789",
|
parse_internal_link("t.me/confirmphone/123456/123123/12/31/a/s//21w/?hash=abc%30ef&phone=123456789",
|
||||||
@ -396,13 +416,16 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("t.me/confirmphone?hash=abc%30ef&phone=123456789",
|
parse_internal_link("t.me/confirmphone?hash=abc%30ef&phone=123456789",
|
||||||
phone_number_confirmation("abc0ef", "123456789"));
|
phone_number_confirmation("abc0ef", "123456789"));
|
||||||
|
|
||||||
parse_internal_link("tg:confirmphone?hash=abc%30ef&phone=", unknown_deep_link());
|
parse_internal_link("tg:confirmphone?hash=abc%30ef&phone=",
|
||||||
|
unknown_deep_link("tg://confirmphone?hash=abc%30ef&phone="));
|
||||||
parse_internal_link("tg:confirmphone?hash=abc%30ef&phone=123456789",
|
parse_internal_link("tg:confirmphone?hash=abc%30ef&phone=123456789",
|
||||||
phone_number_confirmation("abc0ef", "123456789"));
|
phone_number_confirmation("abc0ef", "123456789"));
|
||||||
parse_internal_link("tg://confirmphone?hash=123&phone=123456789123456789",
|
parse_internal_link("tg://confirmphone?hash=123&phone=123456789123456789",
|
||||||
phone_number_confirmation("123", "123456789123456789"));
|
phone_number_confirmation("123", "123456789123456789"));
|
||||||
parse_internal_link("tg://confirmphone?hash=&phone=123456789123456789", unknown_deep_link());
|
parse_internal_link("tg://confirmphone?hash=&phone=123456789123456789",
|
||||||
parse_internal_link("tg://confirmphone?hash=123456789123456789&phone=", unknown_deep_link());
|
unknown_deep_link("tg://confirmphone?hash=&phone=123456789123456789"));
|
||||||
|
parse_internal_link("tg://confirmphone?hash=123456789123456789&phone=",
|
||||||
|
unknown_deep_link("tg://confirmphone?hash=123456789123456789&phone="));
|
||||||
|
|
||||||
parse_internal_link("t.me/setlanguage?lang=abcdef", nullptr);
|
parse_internal_link("t.me/setlanguage?lang=abcdef", nullptr);
|
||||||
parse_internal_link("t.me/setlanguage", nullptr);
|
parse_internal_link("t.me/setlanguage", nullptr);
|
||||||
@ -420,7 +443,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
|
|
||||||
parse_internal_link("tg:setlanguage?lang=abcdef", language_pack("abcdef"));
|
parse_internal_link("tg:setlanguage?lang=abcdef", language_pack("abcdef"));
|
||||||
parse_internal_link("tg:setlanguage?lang=abc%30ef", language_pack("abc0ef"));
|
parse_internal_link("tg:setlanguage?lang=abc%30ef", language_pack("abc0ef"));
|
||||||
parse_internal_link("tg://setlanguage?lang=", unknown_deep_link());
|
parse_internal_link("tg://setlanguage?lang=", unknown_deep_link("tg://setlanguage?lang="));
|
||||||
|
|
||||||
parse_internal_link("t.me/addtheme?slug=abcdef", nullptr);
|
parse_internal_link("t.me/addtheme?slug=abcdef", nullptr);
|
||||||
parse_internal_link("t.me/addtheme", nullptr);
|
parse_internal_link("t.me/addtheme", nullptr);
|
||||||
@ -438,7 +461,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
|
|
||||||
parse_internal_link("tg:addtheme?slug=abcdef", theme("abcdef"));
|
parse_internal_link("tg:addtheme?slug=abcdef", theme("abcdef"));
|
||||||
parse_internal_link("tg:addtheme?slug=abc%30ef", theme("abc0ef"));
|
parse_internal_link("tg:addtheme?slug=abc%30ef", theme("abc0ef"));
|
||||||
parse_internal_link("tg://addtheme?slug=", unknown_deep_link());
|
parse_internal_link("tg://addtheme?slug=", unknown_deep_link("tg://addtheme?slug="));
|
||||||
|
|
||||||
parse_internal_link("t.me/proxy?server=1.2.3.4&port=80&secret=1234567890abcdef1234567890ABCDEF",
|
parse_internal_link("t.me/proxy?server=1.2.3.4&port=80&secret=1234567890abcdef1234567890ABCDEF",
|
||||||
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
||||||
@ -467,16 +490,19 @@ TEST(Link, parse_internal_link) {
|
|||||||
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
||||||
parse_internal_link("tg:proxy?server=1.2.3.4&port=80adasdas&secret=1234567890abcdef1234567890ABCDEF",
|
parse_internal_link("tg:proxy?server=1.2.3.4&port=80adasdas&secret=1234567890abcdef1234567890ABCDEF",
|
||||||
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
proxy_mtproto("1.2.3.4", 80, "1234567890abcdef1234567890ABCDEF"));
|
||||||
parse_internal_link("tg:proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF",
|
parse_internal_link(
|
||||||
unknown_deep_link());
|
"tg:proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF",
|
||||||
parse_internal_link("tg:proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF",
|
unknown_deep_link("tg://proxy?server=1.2.3.4&port=adasdas&secret=1234567890abcdef1234567890ABCDEF"));
|
||||||
unknown_deep_link());
|
parse_internal_link(
|
||||||
|
"tg:proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF",
|
||||||
|
unknown_deep_link("tg://proxy?server=1.2.3.4&port=65536&secret=1234567890abcdef1234567890ABCDEF"));
|
||||||
parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=1234567890abcdef1234567890ABCDEF",
|
parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=1234567890abcdef1234567890ABCDEF",
|
||||||
proxy_mtproto("google.com", 80, "1234567890abcdef1234567890ABCDEF"));
|
proxy_mtproto("google.com", 80, "1234567890abcdef1234567890ABCDEF"));
|
||||||
parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=dd1234567890abcdef1234567890ABCDEF",
|
parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=dd1234567890abcdef1234567890ABCDEF",
|
||||||
proxy_mtproto("google.com", 80, "dd1234567890abcdef1234567890ABCDEF"));
|
proxy_mtproto("google.com", 80, "dd1234567890abcdef1234567890ABCDEF"));
|
||||||
parse_internal_link("tg:proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF",
|
parse_internal_link(
|
||||||
unknown_deep_link());
|
"tg:proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF",
|
||||||
|
unknown_deep_link("tg://proxy?server=google.com&port=8%30&secret=de1234567890abcdef1234567890ABCDEF"));
|
||||||
|
|
||||||
parse_internal_link("t.me/socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", ""));
|
parse_internal_link("t.me/socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", ""));
|
||||||
parse_internal_link("t.me/socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", ""));
|
parse_internal_link("t.me/socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", ""));
|
||||||
@ -489,8 +515,9 @@ TEST(Link, parse_internal_link) {
|
|||||||
|
|
||||||
parse_internal_link("tg:socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", ""));
|
parse_internal_link("tg:socks?server=1.2.3.4&port=80", proxy_socks("1.2.3.4", 80, "", ""));
|
||||||
parse_internal_link("tg:socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", ""));
|
parse_internal_link("tg:socks?server=1.2.3.4&port=80adasdas", proxy_socks("1.2.3.4", 80, "", ""));
|
||||||
parse_internal_link("tg:socks?server=1.2.3.4&port=adasdas", unknown_deep_link());
|
parse_internal_link("tg:socks?server=1.2.3.4&port=adasdas",
|
||||||
parse_internal_link("tg:socks?server=1.2.3.4&port=65536", unknown_deep_link());
|
unknown_deep_link("tg://socks?server=1.2.3.4&port=adasdas"));
|
||||||
|
parse_internal_link("tg:socks?server=1.2.3.4&port=65536", unknown_deep_link("tg://socks?server=1.2.3.4&port=65536"));
|
||||||
parse_internal_link("tg:socks?server=google.com&port=8%30", proxy_socks("google.com", 80, "", ""));
|
parse_internal_link("tg:socks?server=google.com&port=8%30", proxy_socks("google.com", 80, "", ""));
|
||||||
parse_internal_link("tg:socks?server=google.com&port=8%30&user=1&pass=", proxy_socks("google.com", 80, "1", ""));
|
parse_internal_link("tg:socks?server=google.com&port=8%30&user=1&pass=", proxy_socks("google.com", 80, "1", ""));
|
||||||
parse_internal_link("tg:socks?server=google.com&port=8%30&user=&pass=2", proxy_socks("google.com", 80, "", "2"));
|
parse_internal_link("tg:socks?server=google.com&port=8%30&user=&pass=2", proxy_socks("google.com", 80, "", "2"));
|
||||||
@ -502,7 +529,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("tg:resolve:80?domain=username&voicechat=", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username&voicechat=", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username&voicechat=", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username&voicechat=", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username&voicechat=", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username&voicechat=", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=&voicechat=", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=&voicechat=", unknown_deep_link("tg://resolve?domain=&voicechat="));
|
||||||
parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", voice_chat("telegram", "0"));
|
parse_internal_link("tg:resolve?domain=telegram&&&&&&&voicechat=%30", voice_chat("telegram", "0"));
|
||||||
|
|
||||||
parse_internal_link("t.me/username/0/a//s/as?voicechat=", voice_chat("username", ""));
|
parse_internal_link("t.me/username/0/a//s/as?voicechat=", voice_chat("username", ""));
|
||||||
@ -520,7 +547,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("tg:resolve:80?domain=username&start=", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username&start=", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username&start=", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username&start=", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username&start=", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username&start=", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=&start=", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=&start=", unknown_deep_link("tg://resolve?domain=&start="));
|
||||||
parse_internal_link("tg:resolve?domain=telegram&&&&&&&start=%30", bot_start("telegram", "0"));
|
parse_internal_link("tg:resolve?domain=telegram&&&&&&&start=%30", bot_start("telegram", "0"));
|
||||||
|
|
||||||
parse_internal_link("t.me/username/0/a//s/as?start=", bot_start("username", ""));
|
parse_internal_link("t.me/username/0/a//s/as?start=", bot_start("username", ""));
|
||||||
@ -538,7 +565,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("tg:resolve:80?domain=username&startgroup=", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username&startgroup=", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username&startgroup=", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username&startgroup=", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username&startgroup=", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username&startgroup=", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=&startgroup=", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=&startgroup=", unknown_deep_link("tg://resolve?domain=&startgroup="));
|
||||||
parse_internal_link("tg:resolve?domain=telegram&&&&&&&startgroup=%30", bot_start_in_group("telegram", "0"));
|
parse_internal_link("tg:resolve?domain=telegram&&&&&&&startgroup=%30", bot_start_in_group("telegram", "0"));
|
||||||
|
|
||||||
parse_internal_link("t.me/username/0/a//s/as?startgroup=", bot_start_in_group("username", ""));
|
parse_internal_link("t.me/username/0/a//s/as?startgroup=", bot_start_in_group("username", ""));
|
||||||
@ -556,7 +583,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("tg:resolve:80?domain=username&game=asd", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username&game=asd", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username&game=asd", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username&game=asd", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username&game=asd", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username&game=asd", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=&game=asd", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=&game=asd", unknown_deep_link("tg://resolve?domain=&game=asd"));
|
||||||
parse_internal_link("tg:resolve?domain=telegram&&&&&&&game=%30", game("telegram", "0"));
|
parse_internal_link("tg:resolve?domain=telegram&&&&&&&game=%30", game("telegram", "0"));
|
||||||
|
|
||||||
parse_internal_link("t.me/username/0/a//s/as?game=asd", game("username", "asd"));
|
parse_internal_link("t.me/username/0/a//s/as?game=asd", game("username", "asd"));
|
||||||
@ -574,7 +601,7 @@ TEST(Link, parse_internal_link) {
|
|||||||
parse_internal_link("tg:resolve:80?domain=username", nullptr);
|
parse_internal_link("tg:resolve:80?domain=username", nullptr);
|
||||||
parse_internal_link("tg:http://resolve?domain=username", nullptr);
|
parse_internal_link("tg:http://resolve?domain=username", nullptr);
|
||||||
parse_internal_link("tg:https://resolve?domain=username", nullptr);
|
parse_internal_link("tg:https://resolve?domain=username", nullptr);
|
||||||
parse_internal_link("tg:resolve?domain=", unknown_deep_link());
|
parse_internal_link("tg:resolve?domain=", unknown_deep_link("tg://resolve?domain="));
|
||||||
parse_internal_link("tg:resolve?&&&&&&&domain=telegram", public_chat("telegram"));
|
parse_internal_link("tg:resolve?&&&&&&&domain=telegram", public_chat("telegram"));
|
||||||
|
|
||||||
parse_internal_link("t.me/a", public_chat("a"));
|
parse_internal_link("t.me/a", public_chat("a"));
|
||||||
@ -606,16 +633,21 @@ TEST(Link, parse_internal_link) {
|
|||||||
passport_data_request(12345, "asd", "key", "nonce", ""));
|
passport_data_request(12345, "asd", "key", "nonce", ""));
|
||||||
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=asd&payload=nonce",
|
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=asd&payload=nonce",
|
||||||
passport_data_request(12345, "asd", "key", "nonce", ""));
|
passport_data_request(12345, "asd", "key", "nonce", ""));
|
||||||
parse_internal_link("tg://passport?bot_id=0&public_key=key&scope=asd&payload=nonce", unknown_deep_link());
|
parse_internal_link("tg://passport?bot_id=0&public_key=key&scope=asd&payload=nonce",
|
||||||
parse_internal_link("tg://passport?bot_id=-1&public_key=key&scope=asd&payload=nonce", unknown_deep_link());
|
unknown_deep_link("tg://passport?bot_id=0&public_key=key&scope=asd&payload=nonce"));
|
||||||
parse_internal_link("tg://passport?bot_id=12345&public_key=&scope=asd&payload=nonce", unknown_deep_link());
|
parse_internal_link("tg://passport?bot_id=-1&public_key=key&scope=asd&payload=nonce",
|
||||||
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=&payload=nonce", unknown_deep_link());
|
unknown_deep_link("tg://passport?bot_id=-1&public_key=key&scope=asd&payload=nonce"));
|
||||||
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=asd&payload=", unknown_deep_link());
|
parse_internal_link("tg://passport?bot_id=12345&public_key=&scope=asd&payload=nonce",
|
||||||
|
unknown_deep_link("tg://passport?bot_id=12345&public_key=&scope=asd&payload=nonce"));
|
||||||
|
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=&payload=nonce",
|
||||||
|
unknown_deep_link("tg://passport?bot_id=12345&public_key=key&scope=&payload=nonce"));
|
||||||
|
parse_internal_link("tg://passport?bot_id=12345&public_key=key&scope=asd&payload=",
|
||||||
|
unknown_deep_link("tg://passport?bot_id=12345&public_key=key&scope=asd&payload="));
|
||||||
parse_internal_link("t.me/telegrampassport?bot_id=12345&public_key=key&scope=asd&payload=nonce",
|
parse_internal_link("t.me/telegrampassport?bot_id=12345&public_key=key&scope=asd&payload=nonce",
|
||||||
public_chat("telegrampassport"));
|
public_chat("telegrampassport"));
|
||||||
|
|
||||||
parse_internal_link("tg://settings", settings());
|
parse_internal_link("tg://settings", settings());
|
||||||
parse_internal_link("tg://setting", unknown_deep_link());
|
parse_internal_link("tg://setting", unknown_deep_link("tg://setting"));
|
||||||
parse_internal_link("tg://settings?asdsa?D?SADasD?asD", settings());
|
parse_internal_link("tg://settings?asdsa?D?SADasD?asD", settings());
|
||||||
parse_internal_link("tg://settings#test", settings());
|
parse_internal_link("tg://settings#test", settings());
|
||||||
parse_internal_link("tg://settings/#test", settings());
|
parse_internal_link("tg://settings/#test", settings());
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
#include "td/telegram/MessageEntity.h"
|
#include "td/telegram/MessageEntity.h"
|
||||||
|
|
||||||
|
#include "td/utils/algorithm.h"
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/format.h"
|
#include "td/utils/format.h"
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
@ -17,6 +18,7 @@
|
|||||||
#include "td/utils/utf8.h"
|
#include "td/utils/utf8.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
static void check_mention(const td::string &str, const td::vector<td::string> &expected) {
|
static void check_mention(const td::string &str, const td::vector<td::string> &expected) {
|
||||||
auto result_slice = td::find_mentions(str);
|
auto result_slice = td::find_mentions(str);
|
||||||
@ -172,6 +174,48 @@ TEST(MessageEntities, cashtag) {
|
|||||||
check_cashtag(u8"\u2122$ABC\u2122", {"$ABC"});
|
check_cashtag(u8"\u2122$ABC\u2122", {"$ABC"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_media_timestamp(const td::string &str, const td::vector<std::pair<td::string, td::int32>> &expected) {
|
||||||
|
auto result = td::transform(td::find_media_timestamps(str),
|
||||||
|
[](auto &&entity) { return std::make_pair(entity.first.str(), entity.second); });
|
||||||
|
if (result != expected) {
|
||||||
|
LOG(FATAL) << td::tag("text", str) << td::tag("got", td::format::as_array(result))
|
||||||
|
<< td::tag("expected", td::format::as_array(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MessageEntities, media_timestamp) {
|
||||||
|
check_media_timestamp("", {});
|
||||||
|
check_media_timestamp(":", {});
|
||||||
|
check_media_timestamp(":1", {});
|
||||||
|
check_media_timestamp("a:1", {});
|
||||||
|
check_media_timestamp("01", {});
|
||||||
|
check_media_timestamp("01:", {});
|
||||||
|
check_media_timestamp("01::", {});
|
||||||
|
check_media_timestamp("01::", {});
|
||||||
|
check_media_timestamp("a1:1a", {});
|
||||||
|
check_media_timestamp("a1::01a", {});
|
||||||
|
check_media_timestamp("2001:db8::8a2e:f70:13a4", {});
|
||||||
|
check_media_timestamp("0:00", {{"0:00", 0}});
|
||||||
|
check_media_timestamp("+0:00", {{"0:00", 0}});
|
||||||
|
check_media_timestamp("0:00+", {{"0:00", 0}});
|
||||||
|
check_media_timestamp("a0:00", {});
|
||||||
|
check_media_timestamp("0:00a", {});
|
||||||
|
check_media_timestamp("б0:00", {});
|
||||||
|
check_media_timestamp("0:00б", {});
|
||||||
|
check_media_timestamp("_0:00", {});
|
||||||
|
check_media_timestamp("0:00_", {});
|
||||||
|
check_media_timestamp("00:00:00:00", {});
|
||||||
|
check_media_timestamp("1:1:01 1:1:1", {{"1:1:01", 3661}});
|
||||||
|
check_media_timestamp("0:0:00 00:00 000:00 0000:00 00000:00 00:00:00 000:00:00 00:000:00 00:00:000",
|
||||||
|
{{"0:0:00", 0}, {"00:00", 0}, {"000:00", 0}, {"0000:00", 0}, {"00:00:00", 0}});
|
||||||
|
check_media_timestamp("00:0:00 0:00:00 00::00 :00:00 00:00: 00:00:0 00:00:", {{"00:0:00", 0}, {"0:00:00", 0}});
|
||||||
|
check_media_timestamp("1:1:59 1:1:-1 1:1:60", {{"1:1:59", 3719}});
|
||||||
|
check_media_timestamp("1:59:00 1:-1:00 1:60:00", {{"1:59:00", 7140}, {"1:00", 60}});
|
||||||
|
check_media_timestamp("59:59 60:00", {{"59:59", 3599}, {"60:00", 3600}});
|
||||||
|
check_media_timestamp("9999:59 99:59:59 99:60:59", {{"9999:59", 599999}, {"99:59:59", 360000 - 1}});
|
||||||
|
check_media_timestamp("2001:db8::8a2e:f70:13a4", {});
|
||||||
|
}
|
||||||
|
|
||||||
static void check_bank_card_number(const td::string &str, const td::vector<td::string> &expected) {
|
static void check_bank_card_number(const td::string &str, const td::vector<td::string> &expected) {
|
||||||
auto result_slice = td::find_bank_card_numbers(str);
|
auto result_slice = td::find_bank_card_numbers(str);
|
||||||
td::vector<td::string> result;
|
td::vector<td::string> result;
|
||||||
@ -655,16 +699,16 @@ static void check_fix_formatted_text(td::string str, td::vector<td::MessageEntit
|
|||||||
const td::vector<td::MessageEntity> &expected_entities, bool allow_empty = true,
|
const td::vector<td::MessageEntity> &expected_entities, bool allow_empty = true,
|
||||||
bool skip_new_entities = false, bool skip_bot_commands = false,
|
bool skip_new_entities = false, bool skip_bot_commands = false,
|
||||||
bool for_draft = true) {
|
bool for_draft = true) {
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, true, for_draft)
|
||||||
td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_ok());
|
.is_ok());
|
||||||
ASSERT_STREQ(expected_str, str);
|
ASSERT_STREQ(expected_str, str);
|
||||||
ASSERT_EQ(expected_entities, entities);
|
ASSERT_EQ(expected_entities, entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_fix_formatted_text(td::string str, td::vector<td::MessageEntity> entities, bool allow_empty,
|
static void check_fix_formatted_text(td::string str, td::vector<td::MessageEntity> entities, bool allow_empty,
|
||||||
bool skip_new_entities, bool skip_bot_commands, bool for_draft) {
|
bool skip_new_entities, bool skip_bot_commands, bool for_draft) {
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(td::fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, true, for_draft)
|
||||||
fix_formatted_text(str, entities, allow_empty, skip_new_entities, skip_bot_commands, for_draft).is_error());
|
.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MessageEntities, fix_formatted_text) {
|
TEST(MessageEntities, fix_formatted_text) {
|
||||||
@ -689,6 +733,12 @@ TEST(MessageEntities, fix_formatted_text) {
|
|||||||
check_fix_formatted_text(str, {}, false, false, false, false);
|
check_fix_formatted_text(str, {}, false, false, false, false);
|
||||||
check_fix_formatted_text(str, {}, false, false, false, true);
|
check_fix_formatted_text(str, {}, false, false, false, true);
|
||||||
|
|
||||||
|
check_fix_formatted_text(" aba\n ", {}, " aba\n ", {}, true, true, true, true);
|
||||||
|
check_fix_formatted_text(" aba\n ", {}, "aba", {}, true, true, true, false);
|
||||||
|
check_fix_formatted_text(" \n ", {}, "", {}, true, true, true, true);
|
||||||
|
check_fix_formatted_text(" \n ", {}, "", {}, true, true, true, false);
|
||||||
|
check_fix_formatted_text(" \n ", {}, false, true, true, false);
|
||||||
|
|
||||||
str += "a \r\n ";
|
str += "a \r\n ";
|
||||||
fixed_str += "a \n ";
|
fixed_str += "a \n ";
|
||||||
|
|
||||||
@ -1064,7 +1114,7 @@ TEST(MessageEntities, fix_formatted_text) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
auto old_type_mask = get_type_mask(str.size(), entities);
|
auto old_type_mask = get_type_mask(str.size(), entities);
|
||||||
ASSERT_TRUE(td::fix_formatted_text(str, entities, false, false, true, false).is_ok());
|
ASSERT_TRUE(td::fix_formatted_text(str, entities, false, false, true, true, false).is_ok());
|
||||||
auto new_type_mask = get_type_mask(str.size(), entities);
|
auto new_type_mask = get_type_mask(str.size(), entities);
|
||||||
auto splittable_mask = (1 << 5) | (1 << 6) | (1 << 14) | (1 << 15);
|
auto splittable_mask = (1 << 5) | (1 << 6) | (1 << 14) | (1 << 15);
|
||||||
auto pre_mask = (1 << 7) | (1 << 8) | (1 << 9);
|
auto pre_mask = (1 << 7) | (1 << 8) | (1 << 9);
|
||||||
@ -1384,7 +1434,7 @@ static void check_parse_markdown_v3(td::string text, td::vector<td::MessageEntit
|
|||||||
bool fix = false) {
|
bool fix = false) {
|
||||||
auto parsed_text = td::parse_markdown_v3({std::move(text), std::move(entities)});
|
auto parsed_text = td::parse_markdown_v3({std::move(text), std::move(entities)});
|
||||||
if (fix) {
|
if (fix) {
|
||||||
ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok());
|
ASSERT_TRUE(td::fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).is_ok());
|
||||||
}
|
}
|
||||||
ASSERT_STREQ(result_text, parsed_text.text);
|
ASSERT_STREQ(result_text, parsed_text.text);
|
||||||
ASSERT_EQ(result_entities, parsed_text.entities);
|
ASSERT_EQ(result_entities, parsed_text.entities);
|
||||||
@ -1676,9 +1726,9 @@ TEST(MessageEntities, parse_markdown_v3) {
|
|||||||
|
|
||||||
td::FormattedText text{std::move(str), std::move(entities)};
|
td::FormattedText text{std::move(str), std::move(entities)};
|
||||||
while (true) {
|
while (true) {
|
||||||
ASSERT_TRUE(fix_formatted_text(text.text, text.entities, true, true, true, true).is_ok());
|
ASSERT_TRUE(td::fix_formatted_text(text.text, text.entities, true, true, true, true, true).is_ok());
|
||||||
auto parsed_text = td::parse_markdown_v3(text);
|
auto parsed_text = td::parse_markdown_v3(text);
|
||||||
ASSERT_TRUE(fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true).is_ok());
|
ASSERT_TRUE(td::fix_formatted_text(parsed_text.text, parsed_text.entities, true, true, true, true, true).is_ok());
|
||||||
if (parsed_text == text) {
|
if (parsed_text == text) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user