// // 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 managed(push, off) #include "td/telegram/Client.h" #pragma managed(pop) #include "td/telegram/TdDotNetApi.h" #include "td/utils/port/CxCli.h" #pragma managed(push, off) #include <cstdint> #pragma managed(pop) namespace Telegram { namespace Td { using namespace CxCli; #if !TD_CLI /// <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 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">The message added to the log.</param> public delegate void LogMessageCallback(int verbosityLevel, String^ message); #endif /// <summary> /// Interface for handler for results of queries to TDLib and incoming updates from TDLib. /// </summary> public interface class ClientResultHandler { /// <summary> /// Callback called on result of query to TDLib or incoming update from TDLib. /// </summary> /// <param name="object">Result of query or update of type Telegram.Td.Api.Update about new events.</param> void OnResult(Api::BaseObject^ object); }; /// <summary> /// Main class for interaction with the TDLib. /// </summary> public ref class Client sealed { public: /// <summary> /// Sends a request to the TDLib. /// </summary> /// <param name="function">Object representing a query to the TDLib.</param> /// <param name="handler">Result handler with OnResult method which will be called with result /// of the query or with Telegram.Td.Api.Error as parameter. If it is null, nothing will be called.</param> /// <exception cref="NullReferenceException">Thrown when query is null.</exception> void Send(Api::Function^ function, ClientResultHandler^ handler) { std::uint64_t requestId = Increment(currentRequestId); if (handler != nullptr) { handlers[requestId] = handler; } auto request = td::td_api::move_object_as<td::td_api::Function>(ToUnmanaged(function)->get_object_ptr()); td::ClientManager::get_manager_singleton()->send(clientId, requestId, std::move(request)); } /// <summary> /// Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. /// </summary> /// <param name="function">Object representing a query to the TDLib.</param> /// <returns>Returns request result.</returns> /// <exception cref="NullReferenceException">Thrown when query is null.</exception> static Api::BaseObject^ Execute(Api::Function^ function) { auto request = td::td_api::move_object_as<td::td_api::Function>(ToUnmanaged(function)->get_object_ptr()); return Api::FromUnmanaged(*td::ClientManager::execute(std::move(request))); } /// <summary> /// Launches a cycle which will fetch all results of queries to TDLib and incoming updates from TDLib. /// Must be called once on a separate dedicated thread on which all updates and query results from all Clients will be handled. /// Never returns. /// </summary> static void Run() { while (true) { auto response = td::ClientManager::get_manager_singleton()->receive(300.0); if (response.object != nullptr) { bool isClosed = response.object->get_id() == td::td_api::updateAuthorizationState::ID && static_cast<td::td_api::updateAuthorizationState &>(*response.object).authorization_state_->get_id() == td::td_api::authorizationStateClosed::ID && response.request_id == 0; ClientResultHandler^ handler; if (response.request_id == 0 ? updateHandlers.TryGetValue(response.client_id, handler) : handlers.TryRemove(response.request_id, handler)) { // TODO try/catch handler->OnResult(Api::FromUnmanaged(*response.object)); } if (isClosed) { updateHandlers.TryRemove(response.client_id, handler); } } } } /// <summary> /// Creates new Client. /// </summary> /// <param name="updateHandler">Handler for incoming updates.</param> /// <returns>Returns created Client.</returns> static Client^ Create(ClientResultHandler^ updateHandler) { return REF_NEW Client(updateHandler); } #if !TD_CLI /// <summary> /// 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. /// </summary> /// <param name="max_verbosity_level">The maximum verbosity level of messages for which the callback will be called.</param> /// <param name="callback">Callback that will be called when a message is added to the internal TDLib log. /// Pass null to remove the callback.</param> static void SetLogMessageCallback(std::int32_t max_verbosity_level, LogMessageCallback^ callback) { std::lock_guard<std::mutex> lock(logMutex); if (callback == nullptr) { td::ClientManager::set_log_message_callback(max_verbosity_level, nullptr); logMessageCallback = nullptr; } else { logMessageCallback = callback; td::ClientManager::set_log_message_callback(max_verbosity_level, LogMessageCallbackWrapper); } } #endif private: Client(ClientResultHandler^ updateHandler) { clientId = td::ClientManager::get_manager_singleton()->create_client_id(); if (updateHandler != nullptr) { updateHandlers[clientId] = updateHandler; } Send(REF_NEW Api::GetOption("version"), nullptr); } #if !TD_CLI static std::int64_t currentRequestId; #else static std::int64_t currentRequestId = 0; #endif static ConcurrentDictionary<std::uint64_t, ClientResultHandler^> handlers; static ConcurrentDictionary<std::int32_t, ClientResultHandler^> updateHandlers; std::int32_t clientId; #if !TD_CLI static std::mutex logMutex; static LogMessageCallback^ logMessageCallback; static void LogMessageCallbackWrapper(int verbosity_level, const char *message) { auto callback = logMessageCallback; if (callback != nullptr) { callback(verbosity_level, string_from_unmanaged(message)); } } #endif }; #if !TD_CLI std::int64_t Client::currentRequestId = 0; ConcurrentDictionary<std::uint64_t, ClientResultHandler^> Client::handlers; ConcurrentDictionary<std::int32_t, ClientResultHandler^> Client::updateHandlers; std::mutex Client::logMutex; LogMessageCallback^ Client::logMessageCallback; #endif } // namespace Td } // namespace Telegram