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.
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"}')
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).
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
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
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.
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')
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
TDLib on Github
TDLib Documentation
Assembly instructions
Telegram Plugins for Redmine by Southbridge
Ruby-developer Southbridge Vladislav Yashin
Source: https://habr.com/ru/post/349504/