// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // 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 ///\file #include "td/telegram/td_api.h" #include "td/telegram/td_api.hpp" #include <cstdint> #include <memory> namespace td { /** * The native C++ interface for interaction with TDLib. * * A TDLib client instance can be created through the method ClientManager::create_client_id. * Requests can be sent using the method ClientManager::send from any thread. * New updates and responses to requests can be received using the method ClientManager::receive from any thread after * the first request has been sent to the client instance. ClientManager::receive must not be called simultaneously from * two different threads. Also, note that all updates and responses to requests should be applied in the same order as * they were received, to ensure consistency. * Some TDLib requests can be executed synchronously from any thread using the method ClientManager::execute. * * General pattern of usage: * \code * td::ClientManager manager; * auto client_id = manager.create_client_id(); * // somehow share the manager and the client_id with other threads, * // which will be able to send requests via manager.send(client_id, ...) * * // send some dummy requests to the new instance to activate it * manager.send(client_id, ...); * * const double WAIT_TIMEOUT = 10.0; // seconds * while (true) { * auto response = manager.receive(WAIT_TIMEOUT); * if (response.object == nullptr) { * continue; * } * * if (response.request_id == 0) { * // process response.object as an incoming update of the type td_api::Update for the client response.client_id * } else { * // process response.object as an answer to a request response.request_id for the client response.client_id * } * } * \endcode */ class ClientManager final { public: /** * Creates a new TDLib client manager. */ ClientManager(); /** * Opaque TDLib client instance identifier. */ using ClientId = std::int32_t; /** * Request identifier. * Responses to TDLib requests will have the same request id as the corresponding request. * Updates from TDLib will have the request_id == 0, incoming requests are thus not allowed to have request_id == 0. */ using RequestId = std::uint64_t; /** * Returns an opaque identifier of a new TDLib instance. * The TDLib instance will not send updates until the first request is sent to it. * \return Opaque identifier of a new TDLib instance. */ ClientId create_client_id(); /** * Sends request to TDLib. May be called from any thread. * \param[in] client_id TDLib client instance identifier. * \param[in] request_id Request identifier. Must be non-zero. * \param[in] request Request to TDLib. */ void send(ClientId client_id, RequestId request_id, td_api::object_ptr<td_api::Function> &&request); /** * A response to a request, or an incoming update from TDLib. */ struct Response { /** * TDLib client instance identifier, for which the response was received. */ ClientId client_id; /** * Request identifier to which the response corresponds, or 0 for incoming updates from TDLib. */ RequestId request_id; /** * TDLib API object representing a response to a TDLib request or an incoming update. */ td_api::object_ptr<td_api::Object> object; }; /** * Receives incoming updates and responses to requests from TDLib. May be called from any thread, but must not be * called simultaneously from two different threads. * \param[in] timeout The maximum number of seconds allowed for this function to wait for new data. * \return An incoming update or response to a request. The object returned in the response may be a nullptr * if the timeout expires. */ Response receive(double timeout); /** * Synchronously executes a TDLib request. * A request can be executed synchronously, only if it is documented with "Can be called synchronously". * \param[in] request Request to the TDLib. * \return The request response. */ static td_api::object_ptr<td_api::Object> execute(td_api::object_ptr<td_api::Function> &&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 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. */ using LogMessageCallbackPtr = void (*)(int verbosity_level, const char *message); /** * Sets the callback that will be called when a message is added to the internal TDLib log. * None of the TDLib methods can be called from the callback. * By default the callback is not set. * * \param[in] max_verbosity_level The maximum verbosity level of messages for which the callback will be called. * \param[in] callback Callback that will be called when a message is added to the internal TDLib log. * Pass nullptr to remove the callback. */ static void set_log_message_callback(int max_verbosity_level, LogMessageCallbackPtr callback); /** * Destroys the client manager and all TDLib client instances managed by it. */ ~ClientManager(); /** * Move constructor. */ ClientManager(ClientManager &&other) noexcept; /** * Move assignment operator. */ ClientManager &operator=(ClientManager &&other) noexcept; /** * Returns a pointer to a singleton ClientManager instance. * \return A unique singleton ClientManager instance. */ static ClientManager *get_manager_singleton(); private: friend class Client; class Impl; std::unique_ptr<Impl> impl_; }; /** * Old native C++ interface for interaction with TDLib to be removed in TDLib 2.0.0. * * The TDLib instance is created for the lifetime of the Client object. * Requests to TDLib can be sent using the Client::send method from any thread. * New updates and responses to requests can be received using the Client::receive method from any thread, * this function must not be called simultaneously from two different threads. Also, note that all updates and * responses to requests should be applied in the same order as they were received, to ensure consistency. * Given this information, it's advisable to call this function from a dedicated thread. * Some service TDLib requests can be executed synchronously from any thread using the Client::execute method. * * General pattern of usage: * \code * std::shared_ptr<td::Client> client = std::make_shared<td::Client>(); * // somehow share the client with other threads, which will be able to send requests via client->send * * const double WAIT_TIMEOUT = 10.0; // seconds * bool is_closed = false; // should be set to true, when updateAuthorizationState with * // authorizationStateClosed is received * while (!is_closed) { * auto response = client->receive(WAIT_TIMEOUT); * if (response.object == nullptr) { * continue; * } * * if (response.id == 0) { * // process response.object as an incoming update of type td_api::Update * } else { * // process response.object as an answer to a sent request with identifier response.id * } * } * \endcode */ class Client final { public: /** * Creates a new TDLib client. */ Client(); /** * A request to the TDLib. */ struct Request { /** * Request identifier. * Responses to TDLib requests will have the same id as the corresponding request. * Updates from TDLib will have id == 0, incoming requests are thus disallowed to have id == 0. */ std::uint64_t id; /** * TDLib API function representing a request to TDLib. */ td_api::object_ptr<td_api::Function> function; }; /** * Sends request to TDLib. May be called from any thread. * \param[in] request Request to TDLib. */ void send(Request &&request); /** * A response to a request, or an incoming update from TDLib. */ struct Response { /** * TDLib request identifier, which corresponds to the response, or 0 for incoming updates from TDLib. */ std::uint64_t id; /** * TDLib API object representing a response to a TDLib request or an incoming update. */ td_api::object_ptr<td_api::Object> object; }; /** * Receives incoming updates and request responses from TDLib. May be called from any thread, but shouldn't be * called simultaneously from two different threads. * \param[in] timeout The maximum number of seconds allowed for this function to wait for new data. * \return An incoming update or request response. The object returned in the response may be a nullptr * if the timeout expires. */ Response receive(double timeout); /** * Synchronously executes TDLib requests. Only a few requests can be executed synchronously. * May be called from any thread. * \param[in] request Request to the TDLib. * \return The request response. */ static Response execute(Request &&request); /** * Destroys the client and TDLib instance. */ ~Client(); /** * Move constructor. */ Client(Client &&other) noexcept; /** * Move assignment operator. */ Client &operator=(Client &&other) noexcept; private: class Impl; std::unique_ptr<Impl> impl_; }; } // namespace td