core & Channel: Add option --account to @kban and @iban

A future commit will add support for 'account' in
supybot.protocols.irc.banmask, but it is not supported for now,
as that config value is also used for ignore masks
This commit is contained in:
Valentin Lorentz 2022-11-23 18:50:19 +01:00
parent eaf0ce0f59
commit 5056f2e6ef
5 changed files with 157 additions and 3 deletions

View File

@ -321,13 +321,16 @@ class Channel(callbacks.Plugin):
--exact bans only the exact hostmask; --nick bans just the nick;
--user bans just the user, and --host bans just the host
You can combine the --nick, --user, and --host options as you choose.
If --account is provided and the user is logged in and the network
supports account bans, this will ban the user's account instead.
<channel> is only necessary if the message isn't sent in the channel itself.
"""
self._ban(irc, msg, args,
channel, optlist, bannedNick, expiry, reason, True)
kban = wrap(kban,
['op',
getopts({'exact':'', 'nick':'', 'user':'', 'host':''}),
getopts({'exact':'', 'nick':'', 'user':'', 'host':'',
'account': ''}),
('haveHalfop+', _('kick or ban someone')),
'nickInChannel',
optional('expiry', 0),
@ -343,13 +346,16 @@ class Channel(callbacks.Plugin):
don't specify a number of seconds) it will ban the person indefinitely.
--exact can be used to specify an exact hostmask.
You can combine the --nick, --user, and --host options as you choose.
If --account is provided and the user is logged in and the network
supports account bans, this will ban the user's account instead.
<channel> is only necessary if the message isn't sent in the channel itself.
"""
self._ban(irc, msg, args,
channel, optlist, bannedNick, expiry, None, False)
iban = wrap(iban,
['op',
getopts({'exact':'', 'nick':'', 'user':'', 'host':''}),
getopts({'exact':'', 'nick':'', 'user':'', 'host':'',
'account': ''}),
('haveHalfop+', _('ban someone')),
first('nick', 'hostmask'),
optional('expiry', 0)])

View File

@ -219,6 +219,69 @@ class ChannelTestCase(ChannelPluginTestCase):
self.assertRegexp('kban adlkfajsdlfkjsd', 'adlkfajsdlfkjsd is not in')
def testAccountKbanNoAccount(self):
self.irc.prefix = 'something!else@somehwere.else'
self.irc.nick = 'something'
self.irc.state.supported['ACCOUNTEXTBAN'] = 'a,account'
self.irc.state.supported['EXTBAN'] = '~,abc'
def join():
self.irc.feedMsg(ircmsgs.join(
self.channel, prefix='foobar!user@host.domain.tld'))
join()
self.irc.feedMsg(ircmsgs.op(self.channel, self.irc.nick))
self.assertKban('kban --account --exact foobar',
'foobar!user@host.domain.tld')
join()
self.assertKban('kban --account foobar',
'foobar!user@host.domain.tld')
join()
self.assertKban('kban --account --host foobar',
'*!*@host.domain.tld')
def testAccountKbanLoggedOut(self):
self.irc.prefix = 'something!else@somehwere.else'
self.irc.nick = 'something'
self.irc.state.supported['ACCOUNTEXTBAN'] = 'a,account'
self.irc.state.supported['EXTBAN'] = '~,abc'
self.irc.feedMsg(ircmsgs.IrcMsg(
prefix='foobar!user@host.domain.tld',
command='ACCOUNT', args=['*']))
def join():
self.irc.feedMsg(ircmsgs.join(
self.channel, prefix='foobar!user@host.domain.tld'))
join()
self.irc.feedMsg(ircmsgs.op(self.channel, self.irc.nick))
self.assertKban('kban --account --exact foobar',
'foobar!user@host.domain.tld')
join()
self.assertKban('kban --account foobar',
'foobar!user@host.domain.tld')
join()
self.assertKban('kban --account --host foobar',
'*!*@host.domain.tld')
def testAccountKbanLoggedIn(self):
self.irc.prefix = 'something!else@somehwere.else'
self.irc.nick = 'something'
self.irc.state.supported['ACCOUNTEXTBAN'] = 'a,account'
self.irc.state.supported['EXTBAN'] = '~,abc'
self.irc.feedMsg(ircmsgs.IrcMsg(
prefix='foobar!user@host.domain.tld',
command='ACCOUNT', args=['account1']))
def join():
self.irc.feedMsg(ircmsgs.join(
self.channel, prefix='foobar!user@host.domain.tld'))
join()
self.irc.feedMsg(ircmsgs.op(self.channel, self.irc.nick))
self.assertKban('kban --account --exact foobar',
'~a:account1')
join()
self.assertKban('kban --account foobar',
'~a:account1')
join()
self.assertKban('kban --account --host foobar',
'~a:account1')
def testBan(self):
with conf.supybot.protocols.irc.banmask.context(['exact']):
self.assertNotError('ban add foo!bar@baz')

