Release 0.4
[NEW] vCard support [UPD] improved overall stability, no longer use threads [UPD] no longer using tdlib messages and chats database — now correctly /leaving chats [UPD] removed /info command because of vCards support
This commit is contained in:
parent
ef144a3b83
commit
12191222b0
|
@ -4,7 +4,7 @@ telegram:
|
||||||
api_hash: '344583e45741c457fe1862106095a5eb'
|
api_hash: '344583e45741c457fe1862106095a5eb'
|
||||||
verbosity: 2
|
verbosity: 2
|
||||||
useragent: 'Zhabogram XMPP Gateway'
|
useragent: 'Zhabogram XMPP Gateway'
|
||||||
version: '0.10'
|
version: '0.4'
|
||||||
use_test_dc: false
|
use_test_dc: false
|
||||||
loglevel: 0
|
loglevel: 0
|
||||||
content_path: '/var/www/tg_media'
|
content_path: '/var/www/tg_media'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'tdlib-ruby'
|
require 'tdlib-ruby'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
|
require 'base64'
|
||||||
|
|
||||||
class TelegramClient
|
class TelegramClient
|
||||||
|
|
||||||
|
@ -11,12 +12,16 @@ class TelegramClient
|
||||||
@@content_upload_prefix = params["content_upload_prefix"] || 'https://localhost/upload/'
|
@@content_upload_prefix = params["content_upload_prefix"] || 'https://localhost/upload/'
|
||||||
TD.configure do |config|
|
TD.configure do |config|
|
||||||
config.lib_path = params['path'] || 'lib/' # we hope it's here
|
config.lib_path = params['path'] || 'lib/' # we hope it's here
|
||||||
config.client.api_id = params['api_id'] || '17349' # desktop telegram app
|
config.client.api_id = params['api_id'] || '50322' # telegram app. from debian repositories
|
||||||
config.client.api_hash = params['api_hash'] || '344583e45741c457fe1862106095a5eb' # desktop telegram app
|
config.client.api_hash = params['api_hash'] || '9ff1a639196c0779c86dd661af8522ba' # telegram app. from debian repositories
|
||||||
config.client.device_model = params['useragent'] || 'Zhabogram XMPP Gateway'
|
config.client.device_model = params['useragent'] || 'Zhabogram'
|
||||||
config.client.application_version = params['version'] || '-1.0' # hmm...
|
config.client.application_version = params['version'] || '1.0' # hmm...
|
||||||
config.client.use_test_dc = params['use_test_dc'] || false
|
config.client.use_test_dc = params['use_test_dc'] || false
|
||||||
config.client.system_version = '42' # I think I have permission to hardcode The Ultimate Question of Life, the Universe, and Everything?..
|
config.client.system_version = '42' # I think I have permission to hardcode The Ultimate Question of Life, the Universe, and Everything?..
|
||||||
|
config.client.use_file_database = false # wow
|
||||||
|
config.client.use_message_database = false # such library
|
||||||
|
config.client.use_chat_info_database = false # much options
|
||||||
|
config.client.enable_storage_optimizer = false # ...
|
||||||
end
|
end
|
||||||
TD::Api.set_log_verbosity_level(params['verbosity'] || 1)
|
TD::Api.set_log_verbosity_level(params['verbosity'] || 1)
|
||||||
end
|
end
|
||||||
|
@ -28,7 +33,7 @@ class TelegramClient
|
||||||
@logger = Logger.new(STDOUT); @logger.level = @@loglevel; @logger.progname = '[TelegramClient: %s/%s]' % [xmpp.user_jid, login] # create logger
|
@logger = Logger.new(STDOUT); @logger.level = @@loglevel; @logger.progname = '[TelegramClient: %s/%s]' % [xmpp.user_jid, login] # create logger
|
||||||
@xmpp = xmpp # our XMPP user session. we will send messages back to Jabber through this instance.
|
@xmpp = xmpp # our XMPP user session. we will send messages back to Jabber through this instance.
|
||||||
@login = login # store tg login
|
@login = login # store tg login
|
||||||
@cache = {chats: {}, users: {}, unread_msg: {} } # we will store our cache here
|
@cache = {chats: {}, users: {}, users_fi: {}, unread_msg: {} } # we will store our cache here
|
||||||
@files_dir = File.dirname(__FILE__) + '/../sessions/' + @xmpp.user_jid + '/files/'
|
@files_dir = File.dirname(__FILE__) + '/../sessions/' + @xmpp.user_jid + '/files/'
|
||||||
|
|
||||||
# spawn telegram client and specify callback handlers
|
# spawn telegram client and specify callback handlers
|
||||||
|
@ -42,21 +47,8 @@ class TelegramClient
|
||||||
@client.on(TD::Types::Update::NewChat) do |update| self.new_chat_handler(update) end # register new chat handler
|
@client.on(TD::Types::Update::NewChat) do |update| self.new_chat_handler(update) end # register new chat handler
|
||||||
@client.on(TD::Types::Update::User) do |update| self.user_handler(update) end # new user update?
|
@client.on(TD::Types::Update::User) do |update| self.user_handler(update) end # new user update?
|
||||||
@client.on(TD::Types::Update::UserStatus) do |update| self.status_update_handler(update) end # register status handler
|
@client.on(TD::Types::Update::UserStatus) do |update| self.status_update_handler(update) end # register status handler
|
||||||
@client.connect #
|
@client.connect
|
||||||
|
|
||||||
# we will check for outgoing messages in a queue and/or auth data from XMPP thread while XMPP indicates that service is online #
|
|
||||||
begin
|
|
||||||
while not @xmpp.online? === false do
|
|
||||||
self.process_outgoing_msg(@xmpp.message_queue.pop) unless @xmpp.message_queue.empty? # found something in message queue
|
|
||||||
self.process_auth(@xmpp.auth_data.shift) unless @xmpp.auth_data.empty? # found something in auth queue
|
|
||||||
sleep 0.1
|
|
||||||
end
|
|
||||||
rescue Exception => e
|
|
||||||
@logger.error 'Unexcepted exception! %s' % e.to_s
|
|
||||||
ensure
|
|
||||||
@logger.info 'Exitting gracefully...'
|
|
||||||
@client.dispose
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
|
@ -75,22 +67,26 @@ class TelegramClient
|
||||||
# auth stage 1: wait for authorization code #
|
# auth stage 1: wait for authorization code #
|
||||||
when TD::Types::AuthorizationState::WaitCode
|
when TD::Types::AuthorizationState::WaitCode
|
||||||
@logger.info 'Waiting for authorization code..'
|
@logger.info 'Waiting for authorization code..'
|
||||||
@xmpp.send_message(nil, 'Please, enter authorization code via /code 12345')
|
@xmpp.incoming_message(nil, 'Please, enter authorization code via /code 12345')
|
||||||
# auth stage 2: wait for 2fa passphrase #
|
# auth stage 2: wait for 2fa passphrase #
|
||||||
when TD::Types::AuthorizationState::WaitPassword
|
when TD::Types::AuthorizationState::WaitPassword
|
||||||
@logger.info 'Waiting for 2FA password..'
|
@logger.info 'Waiting for 2FA password..'
|
||||||
@xmpp.send_message(nil, 'Please, enter 2FA passphrase via /password 12345')
|
@xmpp.incoming_message(nil, 'Please, enter 2FA passphrase via /password 12345')
|
||||||
# authorization successful -- indicate that client is online and retrieve contact list #
|
# authorization successful -- indicate that client is online and retrieve contact list #
|
||||||
when TD::Types::AuthorizationState::Ready
|
when TD::Types::AuthorizationState::Ready
|
||||||
@logger.info 'Authorization successful!'
|
@logger.info 'Authorization successful!'
|
||||||
@xmpp.online!
|
|
||||||
@client.get_me().then { |user| @me = user }.wait
|
@client.get_me().then { |user| @me = user }.wait
|
||||||
@client.get_chats(limit=9999).wait
|
@client.get_chats(limit=9999)
|
||||||
@logger.info "Contact list updating finished"
|
@logger.info "Contact list updating finished"
|
||||||
self.sync_roster()
|
@xmpp.online!
|
||||||
|
# closing session: sent offline presences to XMPP user #
|
||||||
|
when TD::Types::AuthorizationState::Closing
|
||||||
|
@logger.info 'Closing session..'
|
||||||
|
self.disconnect()
|
||||||
|
# session closed gracefully
|
||||||
when TD::Types::AuthorizationState::Closed
|
when TD::Types::AuthorizationState::Closed
|
||||||
@logger.info 'Session closed.'
|
@logger.info 'Session closed.'
|
||||||
@xmpp.offline!
|
self.disconnect()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -156,7 +152,7 @@ class TelegramClient
|
||||||
|
|
||||||
# send and add message id to unreads
|
# send and add message id to unreads
|
||||||
@cache[:unread_msg][update.message.chat_id] = update.message.id
|
@cache[:unread_msg][update.message.chat_id] = update.message.id
|
||||||
@xmpp.send_message(update.message.chat_id.to_s, text)
|
@xmpp.incoming_message(update.message.chat_id.to_s, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
# new chat update -- when tg client discovers new chat #
|
# new chat update -- when tg client discovers new chat #
|
||||||
|
@ -180,7 +176,7 @@ class TelegramClient
|
||||||
|
|
||||||
# formatting
|
# formatting
|
||||||
text = "[MSG %s EDIT] %s" % [update.message_id.to_s, update.new_content.text.text.to_s]
|
text = "[MSG %s EDIT] %s" % [update.message_id.to_s, update.new_content.text.text.to_s]
|
||||||
@xmpp.send_message(update.chat_id.to_s, text)
|
@xmpp.incoming_message(update.chat_id.to_s, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
# deleted msg #
|
# deleted msg #
|
||||||
|
@ -189,7 +185,7 @@ class TelegramClient
|
||||||
@logger.debug update.to_json
|
@logger.debug update.to_json
|
||||||
return if not update.is_permanent
|
return if not update.is_permanent
|
||||||
text = "[MSG ID %s DELETE]" % update.message_ids.join(',')
|
text = "[MSG ID %s DELETE]" % update.message_ids.join(',')
|
||||||
@xmpp.send_message(update.chat_id.to_s, text)
|
@xmpp.incoming_message(update.chat_id.to_s, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
# file msg -- symlink to download path #
|
# file msg -- symlink to download path #
|
||||||
|
@ -198,7 +194,7 @@ class TelegramClient
|
||||||
@logger.debug update.to_json
|
@logger.debug update.to_json
|
||||||
if update.file.local.is_downloading_completed then
|
if update.file.local.is_downloading_completed then
|
||||||
fname = update.file.local.path.to_s
|
fname = update.file.local.path.to_s
|
||||||
target = "%s/%s%s" % [@@content_path, Digest::SHA256.hexdigest("Current user = %s, File ID = %s" % [@tg_login.to_s, update.file.remote.id]), File.extname(fname)]
|
target = "%s/%s%s" % [@@content_path, Digest::SHA256.hexdigest(update.file.remote.id), File.extname(fname)]
|
||||||
@logger.debug 'Downloading of <%s> completed! Link to <%s>' % [fname, target]
|
@logger.debug 'Downloading of <%s> completed! Link to <%s>' % [fname, target]
|
||||||
File.symlink(fname, target)
|
File.symlink(fname, target)
|
||||||
end
|
end
|
||||||
|
@ -218,10 +214,10 @@ class TelegramClient
|
||||||
###########################################
|
###########################################
|
||||||
|
|
||||||
# processing authorization #
|
# processing authorization #
|
||||||
def process_auth(auth_data)
|
def process_auth(typ, auth_data)
|
||||||
@logger.debug 'check_authorization :%s..' % auth_data[0]
|
@logger.debug 'check_authorization with %s..' % typ
|
||||||
@client.check_authentication_code(auth_data[1]) if auth_data[0] == :code
|
@client.check_authentication_code(auth_data) if typ == '/code'
|
||||||
@client.check_authentication_password(auth_data[1]) if auth_data[0] == :password
|
@client.check_authentication_password(auth_data) if typ == '/password'
|
||||||
end
|
end
|
||||||
|
|
||||||
# /command #
|
# /command #
|
||||||
|
@ -234,8 +230,6 @@ class TelegramClient
|
||||||
@client.search_public_chat(splitted[1][1..-1]).then {|chat| resolved = chat}.wait if splitted[1] and splitted[1][0] == '@'
|
@client.search_public_chat(splitted[1][1..-1]).then {|chat| resolved = chat}.wait if splitted[1] and splitted[1][0] == '@'
|
||||||
|
|
||||||
case splitted[0]
|
case splitted[0]
|
||||||
when '/info' # retrieve some information by link, @username or username
|
|
||||||
response = "Contact id: %s\nContact name: %s\nContact type: %s" % [resolved.id.to_s, resolved.title.to_s, resolved.type.class.to_s] if resolved
|
|
||||||
when '/add' # open new private chat by its id
|
when '/add' # open new private chat by its id
|
||||||
chat = (resolved) ? resolved.id : splitted[1].to_i
|
chat = (resolved) ? resolved.id : splitted[1].to_i
|
||||||
@client.create_private_chat(chat).wait
|
@client.create_private_chat(chat).wait
|
||||||
|
@ -258,8 +252,8 @@ class TelegramClient
|
||||||
@client.close_chat(chat_id).wait
|
@client.close_chat(chat_id).wait
|
||||||
@client.leave_chat(chat_id).wait
|
@client.leave_chat(chat_id).wait
|
||||||
@client.delete_chat_history(chat_id, true).wait
|
@client.delete_chat_history(chat_id, true).wait
|
||||||
@xmpp.presence_update(chat_id, :unsubscribed)
|
@xmpp.presence(chat_id, :unsubscribed)
|
||||||
@xmpp.presence_update(chat_id, :unavailable)
|
@xmpp.presence(chat_id, :unavailable)
|
||||||
@cache[:chats].delete(chat_id)
|
@cache[:chats].delete(chat_id)
|
||||||
when '/sed' # sed-like edit
|
when '/sed' # sed-like edit
|
||||||
sed = splitted[1].split('/')
|
sed = splitted[1].split('/')
|
||||||
|
@ -278,7 +272,6 @@ class TelegramClient
|
||||||
/s/mitsake/mistake/ — Edit last message
|
/s/mitsake/mistake/ — Edit last message
|
||||||
/d — Delete last message
|
/d — Delete last message
|
||||||
|
|
||||||
/info @username — Search public chat or user
|
|
||||||
/add @username or id — Creates conversation with specified user
|
/add @username or id — Creates conversation with specified user
|
||||||
/join chat_link or id — Joins chat by its link or id
|
/join chat_link or id — Joins chat by its link or id
|
||||||
/invite @username — Invites @username to current chat
|
/invite @username — Invites @username to current chat
|
||||||
|
@ -291,13 +284,12 @@ class TelegramClient
|
||||||
'
|
'
|
||||||
end
|
end
|
||||||
|
|
||||||
@xmpp.send_message(chat_id, response) if response
|
@xmpp.incoming_message(chat_id, response) if response
|
||||||
end
|
end
|
||||||
|
|
||||||
# processing outgoing message from queue #
|
# processing outgoing message from queue #
|
||||||
def process_outgoing_msg(msg)
|
def process_outgoing_msg(chat_id, text)
|
||||||
@logger.debug 'Sending message to user/chat <%s> within Telegram network..' % msg[:to]
|
@logger.debug 'Sending message to user/chat <%s> within Telegram network..' % chat_id.to_s
|
||||||
chat_id, text, reply_to = msg[:to].to_i, msg[:text], 0
|
|
||||||
|
|
||||||
# processing /commands #
|
# processing /commands #
|
||||||
return self.process_command(chat_id, text) if text[0] == '/'
|
return self.process_command(chat_id, text) if text[0] == '/'
|
||||||
|
@ -307,6 +299,8 @@ class TelegramClient
|
||||||
splitted = text.split("\n")
|
splitted = text.split("\n")
|
||||||
reply_to = splitted[0].scan(/\d/).join('') || 0
|
reply_to = splitted[0].scan(/\d/).join('') || 0
|
||||||
text = splitted.drop(1).join("\n") if reply_to != 0
|
text = splitted.drop(1).join("\n") if reply_to != 0
|
||||||
|
else
|
||||||
|
reply_to = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# handling files received from xmpp #
|
# handling files received from xmpp #
|
||||||
|
@ -328,15 +322,11 @@ class TelegramClient
|
||||||
# fullfil cache.. pasha durov, privet. #
|
# fullfil cache.. pasha durov, privet. #
|
||||||
@client.get_chat(chat_id).then { |chat|
|
@client.get_chat(chat_id).then { |chat|
|
||||||
@cache[:chats][chat_id] = chat # cache chat
|
@cache[:chats][chat_id] = chat # cache chat
|
||||||
self.process_user_info(chat.type.user_id) if chat.type.instance_of? TD::Types::ChatType::Private # cache user if it is private chat
|
@client.download_file(chat.photo.small.id) if chat.photo # download userpic
|
||||||
|
@xmpp.presence(chat_id.to_s, :subscribe, nil, nil, @cache[:chats][chat_id].title.to_s) # send subscription request
|
||||||
|
@xmpp.presence(chat_id.to_s, nil, :chat, nil, @cache[:chats][chat_id].title.to_s) if chat.type.instance_of? TD::Types::ChatType::BasicGroup orchat.type.instance_of? TD::Types::ChatType::Supergroup # send :chat status if its group/supergroup
|
||||||
|
self.process_user_info(chat.type.user_id) if chat.type.instance_of? TD::Types::ChatType::Private # process user if its a private chat
|
||||||
}.wait
|
}.wait
|
||||||
|
|
||||||
# send to roster #
|
|
||||||
if @cache[:chats].key? chat_id
|
|
||||||
@logger.debug "Sending presence to roster.."
|
|
||||||
@xmpp.presence_update(chat_id.to_s, :subscribe, nil, nil, @cache[:chats][chat_id].title.to_s) # send subscription request
|
|
||||||
@xmpp.presence_update(chat_id.to_s, nil, :chat, nil, @cache[:chats][chat_id].title.to_s) if chat_id < 0 # send :chat status if its group/supergroup
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# update user info in cache and sync status to roster if needed #
|
# update user info in cache and sync status to roster if needed #
|
||||||
|
@ -346,6 +336,9 @@ class TelegramClient
|
||||||
@cache[:users][user_id] = user # add to cache
|
@cache[:users][user_id] = user # add to cache
|
||||||
self.process_status_update(user_id, user.status) # status update
|
self.process_status_update(user_id, user.status) # status update
|
||||||
}.wait
|
}.wait
|
||||||
|
@client.get_user_full_info(user_id).then{ |user_info|
|
||||||
|
@cache[:users_fi][user_id] = user_info # here is user "bio"
|
||||||
|
}.wait
|
||||||
end
|
end
|
||||||
|
|
||||||
# convert telegram status to XMPP one
|
# convert telegram status to XMPP one
|
||||||
|
@ -369,7 +362,41 @@ class TelegramClient
|
||||||
xmpp_show = :unavailable
|
xmpp_show = :unavailable
|
||||||
xmpp_status = "Last seen last month"
|
xmpp_status = "Last seen last month"
|
||||||
end
|
end
|
||||||
@xmpp.presence_update(user_id.to_s, nil, xmpp_show, xmpp_status)
|
@xmpp.presence(user_id.to_s, nil, xmpp_show, xmpp_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
# get contact information (for vcard).
|
||||||
|
def get_contact_info(chat_id)
|
||||||
|
return if not @cache[:chats].key? chat_id # no such chat #
|
||||||
|
|
||||||
|
username, firstname, lastname, phone, bio, userpic = nil
|
||||||
|
title = @cache[:chats][chat_id].title # <FN>
|
||||||
|
|
||||||
|
# user information
|
||||||
|
if @cache[:users].key? chat_id then # its an user
|
||||||
|
firstname = @cache[:users][chat_id].first_name # <N/GIVEN>
|
||||||
|
lastname = @cache[:users][chat_id].last_name # <N/FAMILY>
|
||||||
|
username = @cache[:users][chat_id].username # <NICKNAME>
|
||||||
|
phone = @cache[:users][chat_id].phone_number # <TEL>
|
||||||
|
bio = @cache[:users_fi][chat_id].bio if @cache[:users_fi].key? chat_id # <DESC>
|
||||||
|
end
|
||||||
|
|
||||||
|
# userpic #
|
||||||
|
if @cache[:chats][chat_id].photo then # we have userpic
|
||||||
|
userpic = self.format_content_link(@cache[:chats][chat_id].photo.small.remote.id, 'image.jpg', true)
|
||||||
|
userpic = Base64.encode64(IO.binread(userpic)) if File.exist? userpic
|
||||||
|
end
|
||||||
|
|
||||||
|
# ..
|
||||||
|
return title, username, firstname, lastname, phone, bio, userpic
|
||||||
|
end
|
||||||
|
|
||||||
|
# graceful disconnect
|
||||||
|
def disconnect(logout)
|
||||||
|
@logger.info 'Disconnect request received..'
|
||||||
|
@cache[:chats].each_key do |chat_id| @xmpp.presence(chat_id.to_s, :unavailable) end # send offline presences
|
||||||
|
(logout) ? @client.log_out : @client.dispose # logout if needed
|
||||||
|
@xmpp.offline!
|
||||||
end
|
end
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
|
@ -386,8 +413,9 @@ class TelegramClient
|
||||||
end
|
end
|
||||||
|
|
||||||
# format content link #
|
# format content link #
|
||||||
def format_content_link(file_id, fname)
|
def format_content_link(file_id, fname, local = false)
|
||||||
path = "%s/%s%s" % [@@content_link, Digest::SHA256.hexdigest("Current user = %s, File ID = %s" % [@tg_login.to_s, file_id.to_s]).to_s, File.extname(fname)]
|
prefix = (local) ? @@content_path : @@content_link
|
||||||
|
path = "%s/%s%s" % [prefix, Digest::SHA256.hexdigest(file_id), File.extname(fname)]
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
require 'sqlite3'
|
require 'sqlite3'
|
||||||
require 'fileutils'
|
|
||||||
require 'xmpp4r'
|
require 'xmpp4r'
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
|
@ -9,6 +8,7 @@ require 'xmpp4r'
|
||||||
/login <telegram_login> — Connect to Telegram network
|
/login <telegram_login> — Connect to Telegram network
|
||||||
/code 12345 — Enter confirmation code
|
/code 12345 — Enter confirmation code
|
||||||
/password secret — Enter 2FA password
|
/password secret — Enter 2FA password
|
||||||
|
/connect — Connect to Telegram network if have active session
|
||||||
/disconnect — Disconnect from Telegram network
|
/disconnect — Disconnect from Telegram network
|
||||||
/logout — Disconnect from Telegram network and forget session
|
/logout — Disconnect from Telegram network and forget session
|
||||||
'
|
'
|
||||||
|
@ -59,7 +59,7 @@ class XMPPComponent
|
||||||
@@transport.auth( @config[:secret] )
|
@@transport.auth( @config[:secret] )
|
||||||
@@transport.add_message_callback do |msg| msg.first_element_text('body') ? self.message_handler(msg) : nil end
|
@@transport.add_message_callback do |msg| msg.first_element_text('body') ? self.message_handler(msg) : nil end
|
||||||
@@transport.add_presence_callback do |presence| self.presence_handler(presence) end
|
@@transport.add_presence_callback do |presence| self.presence_handler(presence) end
|
||||||
#@@transport.add_iq_callback do |iq| self.iq_handler(iq) end
|
@@transport.add_iq_callback do |iq| self.iq_handler(iq) end
|
||||||
@logger.info "Connection established"
|
@logger.info "Connection established"
|
||||||
self.load_db()
|
self.load_db()
|
||||||
@logger.info 'Found %s sessions in database.' % @sessions.count
|
@logger.info 'Found %s sessions in database.' % @sessions.count
|
||||||
|
@ -88,20 +88,31 @@ class XMPPComponent
|
||||||
def message_handler(msg)
|
def message_handler(msg)
|
||||||
@logger.info 'New message from [%s] to [%s]' % [msg.from, msg.to]
|
@logger.info 'New message from [%s] to [%s]' % [msg.from, msg.to]
|
||||||
return self.process_internal_command(msg.from.bare.to_s, msg.first_element_text('body') ) if msg.to == @@transport.jid # treat message as internal command if received as transport jid
|
return self.process_internal_command(msg.from.bare.to_s, 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.to_s].queue_message(msg.to.to_s, msg.first_element_text('body')) if @sessions.key? msg.from.bare.to_s and @sessions[msg.from.bare.to_s].online? # queue message for processing session is active for jid from
|
return @sessions[msg.from.bare.to_s].tg_outgoing(msg.to.to_s, msg.first_element_text('body')) #if @sessions.key? msg.from.bare.to_s and @sessions[msg.from.bare.to_s].online? # queue message for processing session is active for jid from
|
||||||
end
|
end
|
||||||
|
|
||||||
def presence_handler(presence)
|
def presence_handler(presence)
|
||||||
@logger.debug "New presence iq received"
|
@logger.debug "New presence iq received"
|
||||||
@logger.debug(presence)
|
@logger.debug(presence)
|
||||||
if presence.type == :subscribe then reply = presence.answer(false); reply.type = :subscribed; @@transport.send(reply); end # send "subscribed" reply to "subscribe" presence
|
if presence.type == :subscribe then reply = presence.answer(false); reply.type = :subscribed; @@transport.send(reply); end # send "subscribed" reply to "subscribe" presence
|
||||||
if presence.to == @@transport.jid and @sessions.key? presence.from.bare.to_s and presence.type == :unavailable then @sessions[presence.from.bare.to_s].offline!; return; end # go offline when received offline presence from jabber user
|
if presence.to == @@transport.jid and @sessions.key? presence.from.bare.to_s and presence.type == :unavailable then @sessions[presence.from.bare.to_s].disconnect(); return; end # go offline when received offline presence from jabber user
|
||||||
if presence.to == @@transport.jid and @sessions.key? presence.from.bare.to_s then @sessions[presence.from.bare.to_s].connect(); return; end # connect if we have session
|
if presence.to == @@transport.jid and @sessions.key? presence.from.bare.to_s then @sessions[presence.from.bare.to_s].connect(); return; end # connect if we have session
|
||||||
end
|
end
|
||||||
|
|
||||||
def iq_handler(iq)
|
def iq_handler(iq)
|
||||||
@logger.debug "New iq received"
|
@logger.debug "New iq received"
|
||||||
@logger.debug(iq)
|
@logger.debug(iq.to_s)
|
||||||
|
reply = iq.answer
|
||||||
|
|
||||||
|
if iq.vcard and @sessions.key? iq.from.bare.to_s then
|
||||||
|
vcard = @sessions[iq.from.bare.to_s].make_vcard(iq.to.to_s)
|
||||||
|
reply.type = :result
|
||||||
|
reply.elements["vCard"] = vcard
|
||||||
|
@@transport.send(reply)
|
||||||
|
else
|
||||||
|
reply.type = :error
|
||||||
|
end
|
||||||
|
@@transport.send(reply)
|
||||||
end
|
end
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
|
@ -116,16 +127,15 @@ class XMPPComponent
|
||||||
@sessions[jfrom].connect()
|
@sessions[jfrom].connect()
|
||||||
self.update_db(jfrom)
|
self.update_db(jfrom)
|
||||||
when '/code', '/password' # pass auth data if we have session
|
when '/code', '/password' # pass auth data if we have session
|
||||||
typ = body.split[0][1..8]
|
@sessions[jfrom].tg_auth(body.split[0], body.split[1]) if @sessions.key? jfrom
|
||||||
data = body.split[1]
|
when '/connect' # going online
|
||||||
@sessions[jfrom].enter_auth_data(typ, data) if @sessions.key? jfrom
|
@sessions[jfrom].connect() if @sessions.key? jfrom
|
||||||
when '/disconnect' # going offline without destroying a session
|
when '/disconnect' # going offline without destroying a session
|
||||||
@sessions[jfrom].offline! if @sessions.key? jfrom
|
@sessions[jfrom].disconnect() if @sessions.key? jfrom
|
||||||
when '/logout' # destroying session
|
when '/logout' # destroying session
|
||||||
@sessions[jfrom].offline! if @sessions.key? jfrom
|
@sessions[jfrom].disconnect(true) if @sessions.key? jfrom
|
||||||
self.update_db(jfrom, true)
|
self.update_db(jfrom, true)
|
||||||
@sessions.delete(jfrom)
|
@sessions.delete(jfrom)
|
||||||
FileUtils.remove_dir('sessions/' + jfrom, true)
|
|
||||||
else # unknown command -- display help #
|
else # unknown command -- display help #
|
||||||
msg = Jabber::Message.new
|
msg = Jabber::Message.new
|
||||||
msg.from = @@transport.jid
|
msg.from = @@transport.jid
|
||||||
|
@ -143,13 +153,13 @@ end
|
||||||
#############################
|
#############################
|
||||||
class XMPPSession < XMPPComponent
|
class XMPPSession < XMPPComponent
|
||||||
attr_reader :user_jid, :tg_login
|
attr_reader :user_jid, :tg_login
|
||||||
attr_accessor :online, :message_queue, :auth_data
|
attr_accessor :online
|
||||||
|
|
||||||
# start XMPP user session and Telegram client instance #
|
# start XMPP user session and Telegram client instance #
|
||||||
def initialize(jid, tg_login)
|
def initialize(jid, tg_login)
|
||||||
@logger = Logger.new(STDOUT); @logger.level = @@loglevel; @logger.progname = '[XMPPSession: %s/%s]' % [jid, tg_login] # init logger
|
@logger = Logger.new(STDOUT); @logger.level = @@loglevel; @logger.progname = '[XMPPSession: %s/%s]' % [jid, tg_login] # init logger
|
||||||
@logger.info "Initializing new session.."
|
@logger.info "Initializing new session.."
|
||||||
@user_jid, @tg_login, @auth_data, @message_queue = jid, tg_login, Hash.new(), Queue.new() # init class variables
|
@user_jid, @tg_login = jid, tg_login
|
||||||
end
|
end
|
||||||
|
|
||||||
# connect to tg #
|
# connect to tg #
|
||||||
|
@ -157,14 +167,20 @@ class XMPPSession < XMPPComponent
|
||||||
return if self.online?
|
return if self.online?
|
||||||
@logger.info "Spawning Telegram client.."
|
@logger.info "Spawning Telegram client.."
|
||||||
@online = nil
|
@online = nil
|
||||||
Thread.kill(@telegram_thr) if defined? @telegram_thr # kill old thread if it exists
|
@telegram = TelegramClient.new(self, @tg_login) # init tg instance in new thread
|
||||||
@telegram_thr = Thread.new{ TelegramClient.new(self, @tg_login) } # init tg instance in new thread
|
end
|
||||||
|
|
||||||
|
# disconnect from tg#
|
||||||
|
def disconnect(logout = false)
|
||||||
|
return if not self.online? or not @telegram
|
||||||
|
@logger.info "Disconnecting Telegram client.."
|
||||||
|
@telegram.disconnect(logout)
|
||||||
end
|
end
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
|
|
||||||
# send message to current user via XMPP #
|
# send message to current user via XMPP #
|
||||||
def send_message(from = nil, body = '')
|
def incoming_message(from = nil, body = '')
|
||||||
@logger.info "Received new message from Telegram peer %s" % from || "[self]"
|
@logger.info "Received new message from Telegram peer %s" % from || "[self]"
|
||||||
reply = Jabber::Message.new
|
reply = Jabber::Message.new
|
||||||
reply.type = :chat
|
reply.type = :chat
|
||||||
|
@ -176,7 +192,7 @@ class XMPPSession < XMPPComponent
|
||||||
end
|
end
|
||||||
|
|
||||||
# presence update #
|
# presence update #
|
||||||
def presence_update(from, type = nil, show = nil, status = nil, nickname = nil)
|
def presence(from, type = nil, show = nil, status = nil, nickname = nil)
|
||||||
@logger.debug "Presence update request from %s.." %from.to_s
|
@logger.debug "Presence update request from %s.." %from.to_s
|
||||||
req = Jabber::Presence.new()
|
req = Jabber::Presence.new()
|
||||||
req.from = from.nil? ? @@transport.jid : from.to_s+'@'+@@transport.jid.to_s # presence <from>
|
req.from = from.nil? ? @@transport.jid : from.to_s+'@'+@@transport.jid.to_s # presence <from>
|
||||||
|
@ -192,21 +208,46 @@ class XMPPSession < XMPPComponent
|
||||||
###########################################
|
###########################################
|
||||||
|
|
||||||
# queue message (we will share this queue within :message_queue to Telegram client thread) #
|
# queue message (we will share this queue within :message_queue to Telegram client thread) #
|
||||||
def queue_message(to, text = '')
|
def tg_outgoing(to, text = '')
|
||||||
@logger.debug "Queuing message to be sent to Telegram network user -> " % to
|
@logger.debug "Sending message to be sent to Telegram network user -> " % to
|
||||||
@message_queue << {to: to.split('@')[0], text: text}
|
@telegram.process_outgoing_msg(to.split('@')[0].to_i, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
# enter auth data (we will share this data within :auth_data {} to Telegram client thread ) #
|
# enter auth data (we will share this data within :auth_data {} to Telegram client thread ) #
|
||||||
def enter_auth_data(typ, data)
|
def tg_auth(typ, data)
|
||||||
@logger.info "Authenticating in Telegram network with :%s" % typ
|
@logger.info "Authenticating in Telegram network with :%s" % typ
|
||||||
@auth_data[typ.to_sym] = data
|
@telegram.process_auth(typ, data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# make vcard from telegram contact #
|
||||||
|
def make_vcard(to)
|
||||||
|
@logger.debug "Requesting information to make a VCard for Telegram contact..." # title, username, firstname, lastname, phone, bio, userpic
|
||||||
|
fn, nickname, given, family, phone, desc, photo = @telegram.get_contact_info(to.split('@')[0].to_i)
|
||||||
|
vcard = Jabber::Vcard::IqVcard.new()
|
||||||
|
vcard["FN"] = fn
|
||||||
|
vcard["NICKNAME"] = nickname if nickname
|
||||||
|
vcard["URL"] = "https://t.me/%s" % nickname if nickname
|
||||||
|
vcard["N/GIVEN"] = given if given
|
||||||
|
vcard["N/FAMILY"] = family if family
|
||||||
|
vcard["DESC"] = desc if desc
|
||||||
|
vcard["PHOTO/TYPE"] = 'image/jpeg' if photo
|
||||||
|
vcard["PHOTO/BINVAL"] = photo if photo
|
||||||
|
if phone then
|
||||||
|
ph = vcard.add_element("TEL")
|
||||||
|
ph.add_element("HOME")
|
||||||
|
ph.add_element("VOICE")
|
||||||
|
ph.add_element("NUMBER")
|
||||||
|
ph.elements["NUMBER"].text = phone
|
||||||
|
end
|
||||||
|
@logger.debug vcard.to_s
|
||||||
|
return vcard
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
|
|
||||||
# session status #
|
# session status #
|
||||||
def online?() @online end
|
def online?() @online end
|
||||||
def online!() @logger.info "Connection established"; @online = true; self.presence_update(nil, :subscribe); self.presence_update(nil, nil, nil, "Logged in as " + @tg_login.to_s) end
|
def online!() @logger.info "Connection established"; @online = true; self.presence(nil, :subscribe); self.presence(nil, nil, nil, "Logged in as " + @tg_login.to_s) end
|
||||||
def offline!() @online = false; self.presence_update(nil, :unavailable, nil, "Logged out"); end
|
def offline!() @online = false; self.presence(nil, :unavailable, nil, "Logged out"); @telegram = nil; end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'yaml'
|
||||||
require 'logger'
|
require 'logger'
|
||||||
require 'xmpp4r'
|
require 'xmpp4r'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
require 'fileutils'
|
require 'base64'
|
||||||
require 'sqlite3'
|
require 'sqlite3'
|
||||||
require 'tdlib-ruby'
|
require 'tdlib-ruby'
|
||||||
require_relative 'inc/telegramclient'
|
require_relative 'inc/telegramclient'
|
||||||
|
|
Reference in a new issue