diff --git a/README.md b/README.md index 395daf0..e282ffa 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ retr0chat is the lightweight, no-dependencies, runs-anywhere solution for when l ## compatibility -* [1980: TVI 920C](https://a.ocv.me/pub/g/nerd-stuff/r0c-tvi-920c.jpg) +* 1980: [TVI 920C](https://a.ocv.me/pub/g/nerd-stuff/r0c-tvi-920c.jpg) +* 1987: [IBM 3151](https://a.ocv.me/pub/g/nerd-stuff/r0c-ibm-3151.jpg) (also [video](https://a.ocv.me/pub/g/nerd-stuff/r0c-ibm-3151.webm)), using gnu-screen to translate VT100 sequences ## features diff --git a/r0c/irc.py b/r0c/irc.py index 8a3665b..0e3e466 100644 --- a/r0c/irc.py +++ b/r0c/irc.py @@ -232,7 +232,8 @@ class IRC_Net(object): return txt = mw[3][1:] - print("irc<%s #%s> [%s] %s" % (self.host, ch_name, nick, txt)) + txt = Util.convert_color_codes(Util.color_from_irc(txt)) + print("irc<%s #%s> [%s] %s\033[0m" % (self.host, ch_name, nick, txt)) try: nch = self.world.get_pub_chan(self.chans[ch_name].r0c_cname) self.world.send_chan_msg(nick, nch, txt, False) diff --git a/r0c/ivt100.py b/r0c/ivt100.py index 2a51384..630682a 100644 --- a/r0c/ivt100.py +++ b/r0c/ivt100.py @@ -2858,9 +2858,10 @@ class VT100_Client(object): Util.convert_color_codes(self.linebuf), ) for ircb in nch.ircb: + t = Util.color_to_irc(self.linebuf) ircb.net.say( "PRIVMSG #%s :<%s> %s" - % (ircb.irc_cname, self.user.nick, self.linebuf) + % (ircb.irc_cname, self.user.nick, t) ) self.msg_hist_n = None diff --git a/r0c/util.py b/r0c/util.py index ce27181..c59272d 100644 --- a/r0c/util.py +++ b/r0c/util.py @@ -9,6 +9,7 @@ import struct import time import sys import os +import re import platform import itertools from datetime import datetime @@ -454,14 +455,17 @@ def convert_color_codes(txt, preview=False): txt = u"%sK%s" % (txt[:ofs], txt[resume_txt:]) scan_from = 0 + is_bold = False while txt: ofs = txt.find(u"\x02", scan_from) if ofs < 0: break scan_from = ofs + 1 - txt = u"%s\033[1m%s%s" % ( + is_bold = not is_bold + txt = u"%s\033[%dm%s%s" % ( txt[:ofs], + 1 if is_bold else 22, u"B" if preview else u"", txt[scan_from:], ) @@ -482,6 +486,108 @@ def convert_color_codes(txt, preview=False): return txt +FG_TO_IRC = {} +FG_FROM_IRC = {} +for n, ch in enumerate(u"f0429153ba6ecd87"): + FG_FROM_IRC[n] = u"\x0b%s" % (ch,) + FG_TO_IRC[ch] = u"\x03%02d" % (n,) + +BG_TO_IRC = {} +BG_FROM_IRC = {} +for n, ch in enumerate(u"7042115332664507"): + BG_FROM_IRC[n] = u",%s" % (ch,) + BG_TO_IRC[ch] = u",%02d" % (n,) + +IRC_COLOR_RE = re.compile("^([0-9]{1,2})(,[0-9]{1,2})?") + + +def color_to_irc(txt): + # mostly copy-pasted from `convert_color_codes` + foregrounds = FG_TO_IRC + backgrounds = BG_TO_IRC + scan_from = 0 + while txt: + ofs = txt.find(u"\x0b", scan_from) + if ofs < 0: + break + + scan_from = ofs + 1 + + fg = None + if len(txt) > ofs + 1: + fg = txt[ofs + 1] + + bg = None + if len(txt) > ofs + 3 and txt[ofs + 2] == u",": + bg = txt[ofs + 3] + + if fg in foregrounds: + fg = foregrounds[fg] + else: + fg = None + bg = None # can't set bg without valid fg + + if bg in backgrounds: + bg = backgrounds[bg] + else: + bg = None + + resume_txt = ofs + 1 + if fg: + resume_txt += 1 + scan_from = len(fg) + 1 + if bg: + resume_txt += 2 + scan_from += len(bg) + + if fg and bg: + txt = u"%s%s%s%s" % (txt[:ofs], fg, bg, txt[resume_txt:]) + elif fg: + txt = u"%s%s%s" % (txt[:ofs], fg, txt[resume_txt:]) + else: + txt = u"%s\x03%s" % (txt[:ofs], txt[resume_txt:]) + + # irc bold == r0c, \x02 + # irc reset == r0c, \x0f + return txt + + +def color_from_irc(txt): + fgs = FG_FROM_IRC + bgs = BG_FROM_IRC + ptn = IRC_COLOR_RE + scan_from = 0 + while txt: + ofs = txt.find(u"\x03", scan_from) + if ofs < 0: + break + + fg = bg = "" + scan_from = ofs + 1 + m = ptn.search(txt[scan_from : scan_from + 5]) + if m: + i = int(m.group(1)) + if i < 16: + fg = fgs[i] + if m.group(2): + i = int(m.group(2)[1:]) + if i < 16: + bg = bgs[i] + + scan_from += len(m.group(0 if bg else 1)) + + if bg: + txt = u"%s%s%s%s" % (txt[:ofs], fg, bg, txt[scan_from:]) + elif fg: + txt = u"%s%s%s" % (txt[:ofs], fg, txt[scan_from:]) + else: + txt = u"%s\x0f%s" % (txt[:ofs], txt[scan_from:]) + # no easy/cheap way to encode "keep boldness and reset color"; + # `convert_color_codes` would then eat any following a..f + + return txt + + """ def visualize_all_unicode_codepoints_as_utf8():