From df45a61f529f65fe9285689e939ecdd35ed07974 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 19 Apr 2025 20:11:41 +0200 Subject: [PATCH 1/3] Channel: Make 'invite' command's required capability configurable It still requires 'op' by default, but can now be changed with a config value. This can be useful on servers that do not have a 'free invite' channel mode (like Charybdis/Solanum's +g) --- plugins/Channel/config.py | 7 ++++++- plugins/Channel/plugin.py | 11 +++++++++-- plugins/Channel/test.py | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/plugins/Channel/config.py b/plugins/Channel/config.py index 5d47a15259..2cd015a7cb 100644 --- a/plugins/Channel/config.py +++ b/plugins/Channel/config.py @@ -1,7 +1,7 @@ ### # Copyright (c) 2004-2005, Jeremiah Fincher # Copyright (c) 2009, James McCoy -# Copyright (c) 2010-2021, Valentin Lorentz +# Copyright (c) 2010-2025, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -63,4 +63,9 @@ def configure(advanced): be used (they are optional in the IRC protocol). The standard substitutions ($version, $nick, etc.) are all handled appropriately."""))) +conf.registerGroup(Channel, 'invite') +conf.registerChannelValue(Channel.invite, 'requireCapability', + registry.String('op', _("""Determines what capability (if any) the bot should + require people trying to use the 'invite' command to have."""))) + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Channel/plugin.py b/plugins/Channel/plugin.py index 3589e8bd5f..00e8e72482 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-2025, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -508,10 +508,17 @@ def invite(self, irc, msg, args, channel, nick): to join . is only necessary if the message isn't sent in the channel itself. """ + capability = self.registryValue('invite.requireCapability', + channel, irc.network) + if capability: + capability = ircdb.makeChannelCapability(channel, capability) + if not ircdb.checkCapability(msg.prefix, capability): + irc.errorNoCapability(capability, Raise=True) + nick = nick or msg.nick self._sendMsg(irc, ircmsgs.invite(nick, channel)) self.invites[(irc.getRealIrc(), ircutils.toLower(nick))] = irc - invite = wrap(invite, ['op', ('haveHalfop+', _('invite someone')), + invite = wrap(invite, [('haveHalfop+', _('invite someone')), additional('nick')]) def do341(self, irc, msg): diff --git a/plugins/Channel/test.py b/plugins/Channel/test.py index bcfc527865..99b3bf59e6 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-2025, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -414,5 +414,39 @@ def getAfterJoinMessages(): self.assertEqual(m.args[0], '#foo') self.assertEqual(m.args[1], 'reason') + def testInvite(self): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + m = self.getMsg('invite foo') + self.assertEqual(m.command, 'INVITE') + self.assertEqual(m.args, ('foo', self.channel)) + + def testInviteNoCapability(self): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + m = self.assertError('invite foo', + frm='test!user@with__no_testcap__') + + def testInviteCustomCapability(self): + self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) + + self.assertError('invite foo', + frm='test!user@with__no_testcap__') + + with conf.supybot.plugins.Channel.invite.requireCapability.context('freeinvite'): + m = self.getMsg('invite foo', + frm='test!user@with__no_testcap__') + self.assertEqual(m.command, 'INVITE') + self.assertEqual(m.args, ('foo', self.channel)) + + self.assertNotError('channel capability set -freeinvite') + + self.assertError('invite foo', + frm='test!user@with__no_testcap__') + + with conf.supybot.plugins.Channel.invite.requireCapability.context(''): + m = self.getMsg('invite foo', + frm='test!user@with__no_testcap__') + self.assertEqual(m.command, 'INVITE') + self.assertEqual(m.args, ('foo', self.channel)) + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: From 64acad0d0b54281b66e624e6507c58bf13744dde Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 19 Apr 2025 21:36:38 +0200 Subject: [PATCH 2/3] Explicit that 'requireCapability' can be empty --- plugins/Channel/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Channel/config.py b/plugins/Channel/config.py index 2cd015a7cb..355582cc25 100644 --- a/plugins/Channel/config.py +++ b/plugins/Channel/config.py @@ -66,6 +66,7 @@ def configure(advanced): conf.registerGroup(Channel, 'invite') conf.registerChannelValue(Channel.invite, 'requireCapability', registry.String('op', _("""Determines what capability (if any) the bot should - require people trying to use the 'invite' command to have."""))) + require people trying to use the 'invite' command to have. + Leave empty to allow anyone to use it."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: From 6760e7952571f001c24b2656005fbf14d77835c5 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 19 Apr 2025 21:36:43 +0200 Subject: [PATCH 3/3] Remove redundant assertion --- plugins/Channel/test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/Channel/test.py b/plugins/Channel/test.py index 99b3bf59e6..c2ed15ae2d 100644 --- a/plugins/Channel/test.py +++ b/plugins/Channel/test.py @@ -428,9 +428,6 @@ def testInviteNoCapability(self): def testInviteCustomCapability(self): self.irc.feedMsg(ircmsgs.op(self.channel, self.nick)) - self.assertError('invite foo', - frm='test!user@with__no_testcap__') - with conf.supybot.plugins.Channel.invite.requireCapability.context('freeinvite'): m = self.getMsg('invite foo', frm='test!user@with__no_testcap__')