From 854b61ada06abe4739c3a5cc6f04e74714754e24 Mon Sep 17 00:00:00 2001 From: annelin Date: Sat, 6 Apr 2019 09:12:54 +0300 Subject: [PATCH] Initial release Implemented authorization and simple messages --- inc/logger.rb | 12 +++++++ inc/telegram.rb | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ inc/xmpp.rb | 86 ++++++++++++++++++++++++++++++++++++++++++++++ main.rb | 7 ++++ 4 files changed, 195 insertions(+) create mode 100644 inc/logger.rb create mode 100644 inc/telegram.rb create mode 100644 inc/xmpp.rb create mode 100644 main.rb diff --git a/inc/logger.rb b/inc/logger.rb new file mode 100644 index 0000000..097438b --- /dev/null +++ b/inc/logger.rb @@ -0,0 +1,12 @@ +require 'logger' + +class Logging + def self.log + if @logger.nil? + @logger = Logger.new STDOUT + @logger.level = Logger::DEBUG + @logger.datetime_format = '%Y-%m-%d %H:%M:%S ' + end + @logger + end +end diff --git a/inc/telegram.rb b/inc/telegram.rb new file mode 100644 index 0000000..ac6ad47 --- /dev/null +++ b/inc/telegram.rb @@ -0,0 +1,90 @@ +require 'tdlib-ruby' + +class TelegramClient + + # tdlib configuration, shared within all instances # + def self.configure(params) + TD.configure do |config| + config.lib_path = params[:lib_path] || 'lib/' + config.client.api_id = params[:api_id] || 430850 + config.client.api_hash = params[:api_hash] || '3d3cfcbd30d0805f757c5fc521004861' + end + TD::Api.set_log_verbosity_level(params[:verbose] || 1) + end + + # instance initialization # + def initialize(xmpp, login) + + @xmpp = xmpp + @login = login + + Logging.log.info '[TelegramClient] [%s] Initializing..' % @login + + @client = TD::Client.new(database_directory: 'sessions/' + @login, files_directory: 'sessions/' + @login + '/files/') # create telegram client instance + @client.on(TD::Types::Update::AuthorizationState) do |update| self.auth_handler(update) end # register auth update handler + @client.on(TD::Types::Update::NewMessage) do |update| self.message_handler(update) end # register new message update handler + @client.connect # + + # we will check new messages in queue and auth data in forever loop # + #begin + loop do + self.process_outgoing_msg(@xmpp.message_queue.pop) unless @xmpp.message_queue.empty? # found something in message queue + self.process_auth(:code, @xmpp.tg_auth_data[:code]) unless @xmpp.tg_auth_data[:code].nil? # found code in auth queue + self.process_auth(:password, @xmpp.tg_auth_data[:password]) unless @xmpp.tg_auth_data[:password].nil? # found 2fa password in auth queue + sleep 0.5 + end + #ensure + #Logging.log.info '[TelegramClient] Exitting gracefully...' + #@client.dispose + #end + end + + # authorization handler # + def auth_handler(update) + Logging.log.debug '[TelegramClient] [%s] Authorization state changed: %s' % [@login, update.authorization_state] + case update.authorization_state + + # auth stage 0: specify login # + when TD::Types::AuthorizationState::WaitPhoneNumber + Logging.log.debug '[TelegramClient] [%s] Logging in..' % @login + @client.set_authentication_phone_number(@login) + # auth stage 1: wait for authorization code # + when TD::Types::AuthorizationState::WaitCode + @xmpp.send_message(nil, 'Please, enter authorization code via /code 12345') + Logging.log.debug '[TelegramClient] [%s] Waiting for Authorization code..' % @login + # auth stage 2: wait for 2fa passphrase # + when TD::Types::AuthorizationState::WaitPassword + @xmpp.send_message(nil, 'Please, enter 2FA passphrase via /password 12345') + Logging.log.debug '[TelegramClient] [%s] Waiting for 2FA password..' % @login + # authorizatio successful # + when TD::Types::AuthorizationState::Ready + @xmpp.send_message(nil, 'Authorization successful.') + Logging.log.debug '[TelegramClient] [%s] Authorization successful.' % @login + end + end + + # message from telegram network handler # + def message_handler(update) + Logging.log.debug '[TelegramClient] [%s] Got NewMessage update <%s>' % [@login, update.message] + from = update.message.chat_id + text = update.message.content.text.text + @xmpp.send_message(from, text) if not update.message.is_outgoing + end + + ################################################## + + # processing authorization # + def process_auth(typ, data) + Logging.log.debug '[TelegramClient] [%s] Authorizing with <%s> in Telegram...' % [@login, typ.to_s] + @client.check_authentication_code(data) if typ == :code + @client.check_authentication_password(data) if typ == :password + @xmpp.tg_auth = {} # unset it to prevent extracting 2fa password from memory + end + + # processing outgoing message from queue # + def process_outgoing_msg(msg) + Logging.log.debug '[TelegramClient] [%s] Sending message to user/chat <%s> within Telegram network..' % [@login, msg[:to]] + message = TD::Types::InputMessageContent::Text.new(:text => { :text => msg[:text], :entities => []}, :disable_web_page_preview => false, :clear_draft => false ) + @client.send_message(msg[:to].to_i, message) + end +end diff --git a/inc/xmpp.rb b/inc/xmpp.rb new file mode 100644 index 0000000..69021dd --- /dev/null +++ b/inc/xmpp.rb @@ -0,0 +1,86 @@ +require 'xmpp4r' + +############################# +## XMPP Transport Class ##### +############################# +class XMPPComponent + attr_accessor :jid + + # transport initialization & connecting to XMPP server # + def connect(params) # :jid => transport_jid, :host => xmpp_server, :port => xmpp_component_port, :secret => xmpp_component_secret + Logging.log.info '[XMPP] Connecting...' + begin + @@transport = Jabber::Component.new( params[:jid] ) + @@transport.connect( params[:host], params[:port] ) + @@transport.auth( params[:secret] ) + @@transport.add_message_callback do |msg| msg.first_element_text('body') ? self.message_handler(msg) : nil end + @sessions = {} + Logging.log.info '[XMPP] Connection established' + Thread.stop() + rescue Exception => e + Logging.log.info '[XMPP] Connection failed (%s)' % e + exit 1 + end + end + + # new message to XMPP component # + def message_handler(msg) + Logging.log.debug '[XMPP] New message from [%s] to [%s]' % [msg.from, msg.to] + + return self.process_internal_command(msg.from.bare, msg.first_element_text('body') ) if msg.to == @@transport.jid # treat message as internal command if received as transport jid + return @sessions[msg.from.bare].queue_message(msg.to, msg.first_element_text('body')) if @sessions.key? msg.from.bare # queue message for processing session is active for jid from + end + + # process internal /command # + def process_internal_command(jfrom, body) + case body.split[0] # /command argument = [command, argument] + when '/help' # + when '/login' # Create new session + @sessions[jfrom] = XMPPSession.new(jfrom, body.split[1]) + when '/code', '/password' # Enter auth code / 2fa password + @sessions[jfrom].enter_auth_data(body.split[0], body.split[1]) + else # Unknown command + reply = Jabber::Message.new; reply.from, reply.to, reply.body, reply.type = @@transport.jid, jfrom, 'Unknown command', :chat + @@transport.send(reply) + end + end +end + +############################# +## XMPP Session Class ####### +############################# + +class XMPPSession < XMPPComponent + attr_accessor :user_jid, :tg_login, :tg_auth_data, :message_queue + + # start XMPP user session and Telegram client instance # + def initialize(jid, tg_login) + Logging.log.info "[XMPPSession] [%s] Starting Telegram session as [%s]" % [jid, tg_login] + @user_jid, @tg_login, @tg_auth_data, @message_queue = jid, tg_login, {code: nil, password: nil}, Queue.new() + @tg_thread = Thread.new{ TelegramClient.new(self, tg_login) } + end + + # send message to XMPP # + def send_message(from = nil, body = '') + Logging.log.info "[XMPPSession] [%s] Incoming message from Telegram network <- [%s].." % [@user_jid, from.to_s] + puts 1 + from = from.nil? ? @@transport.jid : from.to_s+'@'+@@transport.jid.to_s + puts 2 + reply = Jabber::Message.new; reply.from, reply.to, reply.body, reply.type = from, @user_jid, body, :chat + puts reply + @@transport.send(reply) + end + + # queue message (we will share this queue within :message_queue to Telegram client thread) # + def queue_message(to, text = '') + Logging.log.info "[XMPPSession] [%s] Queuying message to Telegram network -> [%s].." % [@user_jid, to] + @message_queue << {to: to.split('@')[0], text: text} + end + + # enter auth data (we will share this data within :tg_auth_data to Telegram client thread ) # + def enter_auth_data(typ, data) + Logging.log.info "[XMPPSession] [%s] Authorizing in Telegram with [%s]" % [@user_jid, typ] + @tg_auth_data[typ[1..8].to_sym] = data + end + +end diff --git a/main.rb b/main.rb new file mode 100644 index 0000000..b5685e0 --- /dev/null +++ b/main.rb @@ -0,0 +1,7 @@ +require_relative 'inc/logger' +require_relative 'inc/telegram' +require_relative 'inc/xmpp' + +Logging.log.info '[MAIN] Starting Zhabogram v0.o1...' +TelegramClient.configure(verbose: 2) +XMPPComponent.new().connect(host: 'localhost', port: '8899', jid: 'tlgrm2.rxtx.us', secret: '')