mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-27 05:09:23 +01:00
Merge branch 'ircv3.2' into testing
This commit is contained in:
commit
39d40ec617
@ -147,6 +147,13 @@ class NickAuth(callbacks.Plugin):
|
||||
irc.queueMsg(ircmsgs.whois(nick, nick))
|
||||
auth = wrap(auth, [])
|
||||
|
||||
def inFilter(self, irc, msg):
|
||||
"""If the messages has a server tag with account name, tries to
|
||||
authenticate it."""
|
||||
if msg.server_tags and 'account' in msg.server_tags:
|
||||
self._auth(irc, msg.prefix, msg.server_tags['account'])
|
||||
return msg
|
||||
|
||||
def do330(self, irc, msg):
|
||||
mynick, theirnick, theiraccount, garbage = msg.args
|
||||
# I would like to use a dict comprehension, but we have to support
|
||||
@ -176,18 +183,8 @@ class NickAuth(callbacks.Plugin):
|
||||
account = msg.args[0]
|
||||
user = ircdb.users.getUserFromNick(irc.network, account)
|
||||
|
||||
if not user:
|
||||
try:
|
||||
user = ircdb.users.getUser(msg.prefix)
|
||||
except KeyError:
|
||||
user = None
|
||||
|
||||
if user:
|
||||
if account == '*':
|
||||
user.clearAuth()
|
||||
else:
|
||||
user.addAuth(msg.prefix)
|
||||
ircdb.users.setUser(user, flush=False)
|
||||
if account != '*':
|
||||
self._auth(irc, msg.prefix, account)
|
||||
|
||||
|
||||
def doJoin(self, irc, msg):
|
||||
@ -196,18 +193,8 @@ class NickAuth(callbacks.Plugin):
|
||||
return
|
||||
|
||||
account = msg.args[1]
|
||||
user = ircdb.users.getUserFromNick(irc.network, account)
|
||||
|
||||
if not user:
|
||||
try:
|
||||
user = ircdb.users.getUser(msg.prefix)
|
||||
except KeyError:
|
||||
user = None
|
||||
|
||||
if user:
|
||||
if account != '*':
|
||||
user.addAuth(msg.prefix)
|
||||
ircdb.users.setUser(user, flush=False)
|
||||
self._auth(irc, msg.prefix, account)
|
||||
|
||||
def do354(self, irc, msg):
|
||||
if len(msg.args) != 6 or msg.args[1] != '1':
|
||||
@ -217,6 +204,12 @@ class NickAuth(callbacks.Plugin):
|
||||
prefix = '%s!%s@%s' % (nick, ident, host)
|
||||
user = ircdb.users.getUserFromNick(irc.network, account)
|
||||
|
||||
if account != '0':
|
||||
self._auth(irc, prefix, account)
|
||||
|
||||
def _auth(self, irc, prefix, account):
|
||||
user = ircdb.users.getUserFromNick(irc.network, account)
|
||||
|
||||
if not user:
|
||||
try:
|
||||
user = ircdb.users.getUser(prefix)
|
||||
@ -224,7 +217,6 @@ class NickAuth(callbacks.Plugin):
|
||||
user = None
|
||||
|
||||
if user:
|
||||
if account != '0':
|
||||
user.addAuth(prefix)
|
||||
ircdb.users.setUser(user, flush=False)
|
||||
|
||||
|
@ -45,6 +45,12 @@ class NickCapture(callbacks.Plugin):
|
||||
self.__parent = super(NickCapture, self)
|
||||
self.__parent.__init__(irc)
|
||||
self.lastIson = 0
|
||||
self.monitoring = []
|
||||
|
||||
def die(self):
|
||||
for irc in self.monitoring:
|
||||
nick = self._getNick(irc.network)
|
||||
irc.unmonitor(nick)
|
||||
|
||||
def _getNick(self, network):
|
||||
network_nick = conf.supybot.networks.get(network).nick()
|
||||
@ -60,10 +66,16 @@ class NickCapture(callbacks.Plugin):
|
||||
# We used to check this, but nicksToHostmasks is never cleared
|
||||
# except on reconnects, which can cause trouble.
|
||||
# if nick not in irc.state.nicksToHostmasks:
|
||||
if 'monitor' in irc.state.supported:
|
||||
if irc not in self.monitoring:
|
||||
irc.monitor(nick)
|
||||
self.monitoring.append(irc)
|
||||
else:
|
||||
self._ison(irc, nick)
|
||||
self.__parent.__call__(irc, msg)
|
||||
|
||||
def _ison(self, irc, nick):
|
||||
assert 'monitor' not in irc.state.supported
|
||||
if self.registryValue('ison'):
|
||||
now = time.time()
|
||||
if now - self.lastIson > self.registryValue('ison.period'):
|
||||
@ -95,6 +107,16 @@ class NickCapture(callbacks.Plugin):
|
||||
nick = self._getNick(irc.network)
|
||||
if nick:
|
||||
self._sendNick(irc, nick)
|
||||
|
||||
def do731(self, irc, msg):
|
||||
"""This is sent by the MONITOR when a nick goes offline."""
|
||||
nick = self._getNick(irc.network)
|
||||
for target in msg.args[1].split(','):
|
||||
if nick == target:
|
||||
self._sendNick(irc, nick)
|
||||
self.monitoring.remove(irc)
|
||||
irc.unmonitor(nick)
|
||||
break
|
||||
NickCapture = internationalizeDocstring(NickCapture)
|
||||
|
||||
Class = NickCapture
|
||||
|
@ -666,6 +666,7 @@ class Irc(IrcCommandDispatcher):
|
||||
self._setNonResettingVariables()
|
||||
self._queueConnectMessages()
|
||||
self.startedSync = ircutils.IrcDict()
|
||||
self.monitoring = ircutils.IrcDict()
|
||||
|
||||
def isChannel(self, s):
|
||||
"""Helper function to check whether a given string is a channel on
|
||||
@ -965,7 +966,8 @@ class Irc(IrcCommandDispatcher):
|
||||
elif self.sasl_username and self.sasl_password:
|
||||
self.sasl = 'plain'
|
||||
|
||||
for cap in ('account-notify', 'extended-join', 'multi-prefix'):
|
||||
for cap in ('account-notify', 'extended-join', 'multi-prefix',
|
||||
'metadata-notify', 'account-tag'):
|
||||
self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('REQ', cap)))
|
||||
|
||||
if self.sasl:
|
||||
@ -1041,6 +1043,38 @@ class Irc(IrcCommandDispatcher):
|
||||
command='CAP',
|
||||
args=('END',)))
|
||||
|
||||
def monitor(self, targets):
|
||||
"""Increment a counter of how many callbacks monitor each target;
|
||||
and send a MONITOR + to the server if the target is not yet
|
||||
monitored."""
|
||||
if isinstance(targets, str):
|
||||
targets = [targets]
|
||||
not_yet_monitored = set()
|
||||
for target in targets:
|
||||
if target in self.monitoring:
|
||||
self.monitoring[target] += 1
|
||||
else:
|
||||
not_yet_monitored.add(target)
|
||||
self.monitoring[target] = 1
|
||||
if not_yet_monitored:
|
||||
self.queueMsg(ircmsgs.monitor('+', not_yet_monitored))
|
||||
return not_yet_monitored
|
||||
|
||||
def unmonitor(self, targets):
|
||||
"""Decrements a counter of how many callbacks monitor each target;
|
||||
and send a MONITOR - to the server if the counter drops to 0."""
|
||||
if isinstance(targets, str):
|
||||
targets = [targets]
|
||||
should_be_unmonitored = set()
|
||||
for target in targets:
|
||||
self.monitoring[target] -= 1
|
||||
if self.monitoring[target] == 0:
|
||||
del self.monitoring[target]
|
||||
should_be_unmonitored.add(target)
|
||||
if should_be_unmonitored:
|
||||
self.queueMsg(ircmsgs.monitor('-', should_be_unmonitored))
|
||||
return should_be_unmonitored
|
||||
|
||||
def do903(self, msg):
|
||||
log.info('%s: SASL authentication successful', self.network)
|
||||
self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('END',)))
|
||||
|
@ -50,6 +50,37 @@ from .utils.iter import all
|
||||
class MalformedIrcMsg(ValueError):
|
||||
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 to represent an IRC message.
|
||||
|
||||
@ -81,7 +112,8 @@ class IrcMsg(object):
|
||||
# data. Goodbye, __slots__.
|
||||
# On second thought, let's use methods for tagging.
|
||||
__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,
|
||||
reply_env=None):
|
||||
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'):
|
||||
s += '\n'
|
||||
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] == ':':
|
||||
self.prefix, s = s[1:].split(None, 1)
|
||||
else:
|
||||
@ -818,6 +855,19 @@ def ison(nick, prefix='', msg=None):
|
||||
prefix = msg.prefix
|
||||
return IrcMsg(prefix=prefix, command='ISON', args=(nick,), msg=msg)
|
||||
|
||||
def monitor(subcommand, nicks=None, prefix='', msg=None):
|
||||
if conf.supybot.protocols.irc.strictRfc():
|
||||
assert isNick(nick), repr(nick)
|
||||
assert subcommand in '+-CLS'
|
||||
if subcommand in 'CLS':
|
||||
assert nicks is None
|
||||
if msg and not prefix:
|
||||
prefix = msg.prefix
|
||||
if not isinstance(nicks, str):
|
||||
nicks = ','.join(nicks)
|
||||
return IrcMsg(prefix=prefix, command='MONITOR', args=(subcommand, nicks),
|
||||
msg=msg)
|
||||
|
||||
def error(s, msg=None):
|
||||
return IrcMsg(command='ERROR', args=(s,), msg=msg)
|
||||
|
||||
|
@ -391,6 +391,10 @@ class IrcTestCase(SupyTestCase):
|
||||
m = self.irc.takeMsg()
|
||||
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||
m = self.irc.takeMsg()
|
||||
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||
m = self.irc.takeMsg()
|
||||
self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||
m = self.irc.takeMsg()
|
||||
self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m)
|
||||
|
||||
def testPingResponse(self):
|
||||
@ -492,6 +496,8 @@ class IrcCallbackTestCase(SupyTestCase):
|
||||
ircmsgs.IrcMsg(command='CAP', args=('REQ', 'account-notify')),
|
||||
ircmsgs.IrcMsg(command='CAP', args=('REQ', 'extended-join')),
|
||||
ircmsgs.IrcMsg(command='CAP', args=('REQ', 'multi-prefix')),
|
||||
ircmsgs.IrcMsg(command='CAP', args=('REQ', 'metadata-notify')),
|
||||
ircmsgs.IrcMsg(command='CAP', args=('REQ', 'account-tag')),
|
||||
ircmsgs.IrcMsg(command='CAP', args=('END',)),
|
||||
ircmsgs.user('limnoria', user)
|
||||
]
|
||||
|
@ -128,6 +128,19 @@ class IrcMsgTestCase(SupyTestCase):
|
||||
m.tag('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):
|
||||
def testIsAction(self):
|
||||
L = [':jemfinch!~jfincher@ts26-2.homenet.ohio-state.edu PRIVMSG'
|
||||
|
Loading…
Reference in New Issue
Block a user