Make IRCbot handler irc->ansi colors. Add support for reset etc. See #1255.
This commit is contained in:
parent
a32a6bb18e
commit
4505ace90d
2 changed files with 57 additions and 67 deletions
|
|
@ -4,7 +4,7 @@ The bot then pipes what is being said between the IRC channel and one or
|
||||||
more Evennia channels.
|
more Evennia channels.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from future.utils import viewkeys
|
from future.utils import viewkeys, viewvalues, viewitems
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from twisted.application import internet
|
from twisted.application import internet
|
||||||
|
|
@ -41,55 +41,21 @@ IRC_DGREY = "14"
|
||||||
IRC_GRAY = "15"
|
IRC_GRAY = "15"
|
||||||
|
|
||||||
# obsolete test:
|
# obsolete test:
|
||||||
# {rred {ggreen {yyellow {bblue {mmagenta {ccyan {wwhite {xdgrey
|
|
||||||
# {Rdred {Gdgreen {Ydyellow {Bdblue {Mdmagenta {Cdcyan {Wlgrey {Xblack
|
|
||||||
# {[rredbg {[ggreenbg {[yyellowbg {[bbluebg {[mmagentabg {[ccyanbg {[wlgreybg {[xblackbg
|
|
||||||
|
|
||||||
# test:
|
# test evennia->irc:
|
||||||
# |rred |ggreen |yyellow |bblue |mmagenta |ccyan |wwhite |xdgrey
|
# |rred |ggreen |yyellow |bblue |mmagenta |ccyan |wwhite |xdgrey
|
||||||
# |Rdred |Gdgreen |Ydyellow |Bdblue |Mdmagenta |Cdcyan |Wlgrey |Xblack
|
# |Rdred |Gdgreen |Ydyellow |Bdblue |Mdmagenta |Cdcyan |Wlgrey |Xblack
|
||||||
# |[rredbg |[ggreenbg |[yyellowbg |[bbluebg |[mmagentabg |[ccyanbg |[wlgreybg |[xblackbg
|
# |[rredbg |[ggreenbg |[yyellowbg |[bbluebg |[mmagentabg |[ccyanbg |[wlgreybg |[xblackbg
|
||||||
|
|
||||||
IRC_COLOR_MAP = dict([
|
# test irc->evennia
|
||||||
# obs - {-type colors are deprecated but still used in many places.
|
# Use Ctrl+C <num> to produce mIRC colors in e.g. irssi
|
||||||
(r'{n', IRC_RESET), # reset
|
|
||||||
(r'{/', ""), # line break
|
|
||||||
(r'{-', " "), # tab
|
|
||||||
(r'{_', " "), # space
|
|
||||||
(r'{*', ""), # invert
|
|
||||||
(r'{^', ""), # blinking text
|
|
||||||
|
|
||||||
(r'{r', IRC_COLOR + IRC_RED),
|
IRC_COLOR_MAP = dict((
|
||||||
(r'{g', IRC_COLOR + IRC_GREEN),
|
(r'|n', IRC_COLOR + IRC_NORMAL), # normal mode
|
||||||
(r'{y', IRC_COLOR + IRC_YELLOW),
|
(r'|H', IRC_RESET), # un-highlight
|
||||||
(r'{b', IRC_COLOR + IRC_BLUE),
|
(r'|/', "\n"), # line break
|
||||||
(r'{m', IRC_COLOR + IRC_MAGENTA),
|
(r'|t', " "), # tab
|
||||||
(r'{c', IRC_COLOR + IRC_CYAN),
|
(r'|-', " "), # fixed tab
|
||||||
(r'{w', IRC_COLOR + IRC_WHITE), # pure white
|
|
||||||
(r'{x', IRC_COLOR + IRC_DGREY), # dark grey
|
|
||||||
|
|
||||||
(r'{R', IRC_COLOR + IRC_DRED),
|
|
||||||
(r'{G', IRC_COLOR + IRC_DGREEN),
|
|
||||||
(r'{Y', IRC_COLOR + IRC_DYELLOW),
|
|
||||||
(r'{B', IRC_COLOR + IRC_DBLUE),
|
|
||||||
(r'{M', IRC_COLOR + IRC_DMAGENTA),
|
|
||||||
(r'{C', IRC_COLOR + IRC_DCYAN),
|
|
||||||
(r'{W', IRC_COLOR + IRC_GRAY), # light grey
|
|
||||||
(r'{X', IRC_COLOR + IRC_BLACK), # pure black
|
|
||||||
|
|
||||||
(r'{[r', IRC_COLOR + IRC_NORMAL + "," + IRC_DRED),
|
|
||||||
(r'{[g', IRC_COLOR + IRC_NORMAL + "," + IRC_DGREEN),
|
|
||||||
(r'{[y', IRC_COLOR + IRC_NORMAL + "," + IRC_DYELLOW),
|
|
||||||
(r'{[b', IRC_COLOR + IRC_NORMAL + "," + IRC_DBLUE),
|
|
||||||
(r'{[m', IRC_COLOR + IRC_NORMAL + "," + IRC_DMAGENTA),
|
|
||||||
(r'{[c', IRC_COLOR + IRC_NORMAL + "," + IRC_DCYAN),
|
|
||||||
(r'{[w', IRC_COLOR + IRC_NORMAL + "," + IRC_GRAY), # light grey background
|
|
||||||
(r'{[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK), # pure black background
|
|
||||||
|
|
||||||
# |-type formatting is the thing to use.
|
|
||||||
(r'|n', IRC_RESET), # reset
|
|
||||||
(r'|/', ""), # line break
|
|
||||||
(r'|-', " "), # tab
|
|
||||||
(r'|_', " "), # space
|
(r'|_', " "), # space
|
||||||
(r'|*', ""), # invert
|
(r'|*', ""), # invert
|
||||||
(r'|^', ""), # blinking text
|
(r'|^', ""), # blinking text
|
||||||
|
|
@ -121,29 +87,49 @@ IRC_COLOR_MAP = dict([
|
||||||
(r'|[c', IRC_COLOR + IRC_NORMAL + "," + IRC_DCYAN),
|
(r'|[c', IRC_COLOR + IRC_NORMAL + "," + IRC_DCYAN),
|
||||||
(r'|[w', IRC_COLOR + IRC_NORMAL + "," + IRC_GRAY), # light grey background
|
(r'|[w', IRC_COLOR + IRC_NORMAL + "," + IRC_GRAY), # light grey background
|
||||||
(r'|[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK) # pure black background
|
(r'|[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK) # pure black background
|
||||||
])
|
))
|
||||||
RE_IRC_COLOR = re.compile(r"|".join([re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL)
|
# ansi->irc
|
||||||
|
RE_ANSI_COLOR = re.compile(r"|".join(
|
||||||
|
[re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL)
|
||||||
RE_MXP = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL)
|
RE_MXP = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL)
|
||||||
RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL)
|
RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL)
|
||||||
|
# irc->ansi
|
||||||
|
_CLR_LIST = [re.escape(val)
|
||||||
|
for val in sorted(viewvalues(IRC_COLOR_MAP), key=len, reverse=True) if val.strip()]
|
||||||
|
_CLR_LIST = _CLR_LIST[-2:] + _CLR_LIST[:-2]
|
||||||
|
RE_IRC_COLOR = re.compile(r"|".join(_CLR_LIST), re.DOTALL)
|
||||||
|
ANSI_COLOR_MAP = dict((tup[1], tup[0]) for tup in viewitems(IRC_COLOR_MAP) if tup[1].strip())
|
||||||
|
|
||||||
|
|
||||||
def sub_irc(ircmatch):
|
def parse_ansi_to_irc(string):
|
||||||
"""
|
"""
|
||||||
Substitute irc color info. Used by re.sub.
|
Parse |-type syntax and replace with IRC color markers
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ircmatch (Match): The match from regex.
|
string (str): String to parse for ANSI colors.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
colored (str): A string with converted IRC colors.
|
parsed_string (str): String with replaced ANSI colors.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return IRC_COLOR_MAP.get(ircmatch.group(), "")
|
|
||||||
|
def _sub_to_irc(ansi_match):
|
||||||
|
return IRC_COLOR_MAP.get(ansi_match.group(), "")
|
||||||
|
|
||||||
|
in_string = utils.to_str(string)
|
||||||
|
parsed_string = []
|
||||||
|
parts = RE_ANSI_ESCAPES.split(in_string) + [" "]
|
||||||
|
for part, sep in zip(parts[::2], parts[1::2]):
|
||||||
|
pstring = RE_ANSI_COLOR.sub(_sub_to_irc, part)
|
||||||
|
parsed_string.append("%s%s" % (pstring, sep[0].strip()))
|
||||||
|
# strip mxp
|
||||||
|
parsed_string = RE_MXP.sub(r'\2', "".join(parsed_string))
|
||||||
|
return parsed_string
|
||||||
|
|
||||||
|
|
||||||
def parse_irc_colors(string):
|
def parse_irc_to_ansi(string):
|
||||||
"""
|
"""
|
||||||
Parse {-type syntax and replace with IRC color markers
|
Parse IRC mIRC color syntax and replace with Evennia ANSI color markers
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
string (str): String to parse for IRC colors.
|
string (str): String to parse for IRC colors.
|
||||||
|
|
@ -152,19 +138,19 @@ def parse_irc_colors(string):
|
||||||
parsed_string (str): String with replaced IRC colors.
|
parsed_string (str): String with replaced IRC colors.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _sub_to_ansi(irc_match):
|
||||||
|
return ANSI_COLOR_MAP.get(irc_match.group(), "")
|
||||||
|
|
||||||
in_string = utils.to_str(string)
|
in_string = utils.to_str(string)
|
||||||
parsed_string = ""
|
print("parse_irc_to_ansi (before): %s" % in_string)
|
||||||
parts = RE_ANSI_ESCAPES.split(in_string) + [" "]
|
pstring = RE_IRC_COLOR.sub(_sub_to_ansi, in_string)
|
||||||
for part, sep in zip(parts[::2], parts[1::2]):
|
print("parse_irc_to_ansi (after): %s" % pstring)
|
||||||
pstring = RE_IRC_COLOR.sub(sub_irc, part)
|
return pstring
|
||||||
parsed_string += "%s%s" % (pstring, sep[0].strip())
|
|
||||||
# strip mxp
|
|
||||||
parsed_string = RE_MXP.sub(r'\2', parsed_string)
|
|
||||||
return parsed_string
|
|
||||||
|
|
||||||
# IRC bot
|
# IRC bot
|
||||||
|
|
||||||
|
|
||||||
class IRCBot(irc.IRCClient, Session):
|
class IRCBot(irc.IRCClient, Session):
|
||||||
"""
|
"""
|
||||||
An IRC bot that tracks activity in a channel as well
|
An IRC bot that tracks activity in a channel as well
|
||||||
|
|
@ -211,6 +197,9 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
|
def at_login(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def privmsg(self, user, channel, msg):
|
def privmsg(self, user, channel, msg):
|
||||||
"""
|
"""
|
||||||
Called when the connected channel receives a message.
|
Called when the connected channel receives a message.
|
||||||
|
|
@ -289,7 +278,7 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
kwargs (any): Other data from protocol.
|
kwargs (any): Other data from protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.sessionhandler.data_in(self, bot_data_in=[text, kwargs])
|
self.sessionhandler.data_in(self, bot_data_in=[parse_irc_to_ansi(text), kwargs])
|
||||||
|
|
||||||
def send_channel(self, *args, **kwargs):
|
def send_channel(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -305,7 +294,7 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
"""
|
"""
|
||||||
text = args[0] if args else ""
|
text = args[0] if args else ""
|
||||||
if text:
|
if text:
|
||||||
text = parse_irc_colors(text)
|
text = parse_ansi_to_irc(text)
|
||||||
self.say(self.channel, text)
|
self.say(self.channel, text)
|
||||||
|
|
||||||
def send_privmsg(self, *args, **kwargs):
|
def send_privmsg(self, *args, **kwargs):
|
||||||
|
|
@ -323,7 +312,7 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
text = args[0] if args else ""
|
text = args[0] if args else ""
|
||||||
user = kwargs.get("user", None)
|
user = kwargs.get("user", None)
|
||||||
if text and user:
|
if text and user:
|
||||||
text = parse_irc_colors(text)
|
text = parse_ansi_to_irc(text)
|
||||||
self.msg(user, text)
|
self.msg(user, text)
|
||||||
|
|
||||||
def send_request_nicklist(self, *args, **kwargs):
|
def send_request_nicklist(self, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -432,7 +432,7 @@ class ANSIParser(object):
|
||||||
in_string = utils.to_str(string)
|
in_string = utils.to_str(string)
|
||||||
|
|
||||||
# do string replacement
|
# do string replacement
|
||||||
parsed_string = ""
|
parsed_string = []
|
||||||
parts = self.ansi_escapes.split(in_string) + [" "]
|
parts = self.ansi_escapes.split(in_string) + [" "]
|
||||||
for part, sep in zip(parts[::2], parts[1::2]):
|
for part, sep in zip(parts[::2], parts[1::2]):
|
||||||
pstring = self.xterm256_fg_sub.sub(do_xterm256_fg, part)
|
pstring = self.xterm256_fg_sub.sub(do_xterm256_fg, part)
|
||||||
|
|
@ -440,7 +440,8 @@ class ANSIParser(object):
|
||||||
pstring = self.xterm256_gfg_sub.sub(do_xterm256_gfg, pstring)
|
pstring = self.xterm256_gfg_sub.sub(do_xterm256_gfg, pstring)
|
||||||
pstring = self.xterm256_gbg_sub.sub(do_xterm256_gbg, pstring)
|
pstring = self.xterm256_gbg_sub.sub(do_xterm256_gbg, pstring)
|
||||||
pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
|
pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
|
||||||
parsed_string += "%s%s" % (pstring, sep[0].strip())
|
parsed_string.append("%s%s" % (pstring, sep[0].strip()))
|
||||||
|
parsed_string = "".join(parsed_string)
|
||||||
|
|
||||||
if not mxp:
|
if not mxp:
|
||||||
parsed_string = self.strip_mxp(parsed_string)
|
parsed_string = self.strip_mxp(parsed_string)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue