hybridbot.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import re
  4. import signal
  5. import sys
  6. import time
  7. from threading import Thread
  8. from irc.bot import SingleServerIRCBot
  9. import sleekxmpp
  10. if sys.version_info.major >= 3:
  11. from configparser import SafeConfigParser
  12. else:
  13. from ConfigParser import SafeConfigParser
  14. class IRCBot:
  15. def __init__(self, opts, inter):
  16. self.client = SingleServerIRCBot([(opts['server'], opts['port'])],
  17. opts['nick'], opts['nick'])
  18. self.conn = self.client.connection
  19. self.opts = opts
  20. self.nick = opts['nick']
  21. self.pure_nick = self.nick
  22. self.chan = opts['chan']
  23. self.inter = inter
  24. def register_handlers(self):
  25. self.conn.add_global_handler('welcome', self.on_session_start)
  26. self.conn.add_global_handler('pubmsg', self.on_message)
  27. self.conn.add_global_handler('action', self.on_message)
  28. self.conn.add_global_handler('join', self.on_presence)
  29. self.conn.add_global_handler('part', self.on_presence)
  30. self.conn.add_global_handler('namreply', self.on_namreply)
  31. self.conn.add_global_handler('kick', self.on_kick)
  32. self.conn.add_global_handler('nicknameinuse', self.on_nicknameinuse)
  33. def join_chan(self):
  34. self.conn.part(self.chan, message='Replaced by new connection')
  35. self.conn.join(self.chan)
  36. def on_session_start(self, conn, event):
  37. print('Connected to IRC')
  38. self.join_chan()
  39. def on_message(self, conn, event):
  40. nick = event.source.split('!')[0]
  41. body = ''.join(event.arguments)
  42. typ = event.type
  43. if typ == 'action':
  44. body = '/me ' + body
  45. self.inter.relay_message('irc', nick, body)
  46. def on_presence(self, conn, event):
  47. try:
  48. typ = event.type
  49. nick = event.source.nick
  50. if typ == 'part':
  51. if nick != self.nick:
  52. if nick in self.inter.get_irc_users():
  53. self.inter.remove_irc_user(nick)
  54. else:
  55. if nick != self.nick:
  56. if nick not in self.inter.get_irc_users():
  57. self.inter.append_irc_user(nick)
  58. except Exception as e:
  59. sys.stderr.write(str(e) + '\n')
  60. def on_namreply(self, conn, event):
  61. for nick in event.arguments[2].split():
  62. if nick != conn.get_nickname():
  63. self.inter.append_irc_user(nick)
  64. def on_kick(self, conn, event):
  65. self.nick = self.pure_nick
  66. conn.nick(self.nick)
  67. time.sleep(0.5)
  68. self.join_chan()
  69. def on_nicknameinuse(self, conn, event):
  70. self.nick = self.nick + '_'
  71. conn.nick(self.nick)
  72. def send_message(self, msg, lead_prefix='', prefix=''):
  73. buf = 460
  74. result = []
  75. try:
  76. for line in msg:
  77. for i in range(0, len(line), buf):
  78. result.append(line[i:i + buf])
  79. self.conn.privmsg(self.chan, lead_prefix + result[0])
  80. for r in result[1:]:
  81. time.sleep(0.5)
  82. self.conn.privmsg(self.chan, prefix + r)
  83. except Exception as e:
  84. sys.stderr.write(str(e) + '\n')
  85. def start(self):
  86. self.register_handlers()
  87. self.client.start()
  88. class XMPPBot:
  89. def __init__(self, opts, inter):
  90. if sys.version_info.major < 3:
  91. sleekxmpp.util.misc_ops.setdefaultencoding('utf-8')
  92. self.client = sleekxmpp.ClientXMPP(opts['jid'], opts['passwd'])
  93. self.opts = opts
  94. self.nick = opts['nick']
  95. self.pure_nick = self.nick
  96. self.muc = opts['muc']
  97. self.inter = inter
  98. def register_handlers(self):
  99. self.client.add_event_handler('session_start', self.on_session_start)
  100. self.client.add_event_handler('groupchat_message', self.on_message)
  101. self.client.add_event_handler('muc::%s::presence' % self.muc,
  102. self.on_presence)
  103. def join_muc(self):
  104. muc_plugin = self.client.plugin['xep_0045']
  105. if self.muc in muc_plugin.getJoinedRooms():
  106. muc_plugin.leaveMUC(self.muc, self.nick,
  107. msg='Replaced by new connection')
  108. muc_plugin.joinMUC(self.muc, self.nick, wait=True)
  109. def on_session_start(self, event):
  110. print('Connected to XMPP')
  111. self.client.get_roster()
  112. self.client.send_presence()
  113. self.join_muc()
  114. def on_message(self, event):
  115. body = event['body']
  116. nick = event['mucnick']
  117. self.inter.relay_message('xmpp', nick, body)
  118. def on_presence(self, event):
  119. try:
  120. muc_plugin = self.client.plugin['xep_0045']
  121. typ = event['muc']['type']
  122. nick = event['muc']['nick']
  123. if not typ:
  124. typ = event['type']
  125. if not nick:
  126. nick = muc_plugin.getNick(self.muc, event['from'])
  127. if typ == 'error':
  128. if event['error']['code'] == '409':
  129. self.nick = self.nick + '_'
  130. self.join_muc()
  131. elif typ == 'unavailable':
  132. if nick != self.nick:
  133. if nick in self.inter.get_xmpp_users():
  134. self.inter.remove_xmpp_user(nick)
  135. else:
  136. self.nick = self.pure_nick
  137. time.sleep(0.5)
  138. self.join_muc()
  139. else:
  140. if nick != self.nick:
  141. if nick not in self.inter.get_xmpp_users():
  142. self.inter.append_xmpp_user(nick)
  143. except Exception as e:
  144. sys.stderr.write(str(e) + '\n')
  145. def send_message(self, msg, prefix=''):
  146. try:
  147. msg[0] = prefix + msg[0]
  148. result = '\n'.join(msg)
  149. self.client.send_message(mto=self.muc, mbody=result,
  150. mtype='groupchat')
  151. except Exception as e:
  152. sys.stderr.write(str(e) + '\n')
  153. def start(self):
  154. self.client.register_plugin('xep_0045') # XMPP MUC.
  155. self.client.register_plugin('xep_0199') # XMPP Ping.
  156. if self.client.connect():
  157. # sys.stderr.write('connected with %s\n'%con)
  158. self.register_handlers()
  159. self.client.process(block=True)
  160. else:
  161. # sys.stderr.write('could not connect!\n')
  162. sys.stderr.write('Could not connect to server, or password ' +
  163. 'mismatch!\n')
  164. sys.exit(1)
  165. class Intermedia:
  166. def __init__(self, shared_opts, irc_chan, xmpp_muc):
  167. self.irc_chan = irc_chan
  168. self.xmpp_muc = xmpp_muc
  169. self.ircbot = None
  170. self.xmppbot = None
  171. self.irc_users = []
  172. self.xmpp_users = []
  173. self.prefix = shared_opts['prefix']
  174. self.owner = shared_opts['owner']
  175. def set_bots(self, ircbot, xmppbot):
  176. self.ircbot = ircbot
  177. self.xmppbot = xmppbot
  178. def to_irc(self, msg, lead_prefix='', prefix=''):
  179. if self.ircbot:
  180. self.ircbot.send_message(msg, lead_prefix, prefix)
  181. def to_xmpp(self, msg, prefix=''):
  182. if self.xmppbot:
  183. self.xmppbot.send_message(msg, prefix)
  184. def relay_message(self, from_net, nick, body):
  185. if not self.ircbot or not self.xmppbot:
  186. return
  187. if from_net != 'irc' and from_net != 'xmpp':
  188. return
  189. if from_net == 'irc' and nick == self.ircbot.nick or \
  190. from_net == 'xmpp' and nick == self.xmppbot.nick:
  191. return
  192. if not body or len(body) <= 0:
  193. return
  194. try:
  195. msg = body.replace('\r\n', '\n').replace('\r', '\n').split('\n')
  196. if msg and len(msg) > 0:
  197. if len(msg) == 1 and msg[0] == self.prefix + 'users':
  198. irc_users = ', '.join(self.get_irc_users())
  199. xmpp_users = ', '.join(self.get_xmpp_users())
  200. if irc_users:
  201. irc_users = '[ IRC Users ] ' + irc_users
  202. if xmpp_users:
  203. xmpp_users = '[ XMPP Users ] ' + xmpp_users
  204. if from_net == 'irc':
  205. for answer in [xmpp_users]:
  206. self.to_irc([answer])
  207. elif from_net == 'xmpp':
  208. for answer in [irc_users]:
  209. self.to_xmpp([answer])
  210. elif len(msg) == 1 and msg[0] == self.prefix + 'help':
  211. answer = 'The only command I have is \'' + self.prefix + \
  212. 'users\'. Also, my owner is ' + self.owner + '.'
  213. if from_net == 'irc':
  214. self.to_irc([answer])
  215. elif from_net == 'xmpp':
  216. self.to_xmpp([answer])
  217. else:
  218. nick_prefix = '[' + nick + '] '
  219. nick_prefix_me = '***' + nick + ' '
  220. if (not re.match('^/me .+$', msg[0])):
  221. nick_prefix_lead = nick_prefix
  222. else:
  223. msg[0] = re.split('^/me ', msg[0])[1]
  224. nick_prefix_lead = nick_prefix_me
  225. if from_net == 'irc':
  226. self.to_xmpp(msg, prefix=nick_prefix_lead)
  227. elif from_net == 'xmpp':
  228. self.to_irc(msg,
  229. lead_prefix=nick_prefix_lead,
  230. prefix=nick_prefix)
  231. except Exception as e:
  232. sys.stderr.write(str(e) + '\n')
  233. def get_irc_users(self):
  234. return self.irc_users
  235. def append_irc_user(self, user):
  236. self.irc_users.append(user)
  237. def remove_irc_user(self, user):
  238. self.irc_users.remove(user)
  239. def get_xmpp_users(self):
  240. return self.xmpp_users
  241. def append_xmpp_user(self, user):
  242. self.xmpp_users.append(user)
  243. def remove_xmpp_user(self, user):
  244. self.xmpp_users.remove(user)
  245. if __name__ == '__main__':
  246. config = SafeConfigParser()
  247. shared_opts = {}
  248. xmpp_opts = {}
  249. irc_opts = {}
  250. if len(sys.argv) > 1:
  251. config.read(sys.argv[1])
  252. else:
  253. config.read('config.ini')
  254. if not config.sections():
  255. sys.stderr.write('Error: Configuration file does not exist ' +
  256. 'or is empty.\n')
  257. sys.exit(1)
  258. shared_opts['prefix'] = config.get('Shared', 'prefix')
  259. shared_opts['owner'] = config.get('Shared', 'owner')
  260. irc_opts['chan'] = config.get('IRC', 'channel')
  261. irc_opts['nick'] = config.get('IRC', 'nick')
  262. irc_opts['server'] = config.get('IRC', 'server')
  263. irc_opts['port'] = int(config.get('IRC', 'port'))
  264. xmpp_opts['jid'] = config.get('XMPP', 'jid')
  265. xmpp_opts['passwd'] = config.get('XMPP', 'password')
  266. xmpp_opts['muc'] = config.get('XMPP', 'muc')
  267. xmpp_opts['nick'] = config.get('XMPP', 'nick')
  268. signal.signal(signal.SIGINT, signal.SIG_DFL)
  269. while True:
  270. inter = Intermedia(shared_opts, irc_opts['chan'], xmpp_opts['muc'])
  271. ircbot = IRCBot(irc_opts, inter)
  272. xmppbot = XMPPBot(xmpp_opts, inter)
  273. inter.set_bots(ircbot, xmppbot)
  274. irc_thread = Thread(target=ircbot.start, args=())
  275. xmpp_thread = Thread(target=xmppbot.start, args=())
  276. irc_thread.daemon = True
  277. xmpp_thread.daemon = True
  278. irc_thread.start()
  279. time.sleep(1)
  280. xmpp_thread.start()
  281. irc_thread.join()
  282. xmpp_thread.join()