jabbergram/jabbergram.py

408 lines
15 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import configparser
import logging
from sys import argv
from threading import Thread
from time import sleep
from xml.dom import minidom
import sleekxmpp
import telegram
from sleekxmpp.xmlstream.stanzabase import ElementBase
from telegram.error import NetworkError, Unauthorized
try:
import requests
except:
logging.error("HTTP Upload support disabled.")
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, verify_ssl):
# XMPP
super(Jabbergram, self).__init__(jid, password)
self.add_event_handler('session_start', self.start)
self.add_event_handler('groupchat_message', self.muc_message)
self.muc_rooms = rooms.split()
self.nick = nick
self.token = token
self.xmpp_users = {}
self.jid = jid
self.verify_ssl = verify_ssl
for muc in self.muc_rooms:
self.add_event_handler("muc::%s::got_online" % muc,
self.muc_online)
self.add_event_handler("muc::%s::got_offline" % muc,
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')
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):
name = ''
size = 0
if update.message.from_user:
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 username or first_name exists
# let's take last_name
if not user:
user = str(update.message.from_user.last_name)
if update.message.audio or update.message.document or \
update.message.photo or update.message.video \
or update.message.voice or update.message.sticker:
# 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'
size = d_file.file_size
elif update.message.document:
d_file = update.message.document
ext = ''
size = d_file.file_size
elif update.message.photo:
d_file = update.message.photo[-1]
ext = '.jpg'
size = d_file.file_size
elif update.message.video:
d_file = update.message.video[-1]
ext = '.mp4'
size = d_file.file_size
elif update.message.voice:
d_file = update.message.voice
ext = '.ogg'
size = d_file.file_size
elif update.message.sticker:
d_file = update.message.sticker
ext = '.png'
size = d_file.file_size
if self.max_size >= int(size):
t_file = self.bot.getFile(d_file.file_id)
f_name = '/tmp/' + d_file.file_id + ext
t_file.download(f_name)
url = self.http_upload.upload(
self.component,
self.verify_ssl,
f_name, size)
if update.message.caption:
message = update.message.caption + ' '
else:
message = 'File uploaded: '
message += url
else:
message = 'A file has been uploaded to Telegr'\
'am, but is too big.'
else:
message = 'A file has been uploaded to Telegram,'\
'but the XMPP server doesn\'t support H'\
'TTP 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 has changed.'
else:
if update.message.reply_to_message:
name = update.message.reply_to_message.from_user\
.username
if name != self.bot.username:
message = name + ': ' + \
update.message.reply_to_message.text
else:
message = update.message.reply_to_message.text
else:
message = update.message.text
if name:
msg = message + ' <- ' + user + ": " + \
update.message.text
else:
msg = user + ": " + message
chat = '@' + update.message.chat.username
if chat not in self.groups:
chat = str(update.message.chat_id)
if message and chat in self.groups:
index = self.groups.index(chat)
receiver = self.muc_rooms[index]
if chat in self.telegram_users:
if user not in self.telegram_users[chat]:
self.telegram_users[chat] += ' ' + user
else:
self.telegram_users[chat] = ' ' + user
if message == '.users':
self.say_users('telegram', receiver, chat)
elif message == '.help':
self.say_help('telegram', receiver, chat)
elif message == '.where':
self.say_where('telegram', receiver, chat)
else:
self.send_message(mto=receiver, mbody=msg,
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()
for muc in self.muc_rooms:
self.plugin['xep_0045'].joinMUC(muc, self.nick, wait=True)
def muc_message(self, msg):
muc_room = str(msg['from']).split('/')[0]
index = self.muc_rooms.index(muc_room)
tg_group = self.groups[index]
if msg['body'] == '.users':
self.say_users('xmpp', muc_room, tg_group)
elif msg['body'] == '.help':
self.say_help('xmpp', muc_room, tg_group)
elif msg['body'] == '.where':
self.say_where('xmpp', muc_room, tg_group)
elif msg['mucnick'] != self.nick:
message = str(msg['from']).split('/')[1] + ': ' + str(msg['body'])
self.bot.sendMessage(tg_group, text=message)
def muc_online(self, presence):
user = presence['muc']['nick']
muc = presence['from'].bare
if user != self.nick:
if muc in self.xmpp_users:
self.xmpp_users[muc].append(presence['muc']['nick'])
else:
self.xmpp_users[muc] = [presence['muc']['nick']]
def muc_offline(self, presence):
user = presence['muc']['nick']
muc = presence['from'].bare
if user != self.nick:
self.xmpp_users[muc].remove(presence['muc']['nick'])
def say_users(self, service, muc, group):
if service == 'xmpp':
if group in self.telegram_users:
tg_users = self.telegram_users[group]
else:
tg_users = ""
msg = 'Telegram Users:' + tg_users
self.send_message(mto=muc, mbody=msg, mtype='groupchat')
elif service == 'telegram':
xmpp_users = ""
if muc in self.xmpp_users:
for i in self.xmpp_users[muc]:
xmpp_users = xmpp_users + ' ' + i
else:
xmpp_users = ""
msg = 'XMPP Users:' + xmpp_users
self.bot.sendMessage(group, text=msg)
def say_help(self, service, muc, group):
msg = 'Hi, I\'m ' + self.bot.username + '. I have two commands : ".us'\
'ers" and ".where".'
if service == 'xmpp':
self.send_message(mto=muc, mbody=msg, mtype='groupchat')
elif service == 'telegram':
self.bot.sendMessage(group, text=msg)
def say_where(self, service, muc, group):
if service == 'xmpp':
if '@' in group:
msg = 'I\'m on http://telegram.me/' + group.split('@')[1] + '.'
else:
msg = 'Sorry, I\'m on a private group, you\'ll have to ask fo'\
'r an invitation.'
self.send_message(mto=muc, mbody=msg, mtype='groupchat')
elif service == 'telegram':
msg = 'I\'m on ' + muc + '.'
self.bot.sendMessage(group, text=msg)
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
if verify_ssl == 'False':
req = requests.put(put_url, data=open(u_file, 'rb'),
verify=False)
elif verify_ssl == '':
req = requests.put(put_url, data=open(u_file, 'rb'))
return put_url
if __name__ == '__main__':
# parse config
config = []
parser = configparser.SafeConfigParser()
if len(argv) == 2:
parser.read(argv[1])
else:
parser.read('config.ini')
for name, value in parser.items('config'):
config.append(value)
# assign values for the bot
jid = config[0]
password = config[1]
muc_rooms = config[2]
nick = config[3]
token = config[4]
groups = config[5]
verify_ssl = config[6]
xmpp = Jabbergram(jid, password, muc_rooms, nick, token, groups,
verify_ssl)
xmpp.register_plugin('xep_0045')
if xmpp.connect():
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")