Permalink
Browse files

Add irc.utils.irc_color_to_ansi() (#249)

from circuits.protocols.irc.utils import irc_color_to_ansi
colorstring = '\x0300white\x03 \x0301black\x03 \x0302blue\x03 \x0303green\x03 \x0304red\x03 '
colorstring += '\x0305brown\x03 \x0306magenta\x03 \x0307orange\x03 \x0308yellow\x03 '
colorstring += '\x0309lightgreen\x03 \x0310cyan\x03 \x0311lightcyan\x03 \x0312lightblue\x03 '
colorstring += '\x0313pink\x03 \x0314grey\x03 \x0315lightgrey\x03\x0f'
print(irc_color_to_ansi('hi \x02bold\x02 \x1ditalic\x1d \x1funderline\x1f \x1estrikethrough\x1e'))
print(irc_color_to_ansi(colorstring))
print(irc_color_to_ansi(colorstring.replace(' \x03', ' \x0301,')))
  • Loading branch information...
spaceone authored and prologic committed Sep 18, 2018
1 parent 4565164 commit 4923743d44fe2739cfe37765e7514539d5724431
Showing with 104 additions and 2 deletions.
  1. +1 −1 circuits/protocols/irc/__init__.py
  2. +88 −0 circuits/protocols/irc/utils.py
  3. +15 −1 tests/protocols/test_irc.py
@@ -9,7 +9,7 @@
from .message import Message # noqa
from .numerics import * # noqa
from .protocol import IRC # noqa
from .utils import joinprefix, parsemsg, parseprefix, strip # noqa
from .utils import joinprefix, parsemsg, parseprefix, strip, irc_color_to_ansi # noqa

sourceJoin = joinprefix
sourceSplit = parseprefix
@@ -6,6 +6,7 @@
from circuits.six import u

PREFIX = compile_regex("([^!].*)!(.*)@(.*)")
COLOR_CODE = compile_regex('(?:(\d\d?)(?:(,)(\d\d?))?)?')


class Error(Exception):
@@ -94,3 +95,90 @@ def parsemsg(s, encoding="utf-8"):
command = command and str(command)

return prefix, command, list(args)


def irc_color_to_ansi(data, reset=True):
"""Maps IRC color codes to ANSI terminal escape sequences"""

def ansi(*seq):
return u("\33[{}m").format(u(";").join(u("{:02}").format(x) for x in seq if x))

ansi_default_fg = 39
ansi_default_bg = 49
color_map_fg = {
0: 37, 1: 30, 2: 34, 3: 32, 4: 31, 5: 36, 6: 35, 7: 33, 8: 93, 9: 92,
10: 36, 11: 96, 12: 94, 13: 95, 14: 90, 15: 37,
16: 52, 17: 94, 18: 100, 19: 58, 20: 22, 21: 29, 22: 23, 23: 24,
24: 17, 25: 54, 26: 53, 27: 89, 28: 88, 29: 130, 30: 142, 31: 64,
32: 28, 33: 35, 34: 30, 35: 25, 36: 18, 37: 91, 38: 90, 39: 125,
40: 124, 41: 166, 42: 184, 43: 106, 44: 34, 45: 49, 46: 37, 47: 33,
48: 19, 49: 129, 50: 127, 51: 161, 52: 196, 53: 208, 54: 226,
55: 154, 56: 46, 57: 86, 58: 51, 59: 75, 60: 21, 61: 171, 62: 201,
63: 198, 64: 203, 65: 215, 66: 227, 67: 191, 68: 83, 69: 122, 70: 87,
71: 111, 72: 63, 73: 177, 74: 207, 75: 205, 76: 217, 77: 223, 78: 229,
79: 193, 80: 157, 81: 158, 82: 159, 83: 153, 84: 147, 85: 183, 86: 219,
87: 212, 88: 16, 89: 233, 90: 235, 91: 237, 92: 239, 93: 241, 94: 244,
95: 247, 96: 250, 97: 254, 98: 231, 99: ansi_default_fg
}
color_map_bg = {
0: 47, 1: 40, 2: 44, 3: 42, 4: 41, 5: 46, 6: 45, 7: 43, 10: 46, 14: 97, 15: 47, 99: ansi_default_bg
}

enable_char = {
u('\x16'): 1, u('\x1d'): 3, u('\x1e'): 9, u('\x1f'): 4, u('\x16'): 7
}
revert_char = {
u('\x02'): 22, u('\x1d'): 23, u('\x1f'): 24, u('\x16'): 27, u('\x1e'): 29
}

def escape(data):
ignore = set()
start = []
current_fg = ansi_default_fg
current_bg = ansi_default_bg
for i, char in enumerate(data):
if i in ignore:
continue
if char == u('\x0f'): # reset
start = []
yield ansi(0)
elif char in start and char in revert_char:
start.remove(char)
yield ansi(revert_char[char])
elif char in enable_char:
start.append(char)
yield ansi(enable_char[char])
elif char == u('\x03'):
i += 1
m = COLOR_CODE.match(data[i:i + 5])
colors = []
if m:
fg, has_bg, bg = m.groups()
if fg:
ignore.update(range(i, i + len(fg)))
colors.append(color_map_fg.get(int(fg), current_fg))
current_fg = int(fg)
if has_bg:
ignore.update(range(i + len(fg), i + len(fg) + 1))
if bg:
ignore.update(range(i + len(fg) + 1, i + len(fg) + 1 + len(bg)))
colors.append(color_map_bg.get(int(bg), current_bg))
current_bg = int(bg)
if char in start:
start.remove(char)
if colors:
start.append(char)
yield ansi(*colors)
else:
yield ansi(ansi_default_fg, ansi_default_bg)
#elif char == u('\x04'):
# if char[i + 1:i + 6].isdigit():
# ignore.update(range(i, i + 6))
# # TODO: parse hex representation
#elif char == u('\x11'): # monospace
# start.append(char)
else:
yield char
if start and reset:
yield ansi(0)
return u("").join(escape(data))
@@ -7,7 +7,7 @@
from circuits.protocols.irc import (
AWAY, INVITE, IRC, JOIN, KICK, MODE, NAMES, NICK, NOTICE, PART, PASS, PONG,
PRIVMSG, QUIT, TOPIC, USER, WHOIS, joinprefix, parsemsg, parseprefix,
strip,
strip, irc_color_to_ansi
)
from circuits.six import b, u

@@ -145,3 +145,17 @@ def test_responses(app, data, event):
assert event.name == e.name
assert event.args == e.args
assert event.kwargs == e.kwargs


@pytest.mark.parametrize('inp,out', [
('hi \x02bold\x02 \x1ditalic\x1d \x1funderline\x1f \x1estrikethrough\x1e', 'hi \x02bold\x02 \x1b[03mitalic\x1b[23m \x1b[04munderline\x1b[24m \x1b[09mstrikethrough\x1b[29m'),
('\x0300white\x03 \x0301black\x03 \x0302blue\x03 \x0303green\x03 \x0304red\x03 ', '\x1b[37mwhite\x1b[39;49m \x1b[30mblack\x1b[39;49m \x1b[34mblue\x1b[39;49m \x1b[32mgreen\x1b[39;49m \x1b[31mred\x1b[39;49m '),
('\x0305brown\x03 \x0306magenta\x03 \x0307orange\x03 \x0308yellow\x03 ', '\x1b[36mbrown\x1b[39;49m \x1b[35mmagenta\x1b[39;49m \x1b[33morange\x1b[39;49m \x1b[93myellow\x1b[39;49m '),
('\x0309lightgreen\x03 \x0310cyan\x03 \x0311lightcyan\x03 \x0312lightblue\x03 ', '\x1b[92mlightgreen\x1b[39;49m \x1b[36mcyan\x1b[39;49m \x1b[96mlightcyan\x1b[39;49m \x1b[94mlightblue\x1b[39;49m '),
('\x0313pink\x03 \x0314grey\x03 \x0315lightgrey\x03', '\x1b[95mpink\x1b[39;49m \x1b[90mgrey\x1b[39;49m \x1b[37mlightgrey\x1b[39;49m'),
('\x0300white\x03 \x0301,01black\x03 \x0301,02blue\x03 \x0301,03green\x03 \x0301,04red\x03 ', '\x1b[37mwhite\x1b[39;49m \x1b[30;40mblack\x1b[39;49m \x1b[30;44mblue\x1b[39;49m \x1b[30;42mgreen\x1b[39;49m \x1b[30;41mred\x1b[39;49m '),
('\x0f', '\x1b[m'),
('\x0302blue', '\x1b[34mblue\x1b[m'),
])
def test_ansi_color(inp, out):
assert irc_color_to_ansi(inp) == out

0 comments on commit 4923743

Please sign in to comment.