Release 1.2

[!] Using YAML serilaization instead of sqlite3. You may convert your SQLite db to YAML format using this:
    (echo "---"; sqlite3 users.db 'select jid,login from users'|sed "s/|/: \'/g;s/$/\'/g") > users.dat

[FIX] Fixed repllies
[UPD] Sending presence probe after disconnect received — maybe there are another XMPP resource? ☺
[UPD] When XMPP user goes online if Telegram session already established -- resync statuses with Telegram network to get all contacts online immediately
This commit is contained in:
annelin 2019-06-11 16:24:44 +03:00
parent 780dd6d6a1
commit da2de42645
4 changed files with 28 additions and 29 deletions

View file

@ -3,8 +3,8 @@ telegram:
api_id: '845316' # telegram API ID (my.telegram.org) # api_id: '845316' # telegram API ID (my.telegram.org) #
api_hash: '27fe5224bc822bf3a45e015b4f9dfdb7' # telegram API HASH (my.telegram.org) # api_hash: '27fe5224bc822bf3a45e015b4f9dfdb7' # telegram API HASH (my.telegram.org) #
verbosity: 2 # 1 = no verbosity, 2 = moderate verbosity, 3 = network requests debug verbosity: 2 # 1 = no verbosity, 2 = moderate verbosity, 3 = network requests debug
useragent: 'Zhabogram XMPP Gateway' # client name useragent: 'Zhabogram' # client name
version: '1.0' # client version version: '1.2' # client version
use_test_dc: false # always use false use_test_dc: false # always use false
loglevel: 0 # 0 = debug, 1 = info, 2 = warn, 3 = err, 4 = fatal, 5 = unknown (ruby logger class) loglevel: 0 # 0 = debug, 1 = info, 2 = warn, 3 = err, 4 = fatal, 5 = unknown (ruby logger class)
content_path: '/var/zhabogram/content' # we will move (symlink) downloaded content here — you must setup web server that serve this directry content_path: '/var/zhabogram/content' # we will move (symlink) downloaded content here — you must setup web server that serve this directry
@ -16,7 +16,7 @@ xmpp:
debug: false debug: false
admins: admins:
- 'root@localhost' - 'root@localhost'
db_path: 'users.db' # sqlite3 users (JID:Telegram Login) database db_path: 'users.dat' # users store (JID:Telegram Login)
jid: 'tlgrm.localhost' # component JID jid: 'tlgrm.localhost' # component JID
host: 'localhost' # XMPP server host: 'localhost' # XMPP server
port: 8888 # component port port: 8888 # component port

View file

@ -54,6 +54,7 @@ class TelegramClient
@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()
return true
end end
# disconnect and destroy telegram client # # disconnect and destroy telegram client #
@ -339,13 +340,12 @@ class TelegramClient
# handling replies # # handling replies #
reply_to = 0 reply_to = 0
if text[0] == '>' then if text[0] == '>' and text.match(Regexp.new /^>( )?[0-9]{10,20}/) then
text = text.split("\n") text = text.split("\n")
reply_to = text[0].scan(/\d/).to_i reply_to = text[0].scan(/\d+/).first.to_i
reply_to = 0 if reply_to < 10000 text = text.drop(1).join("\n")
text = splitted.drop(1).join("\n") if reply_to != 0
end end
# handling files received from xmpp # # handling files received from xmpp #
message = TD::Types::InputMessageContent::Text.new(:text => { :text => text, :entities => []}, :disable_web_page_preview => false, :clear_draft => true ) message = TD::Types::InputMessageContent::Text.new(:text => { :text => text, :entities => []}, :disable_web_page_preview => false, :clear_draft => true )
message = TD::Types::InputMessageContent::Document.new(document: TD::Types::InputFile::Remote.new(id: text), caption: { :text => '', :entities => []}) if text.start_with? @@content_upload_prefix message = TD::Types::InputMessageContent::Document.new(document: TD::Types::InputFile::Remote.new(id: text), caption: { :text => '', :entities => []}) if text.start_with? @@content_upload_prefix
@ -377,6 +377,12 @@ class TelegramClient
return @cache[:users][user_id] if @cache[:users].key? user_id return @cache[:users][user_id] if @cache[:users].key? user_id
end end
# sync statuses with XMPP roster
def sync_status()
@logger.debug "Syncing statuses with roster.."
@cache[:chats].each do |chat| self.process_status_update(chat.id, (@cache[:users].include? chat.id ? @cache[:users][chat.id].status : chat.title.to_s), true) end
end
# convert telegram status to XMPP one # convert telegram status to XMPP one
def process_status_update(user_id, status, immed = true) def process_status_update(user_id, status, immed = true)
@logger.debug "Processing status update for user id %s.." % user_id.to_s @logger.debug "Processing status update for user id %s.." % user_id.to_s

View file

