Compare commits

..

No commits in common. "master" and "dev" have entirely different histories.
master ... dev

4 changed files with 73 additions and 101 deletions

View File

@ -1 +1 @@
The program was made by ninguno. drymer just polished it a bit. XRevan86 rewrote it and made it better. The program was made by ninguno. drymer just polish it a bit.

View File

@ -1,10 +1,10 @@
# hybridbot # hybridbot
Hybridbot is a bot that relays messages from a IRC channel to an XMPP MUC and vice versa. First of all, you need to install slixmpp and irc. Hybridbot is a bot that relays messages from a IRC channel to an XMPP MUC and vice versa. First of all, you need to install sleekxmpp and irc.
su -c "pip3 install slixmpp irc" su -c "pip install sleekxmpp irc"
# or # or
su -c "pip3 install -r requirements.txt" su -c "pip install -r requirements.txt"
It's configuration is simple, you have to create a config file per relay, as this example (remember to change the values): It's configuration is simple, you have to create a config file per relay, as this example (remember to change the values):
@ -32,10 +32,10 @@ The option "owner" is a string that will be printed when issuing the ".help" com
To execute it, just do: To execute it, just do:
python3 hybridbot.py myconfig.ini python hybridbot.py myconfig.ini
Or if the config file is named "config.ini", just do: Or if the config file is named "config.ini", just do:
python3 hybridbot.py python hybridbot.py
And on the MUC or the IRC channel, you can issue two commands, ".help" and ".users". And on the MUC or the IRC channel, you can issue two commands, ".help" and ".users".

View File

@ -1,13 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*-
import asyncio
import re import re
import signal import signal
import sys import sys
import time import time
import slixmpp from threading import Thread
from configparser import SafeConfigParser
from irc.bot import SingleServerIRCBot from irc.bot import SingleServerIRCBot
import sleekxmpp
if sys.version_info.major >= 3:
from configparser import SafeConfigParser
else:
from ConfigParser import SafeConfigParser
class IRCBot: class IRCBot:
@ -15,11 +19,11 @@ class IRCBot:
self.client = SingleServerIRCBot([(opts['server'], opts['port'])], self.client = SingleServerIRCBot([(opts['server'], opts['port'])],
opts['nick'], opts['nick']) opts['nick'], opts['nick'])
self.conn = self.client.connection self.conn = self.client.connection
self.opts = opts
self.nick = opts['nick'] self.nick = opts['nick']
self.pure_nick = self.nick self.pure_nick = self.nick
self.chan = opts['chan'] self.chan = opts['chan']
self.inter = inter self.inter = inter
self.loop = None
def register_handlers(self): def register_handlers(self):
self.conn.add_global_handler('welcome', self.on_session_start) self.conn.add_global_handler('welcome', self.on_session_start)
@ -47,7 +51,7 @@ class IRCBot:
if typ == 'action': if typ == 'action':
body = '/me ' + body body = '/me ' + body
self.call_outside(self.inter.relay_message, 'irc', nick, body) self.inter.relay_message('irc', nick, body)
def on_presence(self, conn, event): def on_presence(self, conn, event):
try: try:
@ -57,19 +61,19 @@ class IRCBot:
if typ == 'part': if typ == 'part':
if nick != self.nick: if nick != self.nick:
if nick in self.inter.get_irc_users(): if nick in self.inter.get_irc_users():
self.call_outside(self.inter.remove_irc_user, nick) self.inter.remove_irc_user(nick)
else: else:
if nick != self.nick: if nick != self.nick:
if nick not in self.inter.get_irc_users(): if nick not in self.inter.get_irc_users():
self.call_outside(self.inter.append_irc_user, nick) self.inter.append_irc_user(nick)
except Exception as e: except Exception as e:
print(str(e), file=sys.stderr) sys.stderr.write(str(e) + '\n')
def on_namreply(self, conn, event): def on_namreply(self, conn, event):
for nick in event.arguments[2].split(): for nick in event.arguments[2].split():
if nick != conn.get_nickname(): if nick != conn.get_nickname():
self.call_outside(self.inter.append_irc_user, nick) self.inter.append_irc_user(nick)
def on_kick(self, conn, event): def on_kick(self, conn, event):
self.nick = self.pure_nick self.nick = self.pure_nick
@ -97,95 +101,51 @@ class IRCBot:
self.conn.privmsg(self.chan, prefix + r) self.conn.privmsg(self.chan, prefix + r)
except Exception as e: except Exception as e:
print(str(e), file=sys.stderr) sys.stderr.write(str(e) + '\n')
def call_outside(self, func, *args): def start(self):
assert self.loop
self.loop.call_soon_threadsafe(func, *args)
@asyncio.coroutine
def run(self):
self.loop = asyncio.get_event_loop()
self.register_handlers() self.register_handlers()
yield from self.loop.run_in_executor(None, self.client.start) self.client.start()
class XMPPBot: class XMPPBot:
def __init__(self, opts, inter, timeout=5.0): def __init__(self, opts, inter):
self.client = slixmpp.ClientXMPP(opts['jid'], opts['passwd']) if sys.version_info.major < 3:
sleekxmpp.util.misc_ops.setdefaultencoding('utf-8')
self.client.register_plugin('xep_0199') # XMPP Ping.
self.client_ping = self.client.plugin['xep_0199']
self.client.register_plugin('xep_0045') # XMPP MUC.
self.client_muc = self.client.plugin['xep_0045']
self.muc_is_joined = False
self.client_ping.timeout = self.timeout = timeout
self.client_ping.keepalive = True
self.client = sleekxmpp.ClientXMPP(opts['jid'], opts['passwd'])
self.opts = opts
self.nick = opts['nick'] self.nick = opts['nick']
self.pure_nick = self.nick self.pure_nick = self.nick
self.muc = opts['muc'] self.muc = opts['muc']
self.inter = inter self.inter = inter
def register_handlers(self): def register_handlers(self):
self.client.add_event_handler('failed_all_auth',
self.on_failed_all_auth)
self.client.add_event_handler('session_start', self.on_session_start) self.client.add_event_handler('session_start', self.on_session_start)
self.client.add_event_handler('got_online', self.on_got_online)
self.client.add_event_handler('disconnected', self.on_disconnected)
self.client.add_event_handler('groupchat_message', self.on_message) self.client.add_event_handler('groupchat_message', self.on_message)
self.client.add_event_handler('muc::%s::presence' % self.muc, self.client.add_event_handler('muc::%s::presence' % self.muc,
self.on_presence) self.on_presence)
def connect(self):
self.client.connect()
def join_muc(self): def join_muc(self):
@asyncio.coroutine muc_plugin = self.client.plugin['xep_0045']
def loop_cycle():
if self._join_muc_block > 0:
return
self._join_muc_block += 1
while not self.muc_is_joined: if self.muc in muc_plugin.getJoinedRooms():
self.client_muc.join_muc(self.muc, self.nick) muc_plugin.leaveMUC(self.muc, self.nick,
yield from asyncio.sleep(self.timeout) msg='Replaced by new connection')
muc_plugin.joinMUC(self.muc, self.nick, wait=True)
self._join_muc_block -= 1
asyncio.async(loop_cycle())
_join_muc_block = 0
def on_failed_all_auth(event):
# print('could not connect!', file=sys.stderr)
print('Could not connect to the server, or password mismatch!',
file=sys.stderr)
sys.exit(1)
def on_session_start(self, event): def on_session_start(self, event):
# print('connected with %s' %con, file=sys.stderr)
print('Connected to XMPP') print('Connected to XMPP')
self.client.get_roster() self.client.get_roster()
self.client.send_presence() self.client.send_presence()
def on_got_online(self, event):
self.join_muc() self.join_muc()
@asyncio.coroutine
def on_disconnected(self, event):
self.muc_is_joined = False
print('Connection lost, reattempting in %d seconds' % self.timeout)
yield from asyncio.sleep(self.timeout)
self.connect()
def on_message(self, event): def on_message(self, event):
body = event['body'] body = event['body']
nick = event['mucnick'] nick = event['mucnick']
self.inter.relay_message('xmpp', nick, body) self.inter.relay_message('xmpp', nick, body)
@asyncio.coroutine
def on_presence(self, event): def on_presence(self, event):
try: try:
muc_plugin = self.client.plugin['xep_0045'] muc_plugin = self.client.plugin['xep_0045']
@ -195,15 +155,11 @@ class XMPPBot:
if not typ: if not typ:
typ = event['type'] typ = event['type']
if not nick: if not nick:
nick = muc_plugin.get_nick(self.muc, event['from']) nick = muc_plugin.getNick(self.muc, event['from'])
if typ == 'available': if typ == 'error':
self.muc_is_joined = True
elif typ == 'error':
self.muc_is_joined = False
if event['error']['code'] == '409': if event['error']['code'] == '409':
self.nick += '_' self.nick = self.nick + '_'
self.join_muc() self.join_muc()
elif typ == 'unavailable': elif typ == 'unavailable':
@ -211,9 +167,8 @@ class XMPPBot:
if nick in self.inter.get_xmpp_users(): if nick in self.inter.get_xmpp_users():
self.inter.remove_xmpp_user(nick) self.inter.remove_xmpp_user(nick)
else: else:
self.muc_is_joined = False
self.nick = self.pure_nick self.nick = self.pure_nick
yield from asyncio.sleep(0.5) time.sleep(0.5)
self.join_muc() self.join_muc()
else: else:
@ -222,7 +177,7 @@ class XMPPBot:
self.inter.append_xmpp_user(nick) self.inter.append_xmpp_user(nick)
except Exception as e: except Exception as e:
print(str(e), file=sys.stderr) sys.stderr.write(str(e) + '\n')
def send_message(self, msg, prefix=''): def send_message(self, msg, prefix=''):
try: try:
@ -232,12 +187,20 @@ class XMPPBot:
mtype='groupchat') mtype='groupchat')
except Exception as e: except Exception as e:
print(str(e), file=sys.stderr) sys.stderr.write(str(e) + '\n')
@asyncio.coroutine def start(self):
def run(self): self.client.register_plugin('xep_0045') # XMPP MUC.
self.client.register_plugin('xep_0199') # XMPP Ping.
if self.client.connect():
# sys.stderr.write('connected with %s\n'%con)
self.register_handlers() self.register_handlers()
self.connect() self.client.process(block=True)
else:
# sys.stderr.write('could not connect!\n')
sys.stderr.write('Could not connect to server, or password ' +
'mismatch!\n')
sys.exit(1)
class Intermedia: class Intermedia:
@ -325,7 +288,7 @@ class Intermedia:
prefix=nick_prefix) prefix=nick_prefix)
except Exception as e: except Exception as e:
print(str(e), file=sys.stderr) sys.stderr.write(str(e) + '\n')
def get_irc_users(self): def get_irc_users(self):
return self.irc_users return self.irc_users
@ -357,8 +320,8 @@ if __name__ == '__main__':
config.read('config.ini') config.read('config.ini')
if not config.sections(): if not config.sections():
print('Error: Configuration file does not exist or is empty.', sys.stderr.write('Error: Configuration file does not exist ' +
file=sys.stderr) 'or is empty.\n')
sys.exit(1) sys.exit(1)
shared_opts['prefix'] = config.get('Shared', 'prefix') shared_opts['prefix'] = config.get('Shared', 'prefix')
@ -375,13 +338,22 @@ if __name__ == '__main__':
xmpp_opts['nick'] = config.get('XMPP', 'nick') xmpp_opts['nick'] = config.get('XMPP', 'nick')
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
loop = asyncio.get_event_loop()
while True:
inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc']) inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc'])
ircbot = IRCBot(irc_opts, inter) ircbot = IRCBot(irc_opts, inter)
xmppbot = XMPPBot(xmpp_opts, inter) xmppbot = XMPPBot(xmpp_opts, inter)
inter.set_bots(ircbot, xmppbot) inter.set_bots(ircbot, xmppbot)
asyncio.async(xmppbot.run()) irc_thread = Thread(target=ircbot.start, args=())
asyncio.async(ircbot.run()) xmpp_thread = Thread(target=xmppbot.start, args=())
loop.run_forever()
irc_thread.daemon = True
xmpp_thread.daemon = True
irc_thread.start()
time.sleep(1)
xmpp_thread.start()
irc_thread.join()
xmpp_thread.join()

View File

@ -1,2 +1,2 @@
slixmpp sleekxmpp>=1.2.0
irc>=5.0.1 irc>=5.0.1