mg76

mt_irc.py

May 17th, 2020
2,080
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.99 KB | None | 0 0
  1. from __future__ import print_function
  2.  
  3. __module_name__ = "mt_irc"
  4. __module_version__ = "0.1.0"
  5. __module_description__ = "Minetest IRC Mod Support plugin for HexChat/XChat"
  6.  
  7. import xchat
  8.  
  9. try:
  10.     from ConfigParser import ConfigParser
  11. except ImportError:
  12.     from configparser import ConfigParser
  13.  
  14. import re
  15. import os
  16.  
  17. HOME = os.getenv("HOME") or "."
  18.  
  19. CFGFILES = (
  20.     os.path.join(HOME, "mt_irc.conf"),
  21.     os.path.join(HOME, ".mt_irc_rc"),
  22. )
  23.  
  24. cfg = ConfigParser()
  25. cfg.read(CFGFILES)
  26.  
  27. known_servers_map = { }
  28. known_servers_map_reverse = { }
  29.  
  30. if cfg.has_section("servers"):
  31.     for key in cfg.options("servers"):
  32.         name = cfg.get("servers", key)
  33.         known_servers_map[key.lower()] = name
  34.         known_servers_map_reverse[name.lower()] = key
  35.         print('Added server "%s" as "%s".' % (key, name))
  36.  
  37. main_re = re.compile(r"^:([^!]+)!.*")
  38.  
  39. mt_message_re = re.compile(r"^<(?P<player>[^>]+)> (?P<message>.*)")
  40. mt_action_re = re.compile(r"^\* (?P<player>[^ ]+) (?P<action>.*)")
  41. mt_join_re = re.compile(r"^\*\*\* (?P<player>[^ ]+) joined the game")
  42. mt_part_re = re.compile(r"^\*\*\* (?P<player>[^ ]+) left the game"
  43.         + r"(?P<timedout> [(]Timed out[)])?")
  44.  
  45. C1 = '\x01'
  46.  
  47. cmd = xchat.command
  48.  
  49. # channels[chan]: dict of channels
  50. # channels[chan][server]: list of idents
  51. # channels[chan][server][ident]: string
  52. channels = { }
  53.  
  54. class Server:
  55.  
  56.     def __init__(self, channame, name):
  57.         self.name = name
  58.         self.channame = channame
  59.         self.users = [ ]
  60.  
  61.     def add(self, ident):
  62.         ident_l = ident.lower()
  63.         if not ident_l in self.users:
  64.             self.users.append(ident_l)
  65.             cmd("recv :%s JOIN %s" % (ident, self.channame))
  66.  
  67.     def rem(self, ident, reason=None, force=False):
  68.         reason = (" :"+reason) if reason else ""
  69.         ident_l = ident.lower()
  70.         if force or (ident_l in self.users):
  71.             i = self.users.index(ident_l)
  72.             del self.users[i]
  73.             cmd("recv :%s QUIT%s" % (ident, reason))
  74.  
  75.     def __del__(self):
  76.         for ident in self.users[:]:
  77.             self.rem(ident, "mt_irc: server deleted", True)
  78.  
  79. class Channel:
  80.  
  81.     def __init__(self, name):
  82.         self.name = name
  83.         self.servers = { }
  84.  
  85.     def get(self, name):
  86.         name_l = name.lower()
  87.         if not name_l in self.servers:
  88.             self.servers[name_l] = Server(self.name, name)
  89.         return self.servers[name_l]
  90.  
  91.     def rem(self, name):
  92.         name_l = name.lower()
  93.         if name_l in self.servers:
  94.             del self.servers[name_l]
  95.  
  96. class ChannelList:
  97.  
  98.     def __init__(self):
  99.         self.channels = { }
  100.  
  101.     def get(self, name):
  102.         name_l = name.lower()
  103.         if not name_l in self.channels:
  104.             self.channels[name_l] = Channel(name)
  105.         return self.channels[name_l]
  106.  
  107.     def rem(self, name, force=False):
  108.         name_l = name.lower()
  109.         if force or (name_l in self.channels):
  110.             del self.channels[name_l]
  111.  
  112. chanlist = ChannelList()
  113.  
  114. def add_user(chan, server, ident):
  115.     global chanlist
  116.     chanlist.get(chan).get(server).add(ident)
  117.  
  118. def del_user(chan, server, ident, reason=None):
  119.     global chanlist
  120.     chanlist.get(chan).get(server).rem(ident, reason)
  121.  
  122. def handle_message(chan, server, ident, match):
  123.     add_user(chan, server, ident)
  124.     message = match.group("message")
  125.     cmd("recv :%s PRIVMSG %s :%s" % (ident, chan, message))
  126.  
  127. def handle_action(chan, server, ident, match):
  128.     add_user(chan, server, ident)
  129.     action = match.group("action")
  130.     cmd("recv :%s PRIVMSG %s :%sACTION %s%s" % (ident, chan, C1, action, C1))
  131.  
  132. def handle_join(chan, server, ident, match):
  133.     add_user(chan, server, ident)
  134.  
  135. def handle_part(chan, server, ident, match):
  136.     del_user(chan, server, ident, "timed out" if match.group(2) else "left")
  137.  
  138. handlers = (
  139.     ( mt_message_re, handle_message ),
  140.     ( mt_action_re,  handle_action  ),
  141.     ( mt_join_re,    handle_join    ),
  142.     ( mt_part_re,    handle_part    ),
  143. )
  144.  
  145. def quit_cb(word, word_eol, userdata):
  146.     m = main_re.match(word[0])
  147.     server = m.group(1)
  148.     server_l = server.lower()
  149.     if server_l in known_servers_map:
  150.         global chanlist
  151.         for chan in chanlist.channels:
  152.             if server_l in chanlist.channels[chan].servers:
  153.                 chanlist.get(chan).rem(server)
  154.                 return
  155.  
  156. def message_cb(word, word_eol, userdata):
  157.     m = main_re.match(word[0])
  158.     server = m.group(1)
  159.     server_l = server.lower()
  160.     if not server_l in known_servers_map:
  161.         return
  162.  
  163.     server_short = known_servers_map[server_l]
  164.  
  165.     chan = word[2].lower()
  166.     message = word_eol[3][1:]
  167.  
  168.     for handler in handlers:
  169.         regex, func = handler
  170.         mm = regex.match(message)
  171.         if mm:
  172.             player = mm.group("player")
  173.             ident = "%s@%s!%s@%s" % (player, server_short, player, server)
  174.             func(chan, server, ident, mm)
  175.             return xchat.EAT_XCHAT
  176.  
  177. pm_re = re.compile(r'^(?P<player>[^@]+)\@(?P<server>.+)$')
  178. def out_message_cb(word, word_eol, userdata):
  179.     chan = xchat.get_info("channel")
  180.     m = pm_re.match(chan)
  181.     if m:
  182.         user = m.group("player")
  183.         serv = m.group("server")
  184.         serv_l = serv.lower()
  185.         if serv_l in known_servers_map_reverse:
  186.             message = word_eol[1]
  187.             serv = known_servers_map_reverse[serv_l]
  188.             xchat.command("msg %s @%s %s" % (serv, user, message))
  189.             return xchat.EAT_XCHAT
  190.  
  191. def unload_cb(userdata):
  192.     global chanlist
  193.     print(__module_description__, "unloading...")
  194.     del chanlist
  195.     print(__module_description__, 'version', __module_version__, ' unloaded!')
  196.  
  197. subcommands = { }
  198.  
  199. def doprint(subcmd, message):
  200.     print("[mt_irc %s] %s" % (subcmd, message))
  201.  
  202. def subcmd_server(word, word_eol):
  203.     """Manage servers.
  204.  
  205.     /mt_irc server add BOTNICK ALIAS
  206.       Add a new server.
  207.  
  208.     /mt_irc server remove BOTNICK
  209.       Remove an existing server. This also causes all fake users
  210.       for that server to part the channel.
  211.     """
  212.     if len(word) > 1:
  213.         subcmd = word[1]
  214.         chan = xchat.get_info("channel")
  215.         if subcmd == "add":
  216.             if len(word) == 4:
  217.                 known_servers_map[word[2].lower()] = word[3].lower()
  218.                 known_servers_map_reverse[word[3].lower()] = word[2].lower()
  219.                 doprint('server', 'Server "%s" added as "%s".' % (word[2], word[3]))
  220.             else:
  221.                 doprint('server', 'Usage: /mt_irc server add BOTNICK ALIAS')
  222.         elif subcmd == "remove":
  223.             if len(word) == 3:
  224.                 if word[2] in known_servers_map:
  225.                     channels[chan].del_server(word[2])
  226.                     del known_servers_map_reverse[known_servers_map[word[2].lower()]]
  227.                     del known_servers_map[word[2].lower()]
  228.                     doprint('server', 'Server "%s" removed.' % word[2])
  229.                 else:
  230.                     doprint('server', 'Unknown server "%s".' % word[2])
  231.             else:
  232.                 doprint('server', 'Usage: /mt_irc server remove BOTNICK')
  233.         else:
  234.             doprint('server', 'Unknown subcommand "%s". Try "/mt_irc help server".' % subcmd)
  235.     else:
  236.         doprint('server', 'Invalid usage. Try "/mt_irc help server"')
  237.  
  238. def subcmd_info(word, word_eol):
  239.     """Show debug information.
  240.  
  241.     /mt_irc info
  242.       Show resume.
  243.  
  244.     /mt_irc info v[erbose]
  245.       Show resume.
  246.     """
  247.     if len(word) == 1:
  248.         chancount = len(chanlist.channels)
  249.         servcount = 0
  250.         usercount = 0
  251.         for chan in chanlist.channels:
  252.             servcount += len(chanlist.channels[chan].servers)
  253.             for serv in chanlist.channels[chan].servers:
  254.                 usercount += len(chanlist.channels[chan].servers[serv].users)
  255.         doprint('info', 'Totals: %d Channels, %d Servers, %d Users' % (chancount, servcount, usercount))
  256.     elif (len(word) == 2) and ((word[1] == "v") or (word[1] == "verbose")):
  257.         chancount = len(chanlist.channels)
  258.         servcount = 0
  259.         usercount = 0
  260.         for chan in chanlist.channels:
  261.             doprint('info', 'Channel %s:' % chanlist.channels[chan].name)
  262.             servcount += len(chanlist.channels[chan].servers)
  263.             for serv in chanlist.channels[chan].servers:
  264.                 doprint('info', '  Server %s:' % chanlist.channels[chan].servers[serv].name)
  265.                 usercount += len(chanlist.channels[chan].servers[serv].users)
  266.                 for user in chanlist.channels[chan].servers[serv].users:
  267.                     doprint('info', '    %s' % user)
  268.         doprint('info', 'Totals: %d Channels, %d Servers, %d Users' % (chancount, servcount, usercount))
  269.     else:
  270.         doprint('info', 'Invalid usage. Try "/mt_irc help info"')
  271.  
  272. def subcmd_help(word, word_eol):
  273.     """Get help.
  274.  
  275.     /mt_irc help [SUBCOMMAND]
  276.       Get help for a sub-command. If no subcommand is specified,
  277.       it list all supported subcommands.
  278.     """
  279.     if len(word) > 1:
  280.         topic = word[1]
  281.         if topic in subcommands:
  282.             doprint('help', subcommands[topic].__doc__)
  283.         else:
  284.             doprint('help', 'Unknown subcommand "%s". Try "/mt_irc help".' % topic)
  285.     else:
  286.         for subcmd in subcommands:
  287.             doprint('help', subcommands[subcmd].__doc__)
  288.  
  289. subcommands["server"] = subcmd_server
  290. subcommands["info"] = subcmd_info
  291. subcommands["help"] = subcmd_help
  292.  
  293. def cmd_mt_irc(word, word_eol, userdata):
  294.     """Manage mt_irc plugin configuration.
  295.  
  296.     Use "/mt_irc help" for subcommands.
  297.     """
  298.     if len(word) > 1:
  299.         subcmd = word[1]
  300.         if subcmd in subcommands:
  301.             subcmd = subcommands[subcmd]
  302.             subcmd(word[1:], word_eol[1:])
  303.         else:
  304.             print('[mt_irc] Unknown subcommand "%s". Try "/mt_irc help".')
  305.     else:
  306.         print('Usage: /mt_irc SUBCOMMAND')
  307.         print('Try "/mt_irc help".')
  308.     return xchat.EAT_XCHAT
  309.  
  310. xchat.hook_unload(unload_cb)
  311.  
  312. xchat.hook_server("PRIVMSG", message_cb)
  313. xchat.hook_server("QUIT", quit_cb)
  314.  
  315. xchat.hook_command("mt_irc", cmd_mt_irc)
  316.  
  317. xchat.hook_print("Your Message", out_message_cb)
  318.  
  319. print(__module_description__, 'version', __module_version__, ' loaded.')
Advertisement
Add Comment
Please, Sign In to add comment