⬆️ ⬇️

tdlib-ruby: how to build a telegram client in ruby

image

One of the features of the Telegram messenger is the wide API capabilities (Bot API and Telegram API). The Telegram team went even further and released the TDLib (Telegram Database Library) library, which allows developing alternative Telegram clients and not thinking about low-level implementation details (networking, encryption, and local data storage).



TDLib works on Android, iOS, Windows, macOS, Linux, Windows Phone, WebAssembly, watchOS, tvOS, Tizen, Cygwin and other * nix systems, and also integrates with any programming language that supports C-functions.



In this article, we will look at using TDLib in Ruby and creating a gem for interacting with the JSON interface of the library.



Libtdjson connection



First we need a compiled TDLib. Assembly instructions can be read on the official website . From the compiled binaries we only need libtdjson. [So | dylib | dll].



To enable library functions in Ruby, you can use the Fiddle module from the standard library. Fiddle :: Importer provides convenient DSL for importing functions from dynamic libraries:



module Dl extend Fiddle::Importer dlload('libtdjson.so') extern 'void* td_json_client_create()' extern 'void* td_json_client_send(void*, char*)' extern 'char* td_json_client_receive(void*, double)' extern 'char* td_json_client_execute(void*, char*)' extern 'void td_set_log_verbosity_level(int)' extern 'void td_json_client_destroy(void*)' extern 'void td_set_log_file_path(char*)' end 


Now we can call TDLib functions:



 client = Dl.td_json_client_create Dl.td_json_client_send(client, '{"@type": "getAuthorizationState"}') 


Customer creation



TDLib is a fully asynchronous library (only a few functions can be called synchronously using td_json_client_execute ), so you need to work with it accordingly:



 Dl.td_json_client_send(client, '{"@type": "getAuthorizationState"}') timeout = 10 loop do update = Dl.td_json_client_receive(client, timeout) next if update.null? update = JSON.parse(update.to_s) if update['@type'] = 'updateAuthorizationState' p update break end end 


This is a working code, but not the most convenient. It is better to develop a wrapper for interacting with the library: with event handlers, callbacks, convenient configuration, and the ability not to write the boilerplate code with the initial authorization.



Next, consider the main functionality of gem tdlib-ruby (link at the end of the article).



Client initialization



Procedures for sending library parameters and verifying the encryption key are hidden inside. To get started, just create an instance of the client:



 client = TD::Client.new client.on_ready do |client| # some useful stuff end 


Sending "messages"



Messages are sent to tdlib asynchronously.



 client.broadcast('@type' => 'getAuthorizationState') 


It is possible to hang the callback handler.



 client.broadcast('@type' => 'getMe') do |update| p update end 


Subscribe to a specific type of update



 client.on('updateAuthorizationState') do |update| p update end 


When receiving updates from TDLib with the type `updateAuthorizationState ', the handler transmitted as a block will always be executed.



Synchronous message sending



Some methods (there are not many of them, and I haven’t met any of them yet) can return a response synchronously. For these cases, the execute method is provided.



 client.execute('@type' => 'someType') 


Work with asynchronous messages / updates in synchronous style



You just need to send a request and get the result? The asynchronous nature of TDLib does not allow this, but the necessary mechanism is implemented in gem.



 authorization_state = client.broadcast_and_receive('@type' => 'getAuthorizationState') 


Finally, I will give an example of a console authorization script:



 require 'tdlib-ruby' TD.configure do |config| config.lib_path = 'path_to_dir_containing_tdlibjson' config.client.api_id = your_api_id config.client.api_hash = 'your_api_hash' 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 


useful links



TDLib on Github

TDLib Documentation

Assembly instructions

Telegram Plugins for Redmine by Southbridge



gem tdlib-ruby



Author



Ruby-developer Southbridge Vladislav Yashin



')

Source: https://habr.com/ru/post/349504/



All Articles