diff --git a/plugins/Channel/plugin.py b/plugins/Channel/plugin.py index 26ad62501..9b66e7f61 100644 --- a/plugins/Channel/plugin.py +++ b/plugins/Channel/plugin.py @@ -1,7 +1,7 @@ ### # Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2009-2012, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz +# Copyright (c) 2010-2023, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -464,6 +464,53 @@ class Channel(callbacks.Plugin): first('hostmask', ('literal', '--all')))]) + @internationalizeDocstring + def redact(self, irc, msg, args, channel, msgid, reason): + """[] [--msgid ] [] + + Redacts the message identified by the given from the + for . may be omitted if this command is given as a + reply to another message (on servers supporting IRCv3 message-tags + and +draft/reply). + This command requires supybot.protocols.irc.experimentalExtensions and + is only available on networks which support IRCv3 + draft/message-redaction. + is only necessary if the message isn't sent in the channel + itself. + """ + if not conf.supybot.protocols.irc.experimentalExtensions(): + irc.error( + _('Experimental IRC extensions are not enabled for this bot.'), + Raise=True) + + msgid = msgid or msg.server_tags.get('+draft/reply') + if not msgid: + irc.error(_('--msgid is required when this command is not a ' + 'reply to a message'), Raise=True) + + for target_message in reversed(irc.state.history): + if target_message.server_tags.get('msgid') == msgid: + break + else: + irc.error(_('Could not find the message in my history'), + Raise=True) + + if target_message.command not in ('PRIVMSG', 'NOTICE', 'TAGMSG'): + irc.error(_('This is not a valid message.'), + Raise=True) + + if ircutils.strEqual(target_message.nick, irc.nick): + irc.error(_('I cowardly refuse to redact my own messages.'), + Raise=True) + + args = [channel, msgid] + if reason: + args.append(reason) + self._sendMsg(irc, ircmsgs.IrcMsg(command="REDACT", args=args)) + redact = wrap(redact, ['op', ('haveHalfop+', _('kick someone')), + getopts({'msgid': 'somethingWithoutSpaces'}), + additional('text')]) + @internationalizeDocstring def listbans(self, irc, msg, args, channel): """[] diff --git a/plugins/Channel/test.py b/plugins/Channel/test.py index 4295ef081..b108f5f42 100644 --- a/plugins/Channel/test.py +++ b/plugins/Channel/test.py @@ -1,7 +1,7 @@ ### # Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz +# Copyright (c) 2010-2023, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -289,5 +289,71 @@ class ChannelTestCase(ChannelPluginTestCase): self.assertEqual(m.args[0], '#foo') self.assertEqual(m.args[1], 'reason') + def assertRedactNoMsgid(self, query, hostmask, **kwargs): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + self.irc.state.capabilities_ack.add('draft/message-redaction') + + with conf.supybot.protocols.irc.experimentalExtensions.context(True): + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, '@redact'))) + self.assertRegexp(' ', 'Error: --msgid is required when') + + def assertRedactMsgid(self, query, hostmask, **kwargs): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + self.irc.state.capabilities_ack.add('draft/message-redaction') + + with conf.supybot.protocols.irc.experimentalExtensions.context(True): + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, '@redact --msgid foobar'))) + self.assertRegexp(' ', 'Error: Cound not find') + + m = self.getMsg(' ', timeout=0.5) + self.assertIsNone(m) + + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, 'oh shit'), + server_tags={'msgid': 'foobar'})) + + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, '@redact --msgid foobar'))) + + m = self.getMsg(' ') + + self.assertEqual(m.command, 'REDACT') + self.assertEqual(m.args, (self.channel, 'foobar')) + + def assertRedactReply(self, query, hostmask, **kwargs): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + self.irc.state.capabilities_ack.add('draft/message-redaction') + + with conf.supybot.protocols.irc.experimentalExtensions.context(True): + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, '@redact'), + server_tags={'+draft/reply=foobar'})) + self.assertRegexp(' ', 'Error: Cound not find') + + m = self.getMsg(' ', timeout=0.5) + self.assertIsNone(m) + + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, 'oh shit'), + server_tags={'msgid': 'foobar'})) + + self.irc.feedMsg(ircmsgs.IrcMsg( + command='PRIVMSG', + args=(self.channel, '@redact'), + server_tags={'+draft/reply=foobar'})) + + m = self.getMsg(' ') + + self.assertEqual(m.command, 'REDACT') + self.assertEqual(m.args, (self.channel, 'foobar')) + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: