diff options
author | aunsane <aunsane@gmail.com> | 2018-04-27 21:33:17 +0300 |
---|---|---|
committer | aunsane <aunsane@gmail.com> | 2018-04-27 21:33:17 +0300 |
commit | e1ec72eab6d00b3ba38e5932bc88920f103b6e4a (patch) | |
tree | 999de2725a83e30fbbf6576200525d4ef0c5fe38 /protocols/Telegram/tdlib/td/example | |
parent | b9ce1d4d98525490ca1a38e2d9fd4f3369adb3e0 (diff) |
Telegram: initial commit
- tdlib moved to telegram dir
Diffstat (limited to 'protocols/Telegram/tdlib/td/example')
55 files changed, 3629 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/example/cpp/.gitignore b/protocols/Telegram/tdlib/td/example/cpp/.gitignore new file mode 100644 index 0000000000..336dc7d458 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/cpp/.gitignore @@ -0,0 +1 @@ +td/ diff --git a/protocols/Telegram/tdlib/td/example/cpp/CMakeLists.txt b/protocols/Telegram/tdlib/td/example/cpp/CMakeLists.txt new file mode 100644 index 0000000000..3e7794d4fa --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/cpp/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +project(TdExample VERSION 1.0 LANGUAGES CXX) + +find_package(Td 1.2.0 REQUIRED) + +add_executable(tdjson_example tdjson_example.cpp) +target_link_libraries(tdjson_example PRIVATE Td::TdJson) +set_property(TARGET tdjson_example PROPERTY CXX_STANDARD 11) + +add_executable(td_example td_example.cpp) +target_link_libraries(td_example PRIVATE Td::TdStatic) +set_property(TARGET td_example PROPERTY CXX_STANDARD 14) diff --git a/protocols/Telegram/tdlib/td/example/cpp/README.md b/protocols/Telegram/tdlib/td/example/cpp/README.md new file mode 100644 index 0000000000..9e5de531ba --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/cpp/README.md @@ -0,0 +1,24 @@ +# TDLib cpp basic usage examples + +TDLib should be prebuilt and installed to local subdirectory `td/`: +``` +cd <path to TDLib sources> +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=../example/cpp/td .. +cmake --build . --target install +``` +Also see [building](https://github.com/tdlib/td#building) for additional details on TDLib building. + +Then you can build the examples: +``` +cd <path to TDLib sources>/example/cpp +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DTd_DIR=<full path to TDLib sources>/example/cpp/td/lib/cmake/Td .. +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. diff --git a/protocols/Telegram/tdlib/td/example/cpp/td_example.cpp b/protocols/Telegram/tdlib/td/example/cpp/td_example.cpp new file mode 100644 index 0000000000..1efbf465b9 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/cpp/td_example.cpp @@ -0,0 +1,308 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/telegram/Client.h> +#include <td/telegram/Log.h> +#include <td/telegram/td_api.h> +#include <td/telegram/td_api.hpp> + +#include <cstdint> +#include <functional> +#include <iostream> +#include <limits> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +// Simple single-threaded example of TDLib usage. +// Real world programs should use separate thread for the user input. +// Example includes user authentication, receiving updates, getting chat list and sending text messages. + +// overloaded +namespace detail { +template <class... Fs> +struct overload; + +template <class F> +struct overload<F> : public F { + explicit overload(F f) : F(f) { + } +}; +template <class F, class... Fs> +struct overload<F, Fs...> + : public overload<F> + , overload<Fs...> { + overload(F f, Fs... fs) : overload<F>(f), overload<Fs...>(fs...) { + } + using overload<F>::operator(); + using overload<Fs...>::operator(); +}; +} // namespace detail + +template <class... F> +auto overloaded(F... f) { + return detail::overload<F...>(f...); +} + +namespace td_api = td::td_api; + +class TdExample { + public: + TdExample() { + td::Log::set_verbosity_level(1); + client_ = std::make_unique<td::Client>(); + } + + void loop() { + while (true) { + if (need_restart_) { + restart(); + } else if (!are_authorized_) { + process_response(client_->receive(10)); + } else { + std::cerr << "Enter action [q] quit [u] check for updates and request results [c] show chats [m <id> <text>] " + "send message [l] logout: " + << std::endl; + std::string line; + std::getline(std::cin, line); + std::istringstream ss(line); + std::string action; + if (!(ss >> action)) { + continue; + } + if (action == "q") { + return; + } + if (action == "u") { + std::cerr << "Checking for updates..." << std::endl; + while (true) { + auto response = client_->receive(0); + if (response.object) { + process_response(std::move(response)); + } else { + break; + } + } + } else if (action == "l") { + std::cerr << "Logging out..." << std::endl; + send_query(td_api::make_object<td_api::logOut>(), {}); + } else if (action == "m") { + std::int64_t chat_id; + ss >> chat_id; + ss.get(); + std::string text; + std::getline(ss, text); + + std::cerr << "Sending message to chat " << chat_id << "..." << std::endl; + auto send_message = td_api::make_object<td_api::sendMessage>(); + send_message->chat_id_ = chat_id; + auto message_content = td_api::make_object<td_api::inputMessageText>(); + message_content->text_ = td_api::make_object<td_api::formattedText>(); + message_content->text_->text_ = std::move(text); + send_message->input_message_content_ = std::move(message_content); + + send_query(std::move(send_message), {}); + } else if (action == "c") { + std::cerr << "Loading chat list..." << std::endl; + send_query(td_api::make_object<td_api::getChats>(std::numeric_limits<std::int64_t>::max(), 0, 20), + [this](Object object) { + if (object->get_id() == td_api::error::ID) { + return; + } + auto chats = td::move_tl_object_as<td_api::chats>(object); + for (auto chat_id : chats->chat_ids_) { + std::cerr << "[id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl; + } + }); + } + } + } + } + + private: + using Object = td_api::object_ptr<td_api::Object>; + std::unique_ptr<td::Client> client_; + + td_api::object_ptr<td_api::AuthorizationState> authorization_state_; + bool are_authorized_{false}; + bool need_restart_{false}; + std::uint64_t current_query_id_{0}; + std::uint64_t authentication_query_id_{0}; + + std::map<std::uint64_t, std::function<void(Object)>> handlers_; + + std::map<std::int32_t, td_api::object_ptr<td_api::user>> users_; + + std::map<std::int64_t, std::string> chat_title_; + + void restart() { + client_.reset(); + *this = TdExample(); + } + + void send_query(td_api::object_ptr<td_api::Function> f, std::function<void(Object)> handler) { + auto query_id = next_query_id(); + if (handler) { + handlers_.emplace(query_id, std::move(handler)); + } + client_->send({query_id, std::move(f)}); + } + + void process_response(td::Client::Response response) { + if (!response.object) { + return; + } + //std::cerr << response.id << " " << to_string(response.object) << std::endl; + if (response.id == 0) { + return process_update(std::move(response.object)); + } + auto it = handlers_.find(response.id); + if (it != handlers_.end()) { + it->second(std::move(response.object)); + } + } + + std::string get_user_name(std::int32_t user_id) { + auto it = users_.find(user_id); + if (it == users_.end()) { + return "unknown user"; + } + return it->second->first_name_ + " " + it->second->last_name_; + } + + void process_update(td_api::object_ptr<td_api::Object> update) { + td_api::downcast_call( + *update, overloaded( + [this](td_api::updateAuthorizationState &update_authorization_state) { + authorization_state_ = std::move(update_authorization_state.authorization_state_); + on_authorization_state_update(); + }, + [this](td_api::updateNewChat &update_new_chat) { + chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_; + }, + [this](td_api::updateChatTitle &update_chat_title) { + chat_title_[update_chat_title.chat_id_] = update_chat_title.title_; + }, + [this](td_api::updateUser &update_user) { + auto user_id = update_user.user_->id_; + users_[user_id] = std::move(update_user.user_); + }, + [this](td_api::updateNewMessage &update_new_message) { + auto chat_id = update_new_message.message_->chat_id_; + auto sender_user_name = get_user_name(update_new_message.message_->sender_user_id_); + std::string text; + if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) { + text = static_cast<td_api::messageText &>(*update_new_message.message_->content_).text_->text_; + } + std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] [" + << text << "]" << std::endl; + }, + [](auto &update) {})); + } + + auto create_authentication_query_handler() { + return [this, id = authentication_query_id_](Object object) { + if (id == authentication_query_id_) { + check_authentication_error(std::move(object)); + } + }; + } + + void on_authorization_state_update() { + authentication_query_id_++; + td_api::downcast_call( + *authorization_state_, + overloaded( + [this](td_api::authorizationStateReady &) { + are_authorized_ = true; + std::cerr << "Got authorization" << std::endl; + }, + [this](td_api::authorizationStateLoggingOut &) { + are_authorized_ = false; + std::cerr << "Logging out" << std::endl; + }, + [this](td_api::authorizationStateClosing &) { std::cerr << "Closing" << std::endl; }, + [this](td_api::authorizationStateClosed &) { + are_authorized_ = false; + need_restart_ = true; + std::cerr << "Terminated" << std::endl; + }, + [this](td_api::authorizationStateWaitCode &wait_code) { + std::string first_name; + std::string last_name; + if (!wait_code.is_registered_) { + std::cerr << "Enter your first name: "; + std::cin >> first_name; + std::cerr << "Enter your last name: "; + std::cin >> last_name; + } + std::cerr << "Enter authentication code: "; + std::string code; + std::cin >> code; + send_query(td_api::make_object<td_api::checkAuthenticationCode>(code, first_name, last_name), + create_authentication_query_handler()); + }, + [this](td_api::authorizationStateWaitPassword &) { + std::cerr << "Enter authentication password: "; + std::string password; + std::cin >> password; + send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password), + create_authentication_query_handler()); + }, + [this](td_api::authorizationStateWaitPhoneNumber &) { + std::cerr << "Enter phone number: "; + std::string phone_number; + std::cin >> phone_number; + send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>( + phone_number, false /*allow_flash_calls*/, false /*is_current_phone_number*/), + create_authentication_query_handler()); + }, + [this](td_api::authorizationStateWaitEncryptionKey &) { + std::cerr << "Enter encryption key or DESTROY: "; + std::string key; + std::getline(std::cin, key); + if (key == "DESTROY") { + send_query(td_api::make_object<td_api::destroy>(), create_authentication_query_handler()); + } else { + send_query(td_api::make_object<td_api::checkDatabaseEncryptionKey>(std::move(key)), + create_authentication_query_handler()); + } + }, + [this](td_api::authorizationStateWaitTdlibParameters &) { + auto parameters = td_api::make_object<td_api::tdlibParameters>(); + parameters->database_directory_ = "tdlib"; + parameters->use_message_database_ = true; + parameters->use_secret_chats_ = true; + parameters->api_id_ = 94575; + parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters->system_language_code_ = "en"; + parameters->device_model_ = "Desktop"; + parameters->system_version_ = "Unknown"; + parameters->application_version_ = "1.0"; + parameters->enable_storage_optimizer_ = true; + send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)), + create_authentication_query_handler()); + })); + } + + void check_authentication_error(Object object) { + if (object->get_id() == td_api::error::ID) { + auto error = td::move_tl_object_as<td_api::error>(object); + std::cerr << "Error: " << to_string(error); + on_authorization_state_update(); + } + } + + std::uint64_t next_query_id() { + return ++current_query_id_; + } +}; + +int main() { + TdExample example; + example.loop(); +} diff --git a/protocols/Telegram/tdlib/td/example/cpp/tdjson_example.cpp b/protocols/Telegram/tdlib/td/example/cpp/tdjson_example.cpp new file mode 100644 index 0000000000..6787b37f86 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/cpp/tdjson_example.cpp @@ -0,0 +1,33 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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 <iostream> +#include <td/telegram/td_json_client.h> + +// Basic example of TDLib JSON interface usage. +// Native interface should be preferred instead in C++, so here is only an example of +// the main event cycle, which should be essentially the same for all languages. + +int main() { + void *client = td_json_client_create(); + // somehow share the client with other threads, which will be able to send requests via td_json_client_send + + const double WAIT_TIMEOUT = 10.0; // seconds + while (true) { + const char *result = td_json_client_receive(client, WAIT_TIMEOUT); + if (result != nullptr) { + // parse the result as JSON object and process it as an incoming update or an answer to a previously sent request + + // if (result is UpdateAuthorizationState with authorizationStateClosed) { + // break; + // } + + std::cout << result << std::endl; + } + } + + td_json_client_destroy(client); +} diff --git a/protocols/Telegram/tdlib/td/example/csharp/.gitignore b/protocols/Telegram/tdlib/td/example/csharp/.gitignore new file mode 100644 index 0000000000..5266ecccc9 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/csharp/.gitignore @@ -0,0 +1,5 @@ +.vs/ +bin/ +obj/ +project.lock.json +TdExample.csproj.user diff --git a/protocols/Telegram/tdlib/td/example/csharp/README.md b/protocols/Telegram/tdlib/td/example/csharp/README.md new file mode 100644 index 0000000000..2faa15e12e --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/csharp/README.md @@ -0,0 +1,32 @@ +# TDLib C# example + +This is an example of building TDLib with `C++/CLI` support and an example of TDLib usage from C#. + +## Building TDLib + +* Download and install Microsoft Visual Studio 2015 or later. +* Download and install [CMake](https://cmake.org/download/). +* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start) or update it to the latest version using `vcpkg update` and following received instructions. +* Install `zlib` and `openssl` for using `vcpkg`: +``` +C:\src\vcpkg> .\vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows +``` +* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download#php-7.2). Add the path to php.exe to the PATH environment variable. +* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable. +* Build `TDLib` with CMake enabling `.NET` support and specifying correct path to `vcpkg` toolchain file: +``` +cd <path to TDLib sources>/example/csharp +mkdir build +cd build +cmake -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=C:\src\vcpkg\scripts\buildsystems\vcpkg.cmake ../../.. +cmake --build . --config Release +cmake --build . --config Debug +``` + +## Example of usage + +After `TDLib` is built you can open and run TdExample project. +It contains a simple console C# application with implementation of authorization and message sending. +Just open it with Visual Studio 2015 or 2017 and run. + +Also see TdExample.csproj for example of including TDLib in C# project with all native shared library dependencies. diff --git a/protocols/Telegram/tdlib/td/example/csharp/TdExample.cs b/protocols/Telegram/tdlib/td/example/csharp/TdExample.cs new file mode 100644 index 0000000000..be98e1a758 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/csharp/TdExample.cs @@ -0,0 +1,270 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// + +using Td = Telegram.Td; +using TdApi = Telegram.Td.Api; + +using System; +using System.Threading; + +namespace TdExample +{ + /// <summary> + /// Example class for TDLib usage from C#. + /// </summary> + class Example + { + private static Td.Client _client = null; + private readonly static Td.ClientResultHandler _defaultHandler = new DefaultHandler(); + + private static TdApi.AuthorizationState _authorizationState = null; + private static volatile bool _haveAuthorization = false; + private static volatile bool _quiting = false; + + private static volatile AutoResetEvent _gotAuthorization = new AutoResetEvent(false); + + private static readonly string _newLine = Environment.NewLine; + private static readonly string _commandsLine = "Enter command (gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): "; + private static volatile string _currentPrompt = null; + + private static Td.Client CreateTdClient() + { + Td.Client result = Td.Client.Create(new UpdatesHandler()); + new Thread(() => + { + Thread.CurrentThread.IsBackground = true; + result.Run(); + }).Start(); + return result; + } + + private static void Print(string str) + { + if (_currentPrompt != null) + { + Console.WriteLine(); + } + Console.WriteLine(str); + if (_currentPrompt != null) + { + Console.Write(_currentPrompt); + } + } + + private static string ReadLine(string str) + { + Console.Write(str); + _currentPrompt = str; + var result = Console.ReadLine(); + _currentPrompt = null; + return result; + } + + private static void OnAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) + { + if (authorizationState != null) + { + _authorizationState = authorizationState; + } + if (_authorizationState is TdApi.AuthorizationStateWaitTdlibParameters) + { + TdApi.TdlibParameters parameters = new TdApi.TdlibParameters(); + parameters.DatabaseDirectory = "tdlib"; + parameters.UseMessageDatabase = true; + parameters.UseSecretChats = true; + parameters.ApiId = 94575; + parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters.SystemLanguageCode = "en"; + parameters.DeviceModel = "Desktop"; + parameters.SystemVersion = "Unknown"; + parameters.ApplicationVersion = "1.0"; + parameters.EnableStorageOptimizer = true; + + _client.Send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler()); + } + else if (_authorizationState is TdApi.AuthorizationStateWaitEncryptionKey) + { + _client.Send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler()); + } + else if (_authorizationState is TdApi.AuthorizationStateWaitPhoneNumber) + { + string phoneNumber = ReadLine("Please enter phone number: "); + _client.Send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, false, false), new AuthorizationRequestHandler()); + } + else if (_authorizationState is TdApi.AuthorizationStateWaitCode) + { + string code = ReadLine("Please enter authentication code: "); + _client.Send(new TdApi.CheckAuthenticationCode(code, "", ""), new AuthorizationRequestHandler()); + } + else if (_authorizationState is TdApi.AuthorizationStateWaitPassword) + { + string password = ReadLine("Please enter password: "); + _client.Send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler()); + } + else if (_authorizationState is TdApi.AuthorizationStateReady) + { + _haveAuthorization = true; + _gotAuthorization.Set(); + } + else if (_authorizationState is TdApi.AuthorizationStateLoggingOut) + { + _haveAuthorization = false; + Print("Logging out"); + } + else if (_authorizationState is TdApi.AuthorizationStateClosing) + { + _haveAuthorization = false; + Print("Closing"); + } + else if (_authorizationState is TdApi.AuthorizationStateClosed) + { + Print("Closed"); + if (!_quiting) + { + _client = CreateTdClient(); // recreate _client after previous has closed + } + } + else + { + Print("Unsupported authorization state:" + _newLine + _authorizationState); + } + } + + private static long GetChatId(string arg) + { + long chatId = 0; + try + { + chatId = Convert.ToInt64(arg); + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + return chatId; + } + + private static void GetCommand() + { + string command = ReadLine(_commandsLine); + string[] commands = command.Split(new char[] { ' ' }, 2); + try + { + switch (commands[0]) + { + case "gc": + _client.Send(new TdApi.GetChat(GetChatId(commands[1])), _defaultHandler); + break; + case "me": + _client.Send(new TdApi.GetMe(), _defaultHandler); + break; + case "sm": + string[] args = commands[1].Split(new char[] { ' ' }, 2); + sendMessage(GetChatId(args[0]), args[1]); + break; + case "lo": + _haveAuthorization = false; + _client.Send(new TdApi.LogOut(), _defaultHandler); + break; + case "q": + _quiting = true; + _haveAuthorization = false; + _client.Send(new TdApi.Close(), _defaultHandler); + break; + default: + Print("Unsupported command: " + command); + break; + } + } + catch (IndexOutOfRangeException) + { + Print("Not enough arguments"); + } + } + + private static void sendMessage(long chatId, string message) + { + // initialize reply markup just for testing + TdApi.InlineKeyboardButton[] row = { new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl()) }; + TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][] { row, row, row }); + + TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true); + _client.Send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), _defaultHandler); + } + + static void Main() + { + // disable TDLib log + Td.Log.SetVerbosityLevel(0); + if (!Td.Log.SetFilePath("tdlib.log")) + { + throw new System.IO.IOException("Write access to the current directory is required"); + } + + // create Td.Client + _client = CreateTdClient(); + + // test Client.Execute + _defaultHandler.OnResult(_client.Execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test"))); + + // main loop + while (!_quiting) + { + // await authorization + _gotAuthorization.Reset(); + _gotAuthorization.WaitOne(); + + _client.Send(new TdApi.GetChats(Int64.MaxValue, 0, 100), _defaultHandler); // preload chat list + while (_haveAuthorization) + { + GetCommand(); + } + } + } + + private class DefaultHandler : Td.ClientResultHandler + { + void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object) + { + Print(@object.ToString()); + } + } + + private class UpdatesHandler : Td.ClientResultHandler + { + void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object) + { + if (@object is TdApi.UpdateAuthorizationState) + { + OnAuthorizationStateUpdated((@object as TdApi.UpdateAuthorizationState).AuthorizationState); + } + else + { + // Print("Unsupported update: " + @object); + } + } + } + + private class AuthorizationRequestHandler : Td.ClientResultHandler + { + void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object) + { + if (@object is TdApi.Error) + { + Print("Receive an error:" + _newLine + @object); + OnAuthorizationStateUpdated(null); // repeat last action + } + else + { + // result is already received through UpdateAuthorizationState, nothing to do + } + } + } + } +} diff --git a/protocols/Telegram/tdlib/td/example/csharp/TdExample.csproj b/protocols/Telegram/tdlib/td/example/csharp/TdExample.csproj new file mode 100644 index 0000000000..ea2ad56532 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/csharp/TdExample.csproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProjectGuid>{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}</ProjectGuid> + <OutputType>Exe</OutputType> + <NoStandardLibraries>false</NoStandardLibraries> + <AssemblyName>ConsoleApplication</AssemblyName> + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> + <TargetFrameworkProfile>Client</TargetFrameworkProfile> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <PropertyGroup> + <RootNamespace>TdExample</RootNamespace> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Data" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="Telegram.Td, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64"> + <SpecificVersion>False</SpecificVersion> + <HintPath Condition=" '$(Configuration)' == 'Debug' ">build\Debug\Telegram.Td.dll</HintPath> + <HintPath Condition=" '$(Configuration)' == 'Release' ">build\Release\Telegram.Td.dll</HintPath> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="TdExample.cs" /> + </ItemGroup> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <Content Include="build\Debug\LIBEAY32.dll"> + <Link>LIBEAY32.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="build\Debug\SSLEAY32.dll"> + <Link>SSLEAY32.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="build\Debug\zlibd1.dll"> + <Link>zlibd1.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + </ItemGroup> + <ItemGroup Condition=" '$(Configuration)' == 'Release' "> + <Content Include="build\Release\LIBEAY32.dll"> + <Link>LIBEAY32.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="build\Release\SSLEAY32.dll"> + <Link>SSLEAY32.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="build\Release\zlib1.dll"> + <Link>zlib1.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSHARP.Targets" /> + <ProjectExtensions> + <VisualStudio AllowExistingFolder="true" /> + </ProjectExtensions> +</Project>
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/go/main.go b/protocols/Telegram/tdlib/td/example/go/main.go new file mode 100644 index 0000000000..430e067019 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/go/main.go @@ -0,0 +1,26 @@ +package main + +// #cgo LDFLAGS: -ltdjson +// #include <td/telegram/td_json_client.h> +import "C" +import ( + "log" + "unsafe" +) + +func td_send(client unsafe.Pointer, query *C.char) { + C.td_json_client_send(client, query) +} + +func td_receive(client unsafe.Pointer) string { + return C.GoString(C.td_json_client_receive(client, 1.0)) +} + +func main() { + var client unsafe.Pointer + client = C.td_json_client_create() + + query := C.CString(`{"@type": "getAuthorizationState"}`) + td_send(client, query) + log.Println(td_receive(client)) +} diff --git a/protocols/Telegram/tdlib/td/example/ios/README.md b/protocols/Telegram/tdlib/td/example/ios/README.md new file mode 100644 index 0000000000..25a1e68fcc --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ios/README.md @@ -0,0 +1,41 @@ +# Build for iOS + +Below are instructions for building TDLib for iOS, watchOS, tvOS, and also macOS. + +If you need only a macOS build, take a look at our build instructions for [macOS](https://github.com/tdlib/td#macos). + +For example of usage take a look at our [Swift example](https://github.com/tdlib/td/tree/master/example/swift). + +To compile `TDLib` you will need to: +* Install the latest Xcode command line tools. +* Install other build dependencies, for example, using [Homebrew](https://brew.sh): +``` +brew install gperf cmake +``` +* If you don't want to build `TDLib` for macOS, you can pregenerate required source code files using CMake prepare_cross_compiling target: +``` +cd <path to TDLib sources> +mkdir native-build +cd native-build +cmake .. +cmake --build . --target prepare_cross_compiling +``` +* Build OpenSSL for iOS, watchOS, tvOS and macOS: +``` +cd <path to TDLib sources>/example/ios +./build-openssl.sh +``` +Here we use scripts from [Python Apple support](https://github.com/pybee/Python-Apple-support), but any other OpenSSL builds should work too. +Built libraries should be stored in `third_party/openssl/<platform>`, because the next script will rely on this location. +* Build TDLib for iOS, watchOS, tvOS and macOS: +``` +cd <path to TDLib sources>/example/ios +./build.sh +``` +This may take a while, because TDLib will be built about 10 times. +Resulting library for iOS will work on any architecture (arv7, armv7s, arm64) and even on a simulator. +We use [CMake/iOS.cmake](https://github.com/tdlib/td/blob/master/CMake/iOS.cmake) toolchain, other toolchains may work too. + +Built libraries will be store in `tdjson` directory. + +Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs. diff --git a/protocols/Telegram/tdlib/td/example/ios/build-openssl.sh b/protocols/Telegram/tdlib/td/example/ios/build-openssl.sh new file mode 100644 index 0000000000..2ad9dbcfee --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ios/build-openssl.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +git clone https://github.com/pybee/Python-Apple-support +cd Python-Apple-support +git checkout 2.7 +cd .. + +#TODO: change openssl version +platforms="macOS iOS watchOS tvOS" +for platform in $platforms; +do + echo $platform + cd Python-Apple-support + #NB: -j will fail + make OpenSSL-$platform + cd .. + rm -rf third_party/openssl/$platform + mkdir -p third_party/openssl/$platform/lib + cp ./Python-Apple-support/build/$platform/libcrypto.a third_party/openssl/$platform/lib/ + cp ./Python-Apple-support/build/$platform/libssl.a third_party/openssl/$platform/lib/ + cp -r ./Python-Apple-support/build/$platform/Support/OpenSSL/Headers/ third_party/openssl/$platform/include +done diff --git a/protocols/Telegram/tdlib/td/example/ios/build.sh b/protocols/Telegram/tdlib/td/example/ios/build.sh new file mode 100644 index 0000000000..9970008a1c --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ios/build.sh @@ -0,0 +1,74 @@ +#/bin/sh +td_path=$(realpath ../..) + +rm -rf build +mkdir -p build +cd build + +platforms="macOS iOS watchOS tvOS" +for platform in $platforms; +do + echo "Platform = ${platform} Simulator = ${simulator}" + openssl_path=$(realpath ../third_party/openssl/${platform}) + echo "OpenSSL path = ${openssl_path}" + openssl_crypto_library="${openssl_path}/lib/libcrypto.a" + openssl_ssl_library="${openssl_path}/lib/libssl.a" + options="$options -DOPENSSL_FOUND=1" + options="$options -DOPENSSL_CRYPTO_LIBRARY=${openssl_crypto_library}" + #options="$options -DOPENSSL_SSL_LIBRARY=${openssl_ssl_library}" + options="$options -DOPENSSL_INCLUDE_DIR=${openssl_path}/include" + options="$options -DOPENSSL_LIBRARIES=${openssl_crypto_library};${openssl_ssl_library}" + options="$options -DCMAKE_BUILD_TYPE=Release" + if [[ $platform = "macOS" ]]; then + build="build-${platform}" + install="install-${platform}" + rm -rf $build + mkdir -p $build + mkdir -p $install + cd $build + cmake $td_path $options -DCMAKE_INSTALL_PREFIX=../${install} + make -j3 install || exit + cd .. + mkdir -p $platform + cp $build/libtdjson.dylib $platform/libtdjson.dylib + install_name_tool -id @rpath/libtdjson.dylib $platform/libtdjson.dylib + else + simulators="0 1" + for simulator in $simulators; + do + build="build-${platform}" + install="install-${platform}" + if [[ $simulator = "1" ]]; then + build="${build}-simulator" + install="${install}-simulator" + ios_platform="SIMULATOR" + else + ios_platform="OS" + fi + if [[ $platform = "watchOS" ]]; then + ios_platform="WATCH${ios_platform}" + fi + if [[ $platform = "tvOS" ]]; then + ios_platform="TV${ios_platform}" + fi + echo $ios_platform + rm -rf $build + mkdir -p $build + mkdir -p $install + cd $build + cmake $td_path $options -DIOS_PLATFORM=${ios_platform} -DCMAKE_TOOLCHAIN_FILE=${td_path}/CMake/iOS.cmake -DCMAKE_INSTALL_PREFIX=../${install} + make -j3 install || exit + cd .. + done + lib="install-${platform}/lib/libtdjson.dylib" + lib_simulator="install-${platform}-simulator/lib/libtdjson.dylib" + mkdir -p $platform + lipo -create $lib $lib_simulator -o $platform/libtdjson.dylib + install_name_tool -id @rpath/libtdjson.dylib $platform/libtdjson.dylib + fi + + mkdir -p ../tdjson/$platform/include + rsync --recursive ${install}/include/ ../tdjson/${platform}/include/ + mkdir -p ../tdjson/$platform/lib + cp $platform/libtdjson.dylib ../tdjson/$platform/lib/ +done diff --git a/protocols/Telegram/tdlib/td/example/java/.gitignore b/protocols/Telegram/tdlib/td/example/java/.gitignore new file mode 100644 index 0000000000..8f846b80d9 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/.gitignore @@ -0,0 +1,5 @@ +**/*build/ +bin/ +docs/ +org/drinkless/tdlib/TdApi.java +td/ diff --git a/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt b/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt new file mode 100644 index 0000000000..e8313a68b7 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +project(TdJavaExample VERSION 1.0 LANGUAGES CXX) + +find_package(Td REQUIRED) + +if (NOT JNI_FOUND) + find_package(JNI REQUIRED) +endif() +message(STATUS "Found JNI: ${JNI_INCLUDE_DIRS} ${JNI_LIBRARIES}") + +if (NOT Java_FOUND) + find_package(Java 1.6 REQUIRED) +endif() +message(STATUS "Found Java: ${Java_JAVAC_EXECUTABLE} ${Java_JAVADOC_EXECUTABLE}") + +# Generating TdApi.java +find_program(PHP_EXECUTABLE php) + +set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib") +set(TD_API_JAVA_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +set(TD_API_TLO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tlo) +set(TD_API_TL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tl) +set(JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/JavadocTlDocumentationGenerator.php) +set(GENERATE_JAVA_API_CMD ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api TdApi ${TD_API_TLO_PATH} ${TD_API_JAVA_PATH} ${TD_API_JAVA_PACKAGE}) +if (PHP_EXECUTABLE) + set(GENERATE_JAVA_API_CMD ${GENERATE_JAVA_API_CMD} && ${PHP_EXECUTABLE} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH} ${TD_API_TL_PATH} ${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}/TdApi.java) +endif() + +add_custom_target(td_generate_java_api + COMMAND ${GENERATE_JAVA_API_CMD} + COMMENT "Generating Java TDLib API source files" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api ${TD_API_TLO_PATH} ${TD_API_TL_PATH} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH} +) + +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} -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 + COMMENT "Building Java code" + DEPENDS td_generate_java_api +) + +set(JAVA_SOURCE_PATH "${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}") +add_custom_target(generate_javadoc + COMMAND ${Java_JAVADOC_EXECUTABLE} -d ${JAVA_OUTPUT_DIRECTORY}/../docs org.drinkless.tdlib + WORKING_DIRECTORY ${TD_API_JAVA_PATH} + COMMENT "Generating Javadoc documentation" + DEPENDS td_generate_java_api +) + +# Building shared library +add_library(tdjni SHARED + td_jni.cpp +) +target_include_directories(tdjni PRIVATE ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) +target_link_libraries(tdjni PRIVATE Td::TdStatic ${JAVA_JVM_LIBRARY}) +target_compile_definitions(tdjni PRIVATE PACKAGE_NAME="${TD_API_JAVA_PACKAGE}") + +set_property(TARGET tdjni PROPERTY CXX_STANDARD 14) + +add_dependencies(tdjni td_generate_java_api build_java generate_javadoc) + +install(TARGETS tdjni + LIBRARY DESTINATION bin + RUNTIME DESTINATION bin +) diff --git a/protocols/Telegram/tdlib/td/example/java/README.md b/protocols/Telegram/tdlib/td/example/java/README.md new file mode 100644 index 0000000000..c2b5c43171 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/README.md @@ -0,0 +1,39 @@ +# TDLib Java example + +To run this example, you will need installed JDK >= 1.6. +For Javadoc documentation generation PHP is needed. + +TDLib should be prebuilt for using with Java and installed to local subdirectory `td/` as follows: +``` +cd <path to TDLib sources> +mkdir jnibuild +cd jnibuild +cmake -DCMAKE_BUILD_TYPE=Release -DTD_ENABLE_JNI=ON -DCMAKE_INSTALL_PREFIX:PATH=../example/java/td .. +cmake --build . --target install +``` +If you want to compile TDLib for 64-bit Java on Windows using MSVC, you will also need to add `-A x64` option to CMake. + +In Windows, use Vcpkg toolchain file by adding parameter -DCMAKE_TOOLCHAIN_FILE=<VCPKG_DIR>/scripts/buildsystems/vcpkg.cmake + +Then you can build this example: +``` +cd <path to TDLib sources>/example/java +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DTd_DIR=<full path to TDLib sources>/example/java/td/lib/cmake/Td -DCMAKE_INSTALL_PREFIX:PATH=.. .. +cmake --build . --target install +``` + +Compiled TDLib shared library and Java example after that will be placed in bin/ and Javadoc documentation in `docs/`. + +Now you can run Java example: +``` +cd <path to TDLib sources>/example/java/bin +java '-Djava.library.path=.' org/drinkless/tdlib/example/Example +``` + +If you get "Could NOT find JNI ..." error from CMake, you need to specify to CMake path to the installed JDK, for example, "-DJAVA_HOME=/usr/lib/jvm/java-8-oracle/". + +If you get java.lang.UnsatisfiedLinkError with "Can't find dependent libraries", you may also need to copy some dependent shared libraries to `bin/`. + +In case you compiled the example as 32-bit version, you may need to give -d32 parameter to Java. diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java new file mode 100644 index 0000000000..efb38e9c5a --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Client.java @@ -0,0 +1,285 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Main class for interaction with the TDLib. + */ +public final class Client implements Runnable { + /** + * Interface for handler for results of queries to TDLib and incoming updates from TDLib. + */ + public interface ResultHandler { + /** + * Callback called on result of query to TDLib or incoming update from TDLib. + * + * @param object Result of query or update of type TdApi.Update about new events. + */ + void onResult(TdApi.Object object); + } + + /** + * Interface for handler of exceptions thrown while invoking ResultHandler. + * By default, all such exceptions are ignored. + * All exceptions thrown from ExceptionHandler are ignored. + */ + public interface ExceptionHandler { + /** + * Callback called on exceptions thrown while invoking ResultHandler. + * + * @param e Exception thrown by ResultHandler. + */ + void onException(Throwable e); + } + + /** + * Sends a request to the TDLib. + * + * @param query Object representing a query to the TDLib. + * @param resultHandler Result handler with onResult method which will be called with result + * of the query or with TdApi.Error as parameter. If it is null, nothing + * will be called. + * @param exceptionHandler Exception handler with onException method which will be called on + * exception thrown from resultHandler. If it is null, then + * defaultExceptionHandler will be called. + * @throws NullPointerException if query is null. + */ + public void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { + if (query == null) { + throw new NullPointerException("query is null"); + } + + readLock.lock(); + try { + if (isClientDestroyed) { + if (resultHandler != null) { + handleResult(new TdApi.Error(500, "Client is closed"), resultHandler, exceptionHandler); + } + return; + } + + long queryId = currentQueryId.incrementAndGet(); + handlers.put(queryId, new Handler(resultHandler, exceptionHandler)); + nativeClientSend(nativeClientId, queryId, query); + } finally { + readLock.unlock(); + } + } + + /** + * Sends a request to the TDLib with an empty ExceptionHandler. + * + * @param query Object representing a query to the TDLib. + * @param resultHandler Result handler with onResult method which will be called with result + * of the query or with TdApi.Error as parameter. If it is null, then + * defaultExceptionHandler will be called. + * @throws NullPointerException if query is null. + */ + public void send(TdApi.Function query, ResultHandler resultHandler) { + send(query, resultHandler, null); + } + + /** + * Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. + * + * @param query Object representing a query to the TDLib. + * @return request result. + * @throws NullPointerException if query is null. + */ + public static TdApi.Object execute(TdApi.Function query) { + if (query == null) { + throw new NullPointerException("query is null"); + } + return nativeClientExecute(query); + } + + /** + * Replaces handler for incoming updates from the TDLib. + * + * @param updatesHandler Handler with onResult method which will be called for every incoming + * update from the TDLib. + * @param exceptionHandler Exception handler with onException method which will be called on + * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked. + */ + public void setUpdatesHandler(ResultHandler updatesHandler, ExceptionHandler exceptionHandler) { + handlers.put(0L, new Handler(updatesHandler, exceptionHandler)); + } + + /** + * Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler. + * + * @param updatesHandler Handler with onResult method which will be called for every incoming + * update from the TDLib. + */ + public void setUpdatesHandler(ResultHandler updatesHandler) { + setUpdatesHandler(updatesHandler, null); + } + + /** + * Replaces default exception handler to be invoked on exceptions thrown from updatesHandler and all other ResultHandler. + * + * @param defaultExceptionHandler Default exception handler. If null Exceptions are ignored. + */ + public void setDefaultExceptionHandler(Client.ExceptionHandler defaultExceptionHandler) { + this.defaultExceptionHandler = defaultExceptionHandler; + } + + /** + * Overridden method from Runnable, do not call it directly. + */ + @Override + public void run() { + while (!stopFlag) { + receiveQueries(300.0 /*seconds*/); + } + } + + /** + * Creates new Client. + * + * @param updatesHandler Handler for incoming updates. + * @param updatesExceptionHandler Handler for exceptions thrown from updatesHandler. If it is null, exceptions will be iggnored. + * @param defaultExceptionHandler Default handler for exceptions thrown from all ResultHandler. If it is null, exceptions will be iggnored. + * @return created Client + */ + public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) { + Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler); + new Thread(client, "TDLib thread").start(); + return client; + } + + /** + * Closes Client. + */ + public void close() { + writeLock.lock(); + try { + if (isClientDestroyed) { + return; + } + if (!stopFlag) { + send(new TdApi.Close(), null); + } + isClientDestroyed = true; + while (!stopFlag) { + Thread.yield(); + } + while (handlers.size() != 1) { + receiveQueries(300.0); + } + destroyNativeClient(nativeClientId); + } finally { + writeLock.unlock(); + } + } + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final Lock readLock = readWriteLock.readLock(); + private final Lock writeLock = readWriteLock.writeLock(); + + private volatile boolean stopFlag = false; + private volatile boolean isClientDestroyed = false; + private final long nativeClientId; + + private final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>(); + private final AtomicLong currentQueryId = new AtomicLong(); + + private volatile ExceptionHandler defaultExceptionHandler = null; + + private static final int MAX_EVENTS = 1000; + private final long[] eventIds = new long[MAX_EVENTS]; + private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS]; + + private static class Handler { + final ResultHandler resultHandler; + final ExceptionHandler exceptionHandler; + + Handler(ResultHandler resultHandler, ExceptionHandler exceptionHandler) { + this.resultHandler = resultHandler; + this.exceptionHandler = exceptionHandler; + } + } + + private Client(ResultHandler updatesHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) { + nativeClientId = createNativeClient(); + handlers.put(0L, new Handler(updatesHandler, updateExceptionHandler)); + this.defaultExceptionHandler = defaultExceptionHandler; + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + private void processResult(long id, TdApi.Object object) { + if (object instanceof TdApi.UpdateAuthorizationState) { + if (((TdApi.UpdateAuthorizationState) object).authorizationState instanceof TdApi.AuthorizationStateClosed) { + stopFlag = true; + } + } + Handler handler; + if (id == 0) { + // update handler stays forever + handler = handlers.get(id); + } else { + handler = handlers.remove(id); + } + if (handler == null) { + return; + } + + handleResult(object, handler.resultHandler, handler.exceptionHandler); + } + + private void handleResult(TdApi.Object object, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { + if (resultHandler == null) { + return; + } + + try { + resultHandler.onResult(object); + } catch (Throwable cause) { + if (exceptionHandler == null) { + exceptionHandler = defaultExceptionHandler; + } + if (exceptionHandler != null) { + try { + exceptionHandler.onException(cause); + } catch (Throwable ignored) { + } + } + } + } + + private void receiveQueries(double timeout) { + int resultN = nativeClientReceive(nativeClientId, eventIds, events, timeout); + for (int i = 0; i < resultN; i++) { + processResult(eventIds[i], events[i]); + events[i] = null; + } + } + + private static native long createNativeClient(); + + private static native void nativeClientSend(long nativeClientId, long eventId, TdApi.Function function); + + private static native int nativeClientReceive(long nativeClientId, long[] eventIds, TdApi.Object[] events, double timeout); + + private static native TdApi.Object nativeClientExecute(TdApi.Function function); + + private static native void destroyNativeClient(long nativeClientId); +} diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java new file mode 100644 index 0000000000..c81ffbeeb7 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/Log.java @@ -0,0 +1,75 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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 for managing internal TDLib logging. + */ +public final class Log { + /** + * Changes TDLib log verbosity. + * + * @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. + */ + 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. + * + * @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. + */ + public static native boolean setFilePath(String filePath); + + /** + * Changes maximum size of TDLib log file. + * + * @param maxFileSize 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. + */ + 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(); + } + } + } +} diff --git a/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java new file mode 100644 index 0000000000..831de88f1d --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/org/drinkless/tdlib/example/Example.java @@ -0,0 +1,533 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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.example; + +import org.drinkless.tdlib.Client; +import org.drinkless.tdlib.Log; +import org.drinkless.tdlib.TdApi; + +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.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Example class for TDLib usage from Java. + */ +public final class Example { + private static Client client = null; + + private static TdApi.AuthorizationState authorizationState = null; + private static volatile boolean haveAuthorization = false; + private static volatile boolean quiting = false; + + private static final Client.ResultHandler defaultHandler = new DefaultHandler(); + + private static final Lock authorizationLock = new ReentrantLock(); + private static final Condition gotAuthorization = authorizationLock.newCondition(); + + private static final ConcurrentMap<Integer, TdApi.User> users = new ConcurrentHashMap<Integer, TdApi.User>(); + private static final ConcurrentMap<Integer, TdApi.BasicGroup> basicGroups = new ConcurrentHashMap<Integer, TdApi.BasicGroup>(); + private static final ConcurrentMap<Integer, TdApi.Supergroup> supergroups = new ConcurrentHashMap<Integer, TdApi.Supergroup>(); + private static final ConcurrentMap<Integer, TdApi.SecretChat> secretChats = new ConcurrentHashMap<Integer, TdApi.SecretChat>(); + + private static final ConcurrentMap<Long, TdApi.Chat> chats = new ConcurrentHashMap<Long, TdApi.Chat>(); + private static final NavigableSet<OrderedChat> chatList = new TreeSet<OrderedChat>(); + private static boolean haveFullChatList = false; + + private static final ConcurrentMap<Integer, TdApi.UserFullInfo> usersFullInfo = new ConcurrentHashMap<Integer, TdApi.UserFullInfo>(); + private static final ConcurrentMap<Integer, TdApi.BasicGroupFullInfo> basicGroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.BasicGroupFullInfo>(); + private static final ConcurrentMap<Integer, TdApi.SupergroupFullInfo> supergroupsFullInfo = new ConcurrentHashMap<Integer, TdApi.SupergroupFullInfo>(); + + private static final String newLine = System.getProperty("line.separator"); + private static final String commandsLine = "Enter command (gcs - GetChats, gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): "; + private static volatile String currentPrompt = null; + + static { + System.loadLibrary("tdjni"); + } + + private static void print(String str) { + if (currentPrompt != null) { + System.out.println(""); + } + System.out.println(str); + if (currentPrompt != null) { + System.out.print(currentPrompt); + } + } + + private static void setChatOrder(TdApi.Chat chat, long order) { + synchronized (chatList) { + if (chat.order != 0) { + boolean isRemoved = chatList.remove(new OrderedChat(chat.order, chat.id)); + assert isRemoved; + } + + chat.order = order; + + if (chat.order != 0) { + boolean isAdded = chatList.add(new OrderedChat(chat.order, chat.id)); + assert isAdded; + } + } + } + + private static void onAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) { + if (authorizationState != null) { + Example.authorizationState = authorizationState; + } + switch (Example.authorizationState.getConstructor()) { + case TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR: + TdApi.TdlibParameters parameters = new TdApi.TdlibParameters(); + parameters.databaseDirectory = "tdlib"; + parameters.useMessageDatabase = true; + parameters.useSecretChats = true; + parameters.apiId = 94575; + parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters.systemLanguageCode = "en"; + parameters.deviceModel = "Desktop"; + parameters.systemVersion = "Unknown"; + parameters.applicationVersion = "1.0"; + parameters.enableStorageOptimizer = true; + + client.send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler()); + break; + case TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR: + client.send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler()); + break; + case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: { + String phoneNumber = promptString("Please enter phone number: "); + client.send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, false, false), new AuthorizationRequestHandler()); + break; + } + case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: { + String code = promptString("Please enter authentication code: "); + client.send(new TdApi.CheckAuthenticationCode(code, "", ""), new AuthorizationRequestHandler()); + break; + } + case TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR: { + String password = promptString("Please enter password: "); + client.send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler()); + break; + } + case TdApi.AuthorizationStateReady.CONSTRUCTOR: + haveAuthorization = true; + authorizationLock.lock(); + try { + gotAuthorization.signal(); + } finally { + authorizationLock.unlock(); + } + break; + case TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR: + haveAuthorization = false; + print("Logging out"); + break; + case TdApi.AuthorizationStateClosing.CONSTRUCTOR: + haveAuthorization = false; + print("Closing"); + break; + case TdApi.AuthorizationStateClosed.CONSTRUCTOR: + print("Closed"); + if (!quiting) { + client = Client.create(new UpdatesHandler(), null, null); // recreate client after previous has closed + } + break; + default: + System.err.println("Unsupported authorization state:" + newLine + Example.authorizationState); + } + } + + private static int toInt(String arg) { + int result = 0; + try { + result = Integer.parseInt(arg); + } catch (NumberFormatException ignored) { + } + return result; + } + + private static long getChatId(String arg) { + long chatId = 0; + try { + chatId = Long.parseLong(arg); + } catch (NumberFormatException ignored) { + } + return chatId; + } + + private static String promptString(String prompt) { + System.out.print(prompt); + currentPrompt = prompt; + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String str = ""; + try { + str = reader.readLine(); + } catch (IOException e) { + e.printStackTrace(); + } + currentPrompt = null; + return str; + } + + private static void getCommand() { + String command = promptString(commandsLine); + String[] commands = command.split(" ", 2); + try { + switch (commands[0]) { + case "gcs": { + int limit = 20; + if (commands.length > 1) { + limit = toInt(commands[1]); + } + getChatList(limit); + break; + } + case "gc": + client.send(new TdApi.GetChat(getChatId(commands[1])), defaultHandler); + break; + case "me": + client.send(new TdApi.GetMe(), defaultHandler); + break; + case "sm": { + String[] args = commands[1].split(" ", 2); + sendMessage(getChatId(args[0]), args[1]); + break; + } + case "lo": + haveAuthorization = false; + client.send(new TdApi.LogOut(), defaultHandler); + break; + case "q": + quiting = true; + haveAuthorization = false; + client.send(new TdApi.Close(), defaultHandler); + break; + default: + System.err.println("Unsupported command: " + command); + } + } catch (ArrayIndexOutOfBoundsException e) { + print("Not enough arguments"); + } + } + + private static void getChatList(final int limit) { + synchronized (chatList) { + if (!haveFullChatList && limit > chatList.size()) { + // have enough chats in the chat list or chat list is too small + long offsetOrder = Long.MAX_VALUE; + long offsetChatId = 0; + if (!chatList.isEmpty()) { + OrderedChat last = chatList.last(); + offsetOrder = last.order; + offsetChatId = last.chatId; + } + client.send(new TdApi.GetChats(offsetOrder, offsetChatId, limit - chatList.size()), new Client.ResultHandler() { + @Override + public void onResult(TdApi.Object object) { + switch (object.getConstructor()) { + case TdApi.Error.CONSTRUCTOR: + System.err.println("Receive an error for GetChats:" + newLine + object); + break; + case TdApi.Chats.CONSTRUCTOR: + long[] chatIds = ((TdApi.Chats) object).chatIds; + if (chatIds.length == 0) { + synchronized (chatList) { + haveFullChatList = true; + } + } + // chats had already been received through updates, let's retry request + getChatList(limit); + break; + default: + System.err.println("Receive wrong response from TDLib:" + newLine + object); + } + } + }); + return; + } + + // have enough chats in the chat list to answer request + java.util.Iterator<OrderedChat> iter = chatList.iterator(); + System.out.println(); + System.out.println("First " + limit + " chat(s) out of " + chatList.size() + " known chat(s):"); + for (int i = 0; i < limit; i++) { + long chatId = iter.next().chatId; + TdApi.Chat chat = chats.get(chatId); + synchronized (chat) { + System.out.println(chatId + ": " + chat.title); + } + } + print(""); + } + } + + private static void sendMessage(long chatId, String message) { + // initialize reply markup just for testing + TdApi.InlineKeyboardButton[] row = {new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl())}; + TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][]{row, row, row}); + + TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true); + client.send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), defaultHandler); + } + + public static void main(String[] args) throws InterruptedException { + // disable TDLib log + Log.setVerbosityLevel(0); + if (!Log.setFilePath("tdlib.log")) { + throw new IOError(new IOException("Write access to the current directory is required")); + } + + // create client + client = Client.create(new UpdatesHandler(), null, null); + + // test Client.execute + defaultHandler.onResult(Client.execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test"))); + + // main loop + while (!quiting) { + // await authorization + authorizationLock.lock(); + try { + while (!haveAuthorization) { + gotAuthorization.await(); + } + } finally { + authorizationLock.unlock(); + } + + while (haveAuthorization) { + getCommand(); + } + } + } + + private static class OrderedChat implements Comparable<OrderedChat> { + final long order; + final long chatId; + + OrderedChat(long order, long chatId) { + this.order = order; + this.chatId = chatId; + } + + @Override + public int compareTo(OrderedChat o) { + if (this.order != o.order) { + return o.order < this.order ? -1 : 1; + } + if (this.chatId != o.chatId) { + return o.chatId < this.chatId ? -1 : 1; + } + return 0; + } + + @Override + public boolean equals(Object obj) { + OrderedChat o = (OrderedChat) obj; + return this.order == o.order && this.chatId == o.chatId; + } + } + + private static class DefaultHandler implements Client.ResultHandler { + @Override + public void onResult(TdApi.Object object) { + print(object.toString()); + } + } + + private static class UpdatesHandler implements Client.ResultHandler { + @Override + public void onResult(TdApi.Object object) { + switch (object.getConstructor()) { + case TdApi.UpdateAuthorizationState.CONSTRUCTOR: + onAuthorizationStateUpdated(((TdApi.UpdateAuthorizationState) object).authorizationState); + break; + + case TdApi.UpdateUser.CONSTRUCTOR: + TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object; + users.put(updateUser.user.id, updateUser.user); + break; + case TdApi.UpdateUserStatus.CONSTRUCTOR: { + TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object; + TdApi.User user = users.get(updateUserStatus.userId); + synchronized (user) { + user.status = updateUserStatus.status; + } + break; + } + case TdApi.UpdateBasicGroup.CONSTRUCTOR: + TdApi.UpdateBasicGroup updateBasicGroup = (TdApi.UpdateBasicGroup) object; + basicGroups.put(updateBasicGroup.basicGroup.id, updateBasicGroup.basicGroup); + break; + case TdApi.UpdateSupergroup.CONSTRUCTOR: + TdApi.UpdateSupergroup updateSupergroup = (TdApi.UpdateSupergroup) object; + supergroups.put(updateSupergroup.supergroup.id, updateSupergroup.supergroup); + break; + case TdApi.UpdateSecretChat.CONSTRUCTOR: + TdApi.UpdateSecretChat updateSecretChat = (TdApi.UpdateSecretChat) object; + secretChats.put(updateSecretChat.secretChat.id, updateSecretChat.secretChat); + break; + + case TdApi.UpdateNewChat.CONSTRUCTOR: { + TdApi.UpdateNewChat updateNewChat = (TdApi.UpdateNewChat) object; + TdApi.Chat chat = updateNewChat.chat; + synchronized (chat) { + chats.put(chat.id, chat); + + long order = chat.order; + chat.order = 0; + setChatOrder(chat, order); + } + break; + } + case TdApi.UpdateChatTitle.CONSTRUCTOR: { + TdApi.UpdateChatTitle updateChat = (TdApi.UpdateChatTitle) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.title = updateChat.title; + } + break; + } + case TdApi.UpdateChatPhoto.CONSTRUCTOR: { + TdApi.UpdateChatPhoto updateChat = (TdApi.UpdateChatPhoto) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.photo = updateChat.photo; + } + break; + } + case TdApi.UpdateChatLastMessage.CONSTRUCTOR: { + TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.lastMessage = updateChat.lastMessage; + setChatOrder(chat, updateChat.order); + } + break; + } + case TdApi.UpdateChatOrder.CONSTRUCTOR: { + TdApi.UpdateChatOrder updateChat = (TdApi.UpdateChatOrder) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + setChatOrder(chat, updateChat.order); + } + break; + } + case TdApi.UpdateChatIsPinned.CONSTRUCTOR: { + TdApi.UpdateChatIsPinned updateChat = (TdApi.UpdateChatIsPinned) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.isPinned = updateChat.isPinned; + setChatOrder(chat, updateChat.order); + } + break; + } + case TdApi.UpdateChatReadInbox.CONSTRUCTOR: { + TdApi.UpdateChatReadInbox updateChat = (TdApi.UpdateChatReadInbox) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId; + chat.unreadCount = updateChat.unreadCount; + } + break; + } + case TdApi.UpdateChatReadOutbox.CONSTRUCTOR: { + TdApi.UpdateChatReadOutbox updateChat = (TdApi.UpdateChatReadOutbox) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId; + } + break; + } + case TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR: { + TdApi.UpdateChatUnreadMentionCount updateChat = (TdApi.UpdateChatUnreadMentionCount) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.unreadMentionCount = updateChat.unreadMentionCount; + } + break; + } + case TdApi.UpdateMessageMentionRead.CONSTRUCTOR: { + TdApi.UpdateMessageMentionRead updateChat = (TdApi.UpdateMessageMentionRead) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.unreadMentionCount = updateChat.unreadMentionCount; + } + break; + } + case TdApi.UpdateChatReplyMarkup.CONSTRUCTOR: { + TdApi.UpdateChatReplyMarkup updateChat = (TdApi.UpdateChatReplyMarkup) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.replyMarkupMessageId = updateChat.replyMarkupMessageId; + } + break; + } + case TdApi.UpdateChatDraftMessage.CONSTRUCTOR: { + TdApi.UpdateChatDraftMessage updateChat = (TdApi.UpdateChatDraftMessage) object; + TdApi.Chat chat = chats.get(updateChat.chatId); + synchronized (chat) { + chat.draftMessage = updateChat.draftMessage; + setChatOrder(chat, updateChat.order); + } + break; + } + case TdApi.UpdateNotificationSettings.CONSTRUCTOR: { + TdApi.UpdateNotificationSettings update = (TdApi.UpdateNotificationSettings) object; + if (update.scope instanceof TdApi.NotificationSettingsScopeChat) { + TdApi.Chat chat = chats.get(((TdApi.NotificationSettingsScopeChat) update.scope).chatId); + synchronized (chat) { + chat.notificationSettings = update.notificationSettings; + } + } + break; + } + + case TdApi.UpdateUserFullInfo.CONSTRUCTOR: + TdApi.UpdateUserFullInfo updateUserFullInfo = (TdApi.UpdateUserFullInfo) object; + usersFullInfo.put(updateUserFullInfo.userId, updateUserFullInfo.userFullInfo); + break; + case TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR: + TdApi.UpdateBasicGroupFullInfo updateBasicGroupFullInfo = (TdApi.UpdateBasicGroupFullInfo) object; + basicGroupsFullInfo.put(updateBasicGroupFullInfo.basicGroupId, updateBasicGroupFullInfo.basicGroupFullInfo); + break; + case TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR: + TdApi.UpdateSupergroupFullInfo updateSupergroupFullInfo = (TdApi.UpdateSupergroupFullInfo) object; + supergroupsFullInfo.put(updateSupergroupFullInfo.supergroupId, updateSupergroupFullInfo.supergroupFullInfo); + break; + default: + // print("Unsupported update:" + newLine + object); + } + } + } + + private static class AuthorizationRequestHandler implements Client.ResultHandler { + @Override + public void onResult(TdApi.Object object) { + switch (object.getConstructor()) { + case TdApi.Error.CONSTRUCTOR: + System.err.println("Receive an error:" + newLine + object); + onAuthorizationStateUpdated(null); // repeat last action + break; + case TdApi.Ok.CONSTRUCTOR: + // result is already received through UpdateAuthorizationState, nothing to do + break; + default: + System.err.println("Receive wrong response from TDLib:" + newLine + object); + } + } + } +}
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/java/td_jni.cpp b/protocols/Telegram/tdlib/td/example/java/td_jni.cpp new file mode 100644 index 0000000000..b9ba74a402 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/java/td_jni.cpp @@ -0,0 +1,158 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/telegram/Client.h> +#include <td/telegram/Log.h> +#include <td/telegram/td_api.h> + +#include <td/tl/tl_jni_object.h> + +#include <cstdint> +#include <cstdlib> +#include <string> +#include <utility> + +namespace td_jni { + +static td::td_api::object_ptr<td::td_api::Function> fetch_function(JNIEnv *env, jobject function) { + td::jni::reset_parse_error(); + auto result = td::td_api::Function::fetch(env, function); + if (td::jni::have_parse_error()) { + std::abort(); + } + return result; +} + +static td::Client *get_client(jlong client_id) { + return reinterpret_cast<td::Client *>(static_cast<std::uintptr_t>(client_id)); +} + +static jlong Client_createNativeClient(JNIEnv *env, jclass clazz) { + return static_cast<jlong>(reinterpret_cast<std::uintptr_t>(new td::Client())); +} + +static void Client_nativeClientSend(JNIEnv *env, jclass clazz, jlong client_id, jlong id, jobject function) { + get_client(client_id)->send({static_cast<std::uint64_t>(id), fetch_function(env, function)}); +} + +static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jlong client_id, jlongArray ids, jobjectArray events, + jdouble timeout) { + auto client = get_client(client_id); + jsize events_size = env->GetArrayLength(ids); // ids and events size must be of equal size + jsize result_size = 0; + + auto response = client->receive(timeout); + while (response.object && result_size < events_size) { + jlong result_id = static_cast<jlong>(response.id); + env->SetLongArrayRegion(ids, result_size, 1, &result_id); + + jobject object; + response.object->store(env, object); + env->SetObjectArrayElement(events, result_size, object); + env->DeleteLocalRef(object); + + result_size++; + response = client->receive(0); + } + return result_size; +} + +static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject function) { + jobject result; + td::Client::execute({0, fetch_function(env, function)}).object->store(env, result); + return result; +} + +static void Client_destroyNativeClient(JNIEnv *env, jclass clazz, jlong client_id) { + delete get_client(client_id); +} + +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))); +} + +static jstring Function_toString(JNIEnv *env, jobject object) { + return td::jni::to_jstring(env, to_string(td::td_api::Function::fetch(env, object))); +} + +static constexpr jint JAVA_VERSION = JNI_VERSION_1_6; +static JavaVM *java_vm; +static jclass log_class; + +static void on_fatal_error(const char *error_message) { + auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION); + jmethodID on_fatal_error_method = env->GetStaticMethodID(log_class, "onFatalError", "(Ljava/lang/String;)V"); + if (env && 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); + } + } +} + +static jint register_native(JavaVM *vm) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast<void **>(&env), JAVA_VERSION) != JNI_OK) { + return -1; + } + + java_vm = vm; + + auto register_method = [env](jclass clazz, std::string name, std::string signature, auto function_ptr) { + td::jni::register_native_method(env, clazz, std::move(name), std::move(signature), + reinterpret_cast<void *>(function_ptr)); + }; + + 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"); + +#define TD_OBJECT "L" PACKAGE_NAME "/TdApi$Object;" +#define TD_FUNCTION "L" PACKAGE_NAME "/TdApi$Function;" + register_method(client_class, "createNativeClient", "()J", Client_createNativeClient); + register_method(client_class, "nativeClientSend", "(JJ" TD_FUNCTION ")V", Client_nativeClientSend); + register_method(client_class, "nativeClientReceive", "(J[J[" TD_OBJECT "D)I", Client_nativeClientReceive); + register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute); + register_method(client_class, "destroyNativeClient", "(J)V", Client_destroyNativeClient); + + 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(object_class, "toString", "()Ljava/lang/String;", Object_toString); + + register_method(function_class, "toString", "()Ljava/lang/String;", Function_toString); +#undef TD_FUNCTION +#undef TD_OBJECT + + 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::Log::set_fatal_error_callback(on_fatal_error); + + return JAVA_VERSION; +} + +} // namespace td_jni + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + static jint jni_version = td_jni::register_native(vm); // call_once + return jni_version; +} diff --git a/protocols/Telegram/tdlib/td/example/python/README.md b/protocols/Telegram/tdlib/td/example/python/README.md new file mode 100644 index 0000000000..c4a4ad3768 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/python/README.md @@ -0,0 +1,11 @@ +# TDLib Python example + +To run this example you need to [build](https://github.com/tdlib/td#building) TDLib and copy built tdjson shared library to this directory. + +Then you can run the example: +``` +python tdjson_example.py +``` + +Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html), +[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation. diff --git a/protocols/Telegram/tdlib/td/example/python/tdjson_example.py b/protocols/Telegram/tdlib/td/example/python/tdjson_example.py new file mode 100644 index 0000000000..d7b2e86fbb --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/python/tdjson_example.py @@ -0,0 +1,106 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// +from ctypes.util import find_library +from ctypes import * +import json +import sys + +# load shared library +tdjson_path = find_library("tdjson") or "tdjson.dll" +if tdjson_path is None: + print('can\'t find tdjson library') + quit() +tdjson = CDLL(tdjson_path) + +# load TDLib functions from shared library +td_json_client_create = tdjson.td_json_client_create +td_json_client_create.restype = c_void_p +td_json_client_create.argtypes = [] + +td_json_client_receive = tdjson.td_json_client_receive +td_json_client_receive.restype = c_char_p +td_json_client_receive.argtypes = [c_void_p, c_double] + +td_json_client_send = tdjson.td_json_client_send +td_json_client_send.restype = None +td_json_client_send.argtypes = [c_void_p, c_char_p] + +td_json_client_execute = tdjson.td_json_client_execute +td_json_client_execute.restype = c_char_p +td_json_client_execute.argtypes = [c_void_p, c_char_p] + +td_json_client_destroy = tdjson.td_json_client_destroy +td_json_client_destroy.restype = None +td_json_client_destroy.argtypes = [c_void_p] + +td_set_log_file_path = tdjson.td_set_log_file_path +td_set_log_file_path.restype = c_int +td_set_log_file_path.argtypes = [c_char_p] + +td_set_log_max_file_size = tdjson.td_set_log_max_file_size +td_set_log_max_file_size.restype = None +td_set_log_max_file_size.argtypes = [c_longlong] + +td_set_log_verbosity_level = tdjson.td_set_log_verbosity_level +td_set_log_verbosity_level.restype = None +td_set_log_verbosity_level.argtypes = [c_int] + +fatal_error_callback_type = CFUNCTYPE(None, c_char_p) + +td_set_log_fatal_error_callback = tdjson.td_set_log_fatal_error_callback +td_set_log_fatal_error_callback.restype = None +td_set_log_fatal_error_callback.argtypes = [fatal_error_callback_type] + +# initialize TDLib log with desired parameters +def on_fatal_error_callback(error_message): + print('TDLib fatal error: ', error_message) + +td_set_log_verbosity_level(2) +c_on_fatal_error_callback = fatal_error_callback_type(on_fatal_error_callback) +td_set_log_fatal_error_callback(c_on_fatal_error_callback) + +# create client +client = td_json_client_create() + +# simple wrappers for client usage +def td_send(query): + query = json.dumps(query).encode('utf-8') + td_json_client_send(client, query) + +def td_receive(): + result = td_json_client_receive(client, 1.0) + if result: + result = json.loads(result.decode('utf-8')) + return result + +def td_execute(query): + query = json.dumps(query).encode('utf-8') + result = td_json_client_execute(client, query) + if result: + result = json.loads(result.decode('utf-8')) + return result + +# testing TDLib execute method +print(td_execute({'@type': 'getTextEntities', 'text': '@telegram /test_command https://telegram.org telegram.me', '@extra': ['5', 7.0]})) + +# testing TDLib send method +td_send({'@type': 'getAuthorizationState', '@extra': 1.01234}) + +# main events cycle +while True: + event = td_receive() + if event: + # if client is closed, we need to destroy it and create new client + if event['@type'] is 'updateAuthorizationState' and event['authorization_state']['@type'] is 'authorizationStateClosed': + break + + # handle an incoming update or an answer to a previously sent request + print(event) + sys.stdout.flush() + +# destroy client when it is closed and isn't needed anymore +td_json_client_destroy(client) diff --git a/protocols/Telegram/tdlib/td/example/ruby/Gemfile b/protocols/Telegram/tdlib/td/example/ruby/Gemfile new file mode 100644 index 0000000000..3a38ffc0a3 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ruby/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'tdlib-ruby' diff --git a/protocols/Telegram/tdlib/td/example/ruby/Gemfile.lock b/protocols/Telegram/tdlib/td/example/ruby/Gemfile.lock new file mode 100644 index 0000000000..22954eddfb --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ruby/Gemfile.lock @@ -0,0 +1,17 @@ +GEM + remote: https://rubygems.org/ + specs: + concurrent-ruby (1.0.5) + dry-configurable (0.7.0) + concurrent-ruby (~> 1.0) + tdlib-ruby (0.2.0) + dry-configurable (~> 0.7) + +PLATFORMS + ruby + +DEPENDENCIES + tdlib-ruby + +BUNDLED WITH + 1.16.1 diff --git a/protocols/Telegram/tdlib/td/example/ruby/example.rb b/protocols/Telegram/tdlib/td/example/ruby/example.rb new file mode 100644 index 0000000000..4b29dfd53a --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/ruby/example.rb @@ -0,0 +1,61 @@ +require 'tdlib-ruby' + +TD.configure do |config| + config.lib_path = 'path/to/dir_containing_lobtdjson' + + # You should obtain your own api_id and api_hash from https://my.telegram.org/apps + config.client.api_id = 12345 + config.client.api_hash = '1234567890abcdefghigklmnopqrstuv' +end + +TD::Api.set_log_verbosity_level(1) + +client = TD::Client.new + +begin + state = nil + + client.on('updateAuthorizationState') do |update| + next unless update.dig('authorization_state', '@type') == 'authorizationStateWaitPhoneNumber' + state = :wait_phone + end + + client.on('updateAuthorizationState') do |update| + next unless update.dig('authorization_state', '@type') == 'authorizationStateWaitCode' + state = :wait_code + end + + client.on('updateAuthorizationState') do |update| + next unless update.dig('authorization_state', '@type') == 'authorizationStateReady' + state = :ready + end + + loop do + case state + when :wait_phone + p 'Please, enter your phone number:' + phone = STDIN.gets.strip + params = { + '@type' => 'setAuthenticationPhoneNumber', + 'phone_number' => phone + } + client.broadcast_and_receive(params) + when :wait_code + p 'Please, enter code from SMS:' + code = STDIN.gets.strip + params = { + '@type' => 'checkAuthenticationCode', + 'code' => code + } + client.broadcast_and_receive(params) + when :ready + @me = client.broadcast_and_receive('@type' => 'getMe') + break + end + end + +ensure + client.close +end + +p @me diff --git a/protocols/Telegram/tdlib/td/example/swift/.gitignore b/protocols/Telegram/tdlib/td/example/swift/.gitignore new file mode 100644 index 0000000000..4737be5996 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/swift/.gitignore @@ -0,0 +1,3 @@ +xcuserdata/ +*workspace/ +td/ diff --git a/protocols/Telegram/tdlib/td/example/swift/README.md b/protocols/Telegram/tdlib/td/example/swift/README.md new file mode 100644 index 0000000000..6a333df766 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/swift/README.md @@ -0,0 +1,15 @@ +# TDLib swift MacOS example + +TDLib should be prebuilt and installed to local subdirectory `td/`: +``` +cd <path to TDLib sources> +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=../example/swift/td .. +cmake --build . --target install +``` + +Then you can open and build the example with the latest Xcode. + +Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html), +[td_log](https://core.telegram.org/tdlib/docs/td__log_8h.html) and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation. diff --git a/protocols/Telegram/tdlib/td/example/swift/src/main.swift b/protocols/Telegram/tdlib/td/example/swift/src/main.swift new file mode 100644 index 0000000000..ac81c632b8 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/swift/src/main.swift @@ -0,0 +1,178 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// + +import Foundation + +// TDLib Client Swift binding +class TdClient { + typealias Client = UnsafeMutableRawPointer + var client = td_json_client_create()! + let tdlibMainLoop = DispatchQueue(label: "TDLib") + let tdlibQueryQueue = DispatchQueue(label: "TDLibQuery") + var queryF = Dictionary<Int64, (Dictionary<String,Any>)->()>() + var updateF: ((Dictionary<String,Any>)->())? + var queryId: Int64 = 0 + + func queryAsync(query: [String: Any], f: ((Dictionary<String,Any>)->())? = nil) { + tdlibQueryQueue.async { + var newQuery = query + + if f != nil { + let nextQueryId = self.queryId + 1 + newQuery["@extra"] = nextQueryId + self.queryF[nextQueryId] = f + self.queryId = nextQueryId + } + td_json_client_send(self.client, to_json(newQuery)) + } + } + + func querySync(query: [String: Any]) -> Dictionary<String,Any> { + let semaphore = DispatchSemaphore(value:0) + var result = Dictionary<String,Any>() + queryAsync(query: query) { + result = $0 + semaphore.signal() + } + semaphore.wait() + return result + } + + init() { + } + + deinit { + td_json_client_destroy(client) + } + + func run(updateHandler: @escaping (Dictionary<String,Any>)->()) { + updateF = updateHandler + tdlibMainLoop.async { [weak self] in + while (true) { + if let s = self { + if let res = td_json_client_receive(s.client, 10) { + let event = String(cString: res) + s.queryResultAsync(event) + } + } else { + break + } + } + } + } + + private func queryResultAsync(_ result: String) { + tdlibQueryQueue.async { + let json = try? JSONSerialization.jsonObject(with: result.data(using: .utf8)!, options:[]) + if let dictionary = json as? [String:Any] { + if let extra = dictionary["@extra"] as? Int64 { + let index = self.queryF.index(forKey: extra)! + self.queryF[index].value(dictionary) + self.queryF.remove(at: index) + } else { + self.updateF!(dictionary) + } + } + } + } +} + +func to_json(_ obj: Any) -> String { + do { + let obj = try JSONSerialization.data(withJSONObject: obj) + return String(data: obj, encoding: .utf8)! + } catch { + return "" + } +} + + +// An example of usage +td_set_log_verbosity_level(1); + +var client = TdClient() + +func myReadLine() -> String { + while (true) { + if let line = readLine() { + return line + } + } +} + +func updateAuthorizationState(authorizationState: Dictionary<String, Any>) { + switch(authorizationState["@type"] as! String) { + case "authorizationStateWaitTdlibParameters": + client.queryAsync(query:[ + "@type":"setTdlibParameters", + "parameters":[ + "database_directory":"tdlib", + "use_message_database":true, + "use_secret_chats":true, + "api_id":94575, + "api_hash":"a3406de8d171bb422bb6ddf3bbd800e2", + "system_language_code":"en", + "device_model":"Desktop", + "system_version":"Unknown", + "application_version":"1.0", + "enable_storage_optimizer":true + ] + ]); + + case "authorizationStateWaitEncryptionKey": + client.queryAsync(query: ["@type":"checkDatabaseEncryptionKey", "key":"cucumber"]) + + case "authorizationStateWaitPhoneNumber": + print("Enter your phone: ") + let phone = myReadLine() + client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone], f:checkAuthenticationError) + + case "authorizationStateWaitCode": + var first_name: String = "" + var last_name: String = "" + var code: String = "" + if let is_registered = authorizationState["is_registered"] as? Bool, is_registered { + } else { + print("Enter your first name: ") + first_name = myReadLine() + print("Enter your last name: ") + last_name = myReadLine() + } + print("Enter (SMS) code: ") + code = myReadLine() + client.queryAsync(query:["@type":"checkAuthenticationCode", "code":code, "first_name":first_name, "last_name":last_name], f:checkAuthenticationError) + + case "authorizationStateWaitPassword": + print("Enter password: ") + let password = myReadLine() + client.queryAsync(query:["@type":"checkAuthenticationPassword", "password":password], f:checkAuthenticationError) + + case "authorizationStateReady": + () + + default: + assert(false, "TODO: Unknown authorization state"); + } +} + +func checkAuthenticationError(error: Dictionary<String, Any>) { + if (error["@type"] as! String == "error") { + client.queryAsync(query:["@type":"getAuthorizationState"], f:updateAuthorizationState) + } +} + +client.run { + let update = $0 + print(update) + if update["@type"] as! String == "updateAuthorizationState" { + updateAuthorizationState(authorizationState: update["authorization_state"] as! Dictionary<String, Any>) + } +} + +while true { + sleep(1) +} diff --git a/protocols/Telegram/tdlib/td/example/swift/src/td-Bridging-Header.h b/protocols/Telegram/tdlib/td/example/swift/src/td-Bridging-Header.h new file mode 100644 index 0000000000..434ab2e4a4 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/swift/src/td-Bridging-Header.h @@ -0,0 +1,15 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/telegram/td_json_client.h" +#include "td/telegram/td_log.h" diff --git a/protocols/Telegram/tdlib/td/example/swift/td.xcodeproj/project.pbxproj b/protocols/Telegram/tdlib/td/example/swift/td.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..7b774c421c --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/swift/td.xcodeproj/project.pbxproj @@ -0,0 +1,310 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1F65E3A42031BF6A00F79763 /* libtdjson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */; }; + 1F65E3A92031C0F000F79763 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F65E3A82031C0F000F79763 /* main.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1FCE2CEF1EC5E1B50061661A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libtdjson.dylib; path = td/lib/libtdjson.dylib; sourceTree = "<group>"; }; + 1F65E3A82031C0F000F79763 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = src/main.swift; sourceTree = SOURCE_ROOT; }; + 1F65E3AA2031C14300F79763 /* td-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "td-Bridging-Header.h"; path = "src/td-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; + 1FCE2CF11EC5E1B50061661A /* td */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = td; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1FCE2CEE1EC5E1B50061661A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F65E3A42031BF6A00F79763 /* libtdjson.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1FCE2CE81EC5E1B50061661A = { + isa = PBXGroup; + children = ( + 1FCE2CF31EC5E1B50061661A /* src */, + 1FCE2CF21EC5E1B50061661A /* Products */, + 1FCE2CFB1EC5E1EE0061661A /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 1FCE2CF21EC5E1B50061661A /* Products */ = { + isa = PBXGroup; + children = ( + 1FCE2CF11EC5E1B50061661A /* td */, + ); + name = Products; + sourceTree = "<group>"; + }; + 1FCE2CF31EC5E1B50061661A /* src */ = { + isa = PBXGroup; + children = ( + 1F65E3AA2031C14300F79763 /* td-Bridging-Header.h */, + 1F65E3A82031C0F000F79763 /* main.swift */, + ); + name = src; + path = td; + sourceTree = "<group>"; + }; + 1FCE2CFB1EC5E1EE0061661A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1F65E3A32031BF6A00F79763 /* libtdjson.dylib */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1FCE2CF01EC5E1B50061661A /* td */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1FCE2CF81EC5E1B50061661A /* Build configuration list for PBXNativeTarget "td" */; + buildPhases = ( + 1FCE2CED1EC5E1B50061661A /* Sources */, + 1FCE2CEE1EC5E1B50061661A /* Frameworks */, + 1FCE2CEF1EC5E1B50061661A /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = td; + productName = td; + productReference = 1FCE2CF11EC5E1B50061661A /* td */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1FCE2CE91EC5E1B50061661A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Arseny Smirnov "; + TargetAttributes = { + 1FCE2CF01EC5E1B50061661A = { + CreatedOnToolsVersion = 8.3.2; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1FCE2CEC1EC5E1B50061661A /* Build configuration list for PBXProject "td" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1FCE2CE81EC5E1B50061661A; + productRefGroup = 1FCE2CF21EC5E1B50061661A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1FCE2CF01EC5E1B50061661A /* td */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1FCE2CED1EC5E1B50061661A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F65E3A92031C0F000F79763 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1FCE2CF61EC5E1B50061661A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Debug; + }; + 1FCE2CF71EC5E1B50061661A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Release; + }; + 1FCE2CF91EC5E1B50061661A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(PROJECT_DIR)/lib"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/td/lib", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "src/td-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + USER_HEADER_SEARCH_PATHS = td/include/; + }; + name = Debug; + }; + 1FCE2CFA1EC5E1B50061661A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(PROJECT_DIR)/lib"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/td/lib", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "src/td-Bridging-Header.h"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + USER_HEADER_SEARCH_PATHS = td/include/; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1FCE2CEC1EC5E1B50061661A /* Build configuration list for PBXProject "td" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1FCE2CF61EC5E1B50061661A /* Debug */, + 1FCE2CF71EC5E1B50061661A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1FCE2CF81EC5E1B50061661A /* Build configuration list for PBXNativeTarget "td" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1FCE2CF91EC5E1B50061661A /* Debug */, + 1FCE2CFA1EC5E1B50061661A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1FCE2CE91EC5E1B50061661A /* Project object */; +} diff --git a/protocols/Telegram/tdlib/td/example/uwp/LICENSE_1_0.txt b/protocols/Telegram/tdlib/td/example/uwp/LICENSE_1_0.txt new file mode 100644 index 0000000000..36b7cd93cd --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/protocols/Telegram/tdlib/td/example/uwp/README.md b/protocols/Telegram/tdlib/td/example/uwp/README.md new file mode 100644 index 0000000000..e7bb9b821b --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/README.md @@ -0,0 +1,28 @@ +# TDLib Universal Windows Platform example + +This is an example of building TDLib SDK for Universal Windows Platform and an example of its usage from C#. + +## Building SDK + +* Download and install Microsoft Visual Studio 2015+ with Windows 10 SDK. We recommend to use the latest available versions of Microsoft Visual Studio and Windows 10 SDK. +* Download and install [CMake](https://cmake.org/download/). +* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start) or update it to the latest version using `vcpkg update` and following received instructions. +* Install `zlib` and `openssl` for all UWP architectures using `vcpkg`: +``` +C:\src\vcpkg> .\vcpkg.exe install openssl:arm-uwp openssl:x64-uwp openssl:x86-uwp zlib:arm-uwp zlib:x64-uwp zlib:x86-uwp +``` +* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download#php-7.2). Add the path to php.exe to the PATH environment variable. +* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable. +* Download and install [7-Zip](http://www.7-zip.org/download.html) archiver, which is used by the `build.ps1` script to create a Telegram.Td.UWP Visual Studio Extension. Add the path to 7z.exe to the PATH environment variable. + Alternatively `build.ps1` supports compressing using [WinRAR](https://en.wikipedia.org/wiki/WinRAR) with option `-compress winrar` and compressing using [zip](http://gnuwin32.sourceforge.net/packages/zip.htm) with `-compress zip`. +* Build `TDLib` using provided `build.ps1` script (TDLib should be built 6 times for multiple platforms in Debug and Release configurations, so it make take few hours). Pass path to vcpkg.exe as `-vcpkg-root` argument: +``` +powershell -ExecutionPolicy ByPass .\build.ps1 -vcpkg_root C:\src\vcpkg +``` +If you need to restart the build from scratch, call `.\build.ps1 -mode clean` first. +* Install Visual Studio Extension "TDLib for Universal Windows Platform" located at `build-uwp\vsix\tdlib.vsix`, which was created on the previous step by `build.ps1` script. + +Now `TDLib` can be freely used from any UWP project, built in Visual Studio. + +## Example of usage +The `app/` directory contains a simple example of a C# application for Universal Windows Platform. Just open it with Visual Studio 2015 or 2017 and run. diff --git a/protocols/Telegram/tdlib/td/example/uwp/SDKManifest.xml b/protocols/Telegram/tdlib/td/example/uwp/SDKManifest.xml new file mode 100644 index 0000000000..d6896fcb65 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/SDKManifest.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8" ?> +<FileList + DisplayName="TDLib for Universal Windows Platform" + ProductFamilyName="Telegram.Td.UWP" + MoreInfo="https://core.telegram.org/tdlib" + MinVSVersion="14.0" + AppliesTo="WindowsAppContainer" + DependsOn="Microsoft.VCLibs, version=14.0" + SupportsMultipleVersions="Error" + SupportedArchitectures="x86;x64;ARM"> + <File Reference="Telegram.Td.winmd" Implementation="Telegram.Td.dll" /> +</FileList> diff --git a/protocols/Telegram/tdlib/td/example/uwp/[Content_Types].xml b/protocols/Telegram/tdlib/td/example/uwp/[Content_Types].xml new file mode 100644 index 0000000000..b91d46fa3c --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/[Content_Types].xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> + <Default Extension="winmd" ContentType="application/octet-stream" /> + <Default Extension="pri" ContentType="application/octet-stream" /> + <Default Extension="dll" ContentType="application/octet-stream" /> + <Default Extension="h" ContentType="application/octet-stream" /> + <Default Extension="lib" ContentType="application/octet-stream" /> + <Default Extension="pdb" ContentType="application/octet-stream" /> + <Default Extension="png" ContentType="application/octet-stream" /> + <Default Extension="props" ContentType="application/octet-stream" /> + <Default Extension="txt" ContentType="text/plain" /> + <Default Extension="vsixmanifest" ContentType="text/xml" /> + <Default Extension="xml" ContentType="text/xml" /> +</Types> diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/.gitignore b/protocols/Telegram/tdlib/td/example/uwp/app/.gitignore new file mode 100644 index 0000000000..37ab08165b --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/.gitignore @@ -0,0 +1,5 @@ +.vs/ +bin/ +obj/ +project.lock.json +TdApp.csproj.user diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml b/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml new file mode 100644 index 0000000000..b4256fd441 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml @@ -0,0 +1,7 @@ +<Application + x:Class="TdApp.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:TdApp" + RequestedTheme="Light"> +</Application> diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml.cs b/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml.cs new file mode 100644 index 0000000000..0ed0f96812 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/App.xaml.cs @@ -0,0 +1,104 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// +using System; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace TdApp +{ + /// <summary> + /// Provides application-specific behavior to supplement the default Application class. + /// </summary> + sealed partial class App : Application + { + /// <summary> + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// </summary> + public App() + { + Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync( + Microsoft.ApplicationInsights.WindowsCollectors.Metadata | + Microsoft.ApplicationInsights.WindowsCollectors.Session); + this.InitializeComponent(); + this.Suspending += OnSuspending; + } + + /// <summary> + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// </summary> + /// <param name="e">Details about the launch request and process.</param> + protected override void OnLaunched(LaunchActivatedEventArgs e) + { + +#if DEBUG + if (System.Diagnostics.Debugger.IsAttached) + { + this.DebugSettings.EnableFrameRateCounter = true; + } +#endif + + Frame rootFrame = Window.Current.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) + { + //TODO: Load state from previously suspended application + } + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(MainPage), e.Arguments); + } + // Ensure the current window is active + Window.Current.Activate(); + } + + /// <summary> + /// Invoked when Navigation to a certain page fails + /// </summary> + /// <param name="sender">The Frame which failed navigation</param> + /// <param name="e">Details about the navigation failure</param> + void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + } + + /// <summary> + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// </summary> + /// <param name="sender">The source of the suspend request.</param> + /// <param name="e">Details about the suspend request.</param> + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + //TODO: Save application state and stop any background activity + deferral.Complete(); + } + } +} diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/ApplicationInsights.config b/protocols/Telegram/tdlib/td/example/uwp/app/ApplicationInsights.config new file mode 100644 index 0000000000..cb2a232da3 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/ApplicationInsights.config @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<ApplicationInsights xmlns = "http://schemas.microsoft.com/ApplicationInsights/2013/Settings"> +</ApplicationInsights> diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/LockScreenLogo.scale-200.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/LockScreenLogo.scale-200.png Binary files differnew file mode 100644 index 0000000000..735f57adb5 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/LockScreenLogo.scale-200.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/SplashScreen.scale-200.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/SplashScreen.scale-200.png Binary files differnew file mode 100644 index 0000000000..023e7f1fed --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/SplashScreen.scale-200.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square150x150Logo.scale-200.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square150x150Logo.scale-200.png Binary files differnew file mode 100644 index 0000000000..af49fec1a5 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square150x150Logo.scale-200.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.scale-200.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.scale-200.png Binary files differnew file mode 100644 index 0000000000..ce342a2ec8 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.scale-200.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.targetsize-24_altform-unplated.png Binary files differnew file mode 100644 index 0000000000..f6c02ce97e --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/StoreLogo.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/StoreLogo.png Binary files differnew file mode 100644 index 0000000000..7385b56c0e --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/StoreLogo.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Wide310x150Logo.scale-200.png b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Wide310x150Logo.scale-200.png Binary files differnew file mode 100644 index 0000000000..288995b397 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Assets/Wide310x150Logo.scale-200.png diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml b/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml new file mode 100644 index 0000000000..cdff97d9f3 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml @@ -0,0 +1,29 @@ +<Page + x:Class="TdApp.MainPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:TdApp" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + x:Name="Self"> + + <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="*"/> + </Grid.RowDefinitions> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*"/> + <ColumnDefinition Width="Auto"/> + </Grid.ColumnDefinitions> + <TextBox x:Name="Input"/> + <Button Grid.Column="1" x:Name="Send" Content="send" Click="Button_Click"/> + </Grid> + <ListBox Grid.Row="1" x:Name="ItemsControl" ItemsSource="{Binding Items, ElementName=Self}"> + + </ListBox> + <!--<Button Content="Test" Click="Button_Click"/>--> + </Grid> +</Page> diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml.cs b/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml.cs new file mode 100644 index 0000000000..9bdf5070ee --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/MainPage.xaml.cs @@ -0,0 +1,171 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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) +// +using System; +using System.IO; +using Td = Telegram.Td; +using TdApi = Telegram.Td.Api; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace TdApp +{ + public sealed partial class MainPage : Page + { + public System.Collections.ObjectModel.ObservableCollection<string> Items { get; set; } + + private static MyClientResultHandler _handler; + + public MainPage() + { + InitializeComponent(); + + Items = new System.Collections.ObjectModel.ObservableCollection<string>(); + _handler = new MyClientResultHandler(this); + + System.Threading.Tasks.Task.Run(() => + { + try + { + Td.Log.SetFilePath(Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "log")); + _client = Td.Client.Create(_handler); + var parameters = new TdApi.TdlibParameters(); + parameters.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path; + parameters.UseSecretChats = true; + parameters.UseMessageDatabase = true; + parameters.ApiId = 94575; + parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2"; + parameters.SystemLanguageCode = "en"; + parameters.DeviceModel = "en"; + parameters.SystemVersion = "en"; + parameters.ApplicationVersion = "1.0.0"; + _client.Send(new TdApi.SetTdlibParameters(parameters), null); + _client.Send(new TdApi.CheckDatabaseEncryptionKey(), null); + _client.Run(); + } + catch (Exception ex) + { + Print(ex.ToString()); + } + }); + } + + public void Print(String str) + { + var delayTask = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Items.Insert(0, str.Substring(0, Math.Min(1024, str.Length))); + }); + } + + private static Td.Client _client; + + private void AcceptCommand(String command) + { + Input.Text = string.Empty; + Items.Insert(0, string.Format(">>{0}", command)); + } + private void Button_Click(object sender, RoutedEventArgs e) + { + var command = Input.Text; + + if (command.StartsWith("DESTROY")) + { + AcceptCommand("Destroy"); + _client.Send(new TdApi.Destroy(), _handler); + } + else if (command.StartsWith("lo")) + { + AcceptCommand("LogOut"); + _client.Send(new TdApi.LogOut(), _handler); + } + else if (command.StartsWith("gas")) + { + AcceptCommand(command); + _client.Send(new TdApi.GetAuthorizationState(), _handler); + } + else if (command.StartsWith("sap")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + _client.Send(new TdApi.SetAuthenticationPhoneNumber(args[1], false, false), _handler); + } + else if (command.StartsWith("cac")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + _client.Send(new TdApi.CheckAuthenticationCode(args[1], String.Empty, String.Empty), _handler); + } + else if (command.StartsWith("cap")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + _client.Send(new TdApi.CheckAuthenticationPassword(args[1]), _handler); + } + else if (command.StartsWith("gco")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + _client.Send(new TdApi.SearchContacts(), _handler); + } + else if (command.StartsWith("df")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + _client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1), _handler); + } + else if (command.StartsWith("bench")) + { + var args = command.Split(" ".ToCharArray(), 2); + AcceptCommand(command); + var cnt = Int32.Parse(args[1]); + var handler = new BenchSimpleHandler(this, cnt); + for (int i = 0; i < cnt; i++) + { + _client.Send(new TdApi.TestSquareInt(123), handler); + } + } + } + } + + class MyClientResultHandler : Td.ClientResultHandler + { + private MainPage _page; + + public MyClientResultHandler(MainPage page) + { + _page = page; + } + + public void OnResult(TdApi.BaseObject obj) + { + var str = obj.ToString(); + _page.Print(str); + } + } + + class BenchSimpleHandler : Td.ClientResultHandler + { + private MainPage _page; + private int _cnt; + + public BenchSimpleHandler(MainPage page, int cnt) + { + _page = page; + _cnt = cnt; + } + + public void OnResult(TdApi.BaseObject obj) + { + _cnt--; + if (_cnt == 0) + { + _page.Print("DONE"); + } + } + } +} diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Package.appxmanifest b/protocols/Telegram/tdlib/td/example/uwp/app/Package.appxmanifest new file mode 100644 index 0000000000..ca1235ffa7 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Package.appxmanifest @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp"> + <Identity Name="1c9ba6de-23cf-4aef-803b-91fcc451b69a" Publisher="CN=arseny30" Version="1.0.0.0" /> + <mp:PhoneIdentity PhoneProductId="1c9ba6de-23cf-4aef-803b-91fcc451b69a" PhonePublisherId="00000000-0000-0000-0000-000000000000" /> + <Properties> + <DisplayName>TdApp</DisplayName> + <PublisherDisplayName>arseny30</PublisherDisplayName> + <Logo>Assets\StoreLogo.png</Logo> + </Properties> + <Dependencies> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" /> + </Dependencies> + <Resources> + <Resource Language="x-generate" /> + </Resources> + <Applications> + <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="TdApp.App"> + <uap:VisualElements DisplayName="TdApp" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="TdApp" BackgroundColor="transparent"> + <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"> + </uap:DefaultTile> + <uap:SplashScreen Image="Assets\SplashScreen.png" /> + </uap:VisualElements> + </Application> + </Applications> + <Capabilities> + <Capability Name="internetClient" /> + </Capabilities> +</Package>
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Properties/AssemblyInfo.cs b/protocols/Telegram/tdlib/td/example/uwp/app/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..99e2b83bea --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("App2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("App2")] +[assembly: AssemblyCopyright("Copyright © 2015-2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)]
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/Properties/Default.rd.xml b/protocols/Telegram/tdlib/td/example/uwp/app/Properties/Default.rd.xml new file mode 100644 index 0000000000..479eb7669d --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/Properties/Default.rd.xml @@ -0,0 +1,31 @@ +<!-- + This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most + developers. However, you can modify these parameters to modify the behavior of the .NET Native + optimizer. + + Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919 + + To fully enable reflection for App1.MyClass and all of its public/private members + <Type Name="App1.MyClass" Dynamic="Required All"/> + + To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32 + <TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" /> + + Using the Namespace directive to apply reflection policy to all the types in a particular namespace + <Namespace Name="DataClasses.ViewModels" Seralize="All" /> +--> + +<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> + <Application> + <!-- + An Assembly element with Name="*Application*" applies to all assemblies in + the application package. The asterisks are not wildcards. + --> + <Assembly Name="*Application*" Dynamic="Required All" /> + + + <!-- Add your application specific runtime directives here. --> + + + </Application> +</Directives>
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/TdApp.csproj b/protocols/Telegram/tdlib/td/example/uwp/app/TdApp.csproj new file mode 100644 index 0000000000..b3d1d944d1 --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/TdApp.csproj @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProjectGuid>{0B971A4C-EC00-4FED-BCC2-FCD03B78D644}</ProjectGuid> + <OutputType>AppContainerExe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>TdApp</RootNamespace> + <AssemblyName>TdApp</AssemblyName> + <DefaultLanguage>en-US</DefaultLanguage> + <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> + <TargetPlatformVersion>10.0.10586.0</TargetPlatformVersion> + <TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion> + <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <PackageCertificateKeyFile>TdApp_TemporaryKey.pfx</PackageCertificateKeyFile> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <NoWarn>;2008</NoWarn> + <DebugType>full</DebugType> + <PlatformTarget>x86</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> + <OutputPath>bin\x86\Release\</OutputPath> + <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <Optimize>true</Optimize> + <NoWarn>;2008</NoWarn> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\ARM\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <NoWarn>;2008</NoWarn> + <DebugType>full</DebugType> + <PlatformTarget>ARM</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'"> + <OutputPath>bin\ARM\Release\</OutputPath> + <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <Optimize>true</Optimize> + <NoWarn>;2008</NoWarn> + <DebugType>pdbonly</DebugType> + <PlatformTarget>ARM</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <NoWarn>;2008</NoWarn> + <DebugType>full</DebugType> + <PlatformTarget>x64</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> + <Optimize>true</Optimize> + <NoWarn>;2008</NoWarn> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <UseVSHostingProcess>false</UseVSHostingProcess> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>true</Prefer32Bit> + <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain> + </PropertyGroup> + <ItemGroup> + <!-- A reference to the entire .Net Framework and Windows SDK are automatically included --> + <Content Include="ApplicationInsights.config"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <None Include="project.json" /> + </ItemGroup> + <ItemGroup> + <Compile Include="App.xaml.cs"> + <DependentUpon>App.xaml</DependentUpon> + </Compile> + <Compile Include="MainPage.xaml.cs"> + <DependentUpon>MainPage.xaml</DependentUpon> + </Compile> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <AppxManifest Include="Package.appxmanifest"> + <SubType>Designer</SubType> + </AppxManifest> + <None Include="TdApp_TemporaryKey.pfx" /> + </ItemGroup> + <ItemGroup> + <Content Include="Properties\Default.rd.xml" /> + <Content Include="Assets\LockScreenLogo.scale-200.png" /> + <Content Include="Assets\SplashScreen.scale-200.png" /> + <Content Include="Assets\Square150x150Logo.scale-200.png" /> + <Content Include="Assets\Square44x44Logo.scale-200.png" /> + <Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" /> + <Content Include="Assets\StoreLogo.png" /> + <Content Include="Assets\Wide310x150Logo.scale-200.png" /> + </ItemGroup> + <ItemGroup> + <ApplicationDefinition Include="App.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </ApplicationDefinition> + <Page Include="MainPage.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + </ItemGroup> + <ItemGroup> + <SDKReference Include="Microsoft.VCLibs, Version=14.0"> + <Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name> + </SDKReference> + <SDKReference Include="Telegram.Td.UWP, Version=1.0"> + <Name>TDLib for Universal Windows Platform</Name> + </SDKReference> + </ItemGroup> + <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' "> + <VisualStudioVersion>14.0</VisualStudioVersion> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/TdApp_TemporaryKey.pfx b/protocols/Telegram/tdlib/td/example/uwp/app/TdApp_TemporaryKey.pfx Binary files differnew file mode 100644 index 0000000000..05ffbcabbc --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/TdApp_TemporaryKey.pfx diff --git a/protocols/Telegram/tdlib/td/example/uwp/app/project.json b/protocols/Telegram/tdlib/td/example/uwp/app/project.json new file mode 100644 index 0000000000..e3b2dba25f --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/app/project.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "Microsoft.ApplicationInsights": "1.0.0", + "Microsoft.ApplicationInsights.PersistenceChannel": "1.0.0", + "Microsoft.ApplicationInsights.WindowsApps": "1.0.0", + "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" + }, + "frameworks": { + "uap10.0": {} + }, + "runtimes": { + "win10-arm": {}, + "win10-arm-aot": {}, + "win10-x86": {}, + "win10-x86-aot": {}, + "win10-x64": {}, + "win10-x64-aot": {} + } +}
\ No newline at end of file diff --git a/protocols/Telegram/tdlib/td/example/uwp/build.ps1 b/protocols/Telegram/tdlib/td/example/uwp/build.ps1 new file mode 100644 index 0000000000..71156b0a2d --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/build.ps1 @@ -0,0 +1,142 @@ +param ( + [string]$vcpkg_root = $(throw "-vcpkg_root=<path to vcpkg> is required"), + [string]$arch = "", + [string]$mode = "all", + [string]$compress = "7z" +) +$ErrorActionPreference = "Stop" + +$vcpkg_root = Resolve-Path $vcpkg_root + +$vcpkg_cmake="${vcpkg_root}\scripts\buildsystems\vcpkg.cmake" +$arch_list = @( "x86", "x64", "arm" ) +if ($arch) { + $arch_list = @(, $arch) +} + +$td_root = Resolve-Path "../.." + +function CheckLastExitCode { + if ($LastExitCode -ne 0) { + $msg = @" +EXE RETURNED EXIT CODE $LastExitCode +CALLSTACK:$(Get-PSCallStack | Out-String) +"@ + throw $msg + } +} + +function clean { + Remove-Item build-* -Force -Recurse -ErrorAction SilentlyContinue +} + +function prepare { + New-Item -ItemType Directory -Force -Path build-native + + cd build-native + + cmake $td_root -DCMAKE_TOOLCHAIN_FILE="$vcpkg_cmake" -DTD_ENABLE_DOTNET=1 + CheckLastExitCode + cmake --build . --target prepare_cross_compiling + CheckLastExitCode + + cd .. +} + +function config { + New-Item -ItemType Directory -Force -Path build-uwp + cd build-uwp + + ForEach($arch in $arch_list) { + echo "Config Arch = [$arch]" + New-Item -ItemType Directory -Force -Path $arch + cd $arch + echo "${td_root}" + $fixed_arch = $arch + if ($arch -eq "x86") { + $fixed_arch = "win32" + } + cmake "$td_root" -A $fixed_arch -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_SYSTEM_NAME="WindowsStore" -DCMAKE_TOOLCHAIN_FILE="$vcpkg_cmake" -DTD_ENABLE_DOTNET=1 + CheckLastExitCode + cd .. + } + echo "done" + cd .. +} + +function build { + cd build-uwp + ForEach($arch in $arch_list) { + echo "Build Arch = [$arch]" + cd $arch + cmake --build . --config Release --target tddotnet + cmake --build . --config Debug --target tddotnet + cd .. + } + cd .. +} + +function export { + cd build-uwp + Remove-Item vsix -Force -Recurse -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Force -Path vsix + cp ../SDKManifest.xml vsix + cp ../extension.vsixmanifest vsix + cp '../`[Content_Types`].xml' vsix + cp ../LICENSE_1_0.txt vsix + + ForEach($arch in $arch_list) { + New-Item -ItemType Directory -Force -Path vsix/DesignTime/Debug/${arch} + New-Item -ItemType Directory -Force -Path vsix/DesignTime/Retail/${arch} + New-Item -ItemType Directory -Force -Path vsix/Redist/Debug/${arch} + New-Item -ItemType Directory -Force -Path vsix/Redist/Retail/${arch} + New-Item -ItemType Directory -Force -Path vsix/References/CommonConfiguration/${arch} + + cp ${arch}/Debug/* -include "LIBEAY*","SSLEAY*","zlib*" vsix/Redist/Debug/${arch}/ + cp ${arch}/Release/* -include "LIBEAY*","SSLEAY*","zlib*" vsix/Redist/Retail/${arch}/ + + cp ${arch}/Debug/* -filter "Telegram.Td.*" -include "*.lib" vsix/DesignTime/Debug/${arch}/ + cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.lib" vsix/DesignTime/Retail/${arch}/ + + cp ${arch}/Debug/* -filter "Telegram.Td.*" -include "*.pdb","*.dll" vsix/Redist/Debug/${arch}/ + cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.pdb","*.dll" vsix/Redist/Retail/${arch}/ + + cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.pri","*.winmd","*.xml" vsix/References/CommonConfiguration/${arch}/ + } + + cd vsix + + if ($compress -eq "zip") { + zip -r tdlib.vsix * + } elseif ($compress -eq "winrar") { + WinRAR.exe a -afzip -r -ep1 tdlib.vsix * + } else { + 7z.exe a -tzip -r tdlib.vsix * + } + cd .. +} + +function run { + Push-Location + Try { + if ($mode -eq "clean") { + clean + } + if (($mode -eq "prepare") -or ($mode -eq "all")) { + prepare + } + if (($mode -eq "config") -or ( $mode -eq "all")) { + config + } + if (($mode -eq "build") -or ($mode -eq "all")) { + build + } + if (($mode -eq "export") -or ($mode -eq "all")) { + export + } + } Finally { + Pop-Location + } +} + +run diff --git a/protocols/Telegram/tdlib/td/example/uwp/extension.vsixmanifest b/protocols/Telegram/tdlib/td/example/uwp/extension.vsixmanifest new file mode 100644 index 0000000000..cfcfdb57af --- /dev/null +++ b/protocols/Telegram/tdlib/td/example/uwp/extension.vsixmanifest @@ -0,0 +1,17 @@ +<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011"> + <Metadata> + <Identity Id="Telegram.Td.UWP" Version="1.2.0" Language="en-US" Publisher="Telegram LLC" /> + <DisplayName>TDLib for Universal Windows Platform</DisplayName> + <Description>TDLib is a library for building Telegram clients</Description> + <MoreInfo>https://core.telegram.org/tdlib</MoreInfo> + <Tags>Telegram, TDLib, library, client, API</Tags> + <License>LICENSE_1_0.txt</License> + <ReleaseNotes>https://github.com/tdlib/td/blob/master/CHANGELOG.md</ReleaseNotes> + </Metadata> + <Installation Scope="Global"> + <InstallationTarget Id="Microsoft.ExtensionSDK" TargetPlatformIdentifier="UAP" TargetPlatformVersion="v0.8.0.0" SdkName="Telegram.Td.UWP" SdkVersion="1.0" /> + </Installation> + <Assets> + <Asset Type="Microsoft.ExtensionSDK" Path="SDKManifest.xml" /> + </Assets> +</PackageManifest> |