@ -32,28 +32,22 @@ class XMPPComponent
@config = { host: params["host"] || 'localhost', port: params["port"] || 8899, jid: params["jid"] || 'tlgrm.localhost', secret: params['password'] || '', admins: params['admins'] || [], debug: params['debug'] } # default config @config = { host: params["host"] || 'localhost', port: params["port"] || 8899, jid: params["jid"] || 'tlgrm.localhost', secret: params['password'] || '', admins: params['admins'] || [], debug: params['debug'] } # default config
@sessions = {} @sessions = {}
@presence_que = {} @presence_que = {}
@db = SQLite3::Database.new(params['db_path'] || 'users.db') @db = params['db_path'] || 'users.dat'
@db.execute("CREATE TABLE IF NOT EXISTS users(jid varchar(256), login varchar(256), PRIMARY KEY(jid) );")
@db.results_as_hash = true
self.load_db() self.load_db()
end end
# load sessions from db # # load sessions from db #
def load_db(jid = nil) def load_db()
@logger.info "Initializing database.." @logger.info "Loading sessions..."
query = (jid.nil?) ? "SELECT * FROM users" : "SELECT * FROM users where jid = '%s';" % jid File.open( @db, 'r' ) {|f| YAML.load(f).each do |jid,login| @sessions[jid] = TelegramClient.new(self, jid, login) end }
@logger.debug(query)
@db.execute(query) do |session| @sessions[session['jid']] = TelegramClient.new(self, session['jid'], session['login']) end
end end
# store session to db # # store session to db #
def update_db(jid, delete = false, register = false) def save_db()
login = (not register and @sessions.key? jid) ? @sessions[jid].login.to_s : register @logger.info "Saving sessions..."
return if not login sessions_store = []
@logger.info "Writing database [%s].." % jid.to_s @sessions.each do |jid,session| store << {jid: jid, login: session.login} end
query = (delete) ? "DELETE FROM users where jid = '%s';" % jid.to_s : "INSERT OR REPLACE INTO users(jid, login) VALUES('%s', '%s');" % [jid.to_s, login] File.open( @db, 'w' ) {|f| f.write(YAML.dump(sessions_store)) }
@logger.debug query
@db.execute(query)
end end
# connecting to XMPP server # # connecting to XMPP server #
@ -97,7 +91,7 @@ class XMPPComponent
return -11 return -11
rescue Exception => e rescue Exception => e
@logger.error 'Connection failed: %s' % e @logger.error 'Connection failed: %s' % e
@db.close self.save_db()
exit -8 exit -8
end end
end end
@ -174,8 +168,8 @@ class XMPPComponent
@logger.debug "Received presence :%s from <%s> to <%s>" % [prsnc.type.to_s, prsnc.from.to_s, prsnc.to.to_s] @logger.debug "Received presence :%s from <%s> to <%s>" % [prsnc.type.to_s, prsnc.from.to_s, prsnc.to.to_s]
@logger.debug(prsnc.to_s) @logger.debug(prsnc.to_s)
if prsnc.type == :subscribe then reply = prsnc.answer(false); reply.type = :subscribed; @component.send(reply); end # send "subscribed" reply to "subscribe" presence if prsnc.type == :subscribe then reply = prsnc.answer(false); reply.type = :subscribed; @component.send(reply); end # send "subscribed" reply to "subscribe" presence
if prsnc.to == @component.jid and @sessions.key? prsnc.from.bare.to_s and prsnc.type == :unavailable then @sessions[prsnc.from.bare.to_s].disconnect(); return; end # go offline when received offline presence from jabber user if prsnc.to == @component.jid and @sessions.key? prsnc.from.bare.to_s and prsnc.type == :unavailable then @sessions[prsnc.from.bare.to_s].disconnect(); self.presence(prsnc.from, nil, :subscribe) ; return; end # go offline when received offline presence from jabber user
if prsnc.to == @component.jid and @sessions.key? prsnc.from.bare.to_s then self.request_tz(prsnc.from); @sessions[prsnc.from.bare.to_s].connect(); return; end # connect if we have session if prsnc.to == @component.jid and @sessions.key? prsnc.from.bare.to_s then self.request_tz(prsnc.from); @sessions[prsnc.from.bare.to_s].connect() || @sessions[prsnc.from.bare.to_s].sync_status(); return; end # connect if we have session
end end
# new iq (vcard/tz) request to XMPP component # # new iq (vcard/tz) request to XMPP component #
@ -226,7 +220,7 @@ class XMPPComponent
@sessions[from.bare.to_s] = TelegramClient.new(self, from.bare.to_s, body.split[1]) if not (@sessions.key? from.bare.to_s and @sessions[from.bare.to_s].online?) @sessions[from.bare.to_s] = TelegramClient.new(self, from.bare.to_s, body.split[1]) if not (@sessions.key? from.bare.to_s and @sessions[from.bare.to_s].online?)
@sessions[from.bare.to_s].connect() @sessions[from.bare.to_s].connect()
self.request_tz(from) self.request_tz(from)
self.update_db(from.bare.to_s) self.save_db()
when '/code', '/password' # pass auth data to telegram when '/code', '/password' # pass auth data to telegram
@sessions[from.bare.to_s].process_auth(body.split[0], body.split[1]) if @sessions.key? from.bare.to_s @sessions[from.bare.to_s].process_auth(body.split[0], body.split[1]) if @sessions.key? from.bare.to_s
when '/connect' # go online when '/connect' # go online
@ -239,7 +233,7 @@ class XMPPComponent
@sessions[from.bare.to_s].connect() if @sessions.key? from.bare.to_s @sessions[from.bare.to_s].connect() if @sessions.key? from.bare.to_s
when '/logout' # go offline and destroy session when '/logout' # go offline and destroy session
@sessions[from.bare.to_s].disconnect(true) if @sessions.key? from.bare.to_s @sessions[from.bare.to_s].disconnect(true) if @sessions.key? from.bare.to_s
self.update_db(from.bare.to_s, true) self.save_db()
@sessions.delete(from.bare.to_s) @sessions.delete(from.bare.to_s)
when '/info' # show some debug information when '/info' # show some debug information
return if not @config[:admins].include? from.bare.to_s return if not @config[:admins].include? from.bare.to_s

View file

@ -5,7 +5,6 @@ require 'xmpp4r'
require 'xmpp4r/discovery' require 'xmpp4r/discovery'
require 'digest' require 'digest'
require 'base64' require 'base64'
require 'sqlite3'
require 'fileutils' require 'fileutils'
require 'tdlib-ruby' require 'tdlib-ruby'
require_relative 'inc/telegramclient' require_relative 'inc/telegramclient'