Compare commits

..

7 Commits
dev ... master

Author SHA1 Message Date
Alexei Sorokin
659bc4c3aa Enable ping keepalive 2017-06-26 19:17:42 +03:00
Alexei Sorokin
0ff3f9754b Fix a reconnecting regression 2017-06-19 19:47:42 +03:00
Alexei Sorokin
33a9780d97 Improve reconnecting ability
And increase timeout to 5 seconds.
2017-06-14 21:02:26 +03:00
Alexei Sorokin
2349db325d irc: Hide threading hacks inside the class 2017-06-06 19:44:03 +03:00
Alexei Sorokin
aa67de2acc Switch to asyncio and slixmpp 2017-06-04 15:32:26 +03:00
Alexei Sorokin
b702c1af45 xmpp: Fix issues with reconnecting 2017-06-03 22:12:30 +03:00
drymer
60dfc17c67 Modify authors 2017-02-22 17:20:17 +00:00
4 changed files with 102 additions and 74 deletions

View File

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

View File

@ -1,10 +1,10 @@
# 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 sleekxmpp 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 slixmpp and irc.
su -c "pip install sleekxmpp irc"
su -c "pip3 install slixmpp irc"
# or
su -c "pip install -r requirements.txt"
su -c "pip3 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):
@ -32,10 +32,10 @@ The option "owner" is a string that will be printed when issuing the ".help" com
To execute it, just do:
python hybridbot.py myconfig.ini
python3 hybridbot.py myconfig.ini
Or if the config file is named "config.ini", just do:
python hybridbot.py
python3 hybridbot.py
And on the MUC or the IRC channel, you can issue two commands, ".help" and ".users".

View File

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

View File

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