diff --git a/inc/telegramclient.rb b/inc/telegramclient.rb index c12435d..1bd95c9 100644 --- a/inc/telegramclient.rb +++ b/inc/telegramclient.rb @@ -1,3 +1,17 @@ +DEFAULTS = { + lib_path: 'lib/', + verbosity: 2, + loglevel: :debug, + client: {api_id: 50322, api_hash: '9ff1a639196c0779c86dd661af8522ba', use_chat_info_database: false}, + content: {path: '', link: '', upload: ''} +} + +PERMISSIONS = { + admin: {can_be_edited: true, can_change_info: true, can_post_messages: true, can_edit_messages: true, can_delete_messages: true, can_invite_users: true, can_restrict_members: true, can_pin_messages: true, can_promote_members: false}, + member: TD::Types::ChatPermissions.new(can_send_messages: true, can_send_media_messages: true, can_send_polls: true, can_send_other_messages: true, can_add_web_page_previews: true, can_change_info: true, can_invite_users: true, can_pin_messages: true), + readonly: TD::Types::ChatPermissions.new(can_send_messages: false, can_send_media_messages: false, can_send_polls: false, can_send_other_messages: false, can_add_web_page_previews: false, can_change_info: false, can_invite_users: false, can_pin_messages: false), +} + HELP_GATE_CMD = %q{Available commands: /login phone — sign in /logout — sign out @@ -26,9 +40,15 @@ HELP_CHAT_CMD= %q{Available commands: /block — blacklist current user /unblock — unblacklist current user /invite id or @username — add user to current chat + /link — get invite link for current chat /kick id or @username — remove user from current chat - /ban id or @username [hours] — restrict @username from current chat for [hours] or forever + /mute id or @username [hours] — mute user in current chat + /unmute id or @username — unrestrict user from current chat + /ban id or @username [hours] — restrict @username from current chat + /unban id or @username — unbans @username in current chat (and devotes from admins) + /promote id or @username — promote user to admin in current chat /leave — leave current chat + /leave! — leave current chat (for owners) /close — close current secret chat /delete — delete current chat from chat list /members [query] — search members [by optional query] in current chat (requires admin rights) @@ -37,18 +57,16 @@ HELP_CHAT_CMD= %q{Available commands: class TelegramClient - attr_reader :session, :state - @@config = {loglevel: :debug, verbosity: 2, lib_path: 'lib/', client: {api_id: 50322, api_hash: '9ff1a639196c0779c86dd661af8522ba', use_chat_info_database: false}, content: {path:'',link:'',upload:''}} # defaults - ## configure tdlib (when valid tdlib params specified) or zhabogram def self.configure(**config) - @@config = @@config.merge(config) + @@config = DEFAULTS.merge(config) TD.config.update(config[:tdlib]) TD::Api.set_log_verbosity_level(@@config[:tdlib_verbosity]) end ## initialize telegram client instance (xmpp = XMPP stream, jid = user's jid , login = user's telegram login (for now, it is phone number) def initialize(xmpp, jid, **session) + return unless @@config @logger = Logger.new(STDOUT, level: @@config[:loglevel], progname: 'TelegramClient: %s | %s' % [jid, session[:login]] ) @xmpp = xmpp @jid = jid @@ -225,6 +243,7 @@ class TelegramClient return @cache[:chats][id], @cache[:users][id] end + ## get last messages from specified chat def get_lastmessages(id, query=nil, from=nil, count=1) result = @telegram.search_chat_messages(id, query.to_s, from.to_i, 0, 0, count.to_i, TD::Types::SearchMessagesFilter::Empty.new).value return (result) ? result.messages : [] @@ -247,15 +266,17 @@ class TelegramClient when TD::Types::UserStatus::Offline then show, status = (Time.now.getutc.to_i-status.was_online.to_i<3600) ? :away : :xa, DateTime.strptime((status.was_online+Time.now.getlocal(@session[:timezone]).utc_offset).to_s,'%s').strftime("Last seen at %H:%M %d/%m/%Y") end - @xmpp.send_presence(@jid, id, nil, show, status, nil, photo, immed) + return @xmpp.send_presence(@jid, id, nil, show, status, nil, photo, immed) end ## send outgoing message to telegram user def process_outgoing_message(id, text) return if self.process_command(id, text.split.first, text.split[1..-1]) or not self.online? # try to execute commands @logger.warn 'Sending message to chat %s' % id - message, reply = self.format_input(text) - @telegram.send_message(id, reply, nil, nil, message).rescue{|e| @xmpp.send_message(@jid, id, "Not sent: %s" % e)} + reply = text.lines[0].scan(/\d+/).first.to_i if text.lines[0] =~ /^> ?[0-9]{10}/ + text = TD::Types::FormattedText.new(text: !reply ? text : text.lines[1..-1].join, entities: []) + message = TD::Types::InputMessageContent::Text.new(text: text, disable_web_page_preview: false, clear_draft: false) + return (id) ? @telegram.send_message(id, reply.to_i, nil, nil, message).rescue{|e| @xmpp.send_message(@jid, id, "Not sent: %s" % e)} : message end ## /commands (some telegram actions) @@ -278,7 +299,7 @@ class TelegramClient else # chat commands case cmd when '/d' then @telegram.delete_messages(chat.id, self.get_lastmessages(chat.id, nil, @me.id, args[0]||1).map(&:id), true) # delete message - when '/s' then @telegram.edit_message_text(chat.id, self.get_lastmessages(chat.id, nil, @me.id, 1).first.id, nil, self.format_input(args.join(' ')).first) # edit message + when '/s' then @telegram.edit_message_text(chat.id, self.get_lastmessages(chat.id, nil, @me.id, 1).first.id, nil, self.process_outgoing_message(nil, args.join(' '))) # edit message when '/add' then @telegram.search_public_chat(args[0]).then{|chat| @xmpp.send_presence(@jid, chat.id, :subscribe)}.wait # add @contact when '/join' then @telegram.join_chat_by_invite_link(args[0]) # join https://t.me/publichat when '/supergroup' then @telegram.create_new_supergroup_chat(args[0], false, args[1..-1].join(' '), nil) # create new supergroup @@ -287,14 +308,20 @@ class TelegramClient when '/group' then @telegram.create_new_basic_group_chat(chat.id, args[0]) # create group chat with current user when '/block' then @telegram.block_user(chat.id) # blacklists current user when '/unblock' then @telegram.unblock_user(chat.id) # unblacklists current user - when '/invite' then @telegram.add_chat_member(chat.id, self.get_contact(args[0]).first.id, 100) # invite @username to current groupchat - when '/kick' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, self.format_restrict()) # kick @username from current group chat - when '/ban' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, self.format_restrict(true, args[1])) # ban @username from current chat [for N hours] - when '/leave' then @telegram.leave_chat(chat.id).then{@xmpp.send_presence(@jid, id, :unsubscribed)} # leave current chat - when '/close' then @telegram.close_secret_chat(chat.type.secret_id).then{@xmpp.send_presence(@jid, id, :unsubscribed)} # close secret chat - when '/delete' then @telegram.delete_chat_history(chat.id, true, true).then{@xmpp.send_presence(@jid, id, :unsubscribed)} # delete current chat when '/members' then @telegram.search_chat_members(chat.id, args[0], 9999, TD::Types::ChatMembersFilter::Members.new).then{|m| @xmpp.send_message(@jid, chat.id, (m.members.map do |u| "%s | role: %s" % [self.format_contact(u.user_id), u.status.class] end).join("\n")) } # chat members - when '/search' then @telegram.search_chat_messages(chat.id, args[0].to_s, 0, 0, 0, args[1].to_i||100, TD::Types::SearchMessagesFilter::Empty.new).value.messages.reverse.each{|m| @xmpp.send_message(@jid, chat.id, self.format_message(nil,nil,nil,msg))} # search + when '/invite' then @telegram.add_chat_member(chat.id, self.get_contact(args[0]).first.id, 100) # invite @username to current groupchat + when '/link' then @telegram.generate_chat_invite_link(chat.id).then{|link| @xmpp.send_message(@jid, chat.id, link.invite_link)} # get link to current chat + when '/kick' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Left.new()) # kick @username + when '/mute' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Restricted.new(is_member: true, restricted_until_date: self.format_bantime(args[0]), permissions: PERMISSIONS[:readonly])) # mute @username [n hours] + when '/unmute' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Restricted.new(is_member: true, restricted_until_date: 0, permissions: PERMISSIONS[:member])) # unmute @username + when '/ban' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Banned.new(banned_until_date: self.format_bantime(args[0]))) # ban @username [n hours] + when '/unban' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Member.new()) # unban @username + when '/promote' then @telegram.set_chat_member_status(chat.id, self.get_contact(args[0]).first.id, TD::Types::ChatMemberStatus::Administrator.new(custom_title: args[0].to_s, **PERMISSIONS[:admin])) # promote @username to admin + when '/leave' then @telegram.leave_chat(chat.id).then{@xmpp.send_presence(@jid, chat.id, :unsubscribed)} # leave current chat + when '/leave!' then @telegram.delete_supergroup(chat.type.supergroup_id).then{@xmpp.send_presence(@jid, chat.id, :unsubscribed)}.rescue{|e| puts e.to_s} # leave current chat (for owners) + when '/close' then @telegram.close_secret_chat(chat.type.secret_id).then{@xmpp.send_presence(@jid, chat.id, :unsubscribed)} # close secret chat + when '/delete' then @telegram.delete_chat_history(chat.id, true, true).then{@xmpp.send_presence(@jid, chat.id, :unsubscribed)} # delete current chat + when '/search' then @telegram.get_messages(chat.id, self.get_lastmessages(chat.id, args[0], 0, args[1]||100).map(&:id)).value.messages.reverse.each{|msg| @xmpp.send_message(@jid, chat.id, self.format_message(chat.id,msg,false))} # message search when '/help' then @xmpp.send_message(@jid, id, HELP_CHAT_CMD) else return # continue end @@ -305,7 +332,7 @@ class TelegramClient ######################################################################### # formatting functions ################################################# ######################################################################### - + def format_contact(id) return if not id or id == 0 chat, user = self.get_contact(id) @@ -325,31 +352,24 @@ class TelegramClient return str end - def format_input(text) - reply = text.lines[0].scan(/\d+/).first.to_i if text.lines[0] =~ /^> ?[0-9]{10}/ # quotes - file = TD::Types::InputFile::Remote.new(id: text) if text.start_with? @@config[:content][:upload] # attaches - text = TD::Types::FormattedText.new(text: (reply or file) ? text.lines[1..-1].join : text, entities: []) # remove first line (quotes/files) - input = (file) ? TD::Types::InputMessageContent::Document.new(document: file, caption: text) : TD::Types::InputMessageContent::Text.new(text: text, disable_web_page_preview: false, clear_draft: false) # message - return input, reply.to_i - end - + def format_content(file, fname) + str = "%s (%d kbytes) | %s/%s%s" % [fname, file.size/1024, @@config[:content][:link], Digest::SHA256.hexdigest(file.remote.id), File.extname(fname).to_s] + return str + end + def format_forward(fwd) str = case fwd.origin - when TD::Types::MessageForwardOrigin::User then self.format_contact(fwd.origin.sender_user_id) - when TD::Types::MessageForwardOrigin::HiddenUser then fwd.origin.sender_name - when TD::Types::MessageForwardOrigin::Channel then [self.format_contact(fwd.origin.chat_id), fwd.origin.author_signature].join(' ') + when TD::Types::MessageForwardOrigin::User then self.format_contact(fwd.origin.sender_user_id) + when TD::Types::MessageForwardOrigin::HiddenUser then fwd.origin.sender_name + when TD::Types::MessageForwardOrigin::Channel then [self.format_contact(fwd.origin.chat_id), fwd.origin.author_signature].join ' ' end return str end - def format_content(file, fname) - str = "%s (%d kbytes) | %s/%s%s" % [fname, file.size/1024, @@config[:content][:link], Digest::SHA256.hexdigest(file.remote.id), File.extname(fname).to_s] - return str - end - - def format_restrict(ban=false, hours=0) - till = (hours==0) ? 0 : Time.now.getutc.to_i + hours.to_i*3600 - return (ban) ? TD::Types::ChatMemberStatus::Banned.new(banned_until_date: till) : TD::Types::ChatMemberStatus::Left.new + def format_bantime(hours=0) + now = Time.now.getutc.to_i + till = now + hours.to_i*3600 + return (hours.to_i>0) ? till : 0 end end