Compare commits
No commits in common. "master" and "dev" have entirely different histories.
2
AUTHORS
2
AUTHORS
@ -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.
|
10
README.md
10
README.md
@ -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".
|
||||||
|
160
hybridbot.py
160
hybridbot.py
@ -1,25 +1,29 @@
|
|||||||
#!/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:
|
||||||
def __init__(self, opts, inter):
|
def __init__(self, opts, inter):
|
||||||
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.register_handlers()
|
self.client.register_plugin('xep_0199') # XMPP Ping.
|
||||||
self.connect()
|
if self.client.connect():
|
||||||
|
# sys.stderr.write('connected with %s\n'%con)
|
||||||
|
self.register_handlers()
|
||||||
|
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()
|
|
||||||
|
|
||||||
inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc'])
|
while True:
|
||||||
ircbot = IRCBot(irc_opts, inter)
|
inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc'])
|
||||||
xmppbot = XMPPBot(xmpp_opts, inter)
|
ircbot = IRCBot(irc_opts, inter)
|
||||||
inter.set_bots(ircbot, xmppbot)
|
xmppbot = XMPPBot(xmpp_opts, inter)
|
||||||
|
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()
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
slixmpp
|
sleekxmpp>=1.2.0
|
||||||
irc>=5.0.1
|
irc>=5.0.1
|
||||||
|
Loading…
Reference in New Issue
Block a user