mirror of
				https://github.com/Mikaela/Limnoria.git
				synced 2025-11-04 01:27:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/env python
 | 
						|
 | 
						|
###
 | 
						|
# Copyright (c) 2002, Jeremiah Fincher
 | 
						|
# All rights reserved.
 | 
						|
#
 | 
						|
# Redistribution and use in source and binary forms, with or without
 | 
						|
# modification, are permitted provided that the following conditions are met:
 | 
						|
#
 | 
						|
#   * Redistributions of source code must retain the above copyright notice,
 | 
						|
#     this list of conditions, and the following disclaimer.
 | 
						|
#   * Redistributions in binary form must reproduce the above copyright notice,
 | 
						|
#     this list of conditions, and the following disclaimer in the
 | 
						|
#     documentation and/or other materials provided with the distribution.
 | 
						|
#   * Neither the name of the author of this software nor the name of
 | 
						|
#     contributors to this software may be used to endorse or promote products
 | 
						|
#     derived from this software without specific prior written consent.
 | 
						|
#
 | 
						|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
						|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | 
						|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
						|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
						|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
						|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
						|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
						|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
						|
# POSSIBILITY OF SUCH DAMAGE.
 | 
						|
###
 | 
						|
 | 
						|
"""
 | 
						|
Provides commands for manipulating channel topics.
 | 
						|
"""
 | 
						|
 | 
						|
__revision__ = "$Id$"
 | 
						|
 | 
						|
import plugins
 | 
						|
 | 
						|
import re
 | 
						|
import random
 | 
						|
 | 
						|
import conf
 | 
						|
import utils
 | 
						|
import ircdb
 | 
						|
import ircmsgs
 | 
						|
import plugins
 | 
						|
import privmsgs
 | 
						|
import registry
 | 
						|
import callbacks
 | 
						|
 | 
						|
conf.registerPlugin('Topic')
 | 
						|
conf.registerChannelValue(conf.supybot.plugins.Topic, 'separator',
 | 
						|
    registry.StringSurroundedBySpaces(' || ', """Determines what separator is
 | 
						|
    used between individually added topics in the channel topic."""))
 | 
						|
 | 
						|
