mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-29 22:29:24 +01:00
Add echo messages, either with the echo-message capability or simulated.
Plugin can opt in to getting echo messages by setting the class attribute 'echo_message = True' if they want to get echos. This defaults to False in order not to break existing plugins, and because they usually don't need it (there's outFilter for most cases).
This commit is contained in:
parent
572c609181
commit
eb1e27e20b
@ -117,6 +117,7 @@ class IrcCallback(IrcCommandDispatcher, log.Firewalled):
|
|||||||
"""
|
"""
|
||||||
callAfter = ()
|
callAfter = ()
|
||||||
callBefore = ()
|
callBefore = ()
|
||||||
|
echo_message = False
|
||||||
__firewalled__ = {'die': None,
|
__firewalled__ = {'die': None,
|
||||||
'reset': None,
|
'reset': None,
|
||||||
'__call__': None,
|
'__call__': None,
|
||||||
@ -181,6 +182,20 @@ class IrcCallback(IrcCommandDispatcher, log.Firewalled):
|
|||||||
|
|
||||||
def __call__(self, irc, msg):
|
def __call__(self, irc, msg):
|
||||||
"""Used for handling each message."""
|
"""Used for handling each message."""
|
||||||
|
if not self.echo_message \
|
||||||
|
and msg.command in ('PRIVMSG', 'NOTICE', 'TAGMSG') \
|
||||||
|
and ('label' in msg.server_tags
|
||||||
|
or not msg.tagged('receivedAt')):
|
||||||
|
# This is an echo of a message we sent; and the plugin didn't
|
||||||
|
# opt-in to receiving echos; ignoring it.
|
||||||
|
# `'label' in msg.server_tags` detects echos when labeled-response
|
||||||
|
# is enabled; and `not msg.tag('receivedAt')` detects simulated
|
||||||
|
# echos. As we don't enable real echo-message unless
|
||||||
|
# labeled-response is enabled; this is an exhaustive check of echos
|
||||||
|
# in all cases.
|
||||||
|
# See "When a client sends a private message to its own nick" at
|
||||||
|
# <https://ircv3.net/specs/extensions/labeled-response>
|
||||||
|
return
|
||||||
method = self.dispatchCommand(msg.command, msg.args)
|
method = self.dispatchCommand(msg.command, msg.args)
|
||||||
if method is not None:
|
if method is not None:
|
||||||
method(irc, msg)
|
method(irc, msg)
|
||||||
@ -1057,6 +1072,13 @@ class Irc(IrcCommandDispatcher, log.Firewalled):
|
|||||||
|
|
||||||
self._truncateMsg(msg)
|
self._truncateMsg(msg)
|
||||||
|
|
||||||
|
if msg.command.upper() in ('PRIVMSG', 'NOTICE', 'TAGMSG') \
|
||||||
|
and 'echo-message' not in self.state.capabilities_ack:
|
||||||
|
# echo-message is not implemented by server; let's emulate it
|
||||||
|
# here, just before sending it to the driver.
|
||||||
|
assert not msg.tagged('receivedAt')
|
||||||
|
self.feedMsg(msg, tag=False)
|
||||||
|
else:
|
||||||
# I don't think we should do this. Why should it matter? If it's
|
# I don't think we should do this. Why should it matter? If it's
|
||||||
# something important, then the server will send it back to us,
|
# something important, then the server will send it back to us,
|
||||||
# and if it's just a privmsg/notice/etc., we don't care.
|
# and if it's just a privmsg/notice/etc., we don't care.
|
||||||
@ -1098,8 +1120,12 @@ class Irc(IrcCommandDispatcher, log.Firewalled):
|
|||||||
return channel.lstrip(statusmsg_chars)
|
return channel.lstrip(statusmsg_chars)
|
||||||
|
|
||||||
_numericErrorCommandRe = re.compile(r'^[45][0-9][0-9]$')
|
_numericErrorCommandRe = re.compile(r'^[45][0-9][0-9]$')
|
||||||
def feedMsg(self, msg):
|
def feedMsg(self, msg, tag=True):
|
||||||
"""Called by the IrcDriver; feeds a message received."""
|
"""Called by the IrcDriver; feeds a message received.
|
||||||
|
|
||||||
|
`tag=False` is used when simulating echo messages, to skip adding
|
||||||
|
received* tags."""
|
||||||
|
if tag:
|
||||||
self._tagMsg(msg)
|
self._tagMsg(msg)
|
||||||
channel = msg.channel
|
channel = msg.channel
|
||||||
|
|
||||||
@ -1250,11 +1276,12 @@ class Irc(IrcCommandDispatcher, log.Firewalled):
|
|||||||
self.REQUEST_CAPABILITIES.add('sasl')
|
self.REQUEST_CAPABILITIES.add('sasl')
|
||||||
|
|
||||||
|
|
||||||
|
# Note: echo-message is only requested if labeled-response is available.
|
||||||
REQUEST_CAPABILITIES = set(['account-notify', 'extended-join',
|
REQUEST_CAPABILITIES = set(['account-notify', 'extended-join',
|
||||||
'multi-prefix', 'metadata-notify', 'account-tag',
|
'multi-prefix', 'metadata-notify', 'account-tag',
|
||||||
'userhost-in-names', 'invite-notify', 'server-time',
|
'userhost-in-names', 'invite-notify', 'server-time',
|
||||||
'chghost', 'batch', 'away-notify', 'message-tags',
|
'chghost', 'batch', 'away-notify', 'message-tags',
|
||||||
'msgid', 'setname', 'labeled-response'])
|
'msgid', 'setname', 'labeled-response', 'echo-message'])
|
||||||
|
|
||||||
def _queueConnectMessages(self):
|
def _queueConnectMessages(self):
|
||||||
if self.zombie:
|
if self.zombie:
|
||||||
@ -1654,7 +1681,24 @@ class Irc(IrcCommandDispatcher, log.Firewalled):
|
|||||||
def _requestCaps(self, caps):
|
def _requestCaps(self, caps):
|
||||||
self.state.capabilities_req |= caps
|
self.state.capabilities_req |= caps
|
||||||
|
|
||||||
caps = ' '.join(sorted(caps))
|
caps = list(sorted(caps))
|
||||||
|
cap_lines = []
|
||||||
|
if 'echo-message' in caps \
|
||||||
|
and 'labeled-response' not in self.state.capabilities_ack:
|
||||||
|
# Make sure echo-message is never requested unless we either have
|
||||||
|
# labeled-response already, or we request it *on the same line*
|
||||||
|
# so they are both accepted or both rejected). The reason for this
|
||||||
|
# is that this is required to properly deal with PRIVMSGs sent to
|
||||||
|
# oneself.
|
||||||
|
# See "When a client sends a private message to its own nick" at
|
||||||
|
# <https://ircv3.net/specs/extensions/labeled-response>
|
||||||
|
caps.remove('echo-message')
|
||||||
|
if 'labeled-response' in caps:
|
||||||
|
caps.remove('labeled-response')
|
||||||
|
# This makes sure they are always on the same line (which
|
||||||
|
# happens to be the first):
|
||||||
|
caps = ['echo-message', 'labeled-response'] + caps
|
||||||
|
caps = ' '.join(caps)
|
||||||
# textwrap works here because in ASCII, all chars are 1 bytes:
|
# textwrap works here because in ASCII, all chars are 1 bytes:
|
||||||
cap_lines = textwrap.wrap(
|
cap_lines = textwrap.wrap(
|
||||||
caps, MAX_LINE_SIZE-len('CAP REQ :'),
|
caps, MAX_LINE_SIZE-len('CAP REQ :'),
|
||||||
|
@ -498,6 +498,68 @@ class IrcCapsTestCase(SupyTestCase):
|
|||||||
self.assertEqual(m.args[0], 'REQ', m)
|
self.assertEqual(m.args[0], 'REQ', m)
|
||||||
self.assertEqual(m.args[1], 'b'*400)
|
self.assertEqual(m.args[1], 'b'*400)
|
||||||
|
|
||||||
|
def testNoEchomessageWithoutLabeledresponse(self):
|
||||||
|
self.irc = irclib.Irc('test')
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertTrue(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m)
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'NICK', 'Expected NICK, got %r.' % m)
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'USER', 'Expected USER, got %r.' % m)
|
||||||
|
|
||||||
|
self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
|
||||||
|
args=('*', 'LS', 'account-notify echo-message')))
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertEqual(m.args[0], 'REQ', m)
|
||||||
|
self.assertEqual(m.args[1], 'account-notify')
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertIsNone(m)
|
||||||
|
|
||||||
|
def testEchomessageLabeledresponseGrouped(self):
|
||||||
|
self.irc = irclib.Irc('test')
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertTrue(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m)
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'NICK', 'Expected NICK, got %r.' % m)
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'USER', 'Expected USER, got %r.' % m)
|
||||||
|
|
||||||
|
self.irc.REQUEST_CAPABILITIES = set([
|
||||||
|
'account-notify', 'a'*490, 'echo-message', 'labeled-response'])
|
||||||
|
self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', args=(
|
||||||
|
'*', 'LS',
|
||||||
|
'account-notify ' + 'a'*490 + ' echo-message labeled-response')))
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertEqual(m.args[0], 'REQ', m)
|
||||||
|
self.assertEqual(m.args[1], 'echo-message labeled-response')
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertEqual(m.args[0], 'REQ', m)
|
||||||
|
self.assertEqual(m.args[1], 'a'*490)
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertTrue(m.command == 'CAP', 'Expected CAP, got %r.' % m)
|
||||||
|
self.assertEqual(m.args[0], 'REQ', m)
|
||||||
|
self.assertEqual(m.args[1], 'account-notify')
|
||||||
|
|
||||||
|
m = self.irc.takeMsg()
|
||||||
|
self.assertIsNone(m)
|
||||||
|
|
||||||
|
|
||||||
class StsTestCase(SupyTestCase):
|
class StsTestCase(SupyTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.irc = irclib.Irc('test')
|
self.irc = irclib.Irc('test')
|
||||||
|
Loading…
Reference in New Issue
Block a user