def parsemsg(s, encoding="utf-8"): """Parse an IRC Message from s :param s bytes: bytes to parse :param encoding str: encoding to use (Default: utf-8) :returns tuple: parsed message in the form of (prefix, command, args) """ s = s.decode(encoding, 'replace') prefix = u("") trailing = [] if s and s[0] == u(":"): prefix, s = s[1:].split(u(" "), 1) prefix = parseprefix(prefix) if s.find(u(" :")) != -1: s, trailing = s.split(u(" :"), 1) args = s.split() args.append(trailing) else: args = s.split() args = iter(args) command = next(args, None) command = command and str(command) return prefix, command, list(args)
def test_default_args(webapp, data, expected): args, kwargs = data url = u("{0:s}/test_default_args/{1:s}".format(webapp.server.http.base, u("/").join(args))) data = urlencode(kwargs).encode("utf-8") f = urlopen(url, data) assert f.read() == expected
def RPL_LUSERCLIENT(nusers, nservices, nservers): return _M( u("251"), u("There are {0} users and {1} services on {2} servers").format( nusers, nservices, nservers ) )
def MODE(target, modes, params=None, prefix=None): if params is None: return Message(u("MODE"), target, modes, prefix=prefix) return Message(u("MODE"), target, modes, u(" ").join(params), prefix=prefix)
def test_default_args(webapp, data, expected): args, kwargs = data url = u("{0:s}/test_default_args/{1:s}".format( webapp.server.http.base, u("/").join(args) )) data = urlencode(kwargs).encode("utf-8") f = urlopen(url, data) assert f.read() == expected
def joinprefix(nick, user, host): """Join the parts of a prefix :param nick str: nickname :param user str: username :param host str: hostname :returns str: a string in the form of <nick>!<user>@<host> """ return u("{0}!{1}@{2}").format(nick or u(""), user or u(""), host or u(""))
def test_parsemsg(): s = b(":foo!bar@localhost NICK foobar") source, command, args = parsemsg(s) assert source == (u("foo"), u("bar"), u("localhost")) assert command == "NICK" assert args == [u("foobar")] s = b("") source, command, args = parsemsg(s) assert source == (None, None, None) assert command is None assert args == []
def __init__(self, command, *args, **kwargs): self.command = command self.args = [x for x in args if x is not None] self.prefix = text_type(kwargs["prefix"]) if "prefix" in kwargs else None self.encoding = kwargs.get("encoding", "utf-8") self.add_nick = kwargs.get("add_nick", False) if any(u(' ') in arg for arg in self.args[:-1]): raise Error("Space can only appear in the very last arg") if any(u('\n') in arg for arg in self.args): raise Error("No newline allowed")
def __init__(self, command, *args, **kwargs): self.command = command self.args = [x for x in args if x is not None] self.prefix = text_type( kwargs["prefix"]) if "prefix" in kwargs else None self.encoding = kwargs.get("encoding", "utf-8") self.add_nick = kwargs.get("add_nick", False) if any(u(' ') in arg for arg in self.args[:-1]): raise Error("Space can only appear in the very last arg") if any(u('\n') in arg for arg in self.args): raise Error("No newline allowed")
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)
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)
def __unicode__(self): args = self.args[:] if any(u(' ') in arg for arg in args[:-1]): raise Error("Space can only appear in the very last arg") if any(u('\n') in arg for arg in args): raise Error("No newline allowed") if args and u(" ") in args[-1] and not args[-1].startswith(u(":")): args[-1] = u(":{0:s}").format(args[-1]) return u("{prefix:s}{command:s} {args:s}\r\n").format( prefix=(u(":{0:s} ").format(self.prefix) if self.prefix is not None else u("")), command=text_type(self.command), args=u(" ").join(args))
def strip(s, color=False): """strip(s, color=False) -> str Strips the : from the start of a string and optionally also strips all colors if color is True. :param s str: string to process :param color bool: whether to strip colors :returns str: returns processes string """ if len(s) > 0: if s[0] == u(":"): s = s[1:] if color: s = s.replace(u("\x01"), u("")) s = s.replace(u("\x02"), u("")) return s
def __init__(self, command, *args, **kwargs): for arg in args[:-1]: if u(" ") in arg: raise Error("Space can only appear in the very last arg") self.command = command self.args = list(filter(lambda x: x is not None, list(args))) self.prefix = text_type(kwargs["prefix"]) if "prefix" in kwargs else None self.encoding = kwargs.get("encoding", "utf-8") self.add_nick = kwargs.get("add_nick", False)
def __unicode__(self): args = self.args[:] for arg in args[:-1]: if arg is not None and u(" ") in arg: raise Error("Space can only appear in the very last arg") if len(args) > 0 and u(" ") in args[-1] and args[-1][0] != u(":"): args[-1] = u(":{0:s}").format(args[-1]) return u("{prefix:s}{command:s} {args:s}\r\n").format( prefix=(u(":{0:s} ").format(self.prefix) if self.prefix is not None else u("")), command=text_type(self.command), args=u(" ").join(args))
def __unicode__(self): self._check_args() args = self.args[:] if args and u(" ") in args[-1] and not args[-1].startswith(u(":")): args[-1] = u(":{0:s}").format(args[-1]) return u("{prefix:s}{command:s} {args:s}\r\n").format( prefix=(u(":{0:s} ").format(self.prefix) if self.prefix is not None else u("")), command=text_type(self.command), args=u(" ").join(args))
def __unicode__(self): args = self.args[:] if any(u(' ') in arg for arg in args[:-1]): raise Error("Space can only appear in the very last arg") if any(u('\n') in arg for arg in args): raise Error("No newline allowed") if args and u(" ") in args[-1] and not args[-1].startswith(u(":")): args[-1] = u(":{0:s}").format(args[-1]) return u("{prefix:s}{command:s} {args:s}\r\n").format( prefix=( u(":{0:s} ").format(self.prefix) if self.prefix is not None else u("") ), command=text_type(self.command), args=u(" ").join(args) )
def __unicode__(self): args = self.args[:] for arg in args[:-1]: if arg is not None and u(" ") in arg: raise Error("Space can only appear in the very last arg") if len(args) > 0 and u(" ") in args[-1] and args[-1][0] != u(":"): args[-1] = u(":{0:s}").format(args[-1]) return u("{prefix:s}{command:s} {args:s}\r\n").format( prefix=( u(":{0:s} ").format(self.prefix) if self.prefix is not None else u("") ), command=text_type(self.command), args=u(" ").join(args) )
def RPL_ISUPPORT(features): return _M(u("005"), *(features + (u("are supported by this server"), )))
def RPL_CREATED(date): return _M(u("003"), u("This server was created {0}").format(date))
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, 8: 103, 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))
def strip(s, color=False): """strip(s, color=False) -> str Strips the : from the start of a string and optionally also strips all colors if color is True. :param s str: string to process :param color bool: whether to strip colors :returns str: returns processes string """ if len(s) > 0: if s[0] == u(":"): s = s[1:] if color: s = s.replace(u("\x01"), u("")) s = s.replace(u("\x02"), u("")) # bold s = s.replace(u("\x1d"), u("")) # italics s = s.replace(u("\x1f"), u("")) # underline s = s.replace(u("\x1e"), u("")) # strikethrough s = s.replace(u("\x11"), u("")) # monospace s = s.replace(u("\x16"), u("")) # reverse color s = COLOR.sub(u(""), s) # color codes s = s.replace(u("\x03"), u("")) # color s = s.replace(u("\x0f"), u("")) # reset return s
def RPL_LUSERCHANNELS(nchannels): return _M(u("254"), u("{0}").format(nchannels), u("channels formed"))
def RPL_AWAY(nick, message): return _M(u("301"), nick, u(":{0}").format(message))
def RPL_LUSEROP(noperators): return _M(u("252"), u("{0}").format(noperators), u("operator(s) online"))
def RPL_LUSERME(nclients, nservers): return _M( u("255"), u("I have {0} clients and {1} servers".format(nclients, nservers)))
def ERR_USERSDONTMATCH(): return _M(u("502"), u("Cannot change mode for other users"))
def PART(channel, nick, reason=None, prefix=None): return Message(u("PART"), channel, nick, reason, prefix=prefix)
def ERR_NOOPERHOST(): return _M(u("491"), u("No O-lines for your host"))
def ERR_NOPRIVILEGES(): return _M(u("481"), u("Permission Denied- You're not an IRC operator"))
def RPL_LUSERUNKOWN(nunknown): return _M(u("253"), u("{0}").format(nunknown), u("unknown connection(s)"))
def __unicode__(self): return u(self.__str__())
def PONG(server, text): return Message(u("PONG"), server, u(":{0}").format(text))
def TOPIC(channel, topic, prefix=None): return Message(u("TOPIC"), channel, topic, prefix=prefix)
def RPL_WELCOME(network): return _M(u("001"), u("Welcome to the {0} IRC Network").format(network))
def RPL_LUSERME(nclients, nservers): return _M(u("255"), u("I have {0} clients and {1} servers".format(nclients, nservers)))
def RPL_YOURHOST(host, version): return _M(u("002"), u("Your host is {0} running {1}").format(host, version))
def ansi(*seq): return u("\33[{}m").format( u(";").join(u("{:02}").format(x) for x in seq if x))
def RPL_MYINFO(server, version, umodes, chmodes): return _M(u("004"), server, version, umodes, chmodes)
def RPL_UMODEIS(modes): return _M(u("221"), modes)
def RPL_ISUPPORT(features): return _M(u("005"), *(features + (u("are supported by this server"),)))
(INVITE("test", "#test"), b"INVITE test #test\r\n"), (NAMES(), b"NAMES\r\n"), (NAMES("#test"), b"NAMES #test\r\n"), (AWAY("I am away."), b"AWAY :I am away.\r\n"), (WHOIS("somenick"), b"WHOIS :somenick\r\n"), ]) def test_commands(event, data): message = event.args[0] return bytes(message) == data @pytest.mark.parametrize("data,event", [ (b":localhost NOTICE * :*** Looking up your hostname...\r\n", Event.create( "notice", (u("localhost"), None, None), u("*"), u("*** Looking up your hostname..."), )), ]) def test_responses(app, data, event): app.reset() app.fire(read(data)) while len(app): app.flush() e = app.events[-1] assert event.name == e.name assert event.args == e.args assert event.kwargs == e.kwargs
def RPL_LUSERCLIENT(nusers, nservices, nservers): return _M( u("251"), u("There are {0} users and {1} services on {2} servers").format( nusers, nservices, nservers))
def PING(server): return Message(u("PING"), u(":{0}").format(server))