Merge commit '00abe2f4019717c6479e30c588059a3cfd3be58e'

Conflicts:
	td/generate/scheme/td_api.tlo
This commit is contained in:
Andrea Cavalli 2020-11-14 11:11:47 +01:00
commit e4bcc81ec7
24 changed files with 209 additions and 80 deletions

View File

@ -891,6 +891,9 @@ install(FILES "${TL_TD_AUTO_INCLUDE_DIR}/td/telegram/td_api.h" "${TL_TD_AUTO_INC
if (TD_ENABLE_JNI)
install(FILES td/tl/tl_jni_object.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/td/tl")
endif()
if (MSVC AND VCPKG_TOOLCHAIN)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/" DESTINATION "${CMAKE_INSTALL_BINDIR}" FILES_MATCHING PATTERN "*.dll")
endif()
include(CMakePackageConfigHelpers)
write_basic_package_version_file("TdConfigVersion.cmake"

View File

@ -160,20 +160,22 @@ int main(int argc, char **argv) {
SET_VERBOSITY_LEVEL(new_verbosity_level);
td::Client client;
td::ClientManager client_manager;
auto client_id = client_manager.create_client();
for (size_t i = 0; i < requests.size(); i++) {
auto &request = requests[i].second;
request->dc_id_ = dc_id;
request->timeout_ = timeout;
client.send({i + 1, std::move(request)});
client_manager.send(client_id, i + 1, std::move(request));
}
size_t successful_requests = 0;
size_t failed_requests = 0;
while (successful_requests + failed_requests != requests.size()) {
auto response = client.receive(100.0);
if (1 <= response.id && response.id <= requests.size()) {
auto &proxy = requests[static_cast<size_t>(response.id - 1)].first;
auto response = client_manager.receive(100.0);
CHECK(client_id == response.client_id);
if (1 <= response.request_id && response.request_id <= requests.size()) {
auto &proxy = requests[static_cast<size_t>(response.request_id - 1)].first;
if (response.object->get_id() == td::td_api::error::ID) {
LOG(ERROR) << proxy << ": " << to_string(response.object);
failed_requests++;

View File

@ -604,7 +604,7 @@ function onOptionsChanged() {
case 'Alpine':
commands.push(sudo + 'apk update');
commands.push(sudo + 'apk upgrade');
var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf php php-ctype cmake';
var packages = 'alpine-sdk linux-headers git zlib-dev openssl-dev gperf php cmake';
if (target === 'JNI') {
packages += ' openjdk8';
}
@ -676,7 +676,7 @@ function onOptionsChanged() {
}
} else if (os_freebsd) {
commands.push(sudo + 'pkg upgrade');
var packages = 'git gperf php72 php72-ctype cmake';
var packages = 'git gperf php72 cmake';
if (target === 'JNI') {
packages += ' openjdk';
}

View File

@ -21,4 +21,4 @@ cmake --build .
Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs.
To run `tdjson_example` you may need to manually copy a `tdjson` shared library from `td/bin` to a directory containing built binaries.
To run the examples you may need to manually copy needed shared libraries from `td/bin` to a directory containing built binaries.

View File

@ -53,8 +53,10 @@ namespace td_api = td::td_api;
class TdExample {
public:
TdExample() {
td::Client::execute({0, td_api::make_object<td_api::setLogVerbosityLevel>(1)});
client_ = std::make_unique<td::Client>();
td::ClientManager::execute(td_api::make_object<td_api::setLogVerbosityLevel>(1));
client_manager_ = std::make_unique<td::ClientManager>();
client_id_ = client_manager_->create_client();
send_query(td_api::make_object<td_api::getOption>("version"), {});
}
void loop() {
@ -62,7 +64,7 @@ class TdExample {
if (need_restart_) {
restart();
} else if (!are_authorized_) {
process_response(client_->receive(10));
process_response(client_manager_->receive(10));
} else {
std::cout << "Enter action [q] quit [u] check for updates and request results [c] show chats [m <id> <text>] "
"send message [me] show self [l] logout: "
@ -80,7 +82,7 @@ class TdExample {
if (action == "u") {
std::cout << "Checking for updates..." << std::endl;
while (true) {
auto response = client_->receive(0);
auto response = client_manager_->receive(0);
if (response.object) {
process_response(std::move(response));
} else {
@ -131,7 +133,8 @@ class TdExample {
private:
using Object = td_api::object_ptr<td_api::Object>;
std::unique_ptr<td::Client> client_;
std::unique_ptr<td::ClientManager> client_manager_;
std::int32_t client_id_{0};
td_api::object_ptr<td_api::AuthorizationState> authorization_state_;
bool are_authorized_{false};
@ -146,7 +149,7 @@ class TdExample {
std::map<std::int64_t, std::string> chat_title_;
void restart() {
client_.reset();
client_manager_.reset();
*this = TdExample();
}
@ -155,18 +158,18 @@ class TdExample {
if (handler) {
handlers_.emplace(query_id, std::move(handler));
}
client_->send({query_id, std::move(f)});
client_manager_->send(client_id_, query_id, std::move(f));
}
void process_response(td::Client::Response response) {
void process_response(td::ClientManager::Response response) {
if (!response.object) {
return;
}
//std::cout << response.id << " " << to_string(response.object) << std::endl;
if (response.id == 0) {
//std::cout << response.request_id << " " << to_string(response.object) << std::endl;
if (response.request_id == 0) {
return process_update(std::move(response.object));
}
auto it = handlers_.find(response.id);
auto it = handlers_.find(response.request_id);
if (it != handlers_.end()) {
it->second(std::move(response.object));
}

View File

@ -18,6 +18,9 @@ int main() {
int client_id = td_create_client();
// somehow share the client_id with other threads, which will be able to send requests via td_send
// start the client by sending request to it
td_send(client_id, "{\"@type\":\"getOption\", \"name\":\"version\"}");
const bool test_incorrect_queries = false;
if (test_incorrect_queries) {
td_execute("{\"@type\":\"setLogVerbosityLevel\", \"new_verbosity_level\":1}");

View File

@ -105,3 +105,6 @@ install(TARGETS tdjni
LIBRARY DESTINATION bin
RUNTIME DESTINATION bin
)
if (MSVC AND VCPKG_TOOLCHAIN)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/" DESTINATION bin FILES_MATCHING PATTERN "*.dll" PATTERN "*.pdb")
endif()

View File

@ -188,6 +188,7 @@ public final class Client {
if (defaultExceptionHandler != null) {
defaultExceptionHandlers.put(nativeClientId, defaultExceptionHandler);
}
send(new TdApi.GetAuthorizationState(), null, null);
}
@Override

View File

@ -47,7 +47,7 @@ endif()
if (GCC OR CLANG)
target_compile_options(tdsqlite PRIVATE -Wno-deprecated-declarations -Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function)
if (CLANG)
target_compile_options(tdsqlite PRIVATE -Wno-parentheses-equality -Wno-unused-value)
target_compile_options(tdsqlite PRIVATE -Wno-parentheses-equality -Wno-unused-value -Wno-unused-command-line-argument -Qunused-arguments)
endif()
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0))
target_compile_options(tdsqlite PRIVATE -Wno-return-local-addr -Wno-stringop-overflow)

View File

@ -53,7 +53,7 @@ abstract class TlDocumentationGenerator
}
}
if ($bracket_count === 0) {
if (ctype_upper($str[$pos + 1])) {
if (ord('A') <= ord($str[$pos + 1]) && ord($str[$pos + 1]) <= ord('Z')) {
return substr($str, 0, -1).'.)';
}
} else {

Binary file not shown.

Binary file not shown.

View File

@ -1053,14 +1053,14 @@ richTextPhoneNumber text:RichText phone_number:string = RichText;
//@height Height of a bounding box in which the image should be shown; 0 if unknown
richTextIcon document:document width:int32 height:int32 = RichText;
//@description A rich text reference of a text on the same web page @text The text @reference_text The text to show on click @url An HTTP URL, opening the reference
richTextReference text:RichText reference_text:RichText url:string = RichText;
//@description A reference to a richTexts object on the same web page @text The text @anchor_name The name of a richTextAnchor object, which is the first element of the target richTexts object @url An HTTP URL, opening the reference
richTextReference text:RichText anchor_name:string url:string = RichText;
//@description An anchor @name Anchor name
richTextAnchor name:string = RichText;
//@description A link to an anchor on the same web page @text The link text @name The anchor name. If the name is empty, the link should bring back to top @url An HTTP URL, opening the anchor
richTextAnchorLink text:RichText name:string url:string = RichText;
//@description A link to an anchor on the same web page @text The link text @anchor_name The anchor name. If the name is empty, the link should bring back to top @url An HTTP URL, opening the anchor
richTextAnchorLink text:RichText anchor_name:string url:string = RichText;
//@description A concatenation of rich texts @texts Texts
richTexts texts:vector<RichText> = RichText;
@ -3794,7 +3794,7 @@ getMessageLinkInfo url:string = MessageLinkInfo;
//@reply_markup Markup for replying to the message; for bots only @input_message_content The content of the message to be sent
sendMessage chat_id:int53 message_thread_id:int53 reply_to_message_id:int53 options:messageSendOptions reply_markup:ReplyMarkup input_message_content:InputMessageContent = Message;
//@description Sends messages grouped together into an album. Currently only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only groupув in an album with messages of the same type. Returns sent messages
//@description Sends messages grouped together into an album. Currently only audio, document, photo and video messages can be grouped into an album. Documents and audio files can be only grouped in an album with messages of the same type. Returns sent messages
//@chat_id Target chat
//@message_thread_id If not 0, a message thread identifier in which the messages will be sent
//@reply_to_message_id Identifier of a message to reply to or 0

Binary file not shown.

Binary file not shown.

View File

@ -79,21 +79,25 @@ class TdReceiver {
class ClientManager::Impl final {
public:
ClientId create_client() {
if (tds_.empty()) {
CHECK(concurrent_scheduler_ == nullptr);
CHECK(options_.net_query_stats == nullptr);
options_.net_query_stats = std::make_shared<NetQueryStats>();
concurrent_scheduler_ = make_unique<ConcurrentScheduler>();
concurrent_scheduler_->init(0);
concurrent_scheduler_->start();
}
CHECK(client_id_ != std::numeric_limits<ClientId>::max());
auto client_id = ++client_id_;
tds_[client_id] =
concurrent_scheduler_->create_actor_unsafe<Td>(0, "Td", receiver_.create_callback(client_id), options_);
pending_clients_.insert(client_id);
return client_id;
}
void send(ClientId client_id, RequestId request_id, td_api::object_ptr<td_api::Function> &&request) {
if (pending_clients_.erase(client_id) != 0) {
if (tds_.empty()) {
CHECK(concurrent_scheduler_ == nullptr);
CHECK(options_.net_query_stats == nullptr);
options_.net_query_stats = std::make_shared<NetQueryStats>();
concurrent_scheduler_ = make_unique<ConcurrentScheduler>();
concurrent_scheduler_->init(0);
concurrent_scheduler_->start();
}
tds_[client_id] =
concurrent_scheduler_->create_actor_unsafe<Td>(0, "Td", receiver_.create_callback(client_id), options_);
}
requests_.push_back({client_id, request_id, std::move(request)});
}
@ -196,6 +200,7 @@ class ClientManager::Impl final {
unique_ptr<ConcurrentScheduler> concurrent_scheduler_;
ClientId client_id_{0};
Td::Options options_;
std::unordered_set<int32> pending_clients_;
std::unordered_map<int32, ActorOwn<Td>> tds_;
};
@ -356,14 +361,19 @@ class MultiImpl {
MultiImpl(MultiImpl &&) = delete;
MultiImpl &operator=(MultiImpl &&) = delete;
int32 create(TdReceiver &receiver) {
auto id = create_id();
create(id, receiver.create_callback(id));
return id;
static int32 create_id() {
auto result = current_id_.fetch_add(1);
CHECK(result <= static_cast<uint32>(std::numeric_limits<int32>::max()));
return static_cast<int32>(result);
}
void create(int32 td_id, unique_ptr<TdCallback> callback) {
auto guard = concurrent_scheduler_->get_send_guard();
send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback));
}
static bool is_valid_client_id(int32 client_id) {
return client_id > 0 && client_id < current_id_.load();
return client_id > 0 && static_cast<uint32>(client_id) < current_id_.load();
}
void send(ClientManager::ClientId client_id, ClientManager::RequestId request_id,
@ -394,19 +404,10 @@ class MultiImpl {
thread scheduler_thread_;
ActorOwn<MultiTd> multi_td_;
static std::atomic<int32> current_id_;
static int32 create_id() {
return current_id_.fetch_add(1);
}
void create(int32 td_id, unique_ptr<TdCallback> callback) {
auto guard = concurrent_scheduler_->get_send_guard();
send_closure(multi_td_, &MultiTd::create, td_id, std::move(callback));
}
static std::atomic<uint32> current_id_;
};
std::atomic<int32> MultiImpl::current_id_{1};
std::atomic<uint32> MultiImpl::current_id_{1};
class MultiImplPool {
public:
@ -456,11 +457,10 @@ class MultiImplPool {
class ClientManager::Impl final {
public:
ClientId create_client() {
auto impl = pool_.get();
auto client_id = impl->create(receiver_);
auto client_id = MultiImpl::create_id();
{
auto lock = impls_mutex_.lock_write().move_as_ok();
impls_[client_id].impl = std::move(impl);
impls_[client_id]; // create empty MultiImplInfo
}
return client_id;
}
@ -472,7 +472,22 @@ class ClientManager::Impl final {
td_api::make_object<td_api::error>(400, "Invalid TDLib instance specified"));
return;
}
auto it = impls_.find(client_id);
if (it != impls_.end() && it->second.impl == nullptr) {
lock.reset();
auto write_lock = impls_mutex_.lock_write().move_as_ok();
it = impls_.find(client_id);
if (it != impls_.end() && it->second.impl == nullptr) {
it->second.impl = pool_.get();
it->second.impl->create(client_id, receiver_.create_callback(client_id));
}
write_lock.reset();
lock = impls_mutex_.lock_read().move_as_ok();
it = impls_.find(client_id);
}
if (it == impls_.end() || it->second.is_closed) {
receiver_.add_response(client_id, request_id, td_api::make_object<td_api::error>(500, "Request aborted"));
return;
@ -515,7 +530,11 @@ class ClientManager::Impl final {
CHECK(it != impls_.end());
if (!it->second.is_closed) {
it->second.is_closed = true;
it->second.impl->close(client_id);
if (it->second.impl == nullptr) {
receiver_.add_response(client_id, 0, nullptr);
} else {
it->second.impl->close(client_id);
}
}
}
@ -552,7 +571,8 @@ class Client::Impl final {
Impl() {
static MultiImplPool pool;
multi_impl_ = pool.get();
td_id_ = multi_impl_->create(receiver_);
td_id_ = MultiImpl::create_id();
multi_impl_->create(td_id_, receiver_.create_callback(td_id_));
}
void send(Request request) {

View File

@ -184,6 +184,7 @@ class ClientManager final {
/**
* Creates a new TDLib client and returns its opaque identifier.
* The client will not send updates until the first request is sent to it.
*/
ClientId create_client();

View File

@ -125,11 +125,11 @@ static std::mutex extra_mutex;
static std::unordered_map<int64, string> extra;
static std::atomic<uint64> extra_id{1};
int td_json_create_client() {
int json_create_client() {
return static_cast<int>(get_manager()->create_client());
}
void td_json_send(int client_id, Slice request) {
void json_send(int client_id, Slice request) {
auto parsed_request = to_request(request);
auto request_id = extra_id.fetch_add(1, std::memory_order_relaxed);
if (!parsed_request.second.empty()) {
@ -139,7 +139,7 @@ void td_json_send(int client_id, Slice request) {
get_manager()->send(client_id, request_id, std::move(parsed_request.first));
}
const char *td_json_receive(double timeout) {
const char *json_receive(double timeout) {
auto response = get_manager()->receive(timeout);
if (!response.object) {
return nullptr;
@ -157,7 +157,7 @@ const char *td_json_receive(double timeout) {
return store_string(from_response(*response.object, extra_str, response.client_id));
}
const char *td_json_execute(Slice request) {
const char *json_execute(Slice request) {
auto parsed_request = to_request(request);
return store_string(
from_response(*ClientManager::execute(std::move(parsed_request.first)), parsed_request.second, 0));

View File

@ -33,12 +33,12 @@ class ClientJson final {
std::atomic<std::uint64_t> extra_id_{1};
};
int td_json_create_client();
int json_create_client();
void td_json_send(int client_id, Slice request);
void json_send(int client_id, Slice request);
const char *td_json_receive(double timeout);
const char *json_receive(double timeout);
const char *td_json_execute(Slice request);
const char *json_execute(Slice request);
} // namespace td

View File

@ -128,11 +128,8 @@ class RichText {
return make_tl_object<td_api::richTextAnchorLink>(texts[0].get_rich_text_object(context), anchor.str(),
content);
} else {
context->is_first_pass_ = true;
auto reference_text = it->second->get_rich_text_object(context);
context->is_first_pass_ = false;
return make_tl_object<td_api::richTextReference>(texts[0].get_rich_text_object(context),
std::move(reference_text), content);
return make_tl_object<td_api::richTextReference>(texts[0].get_rich_text_object(context), anchor.str(),
content);
}
}
}

View File

@ -31,17 +31,17 @@ const char *td_json_client_execute(void *client, const char *request) {
}
int td_create_client() {
return td::td_json_create_client();
return td::json_create_client();
}
void td_send(int client_id, const char *request) {
td::td_json_send(client_id, td::Slice(request == nullptr ? "" : request));
td::json_send(client_id, td::Slice(request == nullptr ? "" : request));
}
const char *td_receive(double timeout) {
return td::td_json_receive(timeout);
return td::json_receive(timeout);
}
const char *td_execute(const char *request) {
return td::td_json_execute(td::Slice(request == nullptr ? "" : request));
return td::json_execute(td::Slice(request == nullptr ? "" : request));
}

View File

@ -126,8 +126,8 @@ TDJSON_EXPORT void td_json_client_destroy(void *client);
*/
/**
* Creates a new instance of TDLib.
* \return Opaque indentifier of the created TDLib client.
* Creates a new instance of TDLib. The TDLib instance will not send updates until the first request is sent to it.
* \return Opaque indentifier of the created TDLib instance.
*/
TDJSON_EXPORT int td_create_client();

View File

@ -20,6 +20,10 @@
#if TD_ANDROID
#include <sys/system_properties.h>
#elif TD_EMSCRIPTEN
#include <cstdlib>
#include <emscripten.h>
#else
#if TD_DARWIN
#include <sys/sysctl.h>
@ -90,6 +94,98 @@ Slice get_operating_system_version() {
if (length > 0) {
return "Android " + string(version, length);
}
#elif TD_EMSCRIPTEN
// clang-format off
char *os_name_js = (char*)EM_ASM_INT(({
function detectOsName() {
if (typeof process === 'object' && typeof process.platform === 'string') { // Node.js
switch (process.platform) {
case 'aix':
return 'IBM AIX';
case 'android':
return 'Android';
case 'darwin':
return 'macOS';
case 'freebsd':
return 'FreeBSD';
case 'linux':
return 'Linux';
case 'openbsd':
return 'OpenBSD';
case 'sunos':
return 'SunOS';
case 'win32':
return 'Windows';
case 'darwin':
return 'macOS';
default:
return 'Node.js';
}
}
var userAgent = 'Unknown';
if (typeof window === 'object') { // Web
userAgent = window.navigator.userAgent;
} else if (typeof importScripts === 'function') { // Web Worker
userAgent = navigator.userAgent;
}
var match = /(Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([._0-9]+)/.exec(userAgent);
if (match !== null) {
return 'macOS ' + match[2].replace('_', '.');
}
match = /Android [._0-9]+/.exec(userAgent);
if (match !== null) {
return match[0].replace('_', '.');
}
if (/(iPhone|iPad|iPod)/.test(userAgent)) {
match = /OS ([._0-9]+)/.exec(userAgent);
if (match !== null) {
return 'iOS ' + match[1].replace('_', '.');
}
return 'iOS';
}
var clientStrings = [
{s:'Windows 10', r:/(Windows 10.0|Windows NT 10.0)/},
{s:'Windows 8.1', r:/(Windows 8.1|Windows NT 6.3)/},
{s:'Windows 8', r:/(Windows 8|Windows NT 6.2)/},
{s:'Windows 7', r:/(Windows 7|Windows NT 6.1)/},
{s:'Windows Vista', r:/Windows NT 6.0/},
{s:'Windows Server 2003', r:/Windows NT 5.2/},
{s:'Windows XP', r:/(Windows XP|Windows NT 5.1)/},
{s:'Windows', r:/Windows/},
{s:'Android', r:/Android/},
{s:'FreeBSD', r:/FreeBSD/},
{s:'OpenBSD', r:/OpenBSD/},
{s:'Chrome OS', r:/CrOS/},
{s:'Linux', r:/(Linux|X11)/},
{s:'macOS', r:/(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},
{s:'QNX', r:/QNX/},
{s:'BeOS', r:/BeOS/}
];
for (var id in clientStrings) {
var cs = clientStrings[id];
if (cs.r.test(userAgent)) {
return cs.s;
}
}
return 'Emscripten';
}
var os_name = detectOsName();
var length = lengthBytesUTF8(os_name) + 1;
var result = _malloc(length);
stringToUTF8(os_name, result, length);
return result;
}));
// clang-format on
string os_name(os_name_js);
std::free(os_name_js);
return os_name;
#else
#if TD_LINUX
auto os_name = read_os_name("/etc/os-release", "PRETTY_NAME=\"", "\"\n");
@ -124,8 +220,6 @@ Slice get_operating_system_version() {
return "NetBSD";
#elif TD_CYGWIN
return "Cygwin";
#elif TD_EMSCRIPTEN
return "Emscripten";
#else
return "Unix";
#endif

View File

@ -941,9 +941,11 @@ TEST(Client, Manager) {
client.send(-1, 3, td::make_tl_object<td::td_api::testSquareInt>(3));
for (int i = 0; i < threads_n; i++) {
threads.emplace_back([&] {
for (int i = 0; i < clients_n; i++) {
for (int i = 0; i <= clients_n; i++) {
auto id = client.create_client();
client.send(id, 3, td::make_tl_object<td::td_api::testSquareInt>(3));
if (i != 0) {
client.send(id, 3, td::make_tl_object<td::td_api::testSquareInt>(3));
}
}
});
}