View File

@ -1217,7 +1217,11 @@ class Banmask(registry.SpaceSeparatedSetOfStrings):
options - A list specifying which parts of the hostmask should
explicitly be matched: nick, user, host. If 'exact' is given, then
only the exact hostmask will be used."""
only the exact hostmask will be used.
If 'account' is given (and not after 'exact') and the user is
logged in and the server supports account extbans, then an account
extban is returned instead.
"""
if not channel:
channel = dynamic.channel
if not network:
@ -1238,6 +1242,14 @@ class Banmask(registry.SpaceSeparatedSetOfStrings):
bhost = host
elif option == 'exact':
return hostmask
elif option == 'account':
import supybot.world as world
irc = world.getIrc(network)
if irc is None:
continue
extban = ircutils.accountExtban(nick, irc)
if extban is not None:
return extban
if (bnick, buser, bhost) == ('*', '*', '*') and \
ircutils.isUserHostmask(hostmask):
return hostmask

View File

@ -345,6 +345,25 @@ def banmask(hostmask):
else:
return '*!*@' + host
def accountExtban(nick, irc):
"""If 'nick' is logged in and the network supports account extbans,
returns a ban mask for it. If not, returns None."""
if 'ACCOUNTEXTBAN' not in irc.state.supported:
return None
if 'EXTBAN' not in irc.state.supported:
return None
try:
account = irc.state.nickToAccount(nick)
except KeyError:
account = None
if account is None:
return None
account_extban = irc.state.supported['ACCOUNTEXTBAN'].split(',')[0]
extban_prefix = irc.state.supported['EXTBAN'].split(',', 1)[0]
return '%s%s:%s'% (extban_prefix, account_extban, account)
_plusRequireArguments = 'ovhblkqeI'
_minusRequireArguments = 'ovhbkqeI'
def separateModes(args):

View File

@ -367,6 +367,60 @@ class FunctionsTestCase(SupyTestCase):
'*!*@*.host.tld')
self.assertEqual(ircutils.banmask('foo!bar@2001::'), '*!*@2001::*')
def testAccountExtban(self):
irc = getTestIrc()
irc.state.addMsg(irc, ircmsgs.IrcMsg(
prefix='foo!bar@baz', command='ACCOUNT', args=['account1']))
irc.state.addMsg(irc, ircmsgs.IrcMsg(
prefix='bar!baz@qux', command='ACCOUNT', args=['*']))
with self.subTest('spec example'):
irc.state.supported['ACCOUNTEXTBAN'] = 'a,account'
irc.state.supported['EXTBAN'] = '~,abc'
self.assertEqual(ircutils.accountExtban('foo', irc),
'~a:account1')
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
with self.subTest('InspIRCd'):
irc.state.supported['ACCOUNTEXTBAN'] = 'account,R'
irc.state.supported['EXTBAN'] = ',abcR'
self.assertEqual(ircutils.accountExtban('foo', irc),
'account:account1')
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
with self.subTest('Solanum'):
irc.state.supported['ACCOUNTEXTBAN'] = 'a'
irc.state.supported['EXTBAN'] = '$,abc'
self.assertEqual(ircutils.accountExtban('foo', irc),
'$a:account1')
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
with self.subTest('UnrealIRCd'):
irc.state.supported['ACCOUNTEXTBAN'] = 'account,a'
irc.state.supported['EXTBAN'] = '~,abc'
self.assertEqual(ircutils.accountExtban('foo', irc),
'~account:account1')
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
with self.subTest('no ACCOUNTEXTBAN'):
irc.state.supported.pop('ACCOUNTEXTBAN')
irc.state.supported['EXTBAN'] = '~,abc'
self.assertIsNone(ircutils.accountExtban('foo', irc))
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
with self.subTest('no EXTBAN'):
irc.state.supported['ACCOUNTEXTBAN'] = 'account,a'
irc.state.supported.pop('EXTBAN')
self.assertIsNone(ircutils.accountExtban('foo', irc))
self.assertIsNone(ircutils.accountExtban('bar', irc))
self.assertIsNone(ircutils.accountExtban('baz', irc))
def testSeparateModes(self):
self.assertEqual(ircutils.separateModes(['+ooo', 'x', 'y', 'z']),
[('+o', 'x'), ('+o', 'y'), ('+o', 'z')])