1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
//
// 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 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
|