Add support for message tags.

This commit is contained in:
Valentin Lorentz 2015-05-15 18:08:50 +02:00
parent ed37fb6646
commit ba495f5719
2 changed files with 51 additions and 1 deletions

View File

@ -50,6 +50,37 @@ from .utils.iter import all
class MalformedIrcMsg(ValueError): class MalformedIrcMsg(ValueError):
pass pass
# http://ircv3.net/specs/core/message-tags-3.2.html#escaping-values
SERVER_TAG_ESCAPE = [
('\\', '\\\\'), # \ -> \\
(' ', r'\s'),
(';', r'\:'),
('\r', r'\r'),
('\n', r'\n'),
]
escape_server_tag_value = utils.str.MultipleReplacer(
dict(SERVER_TAG_ESCAPE))
unescape_server_tag_value = utils.str.MultipleReplacer(
dict(map(lambda x:(x[1],x[0]), SERVER_TAG_ESCAPE)))
def parse_server_tags(s):
server_tags = {}
for tag in s.split(';'):
if '=' not in tag:
server_tags[tag] = None
else:
(key, value) = tag.split('=', 1)
server_tags[key] = unescape_server_tag_value(value)
return server_tags
def format_server_tags(server_tags):
parts = []
for (key, value) in server_tags.items():
if value is None:
parts.append(key)
else:
parts.append('%s=%s' % (key, escape_server_tag_value(value)))
return '@' + ';'.join(parts)
class IrcMsg(object): class IrcMsg(object):
"""Class to represent an IRC message. """Class to represent an IRC message.
@ -81,7 +112,8 @@ class IrcMsg(object):
# data. Goodbye, __slots__. # data. Goodbye, __slots__.
# On second thought, let's use methods for tagging. # On second thought, let's use methods for tagging.
__slots__ = ('args', 'command', 'host', 'nick', 'prefix', 'user', __slots__ = ('args', 'command', 'host', 'nick', 'prefix', 'user',
'_hash', '_str', '_repr', '_len', 'tags', 'reply_env') '_hash', '_str', '_repr', '_len', 'tags', 'reply_env',
'server_tags')
def __init__(self, s='', command='', args=(), prefix='', msg=None, def __init__(self, s='', command='', args=(), prefix='', msg=None,
reply_env=None): reply_env=None):
assert not (msg and s), 'IrcMsg.__init__ cannot accept both s and msg' assert not (msg and s), 'IrcMsg.__init__ cannot accept both s and msg'
@ -99,6 +131,11 @@ class IrcMsg(object):
if not s.endswith('\n'): if not s.endswith('\n'):
s += '\n' s += '\n'
self._str = s self._str = s
if s[0] == '@':
(server_tags, s) = s.split(' ', 1)
self.server_tags = parse_server_tags(server_tags[1:])
else:
self.server_tags = {}
if s[0] == ':': if s[0] == ':':
self.prefix, s = s[1:].split(None, 1) self.prefix, s = s[1:].split(None, 1)
else: else:

View File

@ -128,6 +128,19 @@ class IrcMsgTestCase(SupyTestCase):
m.tag('repliedTo', 12) m.tag('repliedTo', 12)
self.assertEqual(m.repliedTo, 12) self.assertEqual(m.repliedTo, 12)
def testServerTags(self):
s = '@aaa=b\\:bb;ccc;example.com/ddd=ee\\\\se ' \
':nick!ident@host.com PRIVMSG me :Hello'
m = ircmsgs.IrcMsg(s)
self.assertEqual(m.server_tags, {
'aaa': 'b;bb',
'ccc': None,
'example.com/ddd': 'ee\\se'})
self.assertEqual(m.prefix, 'nick!ident@host.com')
self.assertEqual(m.command, 'PRIVMSG')
self.assertEqual(m.args, ('me', 'Hello'))
self.assertEqual(str(m), s + '\n')
class FunctionsTestCase(SupyTestCase): class FunctionsTestCase(SupyTestCase):
def testIsAction(self): def testIsAction(self):
L = [':jemfinch!~jfincher@ts26-2.homenet.ohio-state.edu PRIVMSG' L = [':jemfinch!~jfincher@ts26-2.homenet.ohio-state.edu PRIVMSG'