diff --git a/.gitattributes b/.gitattributes index 2ed11bcb1..ecb5de549 100644 --- a/.gitattributes +++ b/.gitattributes @@ -24,6 +24,9 @@ *.csproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent *.sln text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent *.xml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent +*.rb text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent +*.lock text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent +*.go text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent sqlite/sqlite/* linguist-vendored diff --git a/README.md b/README.md index 2fe8a7c6f..dbe0cff42 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele - [Installing dependencies](#installing-dependencies) - [Using in CMake C++ projects](#using-cxx) - [Using in Java projects](#using-java) +- [Using in .NET projects](#using-dotnet) - [Using with other programming languages](#using-json) - [License](#license) @@ -19,7 +20,7 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele `TDLib` has many advantages. Notably `TDLib` is: * **Cross-platform**: `TDLib` can be used on Android, iOS, Windows, macOS, Linux, Windows Phone, WebAssembly, watchOS, tvOS, Tizen, Cygwin. It should also work on other *nix systems with or without minimal effort. -* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally it already has native Java (using JNI) bindings and .NET (using C++/CLI and C++/CX) bindings. +* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally it already has native Java (using `JNI`) bindings and .NET (using `C++/CLI` and `C++/CX`) bindings. * **Easy to use**: `TDLib` takes care of all network implementation details, encryption and local data storage. * **High-performance**: in the [Telegram Bot API](https://core.telegram.org/bots/api), each `TDLib` instance handles more than 19000 active bots simultaneously. * **Well-documented**: all `TDLib` API methods and public interfaces are fully documented. @@ -117,10 +118,18 @@ See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example ## Using in Java projects -`TDLib` provides native Java interface through JNI. +`TDLib` provides native Java interface through JNI. To enable it, specify option `-DTD_ENABLE_JNI=ON` to CMake. See [example/java](https://github.com/tdlib/td/tree/master/example/java) for example of using `TDLib` from Java and detailed build and usage instructions. + +## Using in .NET projects +`TDLib` provides native .NET interface through `C++/CLI` and `C++/CX`. To enable it, specify option `-DTD_ENABLE_DOTNET=ON` to CMake. +.NET Core doesn't support `C++/CLI`, so if .NET Core is used, then `TDLib` JSON interface should be used through P/Invoke instead. + +See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions. +See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform". + ## Using from other programming languages `TDLib` provides efficient native C++, Java, and .NET interfaces. diff --git a/example/csharp/.gitignore b/example/csharp/.gitignore new file mode 100644 index 000000000..5266ecccc --- /dev/null +++ b/example/csharp/.gitignore @@ -0,0 +1,5 @@ +.vs/ +bin/ +obj/ +project.lock.json +TdExample.csproj.user diff --git a/example/csharp/README.md b/example/csharp/README.md new file mode 100644 index 000000000..b66f124c4 --- /dev/null +++ b/example/csharp/README.md @@ -0,0 +1,29 @@ +# 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 +``` +* 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 /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. diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs new file mode 100644 index 000000000..e1d2e3c67 --- /dev/null +++ b/example/csharp/TdExample.cs @@ -0,0 +1,264 @@ +// +// 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; + +/** + * Example class for TDLib usage from C#. + */ +namespace TdExample +{ + 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 - GetChat, me - GetMe, sm - SendMessage, lo - LogOut, q - Quit): "; + + 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) + { + Console.WriteLine(); + Console.WriteLine(str); + if (_haveAuthorization) + { + Console.Write(_commandsLine); + } + } + + private static string ReadLine(string str) + { + Console.WriteLine(); + Console.Write(str); + return Console.ReadLine(); + } + + 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 + { + // ok result is already received through UpdateAuthorizationState, nothing to do + } + } + } + } +} diff --git a/example/csharp/TdExample.csproj b/example/csharp/TdExample.csproj new file mode 100644 index 000000000..ea2ad5653 --- /dev/null +++ b/example/csharp/TdExample.csproj @@ -0,0 +1,85 @@ + + + + Debug + x86 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7} + Exe + false + ConsoleApplication + v4.0 + Client + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + + + TdExample + + + + + + + + + + + False + build\Debug\Telegram.Td.dll + build\Release\Telegram.Td.dll + + + + + + + + LIBEAY32.dll + PreserveNewest + + + SSLEAY32.dll + PreserveNewest + + + zlibd1.dll + PreserveNewest + + + + + LIBEAY32.dll + PreserveNewest + + + SSLEAY32.dll + PreserveNewest + + + zlib1.dll + PreserveNewest + + + + + + + \ No newline at end of file diff --git a/example/csharp/TdExample.sln b/example/csharp/TdExample.sln new file mode 100644 index 000000000..7ebc73021 --- /dev/null +++ b/example/csharp/TdExample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TdExample", "TdExample.csproj", "{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.ActiveCfg = Debug|x86 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.Build.0 = Debug|x86 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.ActiveCfg = Release|x86 + {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/example/uwp/README.md b/example/uwp/README.md index b129d1bbc..cfbda0ff5 100644 --- a/example/uwp/README.md +++ b/example/uwp/README.md @@ -25,7 +25,3 @@ 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/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index e9ab1582d..6fa83e245 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -138,7 +138,7 @@ if (NOT CMAKE_CROSSCOMPILING) add_custom_target(generate_dotnet_api WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND td_generate_dotnet_api ${TL_TD_API_TLO} - COMMENT "Generate .Net API files" + COMMENT "Generate .NET API files" DEPENDS td_generate_dotnet_api ${TL_TD_API_TLO} ) endif()