From f126d30b36382507457cdcb30ec89cc85b13046d Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 27 Feb 2014 01:11:00 +0100 Subject: [PATCH] More cleanup of irc bot code, including having it cleanly disconnect from irc channel when destroyed. --- src/commands/default/comms.py | 29 ++++++++++++++++++----------- src/server/portal/irc.py | 17 +++++++++++++++++ src/utils/evtable.py | 34 +++++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/commands/default/comms.py b/src/commands/default/comms.py index 62f19691f5..a7aef78be8 100644 --- a/src/commands/default/comms.py +++ b/src/commands/default/comms.py @@ -764,6 +764,8 @@ class CmdIRC2Chan(MuxCommand): Usage: @irc2chan[/switches] = <#irchannel> + @irc2chan/list + @irc2chan/delete botname|dbid Switches: /disconnect - this will delete the bot and remove the irc connection @@ -799,7 +801,7 @@ class CmdIRC2Chan(MuxCommand): ircbots = [bot.typeclass for bot in PlayerDB.objects.filter(db_is_bot=True)] if ircbots: from src.utils.evtable import EvTable - table = EvTable("{wdbid{n", "{wbotname{n", "{wev-channel{n", "{wirc-channel{n", border="cells") + table = EvTable("{wdbid{n", "{wbotname{n", "{wev-channel{n", "{wirc-channel{n", border="cells", maxwidth=78) for ircbot in ircbots: ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel, ircbot.db.irc_network, ircbot.db.irc_port) table.add_row(ircbot.id, ircbot.db.irc_botname, ircbot.db.ev_channel, ircinfo) @@ -808,6 +810,21 @@ class CmdIRC2Chan(MuxCommand): self.msg("No irc bots found.") return + + if('disconnect' in self.switches or 'remove' in self.switches or + 'delete' in self.switches): + botname = "ircbot-%s" % self.lhs + matches = PlayerDB.objects.filter(db_is_bot=True, db_key=botname) + if not matches: + # try dbref match + matches = PlayerDB.objects.filter(db_is_bot=True, id=self.args.lstrip("#")) + if matches: + matches[0].delete() + self.msg("IRC connection destroyed.") + else: + self.msg("IRC connection/bot could not be removed, does it exist?") + return + if not self.args or not self.rhs: string = "Usage: @irc2chan[/switches] = <#irchannel> " self.msg(string) @@ -826,16 +843,6 @@ class CmdIRC2Chan(MuxCommand): botname = "ircbot-%s" % irc_botname - if('disconnect' in self.switches or 'remove' in self.switches or - 'delete' in self.switches): - matches = PlayerDB.objects.filter(db_is_bot=True, db_key=botname) - if matches: - matches[0].delete() - self.msg("IRC connection destroyed.") - else: - self.msg("IRC connection/bot could not be removed, does it exist?") - return - # create a new bot bot = PlayerDB.objects.filter(username__iexact=botname) if bot: diff --git a/src/server/portal/irc.py b/src/server/portal/irc.py index 20f353dea7..1dba478f29 100644 --- a/src/server/portal/irc.py +++ b/src/server/portal/irc.py @@ -34,6 +34,7 @@ class IRCBot(irc.IRCClient, Session): the game as a full session. """ self.join(self.channel) + self.stopping = False self.factory.bot = self self.init_session("ircbot", self.network, self.factory.sessionhandler) # we link back to our bot and log in @@ -43,6 +44,15 @@ class IRCBot(irc.IRCClient, Session): logger.log_infomsg("IRC bot '%s' connected to %s at %s:%s." % (self.nickname, self.channel, self.network, self.port)) + def disconnect(self, reason=None): + """ + Called by sessionhandler to disconnect this protocol + """ + print "irc disconnect called!" + self.sessionhandler.disconnect(self) + self.stopping = True + self.transport.loseConnection() + def privmsg(self, user, channel, msg): "A message was sent to channel" if not msg.startswith('***'): @@ -99,6 +109,13 @@ class IRCBotFactory(protocol.ReconnectingClientFactory): "Tracks reconnections for debugging" logger.log_infomsg("(re)connecting to %s" % self.channel) + def clientConnectionFailed(self, connector, reason): + self.retry(connector) + + def clientConnectionLost(self, connector, reason): + if not self.bot.stopping: + self.retry(connector) + def start(self): "Connect session to sessionhandler" service = internet.TCPClient(self.network, self.port, self) diff --git a/src/utils/evtable.py b/src/utils/evtable.py index 85c9f09527..3dbae6a8df 100644 --- a/src/utils/evtable.py +++ b/src/utils/evtable.py @@ -75,6 +75,7 @@ ANSI-coloured string types. #from textwrap import wrap from textwrap import TextWrapper from copy import deepcopy, copy +from src.utils.utils import to_unicode from src.utils.ansi import ANSIString def make_iter(obj): @@ -86,7 +87,7 @@ def _to_ansi(obj, regexable=False): if hasattr(obj, "__iter__"): return [_to_ansi(o) for o in obj] else: - return ANSIString(unicode(obj), regexable=regexable) + return ANSIString(to_unicode(obj), regexable=regexable) _unicode = unicode @@ -644,6 +645,15 @@ class EvTable(object): corners. Default is 1. corner_char - character to use in corners when border is active. + corner_top_left - character to use in upper left corner of table + (defaults to corner_char) + corner_top_right + corner_bottom_left + corner_bottom_right + pretty_corners - (default True): use custom characters to make + the table corners look "rounded". Uses UTF-8 + characters. + header_line_char - characters to use for underlining the header row (default is '~') Requires border to be active. @@ -707,6 +717,11 @@ class EvTable(object): # border settings are passed into Cell as well (so kwargs.get and not pop) self.border_width = kwargs.get("border_width", 1) self.corner_char = kwargs.get("corner_char", "+") + pcorners = kwargs.pop("pretty_corners", False) + self.corner_top_left = _to_ansi(kwargs.pop("corner_top_left", '.' if pcorners else self.corner_char)) + self.corner_top_right = _to_ansi(kwargs.pop("corner_top_right", '.' if pcorners else self.corner_char)) + self.corner_bottom_left = _to_ansi(kwargs.pop("corner_bottom_left", '`' if pcorners else self.corner_char)) + self.corner_bottom_right = _to_ansi(kwargs.pop("corner_bottom_right", 'ยด' if pcorners else self.corner_char)) self.width = kwargs.pop("width", None) self.height = kwargs.pop("height", None) @@ -747,13 +762,13 @@ class EvTable(object): def corners(ret): "Handle corners of table" if ix == 0 and iy == 0: - ret["corner_top_left"] = cchar + ret["corner_top_left"] = self.corner_top_left if ix == nx and iy == 0: - ret["corner_top_right"] = cchar + ret["corner_top_right"] = self.corner_top_right if ix == 0 and iy == ny: - ret["corner_bottom_left"] = cchar + ret["corner_bottom_left"] = self.corner_bottom_left if ix == nx and iy == ny: - ret["corner_bottom_right"] = cchar + ret["corner_bottom_right"] = self.corner_bottom_right return ret def left_edge(ret): @@ -976,7 +991,7 @@ class EvTable(object): cell_data = [cell.get() for cell in cell_row] cell_height = min(len(lines) for lines in cell_data) for iline in range(cell_height): - yield "".join(celldata[iline] for celldata in cell_data) + yield "".join(_to_ansi(celldata[iline] for celldata in cell_data)) def add_header(self, *args, **kwargs): """ @@ -1094,6 +1109,11 @@ class EvTable(object): self.corner_char = kwargs.get("corner_char", self.corner_char) self.header_line_char = kwargs.get("header_line_char", self.header_line_char) + self.corner_top_left = _to_ansi(kwargs.pop("corner_top_left", self.corner_top_leftorner_char)) + self.corner_top_right = _to_ansi(kwargs.pop("corner_top_right", self.corner_char)) + self.corner_bottom_left = _to_ansi(kwargs.pop("corner_bottom_left", self.corner_char)) + self.corner_bottom_right = _to_ansi(kwargs.pop("corner_bottom_right", self.corner_char)) + self.options.update(kwargs) self._balance() @@ -1105,5 +1125,5 @@ class EvTable(object): def __str__(self): "print table" - return "\n".join([line for line in self._generate_lines()]) + return to_unicode("\n".join([line for line in self._generate_lines()]))