mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-12-23 19:22:45 +01:00
change Topic to have a default required capability set, for all write operations.
by default, now only allows chanops, and users with admin or channel,op capability to change topics Signed-off-by: James Vega <jamessan@users.sourceforge.net>
This commit is contained in:
parent
f880037e41
commit
063dd24677
@ -63,7 +63,14 @@ conf.registerGroup(Topic, 'undo')
|
|||||||
conf.registerChannelValue(Topic.undo, 'max',
|
conf.registerChannelValue(Topic.undo, 'max',
|
||||||
registry.NonNegativeInteger(10, """Determines the number of previous
|
registry.NonNegativeInteger(10, """Determines the number of previous
|
||||||
topics to keep around in case the undo command is called."""))
|
topics to keep around in case the undo command is called."""))
|
||||||
|
conf.registerChannelValue(Topic, 'requireManageCapability',
|
||||||
|
registry.String('admin; channel,op',
|
||||||
|
"""Determines the
|
||||||
|
capabilities required (if any) to make any topic changes,
|
||||||
|
(everything except for read-only operations). Use 'channel,capab' for
|
||||||
|
channel-level capabilities.
|
||||||
|
Note that absence of an explicit anticapability means user has
|
||||||
|
capability."""))
|
||||||
|
|
||||||
|
|
||||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
||||||
|
@ -37,6 +37,7 @@ import supybot.ircmsgs as ircmsgs
|
|||||||
import supybot.plugins as plugins
|
import supybot.plugins as plugins
|
||||||
import supybot.ircutils as ircutils
|
import supybot.ircutils as ircutils
|
||||||
import supybot.callbacks as callbacks
|
import supybot.callbacks as callbacks
|
||||||
|
import supybot.ircdb as ircdb
|
||||||
|
|
||||||
def canChangeTopic(irc, msg, args, state):
|
def canChangeTopic(irc, msg, args, state):
|
||||||
assert not state.channel
|
assert not state.channel
|
||||||
@ -161,6 +162,30 @@ class Topic(callbacks.Plugin):
|
|||||||
irc.queueMsg(ircmsgs.topic(channel, newTopic))
|
irc.queueMsg(ircmsgs.topic(channel, newTopic))
|
||||||
irc.noReply()
|
irc.noReply()
|
||||||
|
|
||||||
|
def _checkManageCapabilities(self, irc, msg, channel):
|
||||||
|
"""Check if the user has any of the required capabilities to manage
|
||||||
|
the channel topic.
|
||||||
|
|
||||||
|
The list of required capabilities is in requireManageCapability
|
||||||
|
channel config.
|
||||||
|
|
||||||
|
Also allow if the user is a chanop. Since he can change the topic
|
||||||
|
manually anyway.
|
||||||
|
"""
|
||||||
|
c = irc.state.channels[channel]
|
||||||
|
if msg.nick in c.ops:
|
||||||
|
return True
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
if capabilities:
|
||||||
|
for capability in re.split(r'\s*;\s*', capabilities):
|
||||||
|
if capability.startswith('channel,'):
|
||||||
|
capability = ircdb.makeChannelCapability(channel, capability[8:])
|
||||||
|
if capability and ircdb.checkCapability(msg.prefix, capability):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def doJoin(self, irc, msg):
|
def doJoin(self, irc, msg):
|
||||||
if ircutils.strEqual(msg.nick, irc.nick):
|
if ircutils.strEqual(msg.nick, irc.nick):
|
||||||
# We're joining a channel, let's watch for the topic.
|
# We're joining a channel, let's watch for the topic.
|
||||||
@ -189,6 +214,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Adds <topic> to the topics for <channel>. <channel> is only necessary
|
Adds <topic> to the topics for <channel>. <channel> is only necessary
|
||||||
if the message isn't sent in the channel itself.
|
if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics.append(topic)
|
topics.append(topic)
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
@ -202,6 +230,9 @@ class Topic(callbacks.Plugin):
|
|||||||
<channel> is only necessary if the message isn't sent in the channel
|
<channel> is only necessary if the message isn't sent in the channel
|
||||||
itself.
|
itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics.append(topic)
|
topics.append(topic)
|
||||||
self._sendTopics(irc, channel, topics, fit=True)
|
self._sendTopics(irc, channel, topics, fit=True)
|
||||||
@ -212,6 +243,9 @@ class Topic(callbacks.Plugin):
|
|||||||
|
|
||||||
Replaces topic <number> with <topic>.
|
Replaces topic <number> with <topic>.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics[i] = topic
|
topics[i] = topic
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
@ -224,6 +258,9 @@ class Topic(callbacks.Plugin):
|
|||||||
currently on <channel>. <channel> is only necessary if the message
|
currently on <channel>. <channel> is only necessary if the message
|
||||||
isn't sent in the channel itself.
|
isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics.insert(0, topic)
|
topics.insert(0, topic)
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
@ -235,6 +272,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Shuffles the topics in <channel>. <channel> is only necessary if the
|
Shuffles the topics in <channel>. <channel> is only necessary if the
|
||||||
message isn't sent in the channel itself.
|
message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
if len(topics) == 0 or len(topics) == 1:
|
if len(topics) == 0 or len(topics) == 1:
|
||||||
irc.error('I can\'t shuffle 1 or fewer topics.', Raise=True)
|
irc.error('I can\'t shuffle 1 or fewer topics.', Raise=True)
|
||||||
@ -255,6 +295,9 @@ class Topic(callbacks.Plugin):
|
|||||||
<channel> is only necessary if the message isn't sent in the channel
|
<channel> is only necessary if the message isn't sent in the channel
|
||||||
itself.
|
itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
num = len(topics)
|
num = len(topics)
|
||||||
if num == 0 or num == 1:
|
if num == 0 or num == 1:
|
||||||
@ -290,6 +333,9 @@ class Topic(callbacks.Plugin):
|
|||||||
index into the topics. <channel> is only necessary if the message
|
index into the topics. <channel> is only necessary if the message
|
||||||
isn't sent in the channel itself.
|
isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
irc.reply(topics[number])
|
irc.reply(topics[number])
|
||||||
get = wrap(get, ['inChannel', 'topicNumber'])
|
get = wrap(get, ['inChannel', 'topicNumber'])
|
||||||
@ -303,6 +349,9 @@ class Topic(callbacks.Plugin):
|
|||||||
s/regexp/replacement/flags. <channel> is only necessary if the message
|
s/regexp/replacement/flags. <channel> is only necessary if the message
|
||||||
isn't sent in the channel itself.
|
isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics[number] = replacer(topics[number])
|
topics[number] = replacer(topics[number])
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
@ -315,6 +364,9 @@ class Topic(callbacks.Plugin):
|
|||||||
sets the entire topic. <channel> is only necessary if the message
|
sets the entire topic. <channel> is only necessary if the message
|
||||||
isn't sent in the channel itself.
|
isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
if number is not None:
|
if number is not None:
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topics[number] = topic
|
topics[number] = topic
|
||||||
@ -333,6 +385,9 @@ class Topic(callbacks.Plugin):
|
|||||||
to topics starting the from the end of the topic. <channel> is only
|
to topics starting the from the end of the topic. <channel> is only
|
||||||
necessary if the message isn't sent in the channel itself.
|
necessary if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
topic = topics.pop(number)
|
topic = topics.pop(number)
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
@ -344,6 +399,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Locks the topic (sets the mode +t) in <channel>. <channel> is only
|
Locks the topic (sets the mode +t) in <channel>. <channel> is only
|
||||||
necessary if the message isn't sent in the channel itself.
|
necessary if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
irc.queueMsg(ircmsgs.mode(channel, '+t'))
|
irc.queueMsg(ircmsgs.mode(channel, '+t'))
|
||||||
irc.noReply()
|
irc.noReply()
|
||||||
lock = wrap(lock, ['channel', ('haveOp', 'lock the topic')])
|
lock = wrap(lock, ['channel', ('haveOp', 'lock the topic')])
|
||||||
@ -354,6 +412,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Locks the topic (sets the mode +t) in <channel>. <channel> is only
|
Locks the topic (sets the mode +t) in <channel>. <channel> is only
|
||||||
necessary if the message isn't sent in the channel itself.
|
necessary if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
irc.queueMsg(ircmsgs.mode(channel, '-t'))
|
irc.queueMsg(ircmsgs.mode(channel, '-t'))
|
||||||
irc.noReply()
|
irc.noReply()
|
||||||
unlock = wrap(unlock, ['channel', ('haveOp', 'unlock the topic')])
|
unlock = wrap(unlock, ['channel', ('haveOp', 'unlock the topic')])
|
||||||
@ -364,6 +425,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Restores the topic to the last topic set by the bot. <channel> is only
|
Restores the topic to the last topic set by the bot. <channel> is only
|
||||||
necessary if the message isn't sent in the channel itself.
|
necessary if the message isn't sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
try:
|
try:
|
||||||
topics = self.lastTopics[channel]
|
topics = self.lastTopics[channel]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -379,6 +443,9 @@ class Topic(callbacks.Plugin):
|
|||||||
set it. <channel> is only necessary if the message isn't sent in the
|
set it. <channel> is only necessary if the message isn't sent in the
|
||||||
channel itself.
|
channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
self._addRedo(channel, self._getUndo(channel)) # current topic.
|
self._addRedo(channel, self._getUndo(channel)) # current topic.
|
||||||
topics = self._getUndo(channel) # This is the topic list we want.
|
topics = self._getUndo(channel) # This is the topic list we want.
|
||||||
if topics is not None:
|
if topics is not None:
|
||||||
@ -393,6 +460,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Undoes the last undo. <channel> is only necessary if the message isn't
|
Undoes the last undo. <channel> is only necessary if the message isn't
|
||||||
sent in the channel itself.
|
sent in the channel itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._getRedo(channel)
|
topics = self._getRedo(channel)
|
||||||
if topics is not None:
|
if topics is not None:
|
||||||
self._sendTopics(irc, channel, topics, isDo=True)
|
self._sendTopics(irc, channel, topics, isDo=True)
|
||||||
@ -407,6 +477,9 @@ class Topic(callbacks.Plugin):
|
|||||||
<channel> is only necessary if the message isn't sent in the channel
|
<channel> is only necessary if the message isn't sent in the channel
|
||||||
itself.
|
itself.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
if first == second:
|
if first == second:
|
||||||
irc.error('I refuse to swap the same topic with itself.')
|
irc.error('I refuse to swap the same topic with itself.')
|
||||||
@ -424,6 +497,9 @@ class Topic(callbacks.Plugin):
|
|||||||
default topic for a channel may be configured via the configuration
|
default topic for a channel may be configured via the configuration
|
||||||
variable supybot.plugins.Topic.default.
|
variable supybot.plugins.Topic.default.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topic = self.registryValue('default', channel)
|
topic = self.registryValue('default', channel)
|
||||||
if topic:
|
if topic:
|
||||||
self._sendTopics(irc, channel, [topic])
|
self._sendTopics(irc, channel, [topic])
|
||||||
@ -438,6 +514,9 @@ class Topic(callbacks.Plugin):
|
|||||||
Sets the topic separator for <channel> to <separator> Converts the
|
Sets the topic separator for <channel> to <separator> Converts the
|
||||||
current topic appropriately.
|
current topic appropriately.
|
||||||
"""
|
"""
|
||||||
|
if not self._checkManageCapabilities(irc, msg, channel):
|
||||||
|
capabilities = self.registryValue('requireManageCapability')
|
||||||
|
irc.errorNoCapability(capabilities, Raise=True)
|
||||||
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
topics = self._splitTopic(irc.state.getTopic(channel), channel)
|
||||||
self.setRegistryValue('separator', separator, channel)
|
self.setRegistryValue('separator', separator, channel)
|
||||||
self._sendTopics(irc, channel, topics)
|
self._sendTopics(irc, channel, topics)
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
from supybot.test import *
|
from supybot.test import *
|
||||||
|
|
||||||
class TopicTestCase(ChannelPluginTestCase):
|
class TopicTestCase(ChannelPluginTestCase):
|
||||||
plugins = ('Topic',)
|
plugins = ('Topic','User',)
|
||||||
def testRemove(self):
|
def testRemove(self):
|
||||||
self.assertError('topic remove 1')
|
self.assertError('topic remove 1')
|
||||||
_ = self.getMsg('topic add foo')
|
_ = self.getMsg('topic add foo')
|
||||||
@ -70,7 +70,23 @@ class TopicTestCase(ChannelPluginTestCase):
|
|||||||
self.assertEqual(m.command, 'TOPIC')
|
self.assertEqual(m.command, 'TOPIC')
|
||||||
self.assertEqual(m.args[0], self.channel)
|
self.assertEqual(m.args[0], self.channel)
|
||||||
self.assertEqual(m.args[1], 'foo (test) || bar (test)')
|
self.assertEqual(m.args[1], 'foo (test) || bar (test)')
|
||||||
|
|
||||||
|
def testManageCapabilities(self):
|
||||||
|
try:
|
||||||
|
world.testing = False
|
||||||
|
origuser = self.prefix
|
||||||
|
self.prefix = 'stuff!stuff@stuff'
|
||||||
|
self.assertNotError('register nottester stuff', private=True)
|
||||||
|
|
||||||
|
self.assertError('topic add foo')
|
||||||
|
origconf = conf.supybot.plugins.Topic.requireManageCapability()
|
||||||
|
conf.supybot.plugins.Topic.requireManageCapability.setValue('')
|
||||||
|
self.assertNotError('topic add foo')
|
||||||
|
finally:
|
||||||
|
world.testing = True
|
||||||
|
self.prefix = origuser
|
||||||
|
conf.supybot.plugins.Topic.requireManageCapability.setValue(origconf)
|
||||||
|
|
||||||
def testInsert(self):
|
def testInsert(self):
|
||||||
m = self.getMsg('topic add foo')
|
m = self.getMsg('topic add foo')
|
||||||
self.assertEqual(m.args[1], 'foo (test)')
|
self.assertEqual(m.args[1], 'foo (test)')
|
||||||
|
Loading…
Reference in New Issue
Block a user