class Topic(callbacks.Privmsg):
 | 
						|
    topicFormatter = '%s (%s)'
 | 
						|
    topicUnformatter = re.compile('(.*) \((\S+)\)')
 | 
						|
    def _splitTopic(self, topic, channel):
 | 
						|
        separator = self.registryValue('separator', channel)
 | 
						|
        return filter(None, topic.split(separator))
 | 
						|
 | 
						|
    def _joinTopic(self, topics, channel):
 | 
						|
        separator = self.registryValue('separator', channel)
 | 
						|
        return separator.join(topics)
 | 
						|
 | 
						|
    def _unformatTopic(self, topic, channel):
 | 
						|
        m = self.topicUnformatter.match(topic)
 | 
						|
        if m:
 | 
						|
            return (m.group(1), m.group(2))
 | 
						|
        else:
 | 
						|
            return (topic, '')
 | 
						|
 | 
						|
    def add(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <topic>
 | 
						|
 | 
						|
        Adds <topic> to the topics for <channel>.  <channel> is only necessary
 | 
						|
        if the message isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        topic = privmsgs.getArgs(args)
 | 
						|
        separator = self.registryValue('separator', channel)
 | 
						|
        if separator in topic:
 | 
						|
            s = 'You can\'t have %s in your topic' % separator
 | 
						|
            irc.error(s)
 | 
						|
            return
 | 
						|
        currentTopic = irc.state.getTopic(channel)
 | 
						|
        try:
 | 
						|
            name = ircdb.users.getUser(msg.prefix).name
 | 
						|
        except KeyError:
 | 
						|
            name = msg.nick
 | 
						|
        formattedTopic = self.topicFormatter % (topic, name)
 | 
						|
        if currentTopic:
 | 
						|
            newTopic = self._joinTopic([currentTopic, formattedTopic], channel)
 | 
						|
        else:
 | 
						|
            newTopic = formattedTopic
 | 
						|
        irc.queueMsg(ircmsgs.topic(channel, newTopic))
 | 
						|
    add = privmsgs.checkChannelCapability(add, 'topic')
 | 
						|
 | 
						|
    def shuffle(self, irc, msg, args, channel):
 | 
						|
        """[<channel>]
 | 
						|
 | 
						|
        Shuffles the topics in <channel>.  <channel> is only necessary if the
 | 
						|
        message isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        newtopic = irc.state.getTopic(channel)
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        if len(topics) == 0 or len(topics) == 1:
 | 
						|
            irc.error('I can\'t shuffle 1 or fewer topics.')
 | 
						|
            return
 | 
						|
        elif len(topics) == 2:
 | 
						|
            topics.reverse()
 | 
						|
            newtopic = self._joinTopic(topics, channel)
 | 
						|
        else:
 | 
						|
            random.shuffle(topics)
 | 
						|
            newtopic = self._joinTopic(topics, channel)
 | 
						|
            while newtopic == irc.state.getTopic(channel):
 | 
						|
                random.shuffle(topics)
 | 
						|
                newtopic = self._joinTopic(topics, channel)
 | 
						|
        irc.queueMsg(ircmsgs.topic(channel, newtopic))
 | 
						|
    shuffle = privmsgs.checkChannelCapability(shuffle, 'topic')
 | 
						|
 | 
						|
    def reorder(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <number> [<number> ...]
 | 
						|
 | 
						|
        Reorders the topics from <channel> in the order of the specified
 | 
						|
        <number> arguments.  <number> is a one-based index into the topics.
 | 
						|
        <channel> is only necessary if the message isn't sent in the channel
 | 
						|
        itself.
 | 
						|
        """
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        num = len(topics)
 | 
						|
        if num == 0 or num == 1:
 | 
						|
            irc.error('I cannot reorder 1 or fewer topics.')
 | 
						|
            return
 | 
						|
        if len(args) != num:
 | 
						|
            irc.error('All topic numbers must be specified.')
 | 
						|
            return
 | 
						|
        order = privmsgs.getArgs(args, required=num)
 | 
						|
        if topics:
 | 
						|
            for i,p in enumerate(order):
 | 
						|
                try:
 | 
						|
                    p = int(p)
 | 
						|
                    if p > 0:
 | 
						|
                        order[i] = p - 1
 | 
						|
                    elif p == 0:
 | 
						|
                        irc.error('0 is not a valid topic number.')
 | 
						|
                        return
 | 
						|
                    else:
 | 
						|
                        order[i] = num + p
 | 
						|
                except ValueError:
 | 
						|
                    irc.error('The positions must be valid integers.')
 | 
						|
                    return
 | 
						|
            if utils.sorted(order) != range(num):
 | 
						|
                irc.error('Duplicate topic numbers cannot be specified.')
 | 
						|
                return
 | 
						|
            try:
 | 
						|
                newtopics = [topics[i] for i in order]
 | 
						|
                newtopic = self._joinTopic(newtopics, channel)
 | 
						|
                irc.queueMsg(ircmsgs.topic(channel, newtopic))
 | 
						|
            except IndexError:
 | 
						|
                irc.error('An invalid topic number was specified.')
 | 
						|
        else:
 | 
						|
            irc.error('There are no topics to reorder.')
 | 
						|
    reorder = privmsgs.checkChannelCapability(reorder, 'topic')
 | 
						|
 | 
						|
    def list(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <number>
 | 
						|
 | 
						|
        Returns a list of the topics in <channel>, prefixed by their indexes.
 | 
						|
        Mostly useful for topic reordering.  <channel> is only necessary if the
 | 
						|
        message isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        L = []
 | 
						|
        for (i, t) in enumerate(topics):
 | 
						|
            (t, _) = self._unformatTopic(t, channel)
 | 
						|
            L.append('%s: %s' % (i+1, utils.ellipsisify(t, 30)))
 | 
						|
        s = utils.commaAndify(L)
 | 
						|
        irc.reply(s)
 | 
						|
    list = privmsgs.channel(list)
 | 
						|
 | 
						|
    def get(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <number>
 | 
						|
 | 
						|
        Returns topic number <number> from <channel>.  <number> is a one-based
 | 
						|
        index into the topics.  <channel> is only necessary if the message
 | 
						|
        isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        number = privmsgs.getArgs(args)
 | 
						|
        try:
 | 
						|
            number = int(number)
 | 
						|
            if number > 0:
 | 
						|
                number -= 1
 | 
						|
            elif number == 0:
 | 
						|
                irc.error('That\'s not a valid topic number.')
 | 
						|
                return
 | 
						|
        except ValueError:
 | 
						|
            irc.error('The argument must be a valid integer.')
 | 
						|
            return
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        if topics:
 | 
						|
            try:
 | 
						|
                irc.reply(self._unformatTopic(topics[number], channel)[0])
 | 
						|
            except IndexError:
 | 
						|
                irc.error('That\'s not a valid topic.')
 | 
						|
        else:
 | 
						|
            irc.error('There are no topics to get.')
 | 
						|
    get = privmsgs.channel(get)
 | 
						|
 | 
						|
    def change(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <number> <regexp>
 | 
						|
 | 
						|
        Changes the topic number <number> on <channel> according to the regular
 | 
						|
        expression <regexp>.  <number> is the one-based index into the topics;
 | 
						|
        <regexp> is a regular expression of the form
 | 
						|
        s/regexp/replacement/flags.  <channel> is only necessary if the message
 | 
						|
        isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        (number, regexp) = privmsgs.getArgs(args, required=2)
 | 
						|
        try:
 | 
						|
            number = int(number)
 | 
						|
            if number > 0:
 | 
						|
                number -= 1
 | 
						|
            elif number == 0:
 | 
						|
                irc.error('That\'s not a valid topic number.')
 | 
						|
                return
 | 
						|
        except ValueError:
 | 
						|
            irc.error('The <number> argument must be a number.')
 | 
						|
            return
 | 
						|
        try:
 | 
						|
            replacer = utils.perlReToReplacer(regexp)
 | 
						|
        except ValueError, e:
 | 
						|
            irc.error('The regexp wasn\'t valid: %s' % e.args[0])
 | 
						|
            return
 | 
						|
        except re.error, e:
 | 
						|
            irc.error(utils.exnToString(e))
 | 
						|
            return
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        if not topics:
 | 
						|
            irc.error('There are no topics to change.')
 | 
						|
            return
 | 
						|
        topic = topics.pop(number)
 | 
						|
        (topic, name) = self._unformatTopic(topic, channel)
 | 
						|
        try:
 | 
						|
            senderName = ircdb.users.getUser(msg.prefix).name
 | 
						|
        except KeyError:
 | 
						|
            irc.errorNoUser()
 | 
						|
            return
 | 
						|
        if name and name != senderName and \
 | 
						|
           not ircdb.checkCapabilities(msg.prefix, ('op', 'admin')):
 | 
						|
            irc.error('You can only modify your own topics.')
 | 
						|
            return
 | 
						|
        newTopic = self.topicFormatter % (replacer(topic), name)
 | 
						|
        if number < 0:
 | 
						|
            number = len(topics)+1+number
 | 
						|
        topics.insert(number, newTopic)
 | 
						|
        newTopic = self._joinTopic(topics, channel)
 | 
						|
        irc.queueMsg(ircmsgs.topic(channel, newTopic))
 | 
						|
    change = privmsgs.checkChannelCapability(change, 'topic')
 | 
						|
 | 
						|
    def remove(self, irc, msg, args, channel):
 | 
						|
        """[<channel>] <number>
 | 
						|
 | 
						|
        Removes topic <number> from the topic for <channel>  Topics are
 | 
						|
        numbered starting from 1; you can also use negative indexes to refer
 | 
						|
        to topics starting the from the end of the topic.  <channel> is only
 | 
						|
        necessary if the message isn't sent in the channel itself.
 | 
						|
        """
 | 
						|
        try:
 | 
						|
            number = int(privmsgs.getArgs(args))
 | 
						|
            if number > 0:
 | 
						|
                number -= 1
 | 
						|
            elif number == 0:
 | 
						|
                irc.error('That\'s not a valid topic number.')
 | 
						|
                return
 | 
						|
        except ValueError:
 | 
						|
            irc.error('The argument must be a number.')
 | 
						|
            return
 | 
						|
        topics = self._splitTopic(irc.state.getTopic(channel), channel)
 | 
						|
        try:
 | 
						|
            topic = topics.pop(number)
 | 
						|
        except IndexError:
 | 
						|
            irc.error('That\'s not a valid topic number.')
 | 
						|
            return
 | 
						|
        (topic, name) = self._unformatTopic(topic, channel)
 | 
						|
        try:
 | 
						|
            username = ircdb.users.getUser(msg.prefix).name
 | 
						|
        except KeyError:
 | 
						|
            username = msg.nick
 | 
						|
        if name and name != username and \
 | 
						|
           not ircdb.checkCapabilities(msg.prefix, ('op', 'admin')):
 | 
						|
            irc.error('You can only remove your own topics.')
 | 
						|
            return
 | 
						|
        newTopic = self._joinTopic(topics, channel)
 | 
						|
        irc.queueMsg(ircmsgs.topic(channel, newTopic))
 | 
						|
    remove = privmsgs.checkChannelCapability(remove, 'topic')
 | 
						|
 | 
						|
 | 
						|
Class = Topic
 | 
						|
 | 
						|
 | 
						|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
 |