123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- import asyncio
- import re
- import signal
- import sys
- import time
- import slixmpp
- from configparser import ConfigParser
- from irc.bot import SingleServerIRCBot
- class IRCBot:
- def __init__(self, opts, inter):
- self.client = SingleServerIRCBot([(opts['server'], opts['port'])],
- opts['nick'], opts['realname'])
- self.conn = self.client.connection
- self.nick = opts['nick']
- self.pure_nick = self.nick
- self.chan = opts['chan']
-
-
-
-
-
-
- self.auth_bot_name = opts['auth_bot_name']
- self.auth_bot_privmsg = opts['auth_bot_privmsg']
- self.msg_part_max_bytes = opts['msg_part_max_bytes']
- self.msg_multipart_sleep_seconds = opts['msg_multipart_sleep_seconds']
- self.inter = inter
- self.loop = None
- def register_handlers(self):
- self.conn.add_global_handler('welcome', self.on_session_start)
-
-
- self.conn.add_global_handler('disconnect', self.on_session_start)
- self.conn.add_global_handler('pubmsg', self.on_message)
- self.conn.add_global_handler('action', self.on_message)
- self.conn.add_global_handler('join', self.on_presence)
- self.conn.add_global_handler('part', self.on_presence)
- self.conn.add_global_handler('namreply', self.on_namreply)
- self.conn.add_global_handler('kick', self.on_kick)
- self.conn.add_global_handler('nicknameinuse', self.on_nicknameinuse)
- def join_chan(self):
- self.conn.part(self.chan, message='Replaced by new connection')
- self.conn.join(self.chan)
- def on_session_start(self, conn, event):
- print('Connected to IRC')
-
-
- self.conn.privmsg(
- self.auth_bot_name,
- self.auth_bot_privmsg
- )
- self.join_chan()
- def on_message(self, conn, event):
- nick = event.source.split('!')[0]
- body = ''.join(event.arguments)
- typ = event.type
- if typ == 'action':
- body = '/me ' + body
- self.call_outside(self.inter.relay_message, 'irc', nick, body)
- def on_presence(self, conn, event):
- try:
- typ = event.type
- nick = event.source.nick
- if typ == 'part':
- if nick != self.nick:
- if nick in self.inter.get_irc_users():
- self.call_outside(self.inter.remove_irc_user, nick)
- else:
- if nick != self.nick:
- if nick not in self.inter.get_irc_users():
- self.call_outside(self.inter.append_irc_user, nick)
- except Exception as e:
- print(str(e), file=sys.stderr)
- def on_namreply(self, conn, event):
- for nick in event.arguments[2].split():
- if nick != conn.get_nickname():
- self.call_outside(self.inter.append_irc_user, nick)
- def on_kick(self, conn, event):
- self.nick = self.pure_nick
- conn.nick(self.nick)
- time.sleep(0.5)
- self.join_chan()
- def on_nicknameinuse(self, conn, event):
- self.nick = self.nick + '_'
- conn.nick(self.nick)
- def send_message(self, msg, lead_prefix='', prefix=''):
-
-
-
-
-
-
-
-
-
-
-
- buf = min(460, self.msg_part_max_bytes) - len(' '.join(['PRIVMSG', ':' + self.nick, ':' + self.chan])) - len(b'\r\n')
- result = []
- try:
- for line in msg:
-
-
-
-
- for i in range(0, len(line), buf - max(len(prefix), len(lead_prefix))):
- result.append(line[i:i + buf - max(len(prefix), len(lead_prefix))])
- self.conn.privmsg(self.chan, lead_prefix + result[0])
- for r in result[1:]:
- time.sleep(max(0.5, self.msg_multipart_sleep_seconds))
- self.conn.privmsg(self.chan, prefix + r)
- except Exception as e:
- print(str(e), file=sys.stderr)
- def call_outside(self, func, *args):
- assert self.loop
- self.loop.call_soon_threadsafe(func, *args)
- async def run(self):
- self.loop = asyncio.get_event_loop()
- self.register_handlers()
- await self.loop.run_in_executor(None, self.client.start)
- class XMPPBot:
- def __init__(self, opts, inter, timeout=5.0):
- self.client = slixmpp.ClientXMPP(opts['jid'], opts['passwd'])
- self.client.register_plugin('xep_0199')
- self.client_ping = self.client.plugin['xep_0199']
- self.client.register_plugin('xep_0045')
- 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.nick = opts['nick']
- self.pure_nick = self.nick
- self.muc = opts['muc']
- self.inter = inter
- 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('got_online', self.on_got_online)
- self.client.add_event_handler('disconnected', self.on_disconnected)
- self.client.add_event_handler('reconnect_delay', self.on_disconnected)
- self.client.add_event_handler('groupchat_message', self.on_message)
- self.client.add_event_handler('muc::%s::presence' % self.muc,
- self.on_presence)
- def connect(self):
- self.client.connect()
- def join_muc(self):
- async def loop_cycle():
- if self._join_muc_block > 0:
- return
- self._join_muc_block += 1
- while not self.muc_is_joined:
- self.client_muc.join_muc(self.muc, self.nick)
- await asyncio.sleep(self.timeout)
- self._join_muc_block -= 1
- asyncio.ensure_future(loop_cycle())
- _join_muc_block = 0
- def on_failed_all_auth(event):
-
- print('Could not connect to the server, or password mismatch!',
- file=sys.stderr)
- sys.exit(1)
- def on_session_start(self, event):
-
- print('Connected to XMPP')
- self.client.get_roster()
- self.client.send_presence()
- def on_got_online(self, event):
- self.join_muc()
- async def on_disconnected(self, event):
- self.muc_is_joined = False
- print('Connection lost, reattempting in %d seconds' % self.timeout)
- await asyncio.sleep(self.timeout)
- self.connect()
- def on_message(self, event):
- body = event['body']
- nick = event['mucnick']
- self.inter.relay_message('xmpp', nick, body)
- async def on_presence(self, event):
- try:
- muc_plugin = self.client.plugin['xep_0045']
- typ = event['muc']['type']
- nick = event['muc']['nick']
- if not typ:
- typ = event['type']
- if not nick:
- nick = muc_plugin.get_nick(self.muc, event['from'])
- if typ == 'available':
- self.muc_is_joined = True
- if nick != self.nick:
- if nick not in self.inter.get_xmpp_users():
- self.inter.append_xmpp_user(nick)
- elif typ == 'error':
- self.muc_is_joined = False
- if event['error']['code'] == '409':
- self.nick += '_'
- self.join_muc()
- elif typ == 'unavailable':
- if nick != self.nick:
- if nick in self.inter.get_xmpp_users():
- self.inter.remove_xmpp_user(nick)
- else:
- self.muc_is_joined = False
- self.nick = self.pure_nick
- await asyncio.sleep(0.5)
- self.join_muc()
- else:
- if nick != self.nick:
- if nick not in self.inter.get_xmpp_users():
- self.inter.append_xmpp_user(nick)
- except Exception as e:
- print(str(e), file=sys.stderr)
- def send_message(self, msg, prefix=''):
- try:
- msg[0] = prefix + msg[0]
- result = '\n'.join(msg)
- self.client.send_message(mto=self.muc, mbody=result,
- mtype='groupchat')
- except Exception as e:
- print(str(e), file=sys.stderr)
- async def run(self):
- self.register_handlers()
- self.connect()
- class Intermedia:
- def __init__(self, shared_opts, irc_chan, xmpp_muc):
- self.irc_chan = irc_chan
- self.xmpp_muc = xmpp_muc
- self.ircbot = None
- self.xmppbot = None
- self.irc_users = []
- self.xmpp_users = []
- self.prefix = shared_opts['prefix']
- self.owner = shared_opts['owner']
- def set_bots(self, ircbot, xmppbot):
- self.ircbot = ircbot
- self.xmppbot = xmppbot
- def to_irc(self, msg, lead_prefix='', prefix=''):
- if self.ircbot:
- self.ircbot.send_message(msg, lead_prefix, prefix)
- def to_xmpp(self, msg, prefix=''):
- if self.xmppbot:
- self.xmppbot.send_message(msg, prefix)
- def relay_message(self, from_net, nick, body):
- if not self.ircbot or not self.xmppbot:
- return
- if from_net != 'irc' and from_net != 'xmpp':
- return
- if from_net == 'irc' and nick == self.ircbot.nick or \
- from_net == 'xmpp' and nick == self.xmppbot.nick:
- return
- if not body or len(body) <= 0:
- return
- try:
- msg = body.replace('\r\n', '\n').replace('\r', '\n').split('\n')
- if msg and len(msg) > 0:
- if len(msg) == 1 and msg[0] == self.prefix + 'users':
- irc_users = ', '.join(self.get_irc_users())
- xmpp_users = ', '.join(self.get_xmpp_users())
- if irc_users:
- irc_users = '[ IRC Users ] ' + irc_users
- if xmpp_users:
- xmpp_users = '[ XMPP Users ] ' + xmpp_users
- if from_net == 'irc':
- for answer in [xmpp_users]:
- self.to_irc([answer])
- elif from_net == 'xmpp':
- for answer in [irc_users]:
- self.to_xmpp([answer])
- elif len(msg) == 1 and msg[0] == self.prefix + 'help':
- answer = 'The only command I have is \'' + self.prefix + \
- 'users\'. Also, my owner is ' + self.owner + '.'
- if from_net == 'irc':
- self.to_irc([answer])
- elif from_net == 'xmpp':
- self.to_xmpp([answer])
- else:
- nick_prefix = '[' + nick + '] '
- nick_prefix_me = '***' + nick + ' '
- if (not re.match('^/me .+$', msg[0])):
- nick_prefix_lead = nick_prefix
- else:
- msg[0] = re.split('^/me ', msg[0])[1]
- nick_prefix_lead = nick_prefix_me
- if from_net == 'irc':
- self.to_xmpp(msg, prefix=nick_prefix_lead)
- elif from_net == 'xmpp':
- self.to_irc(msg,
- lead_prefix=nick_prefix_lead,
- prefix=nick_prefix)
- except Exception as e:
- print(str(e), file=sys.stderr)
- def get_irc_users(self):
- return self.irc_users
- def append_irc_user(self, user):
- self.irc_users.append(user)
- def remove_irc_user(self, user):
- self.irc_users.remove(user)
- def get_xmpp_users(self):
- return self.xmpp_users
- def append_xmpp_user(self, user):
- self.xmpp_users.append(user)
- def remove_xmpp_user(self, user):
- self.xmpp_users.remove(user)
- if __name__ == '__main__':
- config = ConfigParser()
- shared_opts = {}
- xmpp_opts = {}
- irc_opts = {}
- if len(sys.argv) > 1:
- config.read(sys.argv[1])
- else:
- config.read('config.ini')
- if not config.sections():
- print('Error: Configuration file does not exist or is empty.',
- file=sys.stderr)
- sys.exit(1)
- shared_opts['prefix'] = config.get('Shared', 'prefix')
- shared_opts['owner'] = config.get('Shared', 'owner')
- irc_opts['chan'] = config.get('IRC', 'channel')
- irc_opts['nick'] = config.get('IRC', 'nick')
-
-
- irc_opts['realname'] = config.get('IRC', 'realname', fallback = irc_opts['nick'])
- irc_opts['server'] = config.get('IRC', 'server')
- irc_opts['port'] = int(config.get('IRC', 'port'))
-
-
-
-
-
- irc_opts['auth_bot_name'] = config.get('IRC', 'auth_bot_name', fallback = 'NickServ')
- irc_opts['auth_bot_privmsg'] = config.get('IRC', 'auth_bot_privmsg')
- irc_opts['msg_part_max_bytes'] = int(config.get('IRC', 'msg_part_max_bytes', fallback = 460))
- irc_opts['msg_multipart_sleep_seconds'] = float(config.get('IRC', 'msg_multipart_sleep_seconds', fallback = 0.5))
- xmpp_opts['jid'] = config.get('XMPP', 'jid')
- xmpp_opts['passwd'] = config.get('XMPP', 'password')
- xmpp_opts['muc'] = config.get('XMPP', 'muc')
- xmpp_opts['nick'] = config.get('XMPP', 'nick')
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- loop = asyncio.get_event_loop()
- inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc'])
- ircbot = IRCBot(irc_opts, inter)
- xmppbot = XMPPBot(xmpp_opts, inter)
- inter.set_bots(ircbot, xmppbot)
- asyncio.ensure_future(xmppbot.run())
- asyncio.ensure_future(ircbot.run())
- loop.run_forever()
|