Merge remote-tracking branch 'td/master'
This commit is contained in:
commit
cd4a07a7c7
@ -64,6 +64,18 @@ function(generate_pkgconfig TARGET DESCRIPTION)
|
||||
set(LIBRARIES "Libs.private:${LIBRARIES}\n")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig")
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" CONTENT
|
||||
"prefix=${PREFIX}
|
||||
@ -72,8 +84,8 @@ Name: ${TARGET}
|
||||
Description: ${DESCRIPTION}
|
||||
Version: ${PROJECT_VERSION}
|
||||
|
||||
CFlags: -I\"\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\"
|
||||
Libs: -L\"\${prefix}/${CMAKE_INSTALL_LIBDIR}\" -l${TARGET}
|
||||
CFlags: -I\"${PKGCONFIG_INCLUDEDIR}\"
|
||||
Libs: -L\"${PKGCONFIG_LIBDIR}\" -l${TARGET}
|
||||
${REQUIRES}${LIBRARIES}")
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
|
125
CMake/GetGitRevisionDescription.cmake
Normal file
125
CMake/GetGitRevisionDescription.cmake
Normal file
@ -0,0 +1,125 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar>)
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
#
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if (__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This wil return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while (NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if (cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_git_dir_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
set(${_git_dir_var} "${git_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_CURRENT_SOURCE_DIR}" "${GIT_DIR}")
|
||||
if (_relative_to_source_dir MATCHES "^[.][.]")
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
if (GIT_DIR STREQUAL "")
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if ((NOT IS_DIRECTORY ${GIT_DIR}) AND Git_FOUND)
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (NOT out STREQUAL "")
|
||||
# If out is non-empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
if (NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if (NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
43
CMake/GetGitRevisionDescription.cmake.in
Normal file
43
CMake/GetGitRevisionDescription.cmake.in
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# 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)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if (HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if (EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if (PACKED_REFS MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if (NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
@ -152,6 +152,12 @@ if (CLANG OR GCC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(TD_GIT_REFSPEC TD_GIT_COMMIT_HASH)
|
||||
message(STATUS "Git state: ${TD_GIT_COMMIT_HASH}")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/td/telegram/GitCommitHash.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/td/telegram/GitCommitHash.cpp" @ONLY)
|
||||
|
||||
add_subdirectory(tdtl)
|
||||
|
||||
add_subdirectory(tdutils)
|
||||
@ -567,6 +573,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/FullMessageId.h
|
||||
td/telegram/Game.h
|
||||
td/telegram/GameManager.h
|
||||
td/telegram/GitCommitHash.h
|
||||
td/telegram/Global.h
|
||||
td/telegram/GroupCallId.h
|
||||
td/telegram/GroupCallManager.h
|
||||
@ -738,6 +745,8 @@ set(TDLIB_SOURCE
|
||||
td/telegram/VoiceNotesManager.hpp
|
||||
|
||||
${TL_TD_SCHEME_SOURCE}
|
||||
|
||||
${CMAKE_CURRENT_BINARY_DIR}/td/telegram/GitCommitHash.cpp
|
||||
)
|
||||
|
||||
set(MEMPROF_SOURCE
|
||||
@ -750,22 +759,6 @@ set(MEMPROF_STAT_SOURCE
|
||||
memprof/memprof_stat.h
|
||||
)
|
||||
|
||||
#RULES
|
||||
|
||||
file(MAKE_DIRECTORY auto)
|
||||
|
||||
if (CMAKE_HOST_WIN32)
|
||||
set(GIT_COMMIT_CMD powershell -ExecutionPolicy ByPass ./gen_git_commit_h.ps1)
|
||||
else()
|
||||
set(GIT_COMMIT_CMD ./gen_git_commit_h.sh)
|
||||
endif()
|
||||
|
||||
add_custom_target(git_commit ALL
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND ${GIT_COMMIT_CMD}
|
||||
COMMENT "Generate git_commit.h"
|
||||
)
|
||||
|
||||
#LIBRARIES
|
||||
|
||||
# memprof - simple library for memory usage profiling
|
||||
|
@ -133,7 +133,7 @@ Or you could install `TDLib` and then reference it in your CMakeLists.txt like t
|
||||
find_package(Td 1.8.4 REQUIRED)
|
||||
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||
```
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/tree/master/example/cpp/CMakeLists.txt).
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/CMakeLists.txt).
|
||||
|
||||
<a name="using-java"></a>
|
||||
## Using in Java projects
|
||||
|
@ -260,6 +260,7 @@ class QueryBench final : public td::Benchmark {
|
||||
} else if (type == 4) {
|
||||
int val = 0;
|
||||
send_lambda(client_, [&] { val = n_ * n_; });
|
||||
CHECK(val == n_ * n_);
|
||||
} else if (type == 5) {
|
||||
send_closure(client_, &ClientActor::f_promise,
|
||||
td::PromiseCreator::lambda([actor_id = actor_id(this), n = n_](td::Unit) {
|
||||
|
@ -51,7 +51,7 @@ class Backoff {
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
td::usleep_for(1);
|
||||
return cnt < 500;
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +246,7 @@
|
||||
<option>Ubuntu 16</option>
|
||||
<option>Ubuntu 18</option>
|
||||
<option>Ubuntu 20</option>
|
||||
<option>Ubuntu 22</option>
|
||||
<option>Other</option>
|
||||
</select>
|
||||
<p></p>
|
||||
@ -638,7 +639,7 @@ function onOptionsChanged() {
|
||||
var use_vcpkg = os_windows;
|
||||
|
||||
var use_lto = false;
|
||||
if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Other')))) {
|
||||
if (!use_msvc && language !== 'Java' && language !== 'Kotlin' && (os_mac || (os_linux && (linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22' || linux_distro === 'Other')))) {
|
||||
document.getElementById('buildLtoDiv').style.display = 'block';
|
||||
use_lto = document.getElementById('buildLtoCheckbox').checked;
|
||||
} else {
|
||||
@ -797,6 +798,8 @@ function onOptionsChanged() {
|
||||
return '-6.0';
|
||||
case 'Ubuntu 20':
|
||||
return '-10';
|
||||
case 'Ubuntu 22':
|
||||
return '-14';
|
||||
default:
|
||||
return ''; // use default version
|
||||
}
|
||||
@ -849,6 +852,7 @@ function onOptionsChanged() {
|
||||
case 'Ubuntu 16':
|
||||
case 'Ubuntu 18':
|
||||
case 'Ubuntu 20':
|
||||
case 'Ubuntu 22':
|
||||
if (linux_distro.includes('Debian') && !use_root) {
|
||||
commands.push('su -');
|
||||
}
|
||||
@ -873,7 +877,7 @@ function onOptionsChanged() {
|
||||
}
|
||||
if (use_clang) {
|
||||
packages += ' clang' + getClangVersionSuffix() + ' libc++-dev';
|
||||
if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20') {
|
||||
if (linux_distro === 'Debian 10+' || linux_distro === 'Ubuntu 18' || linux_distro === 'Ubuntu 20' || linux_distro === 'Ubuntu 22') {
|
||||
packages += ' libc++abi-dev';
|
||||
}
|
||||
} else {
|
||||
|
@ -46,16 +46,15 @@ Convenient Python wrappers already exist for our JSON interface.
|
||||
|
||||
If you use Python >= 3.6, take a look at [python-telegram](https://github.com/alexander-akhmetov/python-telegram).
|
||||
The wrapper uses the full power of asyncio, has a good documentation and has several examples. It can be installed through pip or used in a Docker container.
|
||||
You can also try a fork [python-telegram](https://github.com/iTeam-co/python-telegram) of this library.
|
||||
You can also try a fork [python-telegram](https://github.com/iTeam-co/pytglib) of this library.
|
||||
|
||||
If you want to use TDLib with asyncio and Python >= 3.9, take a look at [aiotdlib](https://github.com/pylakey/aiotdlib).
|
||||
This wrapper contains automatically generated fully-documented classes for all TDLib API types and functions and provides set of helper methods which makes work with TDLib much simpler.
|
||||
If you want to use TDLib with asyncio and Python >= 3.9, take a look at [aiotdlib](https://github.com/pylakey/aiotdlib) or [Pytdbot](https://github.com/pytdbot/client).
|
||||
|
||||
For older Python versions you can use [pytdlib](https://github.com/pytdlib/pytdlib).
|
||||
This wrapper contains generator for TDLib API classes and basic interface for interaction with TDLib.
|
||||
|
||||
You can also check out [example/python/tdjson_example.py](https://github.com/tdlight-team/tdlight/tree/master/example/python/tdjson_example.py) and
|
||||
[tdlib-python](https://github.com/JunaidBabu/tdlib-python) for some very basic examples of TDLib JSON interface integration with Python.
|
||||
You can also check out [example/python/tdjson_example.py](https://github.com/tdlight-team/tdlight/blob/master/example/python/tdjson_example.py),
|
||||
[tdlib-python](https://github.com/JunaidBabu/tdlib-python), or [Python Wrapper TDLib](https://github.com/alvhix/pywtdlib) for some basic examples of TDLib JSON interface integration with Python.
|
||||
|
||||
<a name="javascript"></a>
|
||||
## Using TDLib in JavaScript projects
|
||||
@ -63,6 +62,8 @@ You can also check out [example/python/tdjson_example.py](https://github.com/tdl
|
||||
TDLib can be compiled to WebAssembly or asm.js and used in a browser from JavaScript. See [tdweb](https://github.com/tdlight-team/tdlight/tree/master/example/web) as a convenient wrapper for TDLib in a browser
|
||||
and [telegram-react](https://github.com/evgeny-nadymov/telegram-react) as an example of a TDLib-based Telegram client.
|
||||
|
||||
See also [Svelte-tdweb-starter](https://github.com/gennadypolakov/svelte-tdweb-starter) - Svelte wrapper for tdweb, and [Telegram-Photoframe](https://github.com/lukefx/telegram-photoframe) - a web application that displays your prefered group or channel as Photoframe.
|
||||
|
||||
TDLib can be used from Node.js through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
Convenient Node.js wrappers already exist for our JSON interface.
|
||||
@ -70,7 +71,9 @@ For example, take a look at [Airgram](https://github.com/airgram/airgram) – mo
|
||||
at [tdl](https://github.com/Bannerets/tdl), which provides a convenient, fully-asynchronous interface for interaction with TDLib and contains a bunch of examples.
|
||||
|
||||
You can also see [TdNode](https://github.com/puppy0cam/TdNode), [tglib](https://github.com/nodegin/tglib), [node-tdlib](https://github.com/wfjsw/node-tdlib), [tdlnode](https://github.com/fonbah/tdlnode),
|
||||
[Paper Plane](https://github.com/BlackSuited/paper-plane), or [node-tlg](https://github.com/dilongfa/node-tlg) for other examples of TDLib JSON interface integration with Node.js.
|
||||
[Paper Plane](https://github.com/par6n/paper-plane), or [node-tlg](https://github.com/dilongfa/node-tlg) for other examples of TDLib JSON interface integration with Node.js.
|
||||
|
||||
See also the source code of [DIBgram](https://github.com/DIBgram/DIBgram) - an unofficial Telegram web application which looks like Telegram Desktop.
|
||||
|
||||
TDLib can be used also from NativeScript through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
See [nativescript-tglib](https://github.com/arpit2438735/nativescript-tglib) as an example of a NativeScript library for building Telegram clients.
|
||||
@ -94,8 +97,6 @@ We provide a generator for JNI bridge methods and Java classes for all TDLib API
|
||||
See [example/java](https://github.com/tdlight-team/tdlight/tree/master/example/java) for an example of using TDLib from desktop Java along with detailed building and usage instructions.
|
||||
To use TDLib to create Android Java applications, use our [prebuilt library for Android](https://core.telegram.org/tdlib/tdlib.zip).
|
||||
|
||||
You can also see [JTDLib](https://github.com/ErnyTech/JTDLib) for another example of Java wrapper for TDLib.
|
||||
|
||||
<a name="kotlin"></a>
|
||||
## Using TDLib in Kotlin projects
|
||||
|
||||
@ -103,8 +104,7 @@ TDLib can be used from the Kotlin/JVM programming language through same way as i
|
||||
|
||||
You can also use [ktd](https://github.com/whyoleg/ktd) library with Kotlin-specific bindings.
|
||||
|
||||
See also [KtLib](https://github.com/nekohasekai/KtLib), which provides an automatically generated classes for TDLib API, and
|
||||
[td-ktx](https://github.com/tdlibx/td-ktx) - Kotlin coroutines wrapper for TDLib.
|
||||
See also [td-ktx](https://github.com/tdlibx/td-ktx) - Kotlin coroutines wrapper for TDLib.
|
||||
|
||||
<a name="csharp"></a>
|
||||
## Using TDLib in C# projects
|
||||
@ -117,6 +117,8 @@ See [example/csharp](https://github.com/tdlight-team/tdlight/tree/master/example
|
||||
If you want to write a cross-platform C# application using .NET Core, see [tdsharp](https://github.com/egramtel/tdsharp). It uses our [JSON](https://github.com/tdlib/td#using-json) interface,
|
||||
provides an asynchronous interface for interaction with TDLib, automatically generated classes for TDLib API and has some examples.
|
||||
|
||||
You can also use [TDLibCore](https://github.com/ph09nix/TDLibCore) library.
|
||||
|
||||
Also see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featured client rewritten from scratch in C# using TDLib SDK for Universal Windows Platform in less than 2 months,
|
||||
[egram.tel](https://github.com/egramtel/egram.tel) – a cross-platform Telegram client written in C#, .NET Core, ReactiveUI and Avalonia, or
|
||||
[telewear](https://github.com/telewear/telewear) - a Telegram client for Samsung watches.
|
||||
@ -127,10 +129,12 @@ Also see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featu
|
||||
TDLib has a simple and convenient C++11-interface for sending and receiving requests and can be statically linked to your application.
|
||||
|
||||
See [example/cpp](https://github.com/tdlight-team/tdlight/tree/master/example/cpp) for an example of TDLib usage from C++.
|
||||
[td_example.cpp](https://github.com/tdlight-team/tdlight/tree/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
|
||||
[td_example.cpp](https://github.com/tdlight-team/tdlight/blob/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
|
||||
|
||||
See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbour-fernschreiber) and [Depecher](https://github.com/blacksailer/depecher) – Telegram apps for Sailfish OS,
|
||||
[TELEports](https://gitlab.com/ubports/apps/teleports) – a Qt-client for Ubuntu Touch, or [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin, all of which are based on TDLib.
|
||||
[TELEports](https://gitlab.com/ubports/development/apps/teleports) – a Qt-client for Ubuntu Touch, [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin,
|
||||
or [MeeGram](https://github.com/qtinsider/meegram2) - a Telegram client for Nokia N9,
|
||||
[TDLib Native Sciter Extension](https://github.com/RadRussianRus/TDLibNSE) - a Sciter native extension for TDLib's JSON interface, all of which are based on TDLib.
|
||||
|
||||
<a name="swift"></a>
|
||||
## Using TDLib in Swift projects
|
||||
@ -141,6 +145,8 @@ See [example/ios](https://github.com/tdlight-team/tdlight/tree/master/example/io
|
||||
|
||||
See [TDLibKit](https://github.com/Swiftgram/TDLibKit), [tdlib-swift](https://github.com/modestman/tdlib-swift), or [TDLib-iOS](https://github.com/leoMehlig/TDLib-iOS), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See also the source code of [Moc](https://github.com/ggoraa/moc) - a native and powerful macOS and iPadOS Telegram client, optimized for moderating large communities and personal use.
|
||||
|
||||
See [example/swift](https://github.com/tdlight-team/tdlight/tree/master/example/swift) for an example of a macOS Swift application.
|
||||
|
||||
<a name="objective-c"></a>
|
||||
@ -164,9 +170,12 @@ See [tdlib-lazarus](https://github.com/dieletro/tdlib-lazarus) for an example of
|
||||
|
||||
TDLib can be used from the Dart programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and a Dart Native Extension or Dart FFI.
|
||||
|
||||
See [dart_tdlib](https://github.com/periodicaidan/dart_tdlib) or [Dart wrapper for TDLib](https://github.com/tdlight-team/tdlight/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49) for an example of a TDLib Dart bindings through FFI.
|
||||
See [dart_tdlib](https://github.com/periodicaidan/dart_tdlib), [flutter_libtdjson](https://github.com/up9cloud/flutter_libtdjson), [Dart wrapper for TDLib](https://github.com/tdlib/td/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49), or [tdlib_bindings](https://github.com/lesnitsky/tdlib_bindings) for an example of a TDLib Dart bindings through FFI.
|
||||
|
||||
See [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib), [tdlib-dart](https://github.com/drewpayment/tdlib-dart), [tdlib-dart](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
|
||||
See [Telegram Client library](https://github.com/azkadev/telegram_client), [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib),
|
||||
[tdlib-dart](https://github.com/drewpayment/tdlib-dart), [FluGram](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
|
||||
|
||||
See also [f-Telegram](https://github.com/evgfilim1/ftg) - Flutter Telegram client.
|
||||
|
||||
<a name="rust"></a>
|
||||
## Using TDLib in Rust projects
|
||||
@ -176,8 +185,7 @@ TDLib can be used from the Rust programming language through the [JSON](https://
|
||||
See [rust-tdlib](https://github.com/aCLr/rust-tdlib), [tdlib](https://github.com/melix99/tdlib-rs), or [tdlib-rs](https://github.com/agnipau/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures),
|
||||
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), or
|
||||
[tdjson-rs](https://github.com/mersinvald/tdjson-rs) for examples of TDLib Rust bindings.
|
||||
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), [tdjson-rs](https://github.com/mersinvald/tdjson-rs), [rust-tdlib](https://github.com/vhaoran/rust-tdlib), or [tdlib-json-sys](https://github.com/aykxt/tdlib-json-sys) for examples of TDLib Rust bindings.
|
||||
|
||||
Also see [Telegrand](https://github.com/melix99/telegrand) – a Telegram client optimized for the GNOME desktop.
|
||||
|
||||
@ -191,7 +199,7 @@ See [erl-tdlib](https://github.com/lattenwald/erl-tdlib) for an example of TDLib
|
||||
<a name="php"></a>
|
||||
## Using TDLib in PHP projects
|
||||
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib) - an FFI-based TDLib wrapper.
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib), or [tdlib-php-ffi](https://github.com/thisismzm/tdlib-php-ffi) - FFI-based TDLib wrappers.
|
||||
|
||||
See also [tdlib-schema](https://github.com/aurimasniekis/php-tdlib-schema) - a generator for TDLib API classes.
|
||||
|
||||
@ -224,7 +232,7 @@ See [d-tdlib-service](https://github.com/Lord-Evil/d-tdlib-service) for an examp
|
||||
|
||||
TDLib can be used from the Ruby programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [tdlib-ruby](https://github.com/centosadmin/tdlib-ruby) for examples of Ruby bindings and a client for TDLib.
|
||||
See [tdlib-ruby](https://github.com/southbridgeio/tdlib-ruby) for examples of Ruby bindings and a client for TDLib.
|
||||
|
||||
<a name="Crystal"></a>
|
||||
## Using TDLib in Crystal projects
|
||||
@ -267,21 +275,23 @@ See [telega.el](https://github.com/zevlg/telega.el) for an example of a GNU Emac
|
||||
|
||||
TDLib can be used from the Elixir programming language.
|
||||
|
||||
See [Elixir TDLib](https://gitlab.com/Fnux/elixir-tdlib) or [Elixir TDLib](https://github.com/QuantLayer/elixir-tdlib) for examples of such usage and an Elixir client for TDLib.
|
||||
These libraries contain automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
See [Elixir TDLib](https://github.com/QuantLayer/elixir-tdlib) for an example of such usage and an Elixir client for TDLib.
|
||||
The library contains automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
<a name="1s"></a>
|
||||
## Using TDLib from 1С:Enterprise
|
||||
|
||||
TDLib can be used from the 1С programming language.
|
||||
|
||||
See [TDLib bindings for 1С:Enterprise](https://github.com/Infactum/telegram-native) for examples of such usage.
|
||||
See [TDLib bindings for 1С:Enterprise](https://github.com/Infactum/telegram-native) and [e1c.tAgents](https://github.com/fedbka/e1c.tAgents) for examples of such usage.
|
||||
|
||||
<a name="c"></a>
|
||||
## Using TDLib in C projects
|
||||
|
||||
TDLib can be used from the C programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
|
||||
|
||||
See [easy-tg](https://github.com/Trumeet/easy-tg) for an example of such usage.
|
||||
|
||||
You can also try to use our [C](https://github.com/tdlight-team/tdlight/blob/master/td/telegram/td_c_client.h) client, which was used by the private TDLib-based version of [telegram-cli](https://github.com/vysheng/tg).
|
||||
|
||||
<a name="g"></a>
|
||||
|
@ -59,7 +59,7 @@ set(JAVA_SOURCE_PATH "${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}")
|
||||
get_filename_component(JAVA_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin REALPATH BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIRECTORY})
|
||||
add_custom_target(build_java
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY} ${JAVA_SOURCE_PATH}/example/Example.java ${JAVA_SOURCE_PATH}/Client.java ${JAVA_SOURCE_PATH}/Log.java ${JAVA_SOURCE_PATH}/TdApi.java
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d ${JAVA_OUTPUT_DIRECTORY} ${JAVA_SOURCE_PATH}/example/Example.java ${JAVA_SOURCE_PATH}/Client.java ${JAVA_SOURCE_PATH}/TdApi.java
|
||||
COMMENT "Building Java code"
|
||||
DEPENDS td_generate_java_api
|
||||
)
|
||||
|
@ -39,6 +39,21 @@ public final class Client {
|
||||
void onException(Throwable e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for handler of messages that are added to the internal TDLib log.
|
||||
*/
|
||||
public interface LogMessageHandler {
|
||||
/**
|
||||
* Callback called on messages that are added to the internal TDLib log.
|
||||
*
|
||||
* @param verbosityLevel Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* @param message The message added to the internal TDLib log.
|
||||
*/
|
||||
void onLogMessage(int verbosityLevel, String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the TDLib.
|
||||
*
|
||||
@ -105,6 +120,17 @@ public final class Client {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the handler for messages that are added to the internal TDLib log.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
*
|
||||
* @param maxVerbosityLevel The maximum verbosity level of messages for which the callback will be called.
|
||||
* @param logMessageHandler Handler for messages that are added to the internal TDLib log. Pass null to remove the handler.
|
||||
*/
|
||||
public static void setLogMessageHandler(int maxVerbosityLevel, Client.LogMessageHandler logMessageHandler) {
|
||||
nativeClientSetLogMessageHandler(maxVerbosityLevel, logMessageHandler);
|
||||
}
|
||||
|
||||
private static class ResponseReceiver implements Runnable {
|
||||
public boolean isRun = false;
|
||||
|
||||
@ -203,4 +229,6 @@ public final class Client {
|
||||
private static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
|
||||
|
||||
private static native TdApi.Object nativeClientExecute(TdApi.Function function);
|
||||
|
||||
private static native void nativeClientSetLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler);
|
||||
}
|
||||
|
@ -1,82 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
package org.drinkless.tdlib;
|
||||
|
||||
/**
|
||||
* Class used for managing internal TDLib logging.
|
||||
* Use TdApi.*Log* methods instead.
|
||||
*/
|
||||
public final class Log {
|
||||
/**
|
||||
* Changes TDLib log verbosity.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogVerbosityLevel}, to be removed in the future.
|
||||
* @param verbosityLevel New value of log verbosity level. Must be non-negative.
|
||||
* Value 0 corresponds to fatal errors,
|
||||
* value 1 corresponds to java.util.logging.Level.SEVERE,
|
||||
* value 2 corresponds to java.util.logging.Level.WARNING,
|
||||
* value 3 corresponds to java.util.logging.Level.INFO,
|
||||
* value 4 corresponds to java.util.logging.Level.FINE,
|
||||
* value 5 corresponds to java.util.logging.Level.FINER,
|
||||
* value greater than 5 can be used to enable even more logging.
|
||||
* Default value of the log verbosity level is 5.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setVerbosityLevel(int verbosityLevel);
|
||||
|
||||
/**
|
||||
* Sets file path for writing TDLib internal log. By default TDLib writes logs to the System.err.
|
||||
* Use this method to write the log to a file instead.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param filePath Path to a file for writing TDLib internal log. Use an empty path to
|
||||
* switch back to logging to the System.err.
|
||||
* @return whether opening the log file succeeded.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native boolean setFilePath(String filePath);
|
||||
|
||||
/**
|
||||
* Changes the maximum size of TDLib log file.
|
||||
*
|
||||
* @deprecated As of TDLib 1.4.0 in favor of {@link TdApi.SetLogStream}, to be removed in the future.
|
||||
* @param maxFileSize The maximum size of the file to where the internal TDLib log is written
|
||||
* before the file will be auto-rotated. Must be positive. Defaults to 10 MB.
|
||||
*/
|
||||
@Deprecated
|
||||
public static native void setMaxFileSize(long maxFileSize);
|
||||
|
||||
/**
|
||||
* This function is called from the JNI when a fatal error happens to provide a better error message.
|
||||
* The function does not return.
|
||||
*
|
||||
* @param errorMessage Error message.
|
||||
*/
|
||||
private static void onFatalError(String errorMessage) {
|
||||
class ThrowError implements Runnable {
|
||||
private ThrowError(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
throw new RuntimeException("TDLib fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
private final String errorMessage;
|
||||
}
|
||||
|
||||
new Thread(new ThrowError(errorMessage), "TDLib fatal error thread").start();
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000); // milliseconds
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,14 +9,15 @@ package org.drinkless.tdlib.example;
|
||||
import org.drinkless.tdlib.Client;
|
||||
import org.drinkless.tdlib.TdApi;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -298,7 +299,10 @@ public final class Example {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// disable TDLib log
|
||||
// set log message handler to handle only fatal errors (0) and plain log messages (-1)
|
||||
Client.setLogMessageHandler(0, new LogMessageHandler());
|
||||
|
||||
// disable TDLib log and redirect fatal errors and plain log messages to a file
|
||||
Client.execute(new TdApi.SetLogVerbosityLevel(0));
|
||||
if (Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))) instanceof TdApi.Error) {
|
||||
throw new IOError(new IOException("Write access to the current directory is required"));
|
||||
@ -377,7 +381,7 @@ public final class Example {
|
||||
TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object;
|
||||
users.put(updateUser.user.id, updateUser.user);
|
||||
break;
|
||||
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
|
||||
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
|
||||
TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object;
|
||||
TdApi.User user = users.get(updateUserStatus.userId);
|
||||
synchronized (user) {
|
||||
@ -438,7 +442,7 @@ public final class Example {
|
||||
case TdApi.UpdateChatPosition.CONSTRUCTOR: {
|
||||
TdApi.UpdateChatPosition updateChat = (TdApi.UpdateChatPosition) object;
|
||||
if (updateChat.position.list.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
TdApi.Chat chat = chats.get(updateChat.chatId);
|
||||
@ -452,7 +456,7 @@ public final class Example {
|
||||
TdApi.ChatPosition[] new_positions = new TdApi.ChatPosition[chat.positions.length + (updateChat.position.order == 0 ? 0 : 1) - (i < chat.positions.length ? 1 : 0)];
|
||||
int pos = 0;
|
||||
if (updateChat.position.order != 0) {
|
||||
new_positions[pos++] = updateChat.position;
|
||||
new_positions[pos++] = updateChat.position;
|
||||
}
|
||||
for (int j = 0; j < chat.positions.length; j++) {
|
||||
if (j != i) {
|
||||
@ -598,4 +602,85 @@ public final class Example {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class LogMessageHandler implements Client.LogMessageHandler {
|
||||
@Override
|
||||
public void onLogMessage(int verbosityLevel, String message) {
|
||||
if (verbosityLevel == 0) {
|
||||
onFatalError(message);
|
||||
return;
|
||||
}
|
||||
System.err.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void onFatalError(String errorMessage) {
|
||||
final class ThrowError implements Runnable {
|
||||
private final String errorMessage;
|
||||
private final AtomicLong errorThrowTime;
|
||||
|
||||
private ThrowError(String errorMessage, AtomicLong errorThrowTime) {
|
||||
this.errorMessage = errorMessage;
|
||||
this.errorThrowTime = errorThrowTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (isDatabaseBrokenError(errorMessage) || isDiskFullError(errorMessage) || isDiskError(errorMessage)) {
|
||||
processExternalError();
|
||||
return;
|
||||
}
|
||||
|
||||
errorThrowTime.set(System.currentTimeMillis());
|
||||
throw new ClientError("TDLib fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
private void processExternalError() {
|
||||
errorThrowTime.set(System.currentTimeMillis());
|
||||
throw new ExternalClientError("Fatal error: " + errorMessage);
|
||||
}
|
||||
|
||||
final class ClientError extends Error {
|
||||
private ClientError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
final class ExternalClientError extends Error {
|
||||
public ExternalClientError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDatabaseBrokenError(String message) {
|
||||
return message.contains("Wrong key or database is corrupted") ||
|
||||
message.contains("SQL logic error or missing database") ||
|
||||
message.contains("database disk image is malformed") ||
|
||||
message.contains("file is encrypted or is not a database") ||
|
||||
message.contains("unsupported file format") ||
|
||||
message.contains("Database was corrupted and deleted during execution and can't be recreated");
|
||||
}
|
||||
|
||||
private boolean isDiskFullError(String message) {
|
||||
return message.contains("PosixError : No space left on device") ||
|
||||
message.contains("database or disk is full");
|
||||
}
|
||||
|
||||
private boolean isDiskError(String message) {
|
||||
return message.contains("I/O error") || message.contains("Structure needs cleaning");
|
||||
}
|
||||
}
|
||||
|
||||
final AtomicLong errorThrowTime = new AtomicLong(Long.MAX_VALUE);
|
||||
new Thread(new ThrowError(errorMessage, errorThrowTime), "TDLib fatal error thread").start();
|
||||
|
||||
// wait at least 10 seconds after the error is thrown
|
||||
while (errorThrowTime.get() >= System.currentTimeMillis() - 10000) {
|
||||
try {
|
||||
Thread.sleep(1000 /* milliseconds */);
|
||||
} catch (InterruptedException ignore) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <td/telegram/Client.h>
|
||||
#include <td/telegram/Log.h>
|
||||
#include <td/telegram/td_api.h>
|
||||
|
||||
#include <td/tl/tl_jni_object.h>
|
||||
@ -77,18 +76,6 @@ static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject fun
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Log_setVerbosityLevel(JNIEnv *env, jclass clazz, jint new_log_verbosity_level) {
|
||||
td::Log::set_verbosity_level(static_cast<int>(new_log_verbosity_level));
|
||||
}
|
||||
|
||||
static jboolean Log_setFilePath(JNIEnv *env, jclass clazz, jstring file_path) {
|
||||
return td::Log::set_file_path(td::jni::from_jstring(env, file_path)) ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static void Log_setMaxFileSize(JNIEnv *env, jclass clazz, jlong max_file_size) {
|
||||
td::Log::set_max_file_size(max_file_size);
|
||||
}
|
||||
|
||||
static jstring Object_toString(JNIEnv *env, jobject object) {
|
||||
return td::jni::to_jstring(env, to_string(td::td_api::Object::fetch(env, object)));
|
||||
}
|
||||
@ -99,23 +86,52 @@ static jstring Function_toString(JNIEnv *env, jobject object) {
|
||||
|
||||
static constexpr jint JAVA_VERSION = JNI_VERSION_1_6;
|
||||
static JavaVM *java_vm;
|
||||
static jclass log_class;
|
||||
static jobject log_message_handler;
|
||||
|
||||
static void on_log_message(int verbosity_level, const char *error_message) {
|
||||
if (verbosity_level != 0) {
|
||||
return;
|
||||
}
|
||||
static void on_log_message(int verbosity_level, const char *log_message) {
|
||||
auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION);
|
||||
if (env == nullptr) {
|
||||
return;
|
||||
}
|
||||
jmethodID on_fatal_error_method = env->GetStaticMethodID(log_class, "onFatalError", "(Ljava/lang/String;)V");
|
||||
if (on_fatal_error_method) {
|
||||
jstring error_str = td::jni::to_jstring(env.get(), error_message);
|
||||
env->CallStaticVoidMethod(log_class, on_fatal_error_method, error_str);
|
||||
if (error_str) {
|
||||
env->DeleteLocalRef(error_str);
|
||||
|
||||
jobject handler = env->NewLocalRef(log_message_handler);
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
jclass handler_class = env->GetObjectClass(handler);
|
||||
if (handler_class) {
|
||||
jmethodID on_log_message_method = env->GetMethodID(handler_class, "onLogMessage", "(ILjava/lang/String;)V");
|
||||
if (on_log_message_method) {
|
||||
jstring log_message_str = td::jni::to_jstring(env.get(), log_message);
|
||||
if (log_message_str) {
|
||||
env->CallVoidMethod(handler, on_log_message_method, static_cast<jint>(verbosity_level), log_message_str);
|
||||
env->DeleteLocalRef((jobject)log_message_str);
|
||||
}
|
||||
}
|
||||
env->DeleteLocalRef((jobject)handler_class);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(handler);
|
||||
}
|
||||
|
||||
static void Client_nativeClientSetLogMessageHandler(JNIEnv *env, jclass clazz, jint max_verbosity_level,
|
||||
jobject new_log_message_handler) {
|
||||
if (log_message_handler) {
|
||||
td::ClientManager::set_log_message_callback(0, nullptr);
|
||||
jobject old_log_message_handler = log_message_handler;
|
||||
log_message_handler = jobject();
|
||||
env->DeleteGlobalRef(old_log_message_handler);
|
||||
}
|
||||
|
||||
if (new_log_message_handler) {
|
||||
log_message_handler = env->NewGlobalRef(new_log_message_handler);
|
||||
if (!log_message_handler) {
|
||||
// out of memory
|
||||
return;
|
||||
}
|
||||
|
||||
td::ClientManager::set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +149,6 @@ static jint register_native(JavaVM *vm) {
|
||||
};
|
||||
|
||||
auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client");
|
||||
log_class = td::jni::get_jclass(env, PACKAGE_NAME "/Log");
|
||||
auto object_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Object");
|
||||
auto function_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Function");
|
||||
|
||||
@ -143,10 +158,8 @@ static jint register_native(JavaVM *vm) {
|
||||
register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend);
|
||||
register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive);
|
||||
register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute);
|
||||
|
||||
register_method(log_class, "setVerbosityLevel", "(I)V", Log_setVerbosityLevel);
|
||||
register_method(log_class, "setFilePath", "(Ljava/lang/String;)Z", Log_setFilePath);
|
||||
register_method(log_class, "setMaxFileSize", "(J)V", Log_setMaxFileSize);
|
||||
register_method(client_class, "nativeClientSetLogMessageHandler", "(IL" PACKAGE_NAME "/Client$LogMessageHandler;)V",
|
||||
Client_nativeClientSetLogMessageHandler);
|
||||
|
||||
register_method(object_class, "toString", "()Ljava/lang/String;", Object_toString);
|
||||
|
||||
@ -157,7 +170,6 @@ static jint register_native(JavaVM *vm) {
|
||||
td::jni::init_vars(env, PACKAGE_NAME);
|
||||
td::td_api::Object::init_jni_vars(env, PACKAGE_NAME);
|
||||
td::td_api::Function::init_jni_vars(env, PACKAGE_NAME);
|
||||
td::ClientManager::set_log_message_callback(0, on_log_message);
|
||||
|
||||
return JAVA_VERSION;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
$commit = git rev-parse HEAD
|
||||
git diff-index --quiet HEAD
|
||||
$dirty = $LASTEXITCODE
|
||||
$commit = try { git rev-parse HEAD } catch { "unknown" }
|
||||
try { git diff-index --quiet HEAD } catch {}
|
||||
$dirty = if ($LASTEXITCODE) { "true" } else { "false" }
|
||||
echo "#pragma once`r`n#define GIT_COMMIT `"$commit`"`r`n#define GIT_DIRTY $dirty" | out-file -encoding ASCII auto/git_info.h.new
|
||||
if (-not (Test-Path .\auto\git_info.h) -or (Compare-Object $(Get-Content .\auto\git_info.h.new) $(Get-Content .\auto\git_info.h))) {
|
||||
mv -Force auto/git_info.h.new auto/git_info.h
|
||||
|
@ -1,8 +1,14 @@
|
||||
#!/bin/sh
|
||||
cd $(dirname $0)
|
||||
commit=$(git rev-parse HEAD)
|
||||
git diff-index --quiet HEAD
|
||||
dirty=$?
|
||||
commit="$(git rev-parse HEAD 2> /dev/null)"
|
||||
commit="${commit:-unknown}"
|
||||
git diff-index --quiet HEAD 2> /dev/null
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
dirty="true"
|
||||
else
|
||||
dirty="false"
|
||||
fi
|
||||
printf "#pragma once\n#define GIT_COMMIT \"$commit\"\n#define GIT_DIRTY $dirty\n" > auto/git_info.h.new
|
||||
if cmp -s auto/git_info.h.new auto/git_info.h 2>&1 > /dev/null
|
||||
then
|
||||
|
@ -4814,7 +4814,7 @@ searchMessages chat_list:ChatList query:string offset_date:int32 offset_chat_id:
|
||||
//@filter Additional filter for messages to search; pass null to search for all messages
|
||||
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 TDLib
|
||||
//@description Searches for call messages. Returns the results in reverse chronological order (i.e., in order of decreasing message_id). For optimal performance, the number of returned messages is chosen by TDLib
|
||||
//@from_message_id Identifier of the message from which to search; use 0 to get results from the last message
|
||||
//@limit The maximum number of messages to be returned; up to 100. For optimal performance, the number of returned messages is chosen by TDLib and can be smaller than the specified limit
|
||||
//@only_missed Pass true to search only for messages with missed/declined calls
|
||||
@ -6102,7 +6102,7 @@ getSupergroupMembers supergroup_id:int53 filter:SupergroupMembersFilter offset:i
|
||||
closeSecretChat secret_chat_id:int32 = Ok;
|
||||
|
||||
|
||||
//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only for supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i. e., in order of decreasing event_id)
|
||||
//@description Returns a list of service actions taken by chat members and administrators in the last 48 hours. Available only for supergroups and channels. Requires administrator rights. Returns results in reverse chronological order (i.e., in order of decreasing event_id)
|
||||
//@chat_id Chat identifier @query Search query by which to filter events @from_event_id Identifier of an event from which to return results. Use 0 to get results from the latest events @limit The maximum number of events to return; up to 100
|
||||
//@filters The types of events to return; pass null to get chat events of all types @user_ids User identifiers by which to filter events. By default, events relating to all users will be returned
|
||||
getChatEventLog chat_id:int53 query:string from_event_id:int64 limit:int32 filters:chatEventLogFilters user_ids:vector<int53> = ChatEvents;
|
||||
@ -6217,7 +6217,7 @@ setUserPrivacySettingRules setting:UserPrivacySetting rules:userPrivacySettingRu
|
||||
getUserPrivacySettingRules setting:UserPrivacySetting = UserPrivacySettingRules;
|
||||
|
||||
|
||||
//@description Returns the value of an option by its name. (Check the list of available options on https://core.telegram.org/tdlib/options.) Can be called before authorization
|
||||
//@description Returns the value of an option by its name. (Check the list of available options on https://core.telegram.org/tdlib/options.) Can be called before authorization. Can be called synchronously for options "version" and "commit_hash"
|
||||
//@name The name of the option
|
||||
getOption name:string = OptionValue;
|
||||
|
||||
|
@ -26,6 +26,7 @@ inputStickerSetThumbLegacy#dbaeae9 stickerset:InputStickerSet volume_id:long loc
|
||||
|
||||
test.useError = Error;
|
||||
test.useConfigSimple = help.ConfigSimple;
|
||||
test.parseInputAppEvent = InputAppEvent;
|
||||
|
||||
---types---
|
||||
|
||||
|
@ -21,7 +21,7 @@ Status check_message_id_duplicates(int64 *saved_message_ids, size_t max_size, si
|
||||
// In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if
|
||||
// a message comes in with msg_id lower than all or equal to any of the stored values, that message is to be
|
||||
// ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is
|
||||
// greater than N, the oldest (i. e. the lowest) is forgotten.
|
||||
// greater than N, the oldest (i.e. the lowest) is forgotten.
|
||||
if (end_pos == 2 * max_size) {
|
||||
std::copy_n(&saved_message_ids[max_size], max_size, &saved_message_ids[0]);
|
||||
end_pos = max_size;
|
||||
|
@ -140,6 +140,10 @@ AnimationsManager::AnimationsManager(Td *td, ActorShared<> parent) : td_(td), pa
|
||||
next_saved_animations_load_time_ = Time::now();
|
||||
}
|
||||
|
||||
AnimationsManager::~AnimationsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), animations_);
|
||||
}
|
||||
|
||||
void AnimationsManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
@ -29,6 +29,11 @@ class Td;
|
||||
class AnimationsManager final : public Actor {
|
||||
public:
|
||||
AnimationsManager(Td *td, ActorShared<> parent);
|
||||
AnimationsManager(const AnimationsManager &) = delete;
|
||||
AnimationsManager &operator=(const AnimationsManager &) = delete;
|
||||
AnimationsManager(AnimationsManager &&) = delete;
|
||||
AnimationsManager &operator=(AnimationsManager &&) = delete;
|
||||
~AnimationsManager() final;
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -7,11 +7,18 @@
|
||||
#include "td/telegram/Application.h"
|
||||
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/logevent/LogEvent.h"
|
||||
#include "td/telegram/logevent/LogEventHelper.h"
|
||||
#include "td/telegram/Td.h"
|
||||
#include "td/telegram/TdDb.h"
|
||||
|
||||
#include "td/db/binlog/BinlogEvent.h"
|
||||
#include "td/db/binlog/BinlogHelper.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -48,11 +55,9 @@ class SaveAppLogQuery final : public Td::ResultHandler {
|
||||
explicit SaveAppLogQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void send(const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data) {
|
||||
CHECK(data != nullptr);
|
||||
void send(telegram_api::object_ptr<telegram_api::inputAppEvent> &&input_app_event) {
|
||||
vector<telegram_api::object_ptr<telegram_api::inputAppEvent>> input_app_events;
|
||||
input_app_events.push_back(
|
||||
make_tl_object<telegram_api::inputAppEvent>(G()->server_time_cached(), type, dialog_id.get(), std::move(data)));
|
||||
input_app_events.push_back(std::move(input_app_event));
|
||||
send_query(G()->net_query_creator().create_unauth(telegram_api::help_saveAppLog(std::move(input_app_events))));
|
||||
}
|
||||
|
||||
@ -76,9 +81,55 @@ void get_invite_text(Td *td, Promise<string> &&promise) {
|
||||
td->create_handler<GetInviteTextQuery>(std::move(promise))->send();
|
||||
}
|
||||
|
||||
class SaveAppLogLogEvent {
|
||||
public:
|
||||
const telegram_api::inputAppEvent *input_app_event_in_ = nullptr;
|
||||
telegram_api::object_ptr<telegram_api::inputAppEvent> input_app_event_out_;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const {
|
||||
input_app_event_in_->store(storer);
|
||||
}
|
||||
|
||||
template <class ParserT>
|
||||
void parse(ParserT &parser) {
|
||||
auto buffer = parser.template fetch_string_raw<BufferSlice>(parser.get_left_len());
|
||||
TlBufferParser buffer_parser{&buffer};
|
||||
input_app_event_out_ = telegram_api::make_object<telegram_api::inputAppEvent>(buffer_parser);
|
||||
}
|
||||
};
|
||||
|
||||
static void save_app_log_impl(Td *td, telegram_api::object_ptr<telegram_api::inputAppEvent> input_app_event,
|
||||
uint64 log_event_id, Promise<Unit> &&promise) {
|
||||
if (log_event_id == 0) {
|
||||
SaveAppLogLogEvent log_event;
|
||||
log_event.input_app_event_in_ = input_app_event.get();
|
||||
log_event_id =
|
||||
binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::SaveAppLog, get_log_event_storer(log_event));
|
||||
}
|
||||
|
||||
td->create_handler<SaveAppLogQuery>(get_erase_log_event_promise(log_event_id, std::move(promise)))
|
||||
->send(std::move(input_app_event));
|
||||
}
|
||||
|
||||
void save_app_log(Td *td, const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data,
|
||||
Promise<Unit> &&promise) {
|
||||
td->create_handler<SaveAppLogQuery>(std::move(promise))->send(type, dialog_id, std::move(data));
|
||||
CHECK(data != nullptr);
|
||||
auto input_app_event = telegram_api::make_object<telegram_api::inputAppEvent>(G()->server_time_cached(), type,
|
||||
dialog_id.get(), std::move(data));
|
||||
save_app_log_impl(td, std::move(input_app_event), 0, std::move(promise));
|
||||
}
|
||||
|
||||
void on_save_app_log_binlog_event(Td *td, BinlogEvent &&event) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
CHECK(event.id_ != 0);
|
||||
CHECK(event.type_ == LogEvent::HandlerType::SaveAppLog);
|
||||
SaveAppLogLogEvent log_event;
|
||||
log_event_parse(log_event, event.data_).ensure();
|
||||
|
||||
save_app_log_impl(td, std::move(log_event.input_app_event_out_), event.id_, Promise<Unit>());
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
struct BinlogEvent;
|
||||
|
||||
class Td;
|
||||
|
||||
void get_invite_text(Td *td, Promise<string> &&promise);
|
||||
@ -21,4 +23,6 @@ void get_invite_text(Td *td, Promise<string> &&promise);
|
||||
void save_app_log(Td *td, const string &type, DialogId dialog_id, tl_object_ptr<telegram_api::JSONValue> &&data,
|
||||
Promise<Unit> &&promise);
|
||||
|
||||
void on_save_app_log_binlog_event(Td *td, BinlogEvent &&event);
|
||||
|
||||
} // namespace td
|
||||
|
@ -24,6 +24,10 @@ namespace td {
|
||||
AudiosManager::AudiosManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
AudiosManager::~AudiosManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), audios_);
|
||||
}
|
||||
|
||||
int32 AudiosManager::get_audio_duration(FileId file_id) const {
|
||||
auto it = audios_.find(file_id);
|
||||
if (it == audios_.end()) {
|
||||
|
@ -23,6 +23,11 @@ class Td;
|
||||
class AudiosManager {
|
||||
public:
|
||||
explicit AudiosManager(Td *td);
|
||||
AudiosManager(const AudiosManager &) = delete;
|
||||
AudiosManager &operator=(const AudiosManager &) = delete;
|
||||
AudiosManager(AudiosManager &&) = delete;
|
||||
AudiosManager &operator=(AudiosManager &&) = delete;
|
||||
~AudiosManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -249,8 +249,8 @@ class MultiTd final : public Actor {
|
||||
auto old_context = set_context(context);
|
||||
auto old_tag = set_tag(to_string(td_id));
|
||||
td = create_actor<Td>("Td", std::move(callback), options_);
|
||||
set_context(old_context);
|
||||
set_tag(old_tag);
|
||||
set_context(std::move(old_context));
|
||||
set_tag(std::move(old_tag));
|
||||
}
|
||||
|
||||
void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id,
|
||||
|
@ -126,7 +126,7 @@ class ClientManager final {
|
||||
/**
|
||||
* A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
*
|
||||
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024).
|
||||
* \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* \param message Null-terminated UTF-8-encoded string with the message added to the log.
|
||||
|
@ -25,10 +25,10 @@ using namespace CxCli;
|
||||
/// <summary>
|
||||
/// A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
/// </summary>
|
||||
/// <param name="verbosityLevel">Log verbosity level with which the message was added (-1 - 1024).
|
||||
/// <param name="verbosityLevel">Log verbosity level with which the message was added from -1 up to 1024.
|
||||
/// If 0, then TDLib will crash as soon as the callback returns.
|
||||
/// None of the TDLib methods can be called from the callback.</param>
|
||||
/// <param name="message">Null-terminated string with the message added to the log.</param>
|
||||
/// <param name="message">The message added to the log.</param>
|
||||
public delegate void LogMessageCallback(int verbosityLevel, String^ message);
|
||||
#endif
|
||||
|
||||
|
@ -3398,7 +3398,45 @@ ContactsManager::ContactsManager(Td *td, ActorShared<> parent) : td_(td), parent
|
||||
channel_participant_cache_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
ContactsManager::~ContactsManager() = default;
|
||||
ContactsManager::~ContactsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), users_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), user_photos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), pending_user_photos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), user_profile_photo_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), my_photo_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chats_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), chat_full_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), min_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channels_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), invalidated_channels_full_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_full_file_source_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unknown_secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), secret_chats_with_user_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), invite_link_infos_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_access_by_invite_link_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_users_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_user_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_chat_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_channels_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), unavailable_channel_fulls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_secret_chats_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_administrators_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), cached_channel_participants_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), resolved_phone_numbers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_participants_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), all_imported_contacts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), linked_channel_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), restricted_user_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), restricted_channel_ids_);
|
||||
}
|
||||
|
||||
void ContactsManager::tear_down() {
|
||||
parent_.reset();
|
||||
@ -5414,7 +5452,7 @@ std::pair<vector<UserId>, vector<int32>> ContactsManager::import_contacts(const
|
||||
|
||||
do {
|
||||
random_id = Random::secure_int64();
|
||||
} while (random_id == 0 || imported_contacts_.count(random_id) > 0);
|
||||
} while (random_id == 0 || random_id == 1 || imported_contacts_.count(random_id) > 0);
|
||||
imported_contacts_[random_id]; // reserve place for result
|
||||
|
||||
do_import_contacts(contacts, random_id, std::move(promise));
|
||||
@ -5759,7 +5797,7 @@ void ContactsManager::on_clear_imported_contacts(vector<Contact> &&contacts, vec
|
||||
imported_contacts_unique_id_ = std::move(contacts_unique_id);
|
||||
imported_contacts_pos_ = std::move(to_add.first);
|
||||
|
||||
do_import_contacts(std::move(to_add.second), 0, std::move(promise));
|
||||
do_import_contacts(std::move(to_add.second), 1, std::move(promise));
|
||||
}
|
||||
|
||||
void ContactsManager::clear_imported_contacts(Promise<Unit> &&promise) {
|
||||
@ -8250,7 +8288,7 @@ void ContactsManager::on_import_contacts_finished(int64 random_id, vector<UserId
|
||||
vector<int32> unimported_contact_invites) {
|
||||
LOG(INFO) << "Contacts import with random_id " << random_id
|
||||
<< " has finished: " << format::as_array(imported_contact_user_ids);
|
||||
if (random_id == 0) {
|
||||
if (random_id == 1) {
|
||||
// import from change_imported_contacts
|
||||
all_imported_contacts_ = std::move(next_all_imported_contacts_);
|
||||
next_all_imported_contacts_.clear();
|
||||
@ -16735,7 +16773,19 @@ tl_object_ptr<td_api::userFullInfo> ContactsManager::get_user_full_info_object(U
|
||||
} else {
|
||||
FormattedText bio;
|
||||
bio.text = user_full->about;
|
||||
bio.entities = find_entities(bio.text, true, true, !is_user_premium(user_id));
|
||||
bio.entities = find_entities(bio.text, true, true);
|
||||
if (!is_user_premium(user_id)) {
|
||||
td::remove_if(bio.entities, [&](const MessageEntity &entity) {
|
||||
if (entity.type == MessageEntity::Type::EmailAddress) {
|
||||
return true;
|
||||
}
|
||||
if (entity.type == MessageEntity::Type::Url &&
|
||||
!LinkManager::is_internal_link(utf8_utf16_substr(Slice(bio.text), entity.offset, entity.length))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
bio_object = get_formatted_text_object(bio, true, 0);
|
||||
}
|
||||
return make_tl_object<td_api::userFullInfo>(
|
||||
|
@ -48,6 +48,10 @@ namespace td {
|
||||
DocumentsManager::DocumentsManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
DocumentsManager::~DocumentsManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), documents_);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::document> DocumentsManager::get_document_object(FileId file_id,
|
||||
PhotoFormat thumbnail_format) const {
|
||||
if (!file_id.is_valid()) {
|
||||
|
@ -31,6 +31,11 @@ class Td;
|
||||
class DocumentsManager {
|
||||
public:
|
||||
explicit DocumentsManager(Td *td);
|
||||
DocumentsManager(const DocumentsManager &) = delete;
|
||||
DocumentsManager &operator=(const DocumentsManager &) = delete;
|
||||
DocumentsManager(DocumentsManager &&) = delete;
|
||||
DocumentsManager &operator=(DocumentsManager &&) = delete;
|
||||
~DocumentsManager();
|
||||
|
||||
class RemoteDocument {
|
||||
public:
|
||||
|
@ -352,7 +352,7 @@ class DownloadManagerImpl final : public DownloadManager {
|
||||
FileId file_id;
|
||||
FileId internal_file_id;
|
||||
FileSourceId file_source_id;
|
||||
int8 priority;
|
||||
int8 priority{};
|
||||
bool is_paused{};
|
||||
bool is_counted{};
|
||||
mutable bool is_registered{};
|
||||
@ -567,8 +567,11 @@ class DownloadManagerImpl final : public DownloadManager {
|
||||
<< file_info->size << '/' << file_info->expected_size
|
||||
<< " with downloaded_size = " << file_info->downloaded_size
|
||||
<< " and is_paused = " << file_info->is_paused;
|
||||
auto it = files_.emplace(download_id, std::move(file_info)).first;
|
||||
auto res = files_.emplace(download_id, std::move(file_info));
|
||||
auto it = res.first;
|
||||
bool was_completed = is_completed(*it->second);
|
||||
LOG_CHECK(!it->second->is_registered)
|
||||
<< res.second << ' ' << download_id << ' ' << max_download_id_ << ' ' << it->second->need_save_to_database;
|
||||
register_file_info(*it->second); // must be called before start_file, which can call update_file_download_state
|
||||
if (is_completed(*it->second)) {
|
||||
bool is_inserted = completed_download_ids_.insert(it->second->download_id).second;
|
||||
|
@ -69,7 +69,8 @@ FileView DownloadManagerCallback::get_file_view(FileId file_id) {
|
||||
}
|
||||
|
||||
FileView DownloadManagerCallback::get_sync_file_view(FileId file_id) {
|
||||
return td_->file_manager_->get_sync_file_view(file_id);
|
||||
td_->file_manager_->check_local_location(file_id);
|
||||
return get_file_view(file_id);
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::fileDownload> DownloadManagerCallback::get_file_download_object(
|
||||
|
@ -33,6 +33,10 @@ namespace td {
|
||||
|
||||
int VERBOSITY_NAME(file_references) = VERBOSITY_NAME(INFO);
|
||||
|
||||
FileReferenceManager::~FileReferenceManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_sources_);
|
||||
}
|
||||
|
||||
bool FileReferenceManager::is_file_reference_error(const Status &error) {
|
||||
return error.is_error() && error.code() == 400 && begins_with(error.message(), "FILE_REFERENCE_");
|
||||
}
|
||||
|
@ -35,6 +35,13 @@ extern int VERBOSITY_NAME(file_references);
|
||||
|
||||
class FileReferenceManager final : public Actor {
|
||||
public:
|
||||
FileReferenceManager() = default;
|
||||
FileReferenceManager(const FileReferenceManager &) = delete;
|
||||
FileReferenceManager &operator=(const FileReferenceManager &) = delete;
|
||||
FileReferenceManager(FileReferenceManager &&) = delete;
|
||||
FileReferenceManager &operator=(FileReferenceManager &&) = delete;
|
||||
~FileReferenceManager() final;
|
||||
|
||||
static bool is_file_reference_error(const Status &error);
|
||||
static size_t get_file_reference_error_pos(const Status &error);
|
||||
|
||||
|
@ -4,17 +4,12 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/GitInfo.h"
|
||||
|
||||
#include "auto/git_info.h"
|
||||
#include "td/telegram/GitCommitHash.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
CSlice GitInfo::commit() {
|
||||
return GIT_COMMIT;
|
||||
}
|
||||
bool GitInfo::is_dirty() {
|
||||
return GIT_DIRTY;
|
||||
const char *get_git_commit_hash() {
|
||||
return "@TD_GIT_COMMIT_HASH@";
|
||||
}
|
||||
|
||||
} // namespace td
|
@ -6,14 +6,8 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class GitInfo {
|
||||
public:
|
||||
static CSlice commit();
|
||||
static bool is_dirty();
|
||||
};
|
||||
const char *get_git_commit_hash();
|
||||
|
||||
} // namespace td
|
@ -924,6 +924,11 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LinkManager::is_internal_link(Slice link) {
|
||||
auto info = get_link_info(link);
|
||||
return info.is_internal_;
|
||||
}
|
||||
|
||||
unique_ptr<LinkManager::InternalLink> LinkManager::parse_internal_link(Slice link, bool is_trusted) {
|
||||
auto info = get_link_info(link);
|
||||
if (!info.is_internal_) {
|
||||
|
@ -52,6 +52,9 @@ class LinkManager final : public Actor {
|
||||
// same as check_link, but returns an empty string instead of an error
|
||||
static string get_checked_link(Slice link, bool http_only = false, bool https_only = false);
|
||||
|
||||
// returns whether a link is an internal link, supported or not
|
||||
static bool is_internal_link(Slice link);
|
||||
|
||||
// checks whether the link is a supported tg or t.me link and parses it
|
||||
static unique_ptr<InternalLink> parse_internal_link(Slice link, bool is_trusted = false);
|
||||
|
||||
|
@ -453,7 +453,7 @@ class MessageChatSetTtl final : public MessageContent {
|
||||
|
||||
class MessageUnsupported final : public MessageContent {
|
||||
public:
|
||||
static constexpr int32 CURRENT_VERSION = 11;
|
||||
static constexpr int32 CURRENT_VERSION = 12;
|
||||
int32 version = CURRENT_VERSION;
|
||||
|
||||
MessageUnsupported() = default;
|
||||
@ -3466,8 +3466,8 @@ void merge_message_contents(Td *td, const MessageContent *old_content, MessageCo
|
||||
old_->telegram_payment_charge_id != new_->telegram_payment_charge_id ||
|
||||
old_->provider_payment_charge_id != new_->provider_payment_charge_id ||
|
||||
((old_->order_info != nullptr || new_->order_info != nullptr) &&
|
||||
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info ||
|
||||
old_->is_recurring != new_->is_recurring || old_->is_first_recurring != new_->is_first_recurring))) {
|
||||
(old_->order_info == nullptr || new_->order_info == nullptr || *old_->order_info != *new_->order_info)) ||
|
||||
old_->is_recurring != new_->is_recurring || old_->is_first_recurring != new_->is_first_recurring) {
|
||||
need_update = true;
|
||||
}
|
||||
break;
|
||||
|
@ -1636,7 +1636,7 @@ static void fix_entity_offsets(Slice text, vector<MessageEntity> &entities) {
|
||||
}
|
||||
}
|
||||
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps, bool skip_urls) {
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps) {
|
||||
vector<MessageEntity> entities;
|
||||
|
||||
auto add_entities = [&entities, &text](MessageEntity::Type type, vector<Slice> (*find_entities_f)(Slice)) mutable {
|
||||
@ -1655,15 +1655,13 @@ vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool ski
|
||||
add_entities(MessageEntity::Type::Cashtag, find_cashtags);
|
||||
// TODO find_phone_numbers
|
||||
add_entities(MessageEntity::Type::BankCardNumber, find_bank_card_numbers);
|
||||
if (!skip_urls) {
|
||||
add_entities(MessageEntity::Type::Url, find_tg_urls);
|
||||
auto urls = find_urls(text);
|
||||
for (auto &url : urls) {
|
||||
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
|
||||
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
|
||||
auto length = narrow_cast<int32>(url.first.size());
|
||||
entities.emplace_back(type, offset, length);
|
||||
}
|
||||
add_entities(MessageEntity::Type::Url, find_tg_urls);
|
||||
auto urls = find_urls(text);
|
||||
for (auto &url : urls) {
|
||||
auto type = url.second ? MessageEntity::Type::EmailAddress : MessageEntity::Type::Url;
|
||||
auto offset = narrow_cast<int32>(url.first.begin() - text.begin());
|
||||
auto length = narrow_cast<int32>(url.first.size());
|
||||
entities.emplace_back(type, offset, length);
|
||||
}
|
||||
if (!skip_media_timestamps) {
|
||||
auto media_timestamps = find_media_timestamps(text);
|
||||
|
@ -143,8 +143,7 @@ vector<tl_object_ptr<td_api::textEntity>> get_text_entities_object(const vector<
|
||||
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, bool skip_media_timestamps,
|
||||
bool skip_urls = false);
|
||||
vector<MessageEntity> find_entities(Slice text, bool skip_bot_commands, bool skip_media_timestamps);
|
||||
|
||||
vector<Slice> find_mentions(Slice str);
|
||||
vector<Slice> find_bot_commands(Slice str);
|
||||
|
@ -5755,12 +5755,6 @@ void MessagesManager::save_calls_db_state() {
|
||||
G()->td_db()->get_sqlite_pmc()->set("calls_db_state", log_event_store(calls_db_state_).as_slice().str(), Auto());
|
||||
}
|
||||
|
||||
MessagesManager::Dialog::~Dialog() {
|
||||
if (!G()->close_flag()) {
|
||||
LOG(ERROR) << "Destroy " << dialog_id;
|
||||
}
|
||||
}
|
||||
|
||||
MessagesManager::MessagesManager(Td *td, ActorShared<> parent)
|
||||
: recently_found_dialogs_{td, "recently_found", MAX_RECENT_DIALOGS}
|
||||
, recently_opened_dialogs_{td, "recently_opened", MAX_RECENT_DIALOGS}
|
||||
@ -5815,7 +5809,36 @@ MessagesManager::MessagesManager(Td *td, ActorShared<> parent)
|
||||
update_viewed_messages_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
MessagesManager::~MessagesManager() = default;
|
||||
MessagesManager::~MessagesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), ttl_nodes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), ttl_heap_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), being_sent_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), update_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), update_scheduled_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), last_clear_history_message_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), postponed_chat_read_inbox_updates_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_public_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_on_server_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_common_dialogs_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_embedding_codes_[0]);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_embedding_codes_[1]);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), replied_by_media_timestamp_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), notification_group_id_to_dialog_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), active_get_channel_differencies_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), get_channel_difference_to_log_event_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), channel_get_difference_retry_timeouts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), is_channel_difference_finished_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), resolved_usernames_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), inaccessible_resolved_usernames_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_bot_command_message_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), full_message_id_to_file_source_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), last_outgoing_forwarded_message_date_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_viewed_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dialog_online_member_counts_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), previous_repaired_read_inbox_max_message_id_);
|
||||
}
|
||||
|
||||
void MessagesManager::on_channel_get_difference_timeout_callback(void *messages_manager_ptr, int64 dialog_id_int) {
|
||||
if (G()->close_flag()) {
|
||||
@ -14887,9 +14910,9 @@ void MessagesManager::set_dialog_last_new_message_id(Dialog *d, MessageId last_n
|
||||
|
||||
auto last_new_message = get_message(d, last_new_message_id);
|
||||
if (last_new_message != nullptr) {
|
||||
add_message_to_database(d, last_new_message, "set_dialog_last_new_message_id");
|
||||
set_dialog_first_database_message_id(d, last_new_message_id, "set_dialog_last_new_message_id");
|
||||
set_dialog_last_database_message_id(d, last_new_message_id, "set_dialog_last_new_message_id");
|
||||
add_message_to_database(d, last_new_message, source);
|
||||
set_dialog_first_database_message_id(d, last_new_message_id, source);
|
||||
set_dialog_last_database_message_id(d, last_new_message_id, source);
|
||||
try_restore_dialog_reply_markup(d, last_new_message);
|
||||
}
|
||||
}
|
||||
@ -36296,6 +36319,7 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
const char *source) {
|
||||
auto dialog_id = d->dialog_id;
|
||||
LOG_CHECK(is_inited_) << dialog_id << ' ' << is_loaded_from_database << ' ' << source;
|
||||
LOG_CHECK(!have_dialog(dialog_id)) << dialog_id << ' ' << is_loaded_from_database << ' ' << source;
|
||||
switch (dialog_id.get_type()) {
|
||||
case DialogType::User:
|
||||
if (dialog_id == get_my_dialog_id() && d->last_read_inbox_message_id == MessageId::max() &&
|
||||
@ -36438,6 +36462,11 @@ MessagesManager::Dialog *MessagesManager::add_new_dialog(unique_ptr<Dialog> &&d,
|
||||
|
||||
being_added_new_dialog_id_ = DialogId();
|
||||
|
||||
LOG_CHECK(dialog->messages == nullptr) << dialog->messages->message_id << ' ' << dialog->last_message_id << ' '
|
||||
<< dialog->last_database_message_id << ' '
|
||||
<< dialog->debug_set_dialog_last_database_message_id << ' '
|
||||
<< dialog->messages->debug_source;
|
||||
|
||||
fix_new_dialog(dialog, std::move(last_database_message), last_database_message_id, order, last_clear_history_date,
|
||||
last_clear_history_message_id, default_join_group_call_as_dialog_id, default_send_message_as_dialog_id,
|
||||
need_drop_default_send_message_as_dialog_id, is_loaded_from_database);
|
||||
@ -36797,13 +36826,16 @@ void MessagesManager::fix_new_dialog(Dialog *d, unique_ptr<Message> &&last_datab
|
||||
|
||||
if (d->messages != nullptr) {
|
||||
LOG_CHECK(d->messages->message_id == last_message_id)
|
||||
<< d->messages->message_id << ' ' << last_message_id << ' ' << d->debug_set_dialog_last_database_message_id
|
||||
<< ' ' << d->messages->debug_source;
|
||||
<< d->messages->message_id << ' ' << last_message_id << ' ' << d->last_message_id << ' '
|
||||
<< d->last_database_message_id << ' ' << d->debug_set_dialog_last_database_message_id << ' '
|
||||
<< d->messages->debug_source;
|
||||
LOG_CHECK(d->messages->left == nullptr)
|
||||
<< d->messages->left->message_id << ' ' << d->messages->message_id << ' ' << last_message_id << ' '
|
||||
<< d->messages->left->message_id << ' ' << d->messages->message_id << ' ' << d->messages->left->message_id
|
||||
<< ' ' << last_message_id << ' ' << d->last_message_id << ' ' << d->last_database_message_id << ' '
|
||||
<< d->debug_set_dialog_last_database_message_id << ' ' << d->messages->debug_source;
|
||||
LOG_CHECK(d->messages->right == nullptr)
|
||||
<< d->messages->right->message_id << ' ' << d->messages->message_id << ' ' << last_message_id << ' '
|
||||
<< d->messages->right->message_id << ' ' << d->messages->message_id << ' ' << d->messages->right->message_id
|
||||
<< ' ' << last_message_id << ' ' << d->last_message_id << ' ' << d->last_database_message_id << ' '
|
||||
<< d->debug_set_dialog_last_database_message_id << ' ' << d->messages->debug_source;
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1433,7 @@ class MessagesManager final : public Actor {
|
||||
Dialog &operator=(const Dialog &) = delete;
|
||||
Dialog(Dialog &&other) = delete;
|
||||
Dialog &operator=(Dialog &&other) = delete;
|
||||
~Dialog();
|
||||
~Dialog() = default;
|
||||
|
||||
template <class StorerT>
|
||||
void store(StorerT &storer) const;
|
||||
|
@ -984,6 +984,17 @@ void NotificationManager::add_update_notification_group(td_api::object_ptr<td_ap
|
||||
if (update->notification_settings_chat_id_ == 0) {
|
||||
update->notification_settings_chat_id_ = update->chat_id_;
|
||||
}
|
||||
if (!update->added_notifications_.empty() && !update->removed_notification_ids_.empty()) {
|
||||
// just in case
|
||||
td::remove_if(update->added_notifications_, [&](const td_api::object_ptr<td_api::notification> ¬ification) {
|
||||
CHECK(notification != nullptr);
|
||||
if (td::contains(update->removed_notification_ids_, notification->id_)) {
|
||||
LOG(ERROR) << "Have the same notification as added and removed";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
add_update(group_id, std::move(update));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "td/telegram/ConfigShared.h"
|
||||
#include "td/telegram/ContactsManager.h"
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/GitCommitHash.h"
|
||||
#include "td/telegram/Global.h"
|
||||
#include "td/telegram/JsonValue.h"
|
||||
#include "td/telegram/LanguagePackManager.h"
|
||||
@ -28,6 +29,7 @@
|
||||
#include "td/telegram/telegram_api.h"
|
||||
#include "td/telegram/TopDialogManager.h"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
@ -210,6 +212,15 @@ bool OptionManager::is_internal_option(Slice name) {
|
||||
}
|
||||
}
|
||||
|
||||
const vector<Slice> &OptionManager::get_synchronous_options() {
|
||||
static const vector<Slice> options{"version", "commit_hash"};
|
||||
return options;
|
||||
}
|
||||
|
||||
bool OptionManager::is_synchronous_option(Slice name) {
|
||||
return td::contains(get_synchronous_options(), name);
|
||||
}
|
||||
|
||||
void OptionManager::on_option_updated(const string &name) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
@ -424,13 +435,25 @@ void OptionManager::get_option(const string &name, Promise<td_api::object_ptr<td
|
||||
return promise.set_value(get_unix_time_option_value_object());
|
||||
}
|
||||
break;
|
||||
}
|
||||
wrap_promise().set_value(Unit());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_synchronously(Slice name) {
|
||||
CHECK(!name.empty());
|
||||
switch (name[0]) {
|
||||
case 'c':
|
||||
if (name == "commit_hash") {
|
||||
return td_api::make_object<td_api::optionValueString>(get_git_commit_hash());
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
if (name == "version") {
|
||||
return promise.set_value(td_api::make_object<td_api::optionValueString>(Td::TDLIB_VERSION));
|
||||
return td_api::make_object<td_api::optionValueString>("1.8.4");
|
||||
}
|
||||
break;
|
||||
}
|
||||
wrap_promise().set_value(Unit());
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void OptionManager::set_option(const string &name, td_api::object_ptr<td_api::OptionValue> &&value,
|
||||
@ -792,9 +815,15 @@ td_api::object_ptr<td_api::OptionValue> OptionManager::get_option_value_object(S
|
||||
return td_api::make_object<td_api::optionValueString>(value.str());
|
||||
}
|
||||
|
||||
void OptionManager::get_common_state(vector<td_api::object_ptr<td_api::Update>> &updates) {
|
||||
for (auto option_name : get_synchronous_options()) {
|
||||
updates.push_back(
|
||||
td_api::make_object<td_api::updateOption>(option_name.str(), get_option_synchronously(option_name)));
|
||||
}
|
||||
}
|
||||
|
||||
void OptionManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"version", td_api::make_object<td_api::optionValueString>(Td::TDLIB_VERSION)));
|
||||
get_common_state(updates);
|
||||
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"online", td_api::make_object<td_api::optionValueBoolean>(td_->is_online())));
|
||||
|
@ -38,6 +38,12 @@ class OptionManager final : public Actor {
|
||||
|
||||
static void clear_options();
|
||||
|
||||
static bool is_synchronous_option(Slice name);
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_option_synchronously(Slice name);
|
||||
|
||||
static void get_common_state(vector<td_api::object_ptr<td_api::Update>> &updates);
|
||||
|
||||
void get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const;
|
||||
|
||||
private:
|
||||
@ -45,6 +51,8 @@ class OptionManager final : public Actor {
|
||||
|
||||
static bool is_internal_option(Slice name);
|
||||
|
||||
static const vector<Slice> &get_synchronous_options();
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_unix_time_option_value_object();
|
||||
|
||||
static td_api::object_ptr<td_api::OptionValue> get_option_value_object(Slice value);
|
||||
|
@ -266,7 +266,13 @@ void PollManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
||||
PollManager::~PollManager() = default;
|
||||
PollManager::~PollManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), polls_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), server_poll_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), other_poll_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), poll_voters_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), loaded_from_database_polls_);
|
||||
}
|
||||
|
||||
void PollManager::on_update_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) {
|
||||
if (G()->close_flag()) {
|
||||
|
@ -30,7 +30,7 @@ class SecretChatsManager final : public Actor {
|
||||
public:
|
||||
explicit SecretChatsManager(ActorShared<> parent);
|
||||
|
||||
// proxy query to corrensponding SecretChatActor
|
||||
// proxy query to corresponding SecretChatActor
|
||||
void on_update_chat(tl_object_ptr<telegram_api::updateEncryption> update);
|
||||
void on_new_message(tl_object_ptr<telegram_api::EncryptedMessage> &&message_ptr, Promise<Unit> &&promise);
|
||||
|
||||
|
@ -10,6 +10,23 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type) {
|
||||
CHECK(type != nullptr);
|
||||
switch (type->get_id()) {
|
||||
case td_api::stickerTypeStatic::ID:
|
||||
return StickerFormat::Webp;
|
||||
case td_api::stickerTypeAnimated::ID:
|
||||
return StickerFormat::Tgs;
|
||||
case td_api::stickerTypeVideo::ID:
|
||||
return StickerFormat::Webm;
|
||||
case td_api::stickerTypeMask::ID:
|
||||
return StickerFormat::Webp;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return StickerFormat::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
StickerFormat get_sticker_format_by_mime_type(Slice mime_type) {
|
||||
if (mime_type == "application/x-tgsticker") {
|
||||
return StickerFormat::Tgs;
|
||||
|
@ -18,6 +18,8 @@ namespace td {
|
||||
// update store_sticker/store_sticker_set when this type changes
|
||||
enum class StickerFormat : int32 { Unknown, Webp, Tgs, Webm };
|
||||
|
||||
StickerFormat get_sticker_format(const td_api::object_ptr<td_api::StickerType> &type);
|
||||
|
||||
StickerFormat get_sticker_format_by_mime_type(Slice mime_type);
|
||||
|
||||
StickerFormat get_sticker_format_by_extension(Slice extension);
|
||||
|
@ -1304,6 +1304,21 @@ StickersManager::StickersManager(Td *td, ActorShared<> parent) : td_(td), parent
|
||||
next_update_animated_emoji_clicked_time_ = Time::now();
|
||||
}
|
||||
|
||||
StickersManager::~StickersManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), stickers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), short_name_to_sticker_set_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), attached_sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_stickers_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), found_sticker_sets_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_codes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_code_versions_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_language_code_last_difference_times_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), reloaded_emoji_keywords_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), dice_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), emoji_messages_);
|
||||
}
|
||||
|
||||
void StickersManager::start_up() {
|
||||
init();
|
||||
}
|
||||
@ -1998,6 +2013,13 @@ double StickersManager::get_sticker_set_minithumbnail_zoom(const StickerSet *sti
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::thumbnail> StickersManager::get_sticker_set_thumbnail_object(
|
||||
const StickerSet *sticker_set) const {
|
||||
CHECK(sticker_set != nullptr);
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
return get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
}
|
||||
|
||||
tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(StickerSetId sticker_set_id) const {
|
||||
const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
|
||||
CHECK(sticker_set != nullptr);
|
||||
@ -2016,10 +2038,8 @@ tl_object_ptr<td_api::stickerSet> StickersManager::get_sticker_set_object(Sticke
|
||||
}
|
||||
emojis.push_back(make_tl_object<td_api::emojis>(std::move(sticker_emojis)));
|
||||
}
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
return make_tl_object<td_api::stickerSet>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_sticker_set_thumbnail_object(sticker_set),
|
||||
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -2,
|
||||
get_sticker_set_minithumbnail_zoom(sticker_set)),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
@ -2091,11 +2111,9 @@ tl_object_ptr<td_api::stickerSetInfo> StickersManager::get_sticker_set_info_obje
|
||||
}
|
||||
}
|
||||
|
||||
auto thumbnail_format = get_sticker_set_thumbnail_format(sticker_set->sticker_format);
|
||||
auto thumbnail = get_thumbnail_object(td_->file_manager_.get(), sticker_set->thumbnail, thumbnail_format);
|
||||
auto actual_count = narrow_cast<int32>(sticker_set->sticker_ids.size());
|
||||
return make_tl_object<td_api::stickerSetInfo>(
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, std::move(thumbnail),
|
||||
sticker_set->id.get(), sticker_set->title, sticker_set->short_name, get_sticker_set_thumbnail_object(sticker_set),
|
||||
get_sticker_minithumbnail(sticker_set->minithumbnail, sticker_set->id, -3,
|
||||
get_sticker_set_minithumbnail_zoom(sticker_set)),
|
||||
sticker_set->is_installed && !sticker_set->is_archived, sticker_set->is_archived, sticker_set->is_official,
|
||||
@ -5794,19 +5812,7 @@ Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_i
|
||||
return Status::Error(400, "Sticker type must be non-empty");
|
||||
}
|
||||
|
||||
switch (sticker->type_->get_id()) {
|
||||
case td_api::stickerTypeStatic::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webp, false);
|
||||
case td_api::stickerTypeAnimated::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Tgs, false);
|
||||
case td_api::stickerTypeVideo::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webm, false);
|
||||
case td_api::stickerTypeMask::ID:
|
||||
return prepare_input_file(sticker->sticker_, StickerFormat::Webp, false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
return prepare_input_file(sticker->sticker_, get_sticker_format(sticker->type_), false);
|
||||
}
|
||||
|
||||
Result<std::tuple<FileId, bool, bool, StickerFormat>> StickersManager::prepare_input_file(
|
||||
|
@ -52,6 +52,11 @@ class StickersManager final : public Actor {
|
||||
static vector<int64> convert_sticker_set_ids(const vector<StickerSetId> &sticker_set_ids);
|
||||
|
||||
StickersManager(Td *td, ActorShared<> parent);
|
||||
StickersManager(const StickersManager &) = delete;
|
||||
StickersManager &operator=(const StickersManager &) = delete;
|
||||
StickersManager(StickersManager &&) = delete;
|
||||
StickersManager &operator=(StickersManager &&) = delete;
|
||||
~StickersManager() final;
|
||||
|
||||
void init();
|
||||
|
||||
@ -500,6 +505,8 @@ class StickersManager final : public Actor {
|
||||
|
||||
static tl_object_ptr<td_api::MaskPoint> get_mask_point_object(int32 point);
|
||||
|
||||
td_api::object_ptr<td_api::thumbnail> get_sticker_set_thumbnail_object(const StickerSet *sticker_set) const;
|
||||
|
||||
tl_object_ptr<td_api::stickerSetInfo> get_sticker_set_info_object(StickerSetId sticker_set_id, size_t covers_limit,
|
||||
bool prefer_premium) const;
|
||||
|
||||
|
@ -2894,8 +2894,8 @@ bool Td::is_authentication_request(int32 id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Td::is_synchronous_request(int32 id) {
|
||||
switch (id) {
|
||||
bool Td::is_synchronous_request(const td_api::Function *function) {
|
||||
switch (function->get_id()) {
|
||||
case td_api::getTextEntities::ID:
|
||||
case td_api::parseTextEntities::ID:
|
||||
case td_api::parseMarkdown::ID:
|
||||
@ -2920,6 +2920,8 @@ bool Td::is_synchronous_request(int32 id) {
|
||||
case td_api::addLogMessage::ID:
|
||||
case td_api::testReturnError::ID:
|
||||
return true;
|
||||
case td_api::getOption::ID:
|
||||
return OptionManager::is_synchronous_option(static_cast<const td_api::getOption *>(function)->name_);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -3010,6 +3012,14 @@ td_api::object_ptr<td_api::AuthorizationState> Td::get_fake_authorization_state_
|
||||
}
|
||||
}
|
||||
|
||||
vector<td_api::object_ptr<td_api::Update>> Td::get_fake_current_state() const {
|
||||
CHECK(state_ != State::Run);
|
||||
vector<td_api::object_ptr<td_api::Update>> updates;
|
||||
OptionManager::get_common_state(updates);
|
||||
updates.push_back(td_api::make_object<td_api::updateAuthorizationState>(get_fake_authorization_state_object()));
|
||||
return updates;
|
||||
}
|
||||
|
||||
DbKey Td::as_db_key(string key, bool custom_db) {
|
||||
// Database will still be effectively not encrypted, but
|
||||
// 1. SQLite database will be protected from corruption, because that's how sqlcipher works
|
||||
@ -3036,7 +3046,7 @@ void Td::request(uint64 id, tl_object_ptr<td_api::Function> function) {
|
||||
}
|
||||
|
||||
VLOG(td_requests) << "Receive request " << id << ": " << to_string(function);
|
||||
if (is_synchronous_request(function->get_id())) {
|
||||
if (is_synchronous_request(function.get())) {
|
||||
// send response synchronously
|
||||
return send_result(id, static_request(std::move(function)));
|
||||
}
|
||||
@ -3060,14 +3070,9 @@ void Td::run_request(uint64 id, tl_object_ptr<td_api::Function> function) {
|
||||
case td_api::getAuthorizationState::ID:
|
||||
// send response synchronously to prevent "Request aborted"
|
||||
return send_result(id, get_fake_authorization_state_object());
|
||||
case td_api::getCurrentState::ID: {
|
||||
vector<td_api::object_ptr<td_api::Update>> updates;
|
||||
updates.push_back(td_api::make_object<td_api::updateOption>(
|
||||
"version", td_api::make_object<td_api::optionValueString>(TDLIB_VERSION)));
|
||||
updates.push_back(td_api::make_object<td_api::updateAuthorizationState>(get_fake_authorization_state_object()));
|
||||
case td_api::getCurrentState::ID:
|
||||
// send response synchronously to prevent "Request aborted"
|
||||
return send_result(id, td_api::make_object<td_api::updates>(std::move(updates)));
|
||||
}
|
||||
return send_result(id, td_api::make_object<td_api::updates>(get_fake_current_state()));
|
||||
case td_api::close::ID:
|
||||
// need to send response before actual closing
|
||||
send_closure(actor_id(this), &Td::send_result, id, td_api::make_object<td_api::ok>());
|
||||
@ -3280,10 +3285,9 @@ void Td::start_up() {
|
||||
alarm_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
|
||||
CHECK(state_ == State::WaitParameters);
|
||||
send_update(td_api::make_object<td_api::updateOption>("version",
|
||||
td_api::make_object<td_api::optionValueString>(TDLIB_VERSION)));
|
||||
send_update(td_api::make_object<td_api::updateAuthorizationState>(
|
||||
td_api::make_object<td_api::authorizationStateWaitTdlibParameters>()));
|
||||
for (auto &update : get_fake_current_state()) {
|
||||
send_update(std::move(update));
|
||||
}
|
||||
}
|
||||
|
||||
void Td::tear_down() {
|
||||
@ -3818,6 +3822,10 @@ void Td::init(Result<TdDb::OpenedDatabase> r_opened_database) {
|
||||
web_pages_manager_->on_binlog_web_page_event(std::move(event));
|
||||
}
|
||||
|
||||
for (auto &event : events.save_app_log_events) {
|
||||
on_save_app_log_binlog_event(this, std::move(event));
|
||||
}
|
||||
|
||||
if (is_online_) {
|
||||
on_online_updated(true, true);
|
||||
}
|
||||
@ -4207,6 +4215,9 @@ void Td::send_error_impl(uint64 id, tl_object_ptr<td_api::error> error) {
|
||||
if (it != request_set_.end()) {
|
||||
request_set_.erase(it);
|
||||
VLOG(td_requests) << "Sending error for request " << id << ": " << oneline(to_string(error));
|
||||
if (error->code_ == 0 && error->message_ == "Lost promise") {
|
||||
LOG(FATAL) << "Lost promise for query " << id;
|
||||
}
|
||||
callback_->on_error(id, std::move(error));
|
||||
}
|
||||
}
|
||||
@ -4342,7 +4353,9 @@ Status Td::set_parameters(td_api::object_ptr<td_api::tdlibParameters> parameters
|
||||
}
|
||||
if (options_.api_id != 21724) {
|
||||
options_.application_version += ", TDLib ";
|
||||
options_.application_version += TDLIB_VERSION;
|
||||
auto version = OptionManager::get_option_synchronously("version");
|
||||
CHECK(version->get_id() == td_api::optionValueString::ID);
|
||||
options_.application_version += static_cast<const td_api::optionValueString *>(version.get())->value_;
|
||||
}
|
||||
options_.language_pack = string();
|
||||
options_.language_code = string();
|
||||
@ -8235,6 +8248,13 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::parseMarkdown &
|
||||
return get_formatted_text_object(parsed_text, false, std::numeric_limits<int32>::max());
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getOption &request) {
|
||||
if (!is_synchronous_request(&request)) {
|
||||
return make_error(400, "The option can't be get synchronously");
|
||||
}
|
||||
return OptionManager::get_option_synchronously(request.name_);
|
||||
}
|
||||
|
||||
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getMarkdownText &request) {
|
||||
if (request.text_ == nullptr) {
|
||||
return make_error(400, "Text must be non-empty");
|
||||
@ -8459,6 +8479,4 @@ void Td::on_request(uint64 id, td_api::testCallVectorStringObject &request) {
|
||||
#undef CREATE_REQUEST_PROMISE
|
||||
#undef CREATE_OK_REQUEST_PROMISE
|
||||
|
||||
constexpr const char *Td::TDLIB_VERSION;
|
||||
|
||||
} // namespace td
|
||||
|
@ -109,8 +109,6 @@ class Td final : public Actor {
|
||||
Td &operator=(Td &&) = delete;
|
||||
~Td() final;
|
||||
|
||||
static constexpr const char *TDLIB_VERSION = "1.8.4";
|
||||
|
||||
struct Options {
|
||||
std::shared_ptr<NetQueryStats> net_query_stats;
|
||||
};
|
||||
@ -339,6 +337,8 @@ class Td final : public Actor {
|
||||
|
||||
td_api::object_ptr<td_api::AuthorizationState> get_fake_authorization_state_object() const;
|
||||
|
||||
vector<td_api::object_ptr<td_api::Update>> get_fake_current_state() const;
|
||||
|
||||
static void on_alarm_timeout_callback(void *td_ptr, int64 alarm_id);
|
||||
void on_alarm_timeout(int64 alarm_id);
|
||||
|
||||
@ -389,7 +389,7 @@ class Td final : public Actor {
|
||||
|
||||
static bool is_authentication_request(int32 id);
|
||||
|
||||
static bool is_synchronous_request(int32 id);
|
||||
static bool is_synchronous_request(const td_api::Function *function);
|
||||
|
||||
static bool is_preinitialization_request(int32 id);
|
||||
|
||||
@ -1419,6 +1419,7 @@ class Td final : public Actor {
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const T &request) {
|
||||
return td_api::make_object<td_api::error>(400, "The method can't be executed synchronously");
|
||||
}
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getOption &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getTextEntities &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::parseTextEntities &request);
|
||||
static td_api::object_ptr<td_api::Object> do_static_request(td_api::parseMarkdown &request);
|
||||
|
@ -117,6 +117,9 @@ Status init_binlog(Binlog &binlog, string path, BinlogKeyValue<Binlog> &binlog_p
|
||||
case LogEvent::HandlerType::EditMessagePushNotification:
|
||||
events.to_notification_manager.push_back(event.clone());
|
||||
break;
|
||||
case LogEvent::HandlerType::SaveAppLog:
|
||||
events.save_app_log_events.push_back(event.clone());
|
||||
break;
|
||||
case LogEvent::HandlerType::BinlogPmcMagic:
|
||||
binlog_pmc.external_init_handle(event);
|
||||
break;
|
||||
|
@ -63,6 +63,7 @@ class TdDb {
|
||||
vector<BinlogEvent> channel_events;
|
||||
vector<BinlogEvent> secret_chat_events;
|
||||
vector<BinlogEvent> web_page_events;
|
||||
vector<BinlogEvent> save_app_log_events;
|
||||
vector<BinlogEvent> to_poll_manager;
|
||||
vector<BinlogEvent> to_messages_manager;
|
||||
vector<BinlogEvent> to_notification_manager;
|
||||
|
@ -23,6 +23,10 @@ namespace td {
|
||||
VideoNotesManager::VideoNotesManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
VideoNotesManager::~VideoNotesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), video_notes_);
|
||||
}
|
||||
|
||||
int32 VideoNotesManager::get_video_note_duration(FileId file_id) const {
|
||||
auto it = video_notes_.find(file_id);
|
||||
CHECK(it != video_notes_.end());
|
||||
|
@ -24,6 +24,11 @@ class Td;
|
||||
class VideoNotesManager {
|
||||
public:
|
||||
explicit VideoNotesManager(Td *td);
|
||||
VideoNotesManager(const VideoNotesManager &) = delete;
|
||||
VideoNotesManager &operator=(const VideoNotesManager &) = delete;
|
||||
VideoNotesManager(VideoNotesManager &&) = delete;
|
||||
VideoNotesManager &operator=(VideoNotesManager &&) = delete;
|
||||
~VideoNotesManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -24,6 +24,10 @@ namespace td {
|
||||
VideosManager::VideosManager(Td *td) : td_(td) {
|
||||
}
|
||||
|
||||
VideosManager::~VideosManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), videos_);
|
||||
}
|
||||
|
||||
int32 VideosManager::get_video_duration(FileId file_id) const {
|
||||
auto it = videos_.find(file_id);
|
||||
CHECK(it != videos_.end());
|
||||
|
@ -24,6 +24,11 @@ class Td;
|
||||
class VideosManager {
|
||||
public:
|
||||
explicit VideosManager(Td *td);
|
||||
VideosManager(const VideosManager &) = delete;
|
||||
VideosManager &operator=(const VideosManager &) = delete;
|
||||
VideosManager(VideosManager &&) = delete;
|
||||
VideosManager &operator=(VideosManager &&) = delete;
|
||||
~VideosManager();
|
||||
|
||||
void memory_stats(vector<string> &output);
|
||||
|
||||
|
@ -100,6 +100,12 @@ VoiceNotesManager::VoiceNotesManager(Td *td, ActorShared<> parent) : td_(td), pa
|
||||
voice_note_transcription_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
VoiceNotesManager::~VoiceNotesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), voice_notes_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), voice_note_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), message_voice_notes_);
|
||||
}
|
||||
|
||||
void VoiceNotesManager::tear_down() {
|
||||
parent_.reset();
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ class Td;
|
||||
class VoiceNotesManager final : public Actor {
|
||||
public:
|
||||
VoiceNotesManager(Td *td, ActorShared<> parent);
|
||||
VoiceNotesManager(const VoiceNotesManager &) = delete;
|
||||
VoiceNotesManager &operator=(const VoiceNotesManager &) = delete;
|
||||
VoiceNotesManager(VoiceNotesManager &&) = delete;
|
||||
VoiceNotesManager &operator=(VoiceNotesManager &&) = delete;
|
||||
~VoiceNotesManager() final;
|
||||
|
||||
int32 get_voice_note_duration(FileId file_id) const;
|
||||
|
||||
|
@ -417,7 +417,13 @@ void WebPagesManager::tear_down() {
|
||||
LOG(DEBUG) << "Have " << web_pages_.size() << " web pages to free";
|
||||
}
|
||||
|
||||
WebPagesManager::~WebPagesManager() = default;
|
||||
WebPagesManager::~WebPagesManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), web_pages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), web_page_messages_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), got_web_page_previews_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), url_to_web_page_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), url_to_file_source_id_);
|
||||
}
|
||||
|
||||
WebPageId WebPagesManager::on_get_web_page(tl_object_ptr<telegram_api::WebPage> &&web_page_ptr,
|
||||
DialogId owner_dialog_id) {
|
||||
|
@ -2401,6 +2401,8 @@ class CliClient final : public Actor {
|
||||
td_api::make_object<td_api::optionValueBoolean>(op == "on")));
|
||||
} else if (op == "go") {
|
||||
send_request(td_api::make_object<td_api::getOption>(args));
|
||||
} else if (op == "gos") {
|
||||
execute(td_api::make_object<td_api::getOption>(args));
|
||||
} else if (op == "sob") {
|
||||
string name;
|
||||
bool value;
|
||||
|
@ -841,6 +841,14 @@ void FileManager::init_actor() {
|
||||
}
|
||||
|
||||
FileManager::~FileManager() {
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), remote_location_info_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_hash_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), local_location_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), generate_location_to_file_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), pmc_id_to_file_node_id_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_id_info_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), empty_file_ids_);
|
||||
Scheduler::instance()->destroy_on_scheduler(G()->get_gc_scheduler_id(), file_nodes_);
|
||||
}
|
||||
|
||||
string FileManager::fix_file_extension(Slice file_name, Slice file_type, Slice file_extension) {
|
||||
@ -1009,6 +1017,13 @@ static Status check_partial_local_location(const PartialLocalFileLocation &locat
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void FileManager::check_local_location(FileId file_id) {
|
||||
auto node = get_sync_file_node(file_id);
|
||||
if (node) {
|
||||
check_local_location(node).ignore();
|
||||
}
|
||||
}
|
||||
|
||||
Status FileManager::check_local_location(FileNodePtr node) {
|
||||
Status status;
|
||||
if (node->local_.type() == LocalFileLocation::Type::Full) {
|
||||
@ -1100,7 +1115,6 @@ FileId FileManager::register_empty(FileType type) {
|
||||
}
|
||||
|
||||
void FileManager::on_file_unlink(const FullLocalFileLocation &location) {
|
||||
// TODO: remove file from the database too
|
||||
auto it = local_location_to_file_id_.find(location);
|
||||
if (it == local_location_to_file_id_.end()) {
|
||||
return;
|
||||
@ -1108,6 +1122,7 @@ void FileManager::on_file_unlink(const FullLocalFileLocation &location) {
|
||||
auto file_id = it->second;
|
||||
auto file_node = get_sync_file_node(file_id);
|
||||
CHECK(file_node);
|
||||
clear_from_pmc(file_node);
|
||||
send_closure(G()->download_manager(), &DownloadManager::remove_file_if_finished, file_node->main_file_id_);
|
||||
file_node->drop_local_location();
|
||||
try_flush_node_info(file_node, "on_file_unlink");
|
||||
|
@ -449,6 +449,8 @@ class FileManager final : public FileLoadManager::Callback {
|
||||
bool set_encryption_key(FileId file_id, FileEncryptionKey key);
|
||||
bool set_content(FileId file_id, BufferSlice bytes);
|
||||
|
||||
void check_local_location(FileId file_id);
|
||||
|
||||
void download(FileId file_id, std::shared_ptr<DownloadCallback> callback, int32 new_priority, int64 offset,
|
||||
int64 limit);
|
||||
void upload(FileId file_id, std::shared_ptr<UploadCallback> callback, int32 new_priority, uint64 upload_order);
|
||||
|
@ -224,9 +224,9 @@ FileId FileManager::parse_file(ParserT &parser) {
|
||||
if (expected_size < 0) {
|
||||
expected_size += static_cast<int64>(1) << 32;
|
||||
}
|
||||
int32 zero;
|
||||
parse(zero, parser);
|
||||
}
|
||||
int32 zero;
|
||||
parse(zero, parser);
|
||||
DialogId owner_dialog_id;
|
||||
if (parser.version() >= static_cast<int32>(Version::StoreFileOwnerId)) {
|
||||
parse(owner_dialog_id, parser);
|
||||
|
@ -104,6 +104,7 @@ class LogEvent {
|
||||
GetChannelDifference = 0x140,
|
||||
AddMessagePushNotification = 0x200,
|
||||
EditMessagePushNotification = 0x201,
|
||||
SaveAppLog = 0x300,
|
||||
ConfigPmcMagic = 0x1f18,
|
||||
BinlogPmcMagic = 0x4327
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
||||
@ -194,7 +194,7 @@ Status NetQueryDispatcher::wait_dc_init(DcId dc_id, bool force) {
|
||||
return Status::Error("Closing");
|
||||
}
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -331,7 +331,7 @@ void NetQueryDispatcher::set_main_dc_id(int32 new_main_dc_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Very rare event. Mutex is ok.
|
||||
// Very rare event; mutex is ok.
|
||||
std::lock_guard<std::mutex> guard(main_dc_id_mutex_);
|
||||
if (new_main_dc_id == main_dc_id_) {
|
||||
return;
|
||||
|
@ -69,11 +69,6 @@ class SemaphoreActor final : public Actor {
|
||||
size_t capacity_;
|
||||
VectorQueue<Promise<Promise<Unit>>> pending_;
|
||||
|
||||
void start_up() final {
|
||||
set_context(std::make_shared<ActorContext>());
|
||||
set_tag(string());
|
||||
}
|
||||
|
||||
void finish(Result<Unit>) {
|
||||
capacity_++;
|
||||
if (!pending_.empty()) {
|
||||
@ -137,7 +132,11 @@ class GenAuthKeyActor final : public Actor {
|
||||
|
||||
static TD_THREAD_LOCAL Semaphore *semaphore_;
|
||||
Semaphore &get_handshake_semaphore() {
|
||||
auto old_context = set_context(std::make_shared<ActorContext>());
|
||||
auto old_tag = set_tag(string());
|
||||
init_thread_local<Semaphore>(semaphore_, 50);
|
||||
set_context(std::move(old_context));
|
||||
set_tag(std::move(old_tag));
|
||||
return *semaphore_;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ TDJSON_EXPORT const char *td_execute(const char *request);
|
||||
/**
|
||||
* A type of callback function that will be called when a message is added to the internal TDLib log.
|
||||
*
|
||||
* \param verbosity_level Log verbosity level with which the message was added (-1 - 1024).
|
||||
* \param verbosity_level Log verbosity level with which the message was added from -1 up to 1024.
|
||||
* If 0, then TDLib will crash as soon as the callback returns.
|
||||
* None of the TDLib methods can be called from the callback.
|
||||
* \param message Null-terminated UTF-8-encoded string with the message added to the log.
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class Timeout final : public Actor {
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "td/utils/Closure.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <type_traits>
|
||||
@ -49,7 +48,6 @@ class CustomEvent {
|
||||
virtual ~CustomEvent() = default;
|
||||
|
||||
virtual void run(Actor *actor) = 0;
|
||||
virtual CustomEvent *clone() const = 0;
|
||||
virtual void start_migrate(int32 sched_id) {
|
||||
}
|
||||
virtual void finish_migrate() {
|
||||
@ -62,9 +60,6 @@ class ClosureEvent final : public CustomEvent {
|
||||
void run(Actor *actor) final {
|
||||
closure_.run(static_cast<typename ClosureT::ActorType *>(actor));
|
||||
}
|
||||
CustomEvent *clone() const final {
|
||||
return new ClosureEvent<ClosureT>(closure_.clone());
|
||||
}
|
||||
template <class... ArgsT>
|
||||
explicit ClosureEvent(ArgsT &&...args) : closure_(std::forward<ArgsT>(args)...) {
|
||||
}
|
||||
@ -93,10 +88,6 @@ class LambdaEvent final : public CustomEvent {
|
||||
void run(Actor *actor) final {
|
||||
f_();
|
||||
}
|
||||
CustomEvent *clone() const final {
|
||||
LOG(FATAL) << "Not supported";
|
||||
return nullptr;
|
||||
}
|
||||
template <class FromLambdaT, std::enable_if_t<!std::is_same<std::decay_t<FromLambdaT>, LambdaEvent>::value, int> = 0>
|
||||
explicit LambdaEvent(FromLambdaT &&lambda) : f_(std::forward<FromLambdaT>(lambda)) {
|
||||
}
|
||||
@ -181,17 +172,6 @@ class Event {
|
||||
destroy();
|
||||
}
|
||||
|
||||
Event clone() const {
|
||||
Event res;
|
||||
res.type = type;
|
||||
if (type == Type::Custom) {
|
||||
res.data.custom_event = data.custom_event->clone();
|
||||
} else {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return type == Type::NoType;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "td/actor/impl/EventFull-decl.h"
|
||||
|
||||
#include "td/utils/Closure.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/Heap.h"
|
||||
#include "td/utils/List.h"
|
||||
@ -101,6 +102,9 @@ class Scheduler {
|
||||
|
||||
void run_on_scheduler(int32 sched_id, Promise<Unit> action); // TODO Action
|
||||
|
||||
template <class T>
|
||||
void destroy_on_scheduler(int32 sched_id, T &value);
|
||||
|
||||
template <ActorSendType send_type, class EventT>
|
||||
void send_lambda(ActorRef actor_ref, EventT &&lambda);
|
||||
|
||||
@ -149,6 +153,8 @@ class Scheduler {
|
||||
private:
|
||||
static void set_scheduler(Scheduler *scheduler);
|
||||
|
||||
void destroy_on_scheduler_impl(int32 sched_id, Promise<Unit> action);
|
||||
|
||||
class ServiceActor final : public Actor {
|
||||
public:
|
||||
void set_queue(std::shared_ptr<MpscPollableQueue<EventFull>> queues);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "td/utils/MpscPollableQueue.h"
|
||||
#include "td/utils/ObjectPool.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
@ -361,6 +362,21 @@ void Scheduler::run_on_scheduler(int32 sched_id, Promise<Unit> action) {
|
||||
action.set_value(Unit());
|
||||
}
|
||||
|
||||
void Scheduler::destroy_on_scheduler_impl(int32 sched_id, Promise<Unit> action) {
|
||||
auto empty_context = std::make_shared<ActorContext>();
|
||||
empty_context->this_ptr_ = empty_context;
|
||||
ActorContext *current_context = context_;
|
||||
context_ = empty_context.get();
|
||||
|
||||
const char *current_tag = LOG_TAG;
|
||||
LOG_TAG = nullptr;
|
||||
|
||||
run_on_scheduler(sched_id, std::move(action));
|
||||
|
||||
context_ = current_context;
|
||||
LOG_TAG = current_tag;
|
||||
}
|
||||
|
||||
void Scheduler::add_to_mailbox(ActorInfo *actor_info, Event &&event) {
|
||||
if (!actor_info->is_running()) {
|
||||
auto node = actor_info->get_list_node();
|
||||
|
@ -161,7 +161,7 @@ void Scheduler::flush_mailbox(ActorInfo *actor_info, const RunFuncT &run_func, c
|
||||
mailbox.erase(mailbox.begin(), mailbox.begin() + i);
|
||||
}
|
||||
|
||||
inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<> &actor_id, Event &&event) {
|
||||
inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<Actor> &actor_id, Event &&event) {
|
||||
if (sched_id == sched_id_) {
|
||||
ActorInfo *actor_info = actor_id.get_actor_info();
|
||||
pending_events_[actor_info].push_back(std::move(event));
|
||||
@ -170,6 +170,15 @@ inline void Scheduler::send_to_scheduler(int32 sched_id, const ActorId<> &actor_
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Scheduler::destroy_on_scheduler(int32 sched_id, T &value) {
|
||||
if (!value.empty()) {
|
||||
destroy_on_scheduler_impl(sched_id, PromiseCreator::lambda([value = std::move(value)](Unit) {
|
||||
// destroy value
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
inline void Scheduler::before_tail_send(const ActorId<> &actor_id) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -98,6 +98,11 @@ long strm_ctrl(BIO *b, int cmd, long num, void *ptr) {
|
||||
case BIO_CTRL_PUSH:
|
||||
case BIO_CTRL_POP:
|
||||
return 0;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
case BIO_CTRL_GET_KTLS_SEND:
|
||||
case BIO_CTRL_GET_KTLS_RECV:
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
LOG(FATAL) << b << " " << cmd << " " << num << " " << ptr;
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/port/IPAddress.h
|
||||
td/utils/port/IoSlice.h
|
||||
td/utils/port/MemoryMapping.h
|
||||
td/utils/port/Mutex.h
|
||||
td/utils/port/path.h
|
||||
td/utils/port/platform.h
|
||||
td/utils/port/Poll.h
|
||||
@ -337,7 +338,6 @@ set(TDUTILS_TEST_SOURCE
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
#RULES
|
||||
#LIBRARIES
|
||||
add_library(tdutils STATIC ${TDUTILS_SOURCE})
|
||||
|
||||
|
@ -3,7 +3,7 @@ if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.0.2"))
|
||||
endif()
|
||||
|
||||
# Generates files for MIME type <-> extension conversions
|
||||
# DEPENDS ON: gperf grep bash/powershell
|
||||
# DEPENDS ON: gperf grep
|
||||
|
||||
if (NOT TDUTILS_MIME_TYPE)
|
||||
return()
|
||||
@ -25,7 +25,7 @@ add_custom_target(tdmime_auto DEPENDS ${TDMIME_SOURCE})
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
find_program(GPERF_EXECUTABLE gperf)
|
||||
if (NOT GPERF_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could NOT find gperf. Add path to gperf executable to PATH environment variable or specify it manually using GPERF_EXECUTABLE option, i. e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\"'.")
|
||||
message(FATAL_ERROR "Could NOT find gperf. Add path to gperf executable to PATH environment variable or specify it manually using GPERF_EXECUTABLE option, i.e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\"'.")
|
||||
endif()
|
||||
|
||||
set(GPERF_FILES
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/type_traits.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -20,6 +20,15 @@ template <class T>
|
||||
class AtomicRead {
|
||||
public:
|
||||
void read(T &dest) const {
|
||||
uint32 counter = 0;
|
||||
auto wait = [&] {
|
||||
counter++;
|
||||
const int wait_each_count = 4;
|
||||
if (counter % wait_each_count == 0) {
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
static_assert(TD_IS_TRIVIALLY_COPYABLE(T), "T must be trivially copyable");
|
||||
auto version_before = version.load();
|
||||
@ -30,7 +39,7 @@ class AtomicRead {
|
||||
break;
|
||||
}
|
||||
}
|
||||
td::this_thread::yield();
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,7 @@
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/invoke.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -55,7 +53,6 @@
|
||||
//
|
||||
//
|
||||
// create_immediate_closure(&ActorT::func, arg1, arg2, ..., argn).run(actor)
|
||||
// to_delayed_closure(std::move(immediate)).run(actor)
|
||||
|
||||
namespace td {
|
||||
template <class ActorT, class FunctionT, class... ArgsT>
|
||||
@ -92,11 +89,6 @@ template <class ActorT, class FunctionT, class... ArgsT>
|
||||
class DelayedClosure {
|
||||
public:
|
||||
using ActorType = ActorT;
|
||||
using Delayed = DelayedClosure<ActorT, FunctionT, ArgsT...>;
|
||||
|
||||
DelayedClosure clone() const {
|
||||
return do_clone(*this);
|
||||
}
|
||||
|
||||
explicit DelayedClosure(ImmediateClosure<ActorT, FunctionT, ArgsT...> &&other) : args(std::move(other.args)) {
|
||||
}
|
||||
@ -110,38 +102,7 @@ class DelayedClosure {
|
||||
}
|
||||
|
||||
private:
|
||||
using ArgsStorageT = std::tuple<FunctionT, typename std::decay<ArgsT>::type...>;
|
||||
|
||||
ArgsStorageT args;
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
explicit DelayedClosure(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
|
||||
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0)
|
||||
: args(other.args) {
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
explicit DelayedClosure(
|
||||
const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
|
||||
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0) {
|
||||
LOG(FATAL) << "Deleted constructor";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
|
||||
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
|
||||
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
|
||||
LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
template <class FromActorT, class FromFunctionT, class... FromArgsT>
|
||||
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
|
||||
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
|
||||
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
|
||||
return DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>(value);
|
||||
}
|
||||
std::tuple<FunctionT, typename std::decay<ArgsT>::type...> args;
|
||||
|
||||
public:
|
||||
auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
|
||||
@ -149,16 +110,6 @@ class DelayedClosure {
|
||||
}
|
||||
};
|
||||
|
||||
template <class... ArgsT>
|
||||
typename ImmediateClosure<ArgsT...>::Delayed to_delayed_closure(ImmediateClosure<ArgsT...> &&other) {
|
||||
return typename ImmediateClosure<ArgsT...>::Delayed(std::move(other));
|
||||
}
|
||||
|
||||
template <class... ArgsT>
|
||||
DelayedClosure<ArgsT...> to_delayed_closure(DelayedClosure<ArgsT...> &&other) {
|
||||
return std::move(other);
|
||||
}
|
||||
|
||||
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
|
||||
auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&...args) {
|
||||
return DelayedClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
|
||||
|
@ -42,6 +42,10 @@ class Enumerator {
|
||||
return arr_.size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<ValueT, int32> map_;
|
||||
std::vector<const ValueT *> arr_;
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/HazardPointers.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
#include <array>
|
||||
@ -305,7 +305,7 @@ class MpmcQueueOld {
|
||||
if (try_pop(value, thread_id)) {
|
||||
return value;
|
||||
}
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +429,7 @@ class MpmcQueue {
|
||||
if (try_pop(value, thread_id)) {
|
||||
return value;
|
||||
}
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@ -29,16 +29,17 @@ class MpmcEagerWaiter {
|
||||
slot.yields = 0;
|
||||
slot.worker_id = worker_id;
|
||||
}
|
||||
|
||||
void wait(Slot &slot) {
|
||||
if (slot.yields < RoundsTillSleepy) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
} else if (slot.yields == RoundsTillSleepy) {
|
||||
auto state = state_.load(std::memory_order_relaxed);
|
||||
if (!State::has_worker(state)) {
|
||||
auto new_state = State::with_worker(state, slot.worker_id);
|
||||
if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
@ -47,12 +48,12 @@ class MpmcEagerWaiter {
|
||||
return;
|
||||
}
|
||||
}
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields = 0;
|
||||
} else if (slot.yields < RoundsTillAsleep) {
|
||||
auto state = state_.load(std::memory_order_acquire);
|
||||
if (State::still_sleepy(state, slot.worker_id)) {
|
||||
td::this_thread::yield();
|
||||
yield();
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
@ -121,6 +122,10 @@ class MpmcEagerWaiter {
|
||||
condition_variable_.notify_all();
|
||||
}
|
||||
}
|
||||
static void yield() {
|
||||
// whatever, this is better than sched_yield
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
class MpmcSleepyWaiter {
|
||||
@ -208,7 +213,7 @@ class MpmcSleepyWaiter {
|
||||
}
|
||||
if (slot.state_ == Slot::Search) {
|
||||
if (slot.yield_cnt++ < 10 && false) {
|
||||
td::this_thread::yield();
|
||||
// TODO some sleep backoff is possible
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -91,7 +91,7 @@ class MpscPollableQueue {
|
||||
}
|
||||
|
||||
private:
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
bool wait_event_fd_{false};
|
||||
EventFd event_fd_;
|
||||
std::vector<ValueType> writer_vector_;
|
||||
|
@ -6,7 +6,7 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -30,7 +30,7 @@ class SpinLock {
|
||||
//TODO pause
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,31 @@ class WaitFreeHashMap {
|
||||
size_t erase(const KeyT &key) {
|
||||
return get_storage(key).erase(key);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
if (wait_free_storage_ == nullptr) {
|
||||
return default_map_.size();
|
||||
}
|
||||
|
||||
size_t result = 0;
|
||||
for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
|
||||
result += wait_free_storage_->maps_[i].size();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
if (wait_free_storage_ == nullptr) {
|
||||
return default_map_.empty();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
|
||||
if (!wait_free_storage_->maps_[i].empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
30
tdutils/td/utils/port/Mutex.h
Normal file
30
tdutils/td/utils/port/Mutex.h
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
struct Guard {
|
||||
std::unique_lock<std::mutex> guard;
|
||||
void reset() {
|
||||
guard.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
Guard lock() {
|
||||
return {std::unique_lock<std::mutex>(mutex_)};
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace td
|
@ -29,7 +29,7 @@
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/VectorQueue.h"
|
||||
#endif
|
||||
|
||||
@ -87,7 +87,7 @@ class ServerSocketFdImpl final : private Iocp::Callback {
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
VectorQueue<SocketFd> accepted_;
|
||||
VectorQueue<Status> pending_errors_;
|
||||
static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16;
|
||||
|
@ -17,7 +17,7 @@
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/VectorQueue.h"
|
||||
|
||||
#include <limits>
|
||||
@ -164,7 +164,7 @@ class SocketFdImpl final : private Iocp::Callback {
|
||||
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
|
||||
std::atomic<int> refcnt_{1};
|
||||
bool close_flag_{false};
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
@ -154,7 +154,7 @@ class UdpSocketFdImpl final : private Iocp::Callback {
|
||||
|
||||
private:
|
||||
PollableFdInfo info_;
|
||||
SpinLock lock_;
|
||||
Mutex lock_;
|
||||
|
||||
std::atomic<int> refcnt_{1};
|
||||
bool is_connected_{false};
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Observer.h"
|
||||
#include "td/utils/port/detail/NativeFd.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -140,7 +140,7 @@ class PollableFdInfo final : private ListNode {
|
||||
std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
|
||||
PollFlagsSet flags_;
|
||||
#if TD_PORT_WINDOWS
|
||||
SpinLock observer_lock_;
|
||||
Mutex observer_lock_;
|
||||
#endif
|
||||
ObserverBase *observer_{nullptr};
|
||||
|
||||
|
@ -88,9 +88,6 @@ int ThreadPthread::do_pthread_create(pthread_t *thread, const pthread_attr_t *at
|
||||
}
|
||||
|
||||
namespace this_thread_pthread {
|
||||
void yield() {
|
||||
sched_yield();
|
||||
}
|
||||
ThreadPthread::id get_id() {
|
||||
return pthread_self();
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ class ThreadPthread {
|
||||
};
|
||||
|
||||
namespace this_thread_pthread {
|
||||
void yield();
|
||||
ThreadPthread::id get_id();
|
||||
} // namespace this_thread_pthread
|
||||
} // namespace detail
|
||||
|
@ -70,7 +70,9 @@ class ThreadStl {
|
||||
return std::forward<T>(v);
|
||||
}
|
||||
};
|
||||
namespace this_thread_stl = std::this_thread;
|
||||
namespace this_thread_stl {
|
||||
using std::this_thread::get_id;
|
||||
} // namespace this_thread_stl
|
||||
} // namespace detail
|
||||
} // namespace td
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/EventFd.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
|
||||
@ -32,7 +32,7 @@ class Backoff {
|
||||
if (cnt < 1) { // 50
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return cnt < 3; // 500
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ class InfBackoff {
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,14 @@
|
||||
#include "td/utils/Context.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace td {
|
||||
@ -126,12 +128,12 @@ class RegisterTest {
|
||||
}
|
||||
};
|
||||
|
||||
class Stage {
|
||||
class StageWait {
|
||||
public:
|
||||
void wait(uint64 need) {
|
||||
value_.fetch_add(1, std::memory_order_release);
|
||||
while (value_.load(std::memory_order_acquire) < need) {
|
||||
td::this_thread::yield();
|
||||
usleep_for(1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -139,6 +141,26 @@ class Stage {
|
||||
std::atomic<uint64> value_{0};
|
||||
};
|
||||
|
||||
class StageMutex {
|
||||
public:
|
||||
void wait(uint64 need) {
|
||||
std::unique_lock<std::mutex> lock{mutex_};
|
||||
value_++;
|
||||
if (value_ == need) {
|
||||
cond_.notify_all();
|
||||
return;
|
||||
}
|
||||
cond_.wait(lock, [&] { return value_ >= need; });
|
||||
};
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_;
|
||||
uint64 value_{0};
|
||||
};
|
||||
|
||||
using Stage = StageMutex;
|
||||
|
||||
string rand_string(int from, int to, size_t len);
|
||||
|
||||
vector<string> rand_split(Slice str);
|
||||
|
@ -7,12 +7,12 @@
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/ConcurrentHashTable.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Mutex.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/SpinLock.h"
|
||||
#include "td/utils/tests.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
|
||||
@ -72,11 +72,11 @@ class ConcurrentHashMapMutex {
|
||||
return "ConcurrentHashMapMutex";
|
||||
}
|
||||
void insert(KeyT key, ValueT value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
auto guard = mutex_.lock();
|
||||
hash_map_.emplace(key, value);
|
||||
}
|
||||
ValueT find(KeyT key, ValueT default_value) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
auto guard = mutex_.lock();
|
||||
auto it = hash_map_.find(key);
|
||||
if (it == hash_map_.end()) {
|
||||
return default_value;
|
||||
@ -85,7 +85,7 @@ class ConcurrentHashMapMutex {
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
Mutex mutex_;
|
||||
#if TD_HAVE_ABSL
|
||||
absl::flat_hash_map<KeyT, ValueT> hash_map_;
|
||||
#else
|
||||
|
@ -265,8 +265,7 @@ TEST(FlatHashMap, stress_test) {
|
||||
|
||||
td::vector<td::RandomSteps::Step> steps;
|
||||
auto add_step = [&](td::Slice step_name, td::uint32 weight, auto f) {
|
||||
auto g = [&, step_name, f = std::move(f)] {
|
||||
//LOG(ERROR) << step_name;
|
||||
auto g = [&, f = std::move(f)] {
|
||||
//ASSERT_EQ(ref.size(), tbl.size());
|
||||
f();
|
||||
ASSERT_EQ(ref.size(), tbl.size());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user