diff --git a/jabbergram.py b/jabbergram.py index c54bc44..1fdac18 100755 --- a/jabbergram.py +++ b/jabbergram.py @@ -4,18 +4,29 @@ import sleekxmpp import telegram import configparser +import requests from threading import Thread from queue import Queue from telegram.error import NetworkError, Unauthorized from time import sleep from sys import argv from sys import exit +from sleekxmpp.xmlstream.stanzabase import ElementBase +from sleekxmpp.stanza.iq import Iq +from xml.dom import minidom -class jabbegram(sleekxmpp.ClientXMPP): +class Request(ElementBase): + namespace = 'urn:xmpp:http:upload' + name = 'request' + plugin_attrib = 'request' + interfaces = set(('filename', 'size')) + sub_interfaces = interfaces + +class Jabbergram(sleekxmpp.ClientXMPP): def __init__(self, jid, password, rooms, nick, token, groups): # XMPP - super(jabbergram, self).__init__(jid, password) + super(Jabbergram, self).__init__(jid, password) self.add_event_handler('session_start', self.start) self.add_event_handler('groupchat_message', self.muc_message) @@ -23,41 +34,127 @@ class jabbegram(sleekxmpp.ClientXMPP): self.nick = nick self.token = token self.xmpp_users = {} + self.jid = jid for muc in self.muc_rooms: self.add_event_handler("muc::%s::got_online" % muc, - self.muc_online) + self.muc_online) self.add_event_handler("muc::%s::got_offline" % muc, - self.muc_offline) + self.muc_offline) # Telegram self.groups = groups.split() self.bot = telegram.Bot(self.token) self.telegram_users = {} + # initialize http upload on a thread since its needed to be connected + # to xmpp + t = Thread(target=self.init_http) + t.daemon = True + t.start() + # put tg connector in a thread t = Thread(target=self.read_tg) t.daemon = True t.start() print('Please wait a couple of minutes until it\'s correctly ' - 'connected') + 'connected') + + def init_http(self): + self.http_upload = self.HttpUpload(self) + self.component = self.http_upload.discovery() + + if self.component: + xml = self.http_upload.disco_info(self.component) + xml = minidom.parseString(str(xml)) + self.max_size = int(xml.getElementsByTagName('value') + [1].firstChild.data) + else: + try: + self.component = self.jid.split('@')[1] + xml = self.http_upload.disco_info(self.component) + xml = minidom.parseString(str(xml)) + self.max_size = int(xml.getElementsByTagName('value') + [1].firstChild.data) + except: + self.max_size = None def read_tg(self): update_id = 0 + # wait until http_upload has been tested + sleep(5) while True: try: for update in self.bot.getUpdates(offset=update_id, - timeout=10): - message = update.message.text + timeout=10): + + if update.message.audio or update.message.document or \ + update.message.photo or update.message.video \ + or update.message.voice: + + # proceed only if http upload is available + if self.max_size is not None: + if update.message.audio: + d_file = update.message.audio + ext = '.ogg' + elif update.message.document: + d_file = update.message.document + ext = '' + elif update.message.photo: + d_file = update.message.photo[-1] + ext = '.jpg' + elif update.message.video: + d_file = update.message.video[-1] + ext = '.mp4' + elif update.message.voice: + d_file = update.message.voice + ext = '.ogg' + + size = d_file.file_size + + if self.max_size >= size: + t_file = self.bot.getFile(d_file.file_id) + name = '/tmp/' + d_file.file_id + ext + t_file.download(name) + + url = self.http_upload.upload( + self.component, + '', name, size) + + if update.message.caption: + message = update.message.caption + ' ' + else: + message = 'File uploaded: ' + + message += url + else: + message = 'A file has been uploaded to Telegra' + 'm, but is too big.' + else: + message = 'A file has been uploaderd to Telegram, ' + 'but the XMPP server doesn\'t support HTTP Upload.' + + elif update.message.new_chat_member: + message = 'This user has joined the group.' + elif update.message.left_chat_member: + message = 'This user has left the group.' + elif update.message.new_chat_title: + message = 'The group\'s title has changed: '+ \ + update.message.new_chat_title + elif update.message.new_chat_photo: + message = 'The group\'s photo haschanged.' + else: + message = update.message.text + user = str(update.message.from_user.username) # sometimes there's no user. weird, but it happens if not user: user = str(update.message.from_user.first_name) - # even weirder is that none of them exist + # even weirder is that username or first_name exists # let's take last_name if not user: user = str(update.message.from_user.last_name) @@ -81,20 +178,21 @@ class jabbegram(sleekxmpp.ClientXMPP): self.say_users('telegram', muc, chat_id) else: self.send_message(mto=receiver, mbody=msg, - mtype='groupchat') + mtype='groupchat') update_id = update.update_id + 1 except NetworkError as e: + print(e) sleep(1) except Unauthorized: + print(e) sleep(1) except Exception as e: update_id += 1 print(e) - def start(self, event): self.get_roster() self.send_presence() @@ -155,14 +253,78 @@ class jabbegram(sleekxmpp.ClientXMPP): if service == 'xmpp': self.send_message(mto=muc, mbody=message, mtype='groupchat') - # arreglar el .users por el lado de tg + elif service == 'telegram': self.bot.sendMessage(group, text=message) + class HttpUpload(): + def __init__(self, parent_self): + self.parent_self = parent_self + + def discovery(self): + + disco = sleekxmpp.basexmpp.BaseXMPP.Iq(self.parent_self) + disco['query'] = "http://jabber.org/protocol/disco#items" + disco['type'] = 'get' + disco['from'] = self.parent_self.jid + disco['to'] = self.parent_self.jid.split('@')[1] + + d = disco.send(timeout=30) + xml = minidom.parseString(str(d)) + item = xml.getElementsByTagName('item') + + for component in item: + component = component.getAttribute('jid') + info = self.disco_info(component) + + if "urn:xmpp:http:upload" in info: + http_upload_component = component + break + else: + http_upload_component = "" + + return http_upload_component + + def disco_info(self, component): + + info = sleekxmpp.basexmpp.BaseXMPP.Iq(self.parent_self) + info['query'] = "http://jabber.org/protocol/disco#info" + info['type'] = 'get' + info['from'] = self.parent_self.jid + info['to'] = component + + return str(info.send(timeout=30)) + + def upload(self, component, verify_ssl, u_file, size): + + peticion = Request() + peticion['filename'] = u_file.split('/')[-1] + peticion['size'] = str(size) + + iq = sleekxmpp.basexmpp.BaseXMPP.Iq(self.parent_self) + iq.set_payload(peticion) + iq['type'] = 'get' + iq['to'] = component + iq['from'] = self.parent_self.jid + + send = iq.send(timeout=30) + + xml = minidom.parseString(str(send)) + put_url = xml.getElementsByTagName('put')[0].firstChild.data + + verify_ssl = 'False' + if verify_ssl == 'False': + req = requests.put(put_url, data=open(u_file, 'rb'), + verify=False) + else: + req = requests.put(put_url, data=open(u_file, 'rb')) + + return put_url + if __name__ == '__main__': - # parsear config + # parse config config = [] parser = configparser.SafeConfigParser() @@ -174,7 +336,7 @@ if __name__ == '__main__': for name, value in parser.items('config'): config.append(value) - # asignar valores para el bot + # assign values for the bot jid = config[0] password = config[1] muc_rooms = config[2] @@ -182,7 +344,7 @@ if __name__ == '__main__': token = config[4] groups = config[5] - xmpp = jabbergram(jid, password, muc_rooms, nick, token, groups) + xmpp = Jabbergram(jid, password, muc_rooms, nick, token, groups) xmpp.register_plugin('xep_0045') if xmpp.connect():