Example #1
0
    def add(self, irc, msg, args, name, trackertype, url, description):
        """<name> <type> <url> [<description>]

        Add a bugtracker <url> to the list of defined bugtrackers. <type> is the
        type of the tracker (currently only Launchpad, Debbugs, Bugzilla,
        Issuezilla, Mantis and Trac are known). <name> is the name that will be used to
        reference the bugzilla in all commands. Unambiguous abbreviations of
        <name> will be accepted also.  <description> is the common name for the
        bugzilla and will be listed with the bugzilla query; if not given, it
        defaults to <name>.
        """
        name = name.lower()
        if not description:
            description = name
        if url[-1] == '/':
            url = url[:-1]
        trackertype = trackertype.lower()
        if trackertype in defined_bugtrackers:
            self.db[name] = defined_bugtrackers[trackertype](name,url,description)
        else:
            irc.error("Bugtrackers of type '%s' are not understood" % trackertype)
            return
        registerBugtracker(name, url, description, trackertype)
        self.shorthand = utils.abbrev(self.db.keys())
        irc.replySuccess()
Example #2
0
    def get_tracker(self, snarfurl, sfdata):
        snarfurl = snarfurl.replace('sf.net','sourceforge.net')
        snarfhost = snarfurl.replace('http://','').replace('https://','')

        # Begin HACK
        # launchpad.net has many URLs that can confuse us
        # make sure 'bug' in in the URL, somewhere
        if 'launchpad' in snarfhost:
            if not 'bug' in snarfhost: # Not a bug URL
                return None
            if snarfhost.lower().startswith("code."):
                return None

        if snarfhost.startswith('pad.lv'): # Launchpad URL shortening
            snarfhost = snarfhost[:snarfhost.rfind('/')]
            snarfhost = '/'.join( (_ for _ in snarfhost.split('/') if _) )
            if '/' in snarfhost: # it's not a bug URL
                return None
            return self.db.get('launchpad', None)
        # End HACK

        # At this point, we are only interested in the host part of the URL
        if '/' in snarfurl:
            snarfhost = snarfhost[:snarfhost.index('/')]

        if 'sourceforge.net' in snarfurl: # See below
            return None

        for t in self.db.keys():
            tracker = self.db.get(t, None)
            if not tracker:
                self.log.error("No tracker for key %r" % t)
                continue
            url = tracker.url.replace('http://','').replace('https://','')

            if 'sourceforge.net' in url:
                # sourceforge.net has no API or structured bug exporting, HTML
                # scraping is not good enough. Especially as SF keep changing it
                continue

            if '/' in url:
                url = url[:url.index('/')]
            if url in snarfhost:
                return tracker

        if snarfhost == 'pad.lv': # Launchpad URL shortening
            return self.db.get('lp', None)

        # No tracker found, bummer. Let's try and add one
        if 'show_bug.cgi' in snarfurl:
            tracker = Bugzilla().get_tracker(snarfurl)
            if tracker:
                self.db[tracker.name] = tracker
                self.shorthand = utils.abbrev(self.db.keys())
                return tracker
        return None
Example #3
0
def getLiteral(irc, msg, args, state, literals, errmsg=None):
    # ??? Should we allow abbreviations?
    if isinstance(literals, basestring):
        literals = (literals,)
    abbrevs = utils.abbrev(literals)
    if args[0] in abbrevs:
        state.args.append(abbrevs[args.pop(0)])
    elif errmsg is not None:
        state.error(errmsg, Raise=True)
    else:
        raise callbacks.ArgumentError
Example #4
0
def getLiteral(irc, msg, args, state, literals, errmsg=None):
    # ??? Should we allow abbreviations?
    if isinstance(literals, basestring):
        literals = (literals, )
    abbrevs = utils.abbrev(literals)
    if args[0] in abbrevs:
        state.args.append(abbrevs[args.pop(0)])
    elif errmsg is not None:
        state.error(errmsg, Raise=True)
    else:
        raise callbacks.ArgumentError
Example #5
0
 def __init__(self, irc):
     self.__parent = super(Bugtracker, self)
     self.__parent.__init__(irc)
     self.db = ircutils.IrcDict()
     for name in self.registryValue('bugtrackers'):
         registerBugtracker(name)
         group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
         if group.trackertype() in defined_bugtrackers:
             self.db[name] = defined_bugtrackers[group.trackertype()](name, group.url(), group.description(), group.trackertype())
         else:
             supylog.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name))
     self.shorthand = utils.abbrev(list(self.db.keys()))
     self.shown = {}
Example #6
0
    def __init__(self, irc):
        callbacks.PluginRegexp.__init__(self, irc)
        self.db = ircutils.IrcDict()
#        self.events = []
        for name in self.registryValue('bugtrackers'):
            registerBugtracker(name)
            group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
            if group.trackertype() in defined_bugtrackers.keys():
                self.db[name] = defined_bugtrackers[group.trackertype()](name, group.url(), group.description())
            else:
                self.log.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name))
        self.shorthand = utils.abbrev(self.db.keys())
        self.shown = {}
Example #7
0
 def testAbbrev(self):
     L = ['abc', 'bcd', 'bbe', 'foo', 'fool']
     d = utils.abbrev(L)
     def getItem(s):
         return d[s]
     self.assertRaises(KeyError, getItem, 'f')
     self.assertRaises(KeyError, getItem, 'fo')
     self.assertRaises(KeyError, getItem, 'b')
     self.assertEqual(d['bb'], 'bbe')
     self.assertEqual(d['bc'], 'bcd')
     self.assertEqual(d['a'], 'abc')
     self.assertEqual(d['ab'], 'abc')
     self.assertEqual(d['fool'], 'fool')
     self.assertEqual(d['foo'], 'foo')
Example #8
0
 def testAbbrev(self):
     L = ['abc', 'bcd', 'bbe', 'foo', 'fool']
     d = utils.abbrev(L)
     def getItem(s):
         return d[s]
     self.assertRaises(KeyError, getItem, 'f')
     self.assertRaises(KeyError, getItem, 'fo')
     self.assertRaises(KeyError, getItem, 'b')
     self.assertEqual(d['bb'], 'bbe')
     self.assertEqual(d['bc'], 'bcd')
     self.assertEqual(d['a'], 'abc')
     self.assertEqual(d['ab'], 'abc')
     self.assertEqual(d['fool'], 'fool')
     self.assertEqual(d['foo'], 'foo')
Example #9
0
    def remove(self, irc, msg, args, name):
        """<abbreviation>

        Remove the bugtracker associated with <abbreviation> from the list of
        defined bugtrackers.
        """
        try:
            name = self.shorthand[name.lower()]
            del self.db[name]
            self.registryValue('bugtrackers').remove(name)
            self.shorthand = utils.abbrev(self.db.keys())
            irc.replySuccess()
        except KeyError:
            s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None)
            irc.error(s % name)
Example #10
0
    def rename(self, irc, msg, args, oldname, newname, newdesc):
        """<oldname> <newname>

        Rename the bugtracker associated with <oldname> to <newname>.
        """
        try:
            name = self.shorthand[oldname.lower()]
            group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
            d = group.description()
            if newdesc:
                d = newdesc
            self.db[newname] = defined_bugtrackers[group.trackertype()](name,group.url(),d)
            registerBugtracker(newname, group.url(), d, group.trackertype())
            del self.db[name]
            self.registryValue('bugtrackers').remove(name)
            self.shorthand = utils.abbrev(self.db.keys())
            irc.replySuccess()
        except KeyError:
            s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None)
            irc.error(s % name)
Example #11
0
    def get_tracker(self, snarfurl, bugid):
        # SourceForge short domain
        snarfurl = snarfurl.replace('sf.net', 'sourceforge.net', 1)

        # Launchpad URL shortening
        snarfurl = re.sub(r'pad\.lv/(bug=)?(?P<bug>[0-9]+)', r'launchpad.net/bugs/\g<bug>', snarfurl)

        for t in list(self.db.keys()):
            tracker = self.db.get(t, None)
            if not tracker:
                supylog.error("No tracker for key '%s'" % t)
                continue
            url = tracker.url[tracker.url.rfind('://')+3:]
            if url in snarfurl:
                return tracker

        # No tracker found, bummer. Let's try and get one
        if 'show_bug.cgi' in snarfurl:
            tracker = Bugzilla().get_tracker(snarfurl)
        elif 'sourceforge.net' in snarfurl:
            tracker = SourceForge().get_tracker(snarfurl)
        elif 'github.com' in snarfurl:
            tracker = GitHub().get_tracker(snarfurl)
        elif re.match(r'[^\s/]+/[^\s/]+/[^\s/]+/(issues|pulls|merge_requests)', snarfurl):
            tracker = GitLab().get_tracker(snarfurl, bugid)
            if not tracker:
                tracker = Gitea().get_tracker(snarfurl, bugid)
        elif 'view.php' in snarfurl:
            tracker = Mantis().get_tracker(snarfurl)
        elif '/ticket/' in snarfurl:
            tracker = Trac().get_tracker(snarfurl)
        else:
            return

        if tracker:
            self.db[tracker.name] = tracker
            self.shorthand = utils.abbrev(list(self.db.keys()))
            return tracker
Example #12
0
    def add(self, irc, msg, args, name, trackertype, url, description):
        """<name> <type> <url> [<description>]

        Add a bugtracker to the list of defined bugtrackers. Currently supported types are
        Launchpad, Debbugs, Bugzilla, SourceForge, Github, GitLab, Gitea, Mantis, and Trac.
        <name> will be used to reference the bugtracker in all commands.
            Unambiguous abbreviations of it will also be accepted.
        <description> will be used to reference the bugtracker in the
            query result. If not given, it defaults to <name>.
        """
        name = name.lower()
        if not description:
            description = name
        if url[-1] == '/':
            url = url[:-1]
        trackertype = trackertype.lower()
        if trackertype in defined_bugtrackers:
            self.db[name] = defined_bugtrackers[trackertype](name, url, description, trackertype)
        else:
            irc.error("Bugtrackers of type '%s' are not understood" % trackertype)
            return
        registerBugtracker(name, url, description, trackertype)
        self.shorthand = utils.abbrev(list(self.db.keys()))
        irc.replySuccess()
Example #13
0
class Karma(callbacks.Plugin):
    """
    Provides a simple tracker for setting Karma (thing++, thing--).
    If ``config plugins.karma.allowUnaddressedKarma`` is set to ``True``
    (default since 2014.05.07), saying `boats++` will give 1 karma
    to ``boats``, and ``ships--`` will subtract 1 karma from ``ships``.

    However, if you use this in a sentence, like
    ``That deserves a ++. Kevin++``, 1 karma will be added to
    ``That deserves a ++. Kevin``, so you should only add or subtract karma
    in a line that doesn't have anything else in it.
    Alternatively, you can restrict karma tracking to nicks in the current
    channel by setting `config plugins.Karma.onlyNicks` to ``True``.

    If ``config plugins.karma.allowUnaddressedKarma` is set to `False``,
    you must address the bot with nick or prefix to add or subtract karma.
    """
    callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')

    def __init__(self, irc):
        self.__parent = super(Karma, self)
        self.__parent.__init__(irc)
        self.db = KarmaDB()

    def die(self):
        self.__parent.die()
        self.db.close()

    def _normalizeThing(self, thing):
        assert thing
        if thing[0] == '(' and thing[-1] == ')':
            thing = thing[1:-1]
        return thing

    def _respond(self, irc, channel, thing, karma):
        if self.registryValue('response', channel, irc.network):
            irc.reply(
                _('%(thing)s\'s karma is now %(karma)i') % {
                    'thing': thing,
                    'karma': karma
                })
        else:
            irc.noReply()

    def _doKarma(self, irc, msg, channel, thing):
        inc = self.registryValue('incrementChars', channel, irc.network)
        dec = self.registryValue('decrementChars', channel, irc.network)
        onlynicks = self.registryValue('onlyNicks', channel, irc.network)
        karma = ''
        for s in inc:
            if thing.endswith(s):
                thing = thing[:-len(s)]
                # Don't reply if the target isn't a nick
                if onlynicks and thing.lower() not in map(
                        ircutils.toLower, irc.state.channels[channel].users):
                    return
                if ircutils.strEqual(thing, msg.nick) and \
                    not self.registryValue('allowSelfRating',
                                           channel, irc.network):
                    irc.error(
                        _('You\'re not allowed to adjust your own karma.'))
                    return
                self.db.increment(channel, self._normalizeThing(thing))
                karma = self.db.get(channel, self._normalizeThing(thing))
        for s in dec:
            if thing.endswith(s):
                thing = thing[:-len(s)]
                if onlynicks and thing.lower() not in map(
                        ircutils.toLower, irc.state.channels[channel].users):
                    return
                if ircutils.strEqual(thing, msg.nick) and \
                    not self.registryValue('allowSelfRating',
                                           channel, irc.network):
                    irc.error(
                        _('You\'re not allowed to adjust your own karma.'))
                    return
                self.db.decrement(channel, self._normalizeThing(thing))
                karma = self.db.get(channel, self._normalizeThing(thing))
        if karma:
            self._respond(irc, channel, thing, karma[0] - karma[1])

    def invalidCommand(self, irc, msg, tokens):
        if msg.channel and tokens:
            thing = ' '.join(tokens)
            self._doKarma(irc, msg, msg.channel, thing)

    def doPrivmsg(self, irc, msg):
        # We don't handle this if we've been addressed because invalidCommand
        # will handle it for us.  This prevents us from accessing the db twice
        # and therefore crashing.
        if not (msg.addressed or msg.repliedTo):
            if msg.channel and \
               not ircmsgs.isCtcp(msg) and \
               self.registryValue('allowUnaddressedKarma',
                                  msg.channel, irc.network):
                irc = callbacks.SimpleProxy(irc, msg)
                thing = msg.args[1].rstrip()
                self._doKarma(irc, msg, msg.channel, thing)

    @internationalizeDocstring
    def karma(self, irc, msg, args, channel, things):
        """[<channel>] [<thing> ...]

        Returns the karma of <thing>.  If <thing> is not given, returns the top
        N karmas, where N is determined by the config variable
        supybot.plugins.Karma.rankingDisplay.  If one <thing> is given, returns
        the details of its karma; if more than one <thing> is given, returns
        the total karma of each of the things. <channel> is only necessary
        if the message isn't sent on the channel itself.
        """
        if len(things) == 1:
            name = things[0]
            t = self.db.get(channel, name)
            if t is None:
                irc.reply(format(_('%s has neutral karma.'), name))
            else:
                (added, subtracted) = t
                total = added - subtracted
                if self.registryValue('simpleOutput', channel, irc.network):
                    s = format('%s: %i', name, total)
                else:
                    s = format(
                        _('Karma for %q has been increased %n and '
                          'decreased %n for a total karma of %s.'), name,
                        (added, _('time')), (subtracted, _('time')), total)
                irc.reply(s)
        elif len(things) > 1:
            (L, neutrals) = self.db.gets(channel, things)
            if L:
                s = format('%L', [format('%s: %i', *t) for t in L])
                if neutrals:
                    neutral = format('.  %L %h neutral karma', neutrals,
                                     len(neutrals))
                    s += neutral
                irc.reply(s + '.')
            else:
                irc.reply(
                    _('I didn\'t know the karma for any of those '
                      'things.'))
        else:  # No name was given.  Return the top/bottom N karmas.
            limit = self.registryValue('rankingDisplay', channel, irc.network)
            highest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.top(channel, limit)
            ]
            lowest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.bottom(channel, limit)
            ]
            if not (highest and lowest):
                irc.error(_('I have no karma for this channel.'))
                return
            rank = self.db.rank(channel, msg.nick)
            if rank is not None:
                total = self.db.size(channel)
                rankS = format(_('  You (%s) are ranked %i out of %i.'),
                               msg.nick, rank, total)
            else:
                rankS = ''
            s = format(_('Highest karma: %L.  Lowest karma: %L.%s'), highest,
                       lowest, rankS)
            irc.reply(s)

    karma = wrap(karma, ['channel', any('something')])

    _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active'])

    @internationalizeDocstring
    def most(self, irc, msg, args, channel, kind):
        """[<channel>] {increased,decreased,active}

        Returns the most increased, the most decreased, or the most active
        (the sum of increased and decreased) karma things.  <channel> is only
        necessary if the message isn't sent in the channel itself.
        """
        L = self.db.most(
            channel, kind,
            self.registryValue('mostDisplay', channel, irc.network))
        if L:
            L = [format('%q: %i', name, i) for (name, i) in L]
            irc.reply(format('%L', L))
        else:
            irc.error(_('I have no karma for this channel.'))

    most = wrap(most,
                ['channel', ('literal', ['increased', 'decreased', 'active'])])

    @internationalizeDocstring
    def clear(self, irc, msg, args, channel, name):
        """[<channel>] [<name>]

        Resets the karma of <name> to 0. If <name> is not given, resets
        everything.
        """
        self.db.clear(channel, name or None)
        irc.replySuccess()

    clear = wrap(clear, [('checkChannelCapability', 'op'), optional('text')])

    @internationalizeDocstring
    def dump(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Dumps the Karma database for <channel> to <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        checkAllowShell(irc)
        self.db.dump(channel, filename)
        irc.replySuccess()

    dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    @internationalizeDocstring
    def load(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Loads the Karma database for <channel> from <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        checkAllowShell(irc)
        self.db.load(channel, filename)
        irc.replySuccess()

    load = wrap(load, [('checkCapability', 'owner'), 'channeldb', 'filename'])
Example #14
0
class Karma(callbacks.Plugin):
    callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')

    def __init__(self, irc):
        self.__parent = super(Karma, self)
        self.__parent.__init__(irc)
        self.db = KarmaDB()

    def die(self):
        self.__parent.die()
        self.db.close()

    def _normalizeThing(self, thing):
        assert thing
        if thing[0] == '(' and thing[-1] == ')':
            thing = thing[1:-1]
        return thing

    def _respond(self, irc, channel):
        if self.registryValue('response', channel):
            irc.replySuccess()
        else:
            irc.noReply()

    def _doKarma(self, irc, channel, thing):
        assert thing[-2:] in ('++', '--')
        if thing.endswith('++'):
            thing = thing[:-2]
            if ircutils.strEqual(thing, irc.msg.nick) and \
               not self.registryValue('allowSelfRating', channel):
                irc.error('You\'re not allowed to adjust your own karma.')
            elif thing:
                self.db.increment(channel, self._normalizeThing(thing))
                self._respond(irc, channel)
        else:
            thing = thing[:-2]
            if ircutils.strEqual(thing, irc.msg.nick) and \
               not self.registryValue('allowSelfRating', channel):
                irc.error('You\'re not allowed to adjust your own karma.')
            elif thing:
                self.db.decrement(channel, self._normalizeThing(thing))
                self._respond(irc, channel)

    def invalidCommand(self, irc, msg, tokens):
        channel = msg.args[0]
        if not irc.isChannel(channel):
            return
        if tokens[-1][-2:] in ('++', '--'):
            thing = ' '.join(tokens)
            self._doKarma(irc, channel, thing)

    def doPrivmsg(self, irc, msg):
        # We don't handle this if we've been addressed because invalidCommand
        # will handle it for us.  This prevents us from accessing the db twice
        # and therefore crashing.
        if not (msg.addressed or msg.repliedTo):
            channel = msg.args[0]
            if irc.isChannel(channel) and \
               not ircmsgs.isCtcp(msg) and \
               self.registryValue('allowUnaddressedKarma', channel):
                irc = callbacks.SimpleProxy(irc, msg)
                thing = msg.args[1].rstrip()
                if thing[-2:] in ('++', '--'):
                    self._doKarma(irc, channel, thing)

    def karma(self, irc, msg, args, channel, things):
        """[<channel>] [<thing> ...]

        Returns the karma of <thing>.  If <thing> is not given, returns the top
        N karmas, where N is determined by the config variable
        supybot.plugins.Karma.rankingDisplay.  If one <thing> is given, returns
        the details of its karma; if more than one <thing> is given, returns
        the total karma of each of the the things. <channel> is only necessary
        if the message isn't sent on the channel itself.
        """
        if len(things) == 1:
            name = things[0]
            t = self.db.get(channel, name)
            if t is None:
                irc.reply(format('%s has neutral karma.', name))
            else:
                (added, subtracted) = t
                total = added - subtracted
                if self.registryValue('simpleOutput', channel):
                    s = format('%s: %i', name, total)
                else:
                    s = format(
                        'Karma for %q has been increased %n and '
                        'decreased %n for a total karma of %s.', name,
                        (added, 'time'), (subtracted, 'time'), total)
                irc.reply(s)
        elif len(things) > 1:
            (L, neutrals) = self.db.gets(channel, things)
            if L:
                s = format('%L', [format('%s: %i', *t) for t in L])
                if neutrals:
                    neutral = format('.  %L %h neutral karma', neutrals,
                                     len(neutrals))
                    s += neutral
                irc.reply(s + '.')
            else:
                irc.reply('I didn\'t know the karma for any of those things.')
        else:  # No name was given.  Return the top/bottom N karmas.
            limit = self.registryValue('rankingDisplay', channel)
            top = self.db.top(channel, limit)
            highest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.top(channel, limit)
            ]
            lowest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.bottom(channel, limit)
            ]
            if not (highest and lowest):
                irc.error('I have no karma for this channel.')
                return
            rank = self.db.rank(channel, msg.nick)
            if rank is not None:
                total = self.db.size(channel)
                rankS = format('  You (%s) are ranked %i out of %i.', msg.nick,
                               rank, total)
            else:
                rankS = ''
            s = format('Highest karma: %L.  Lowest karma: %L.%s', highest,
                       lowest, rankS)
            irc.reply(s)

    karma = wrap(karma, ['channel', any('something')])

    _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active'])

    def most(self, irc, msg, args, channel, kind):
        """[<channel>] {increased,decreased,active}

        Returns the most increased, the most decreased, or the most active
        (the sum of increased and decreased) karma things.  <channel> is only
        necessary if the message isn't sent in the channel itself.
        """
        L = self.db.most(channel, kind,
                         self.registryValue('mostDisplay', channel))
        if L:
            L = [format('%q: %i', name, i) for (name, i) in L]
            irc.reply(format('%L', L))
        else:
            irc.error('I have no karma for this channel.')

    most = wrap(most,
                ['channel', ('literal', ['increased', 'decreased', 'active'])])

    def clear(self, irc, msg, args, channel, name):
        """[<channel>] <name>

        Resets the karma of <name> to 0.
        """
        self.db.clear(channel, name)
        irc.replySuccess()

    clear = wrap(clear, [('checkChannelCapability', 'op'), 'text'])

    def dump(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Dumps the Karma database for <channel> to <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.dump(channel, filename)
        irc.replySuccess()

    dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    def load(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Loads the Karma database for <channel> from <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.load(channel, filename)
        irc.replySuccess()

    load = wrap(load, [('checkCapability', 'owner'), 'channeldb', 'filename'])
Example #15
0
class Karma(callbacks.Plugin):
    callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')

    def __init__(self, irc):
        self.__parent = super(Karma, self)
        self.__parent.__init__(irc)
        self.db = KarmaDB()

    def die(self):
        self.__parent.die()
        self.db.close()

    def _normalizeThing(self, thing):
        assert thing
        if thing[0] == '(' and thing[-1] == ')':
            thing = thing[1:-1]
        return thing

    def _respond(self, irc, channel):
        if self.registryValue('response', channel):
            irc.replySuccess()
        else:
            irc.noReply()

    def _doKarma(self, irc, channel, thing):
        assert thing[-2:] in ('++', '--', '+-', '-+')
        assert thing[:-2] not in ('<', '-', '<!')
        thing_end = thing[-2:]
        thing = thing[:-2].strip('\'"')
        if thing:
            if ircutils.strEqual(thing, irc.msg.nick) and \
                    not self.registryValue('allowSelfRating', channel):
                irc.error('You\'re not allowed to adjust your own karma.')
            else:
                if thing_end == '++':
                    self.db.increment(channel, self._normalizeThing(thing))
                elif thing_end == '--':
                    self.db.decrement(channel, self._normalizeThing(thing))
                else:
                    self.db.increment(channel, self._normalizeThing(thing))
                    self.db.decrement(channel, self._normalizeThing(thing))
                self._respond(irc, channel)

#        if thing.endswith('++'):
#            thing = thing[:-2]
#            thing = thing.strip('\'"')
#            if ircutils.strEqual(thing, irc.msg.nick) and \
#               not self.registryValue('allowSelfRating', channel):
#                irc.error('You\'re not allowed to adjust your own karma.')
#            elif thing:
#                self.db.increment(channel, self._normalizeThing(thing))
#                self._respond(irc, channel)
#        else:
#            thing = thing[:-2]
#            thing = thing.strip('\'"')
#            if ircutils.strEqual(thing, irc.msg.nick) and \
#               not self.registryValue('allowSelfRating', channel):
#                irc.error('You\'re not allowed to adjust your own karma.')
#            elif thing:
#                self.db.decrement(channel, self._normalizeThing(thing))
#                self._respond(irc, channel)

    def invalidCommand(self, irc, msg, tokens):
        channel = msg.args[0]
        if not irc.isChannel(channel):
            return
#        if tokens[-1][-2:] in ('++', '--'):
#            thing = ' '.join(tokens)
#            self._doKarma(irc, channel, thing)
# let's be a little smarter about grabbing things
# <bob> thanks.  john++  you're awesome
# should only increment 'john'
        for token in tokens:
            self._doKarma(irc, channel, thing)

    def doPrivmsg(self, irc, msg):
        # We don't handle this if we've been addressed because invalidCommand
        # will handle it for us.  This prevents us from accessing the db twice
        # and therefore crashing.
        if not (msg.addressed or msg.repliedTo):
            channel = msg.args[0]
            if irc.isChannel(channel) and \
               self.registryValue('allowUnaddressedKarma', channel):
                irc = callbacks.SimpleProxy(irc, msg)
                #                thing = msg.args[1].rstrip()
                #                if thing[-2:] in ('++', '--'):
                #                    self._doKarma(irc, channel, thing)
                # same here as above in invalidCommand
                #for token in msg.args[1].split():
                for token in smart_split(msg.args[1]):
                    self._doKarma(irc, channel, token)

    def karma(self, irc, msg, args, channel, things):
        """[<channel>] [<thing> ...]

        Returns the karma of <text>.  If <thing> is not given, returns the top
        three and bottom three karmas.  If one <thing> is given, returns the
        details of its karma; if more than one <thing> is given, returns the
        total karma of each of the things. <channel> is only necessary if
        the message isn't sent on the channel itself.
        """
        name = ' '.join(things)
        #        if len(things) == 1:
        #            name = things[0]
        if name:
            t = self.db.get(channel, name)
            if t is None:
                irc.reply(format('%s has neutral karma.', name))
            else:
                (added, subtracted) = t
                total = added - subtracted
                if self.registryValue('simpleOutput', channel):
                    s = format('%s: %i', name, total)
                else:
                    s = format(
                        'Karma for %q has been increased %n and '
                        'decreased %n for a total karma of %s.', name,
                        (added, 'time'), (subtracted, 'time'), total)
                irc.reply(s)


#        elif len(things) > 1:
#            (L, neutrals) = self.db.gets(channel, things)
#            if L:
#                s = format('%L', [format('%s: %i', *t) for t in L])
#                if neutrals:
#                    neutral = format('.  %L %h neutral karma',
#                                     neutrals, len(neutrals))
#                    s += neutral
#                irc.reply(s + '.')
#            else:
#                irc.reply('I didn\'t know the karma for any of those things.')
        else:  # No name was given.  Return the top/bottom N karmas.
            limit = self.registryValue('rankingDisplay', channel)
            top = self.db.top(channel, limit)
            highest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.top(channel, limit)
            ]
            lowest = [
                format('%q (%s)', s, t)
                for (s, t) in self.db.bottom(channel, limit)
            ]
            if not (highest and lowest):
                irc.error('I have no karma for this channel.')
                return
            rank = self.db.rank(channel, msg.nick)
            if rank is not None:
                total = self.db.size(channel)
                rankS = format('  You (%s) are ranked %i out of %i.', msg.nick,
                               rank, total)
            else:
                rankS = ''
            s = format('Highest karma: %L.  Lowest karma: %L.%s', highest,
                       lowest, rankS)
            irc.reply(s)

    karma = wrap(karma, ['channel', any('something')])

    _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active'])

    def most(self, irc, msg, args, channel, kind):
        """[<channel>] {increased,decreased,active}

        Returns the most increased, the most decreased, or the most active
        (the sum of increased and decreased) karma things.  <channel> is only
        necessary if the message isn't sent in the channel itself.
        """
        L = self.db.most(channel, kind,
                         self.registryValue('mostDisplay', channel))
        if L:
            L = [format('%q: %i', name, i) for (name, i) in L]
            irc.reply(format('%L', L))
        else:
            irc.error('I have no karma for this channel.')

    most = wrap(most,
                ['channel', ('literal', ['increased', 'decreased', 'active'])])

    def clear(self, irc, msg, args, channel, name):
        """[<channel>] <name>

        Resets the karma of <name> to 0.
        """
        self.db.clear(channel, name)
        irc.replySuccess()

    clear = wrap(clear, [('checkChannelCapability', 'op'), 'text'])

    def getName(self, nick, msg, match):
        addressed = callbacks.addressed(nick, msg)
        name = callbacks.addressed(
            nick,
            ircmsgs.IrcMsg(prefix='',
                           args=(msg.args[0], match.group(1)),
                           msg=msg))
        if not name:
            name = match.group(1)
        if not addressed:
            if not self.registryValue('allowUnaddressedKarma'):
                return ''
            if not msg.args[1].startswith(match.group(1)):
                return ''
            name = match.group(1)
        elif addressed:
            if not addressed.startswith(name):
                return ''
        name = name.strip('()')
        return name

    def dump(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Dumps the Karma database for <channel> to <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.dump(channel, filename)
        irc.replySuccess()

    dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    def load(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Loads the Karma database for <channel> from <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.load(channel, filename)
        irc.replySuccess()

    load = wrap(load, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    def karmawar(self, irc, msg, args, nick, dice):
        """<nick> [<dice>]

        Initiate a karma battle with another user
        """
        if not dice:
            dice = 3
        attack_rolls = ["%d" % self.rng.randrange(1, 6) for x in range(3)]
        irc.reply("%s rolls %d dice: %s" %
                  (nick, dice, commaAndify(attack_rolls)))
        def_rols = ["%d" % self.rng.randrange(1, 6) for x in range(2)]
        irc.reply("%s rolls 2 dice: %s" % (nick, commaAndify(def_rolls)))

    karmawar = wrap(karmawar, ['nickInChannel', optional('nonNegativeInt')])
Example #16
0
# POSSIBILITY OF SUCH DAMAGE.
###

import re

import rssparser
import BeautifulSoup

import supybot.conf as conf
import supybot.utils as utils
from supybot.commands import *
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks


unitAbbrevs = utils.abbrev(['Fahrenheit', 'Celsius', 'Centigrade', 'Kelvin'])
unitAbbrevs['C'] = 'Celsius'
unitAbbrevs['Ce'] = 'Celsius'

noLocationError = 'No such location could be found.'
class NoLocation(callbacks.Error):
    pass

class Weather(callbacks.Plugin):
    weatherCommands = {'wunder': 'wunder.wunder', 'rsswunder': 'wunder.rss',
                       'wunder rss': 'wunder.rss', 'cnn': 'cnn', 'ham': 'ham'}
    threaded = True
    headers = {'User-agent':
               'Mozilla/5.0 (compatible; U; utils.web python module)'}
    def callCommand(self, method, irc, msg, *args, **kwargs):
        try:
Example #17
0
class Babelfish(callbacks.Plugin):
    threaded = True
    _abbrevs = utils.abbrev(map(str.lower, babelfish.available_languages))
    _abbrevs['de'] = 'german'
    _abbrevs['jp'] = 'japanese'
    _abbrevs['kr'] = 'korean'
    _abbrevs['es'] = 'spanish'
    _abbrevs['pt'] = 'portuguese'
    _abbrevs['it'] = 'italian'
    _abbrevs['zh'] = 'chinese_simple'
    _abbrevs['zt'] = 'chinese_traditional'
    _abbrevs['nl'] = 'dutch'
    _abbrevs['el'] = 'greek'
    for language in babelfish.available_languages:
        _abbrevs[language] = language

    def _getLang(self, fromLang, toLang, chan):
        fromLang = self._abbrevs[fromLang.lower()]
        toLang = self._abbrevs[toLang.lower()]
        languages = map(str.lower, self.registryValue('languages', chan))
        if fromLang not in languages:
            fromLang = None
        if toLang not in languages:
            toLang = None
        return (fromLang, toLang)

    class language(callbacks.Commands):
        def list(self, irc, msg, args):
            """takes no arguments

            Returns the languages that Babelfish can translate to/from.
            """
            irc.reply(format('%L', babelfish.available_languages))

        def random(self, irc, msg, args, optlist):
            """[--allow-english]

            Returns a random language supported by babelfish.  If --allow-english
            is provided, will include English in the list of possible languages.
            """
            allowEnglish = False
            for (option, arg) in optlist:
                if option == 'allow-english':
                    allowEnglish = True
            languages = conf.get(conf.supybot.plugins.Babelfish.languages,
                                 msg.args[0])
            if not languages:
                irc.error('I can\'t speak any other languages.', Raise=True)
            language = utils.iter.choice(languages)
            while not allowEnglish and language == 'English':
                language = utils.iter.choice(languages)
            irc.reply(language)

        random = wrap(random, [getopts({'allow-english': ''})])

    def translate(self, irc, msg, args, fromLang, toLang, text):
        """<from-language> [to] <to-language> <text>

        Returns <text> translated from <from-language> into <to-language>.
        Beware that translating to or from languages that use multi-byte
        characters may result in some very odd results.
        """
        chan = msg.args[0]
        try:
            (fromLang, toLang) = self._getLang(fromLang, toLang, chan)
            if not fromLang or not toLang:
                langs = list(self.registryValue('languages', chan))
                if not langs:
                    irc.error('I do not speak any other languages.')
                    return
                else:
                    irc.error(format('I only speak %L.', langs))
                    return
            translation = babelfish.translate(text, fromLang, toLang)
            irc.reply(utils.web.htmlToText(translation))
        except (KeyError, babelfish.LanguageNotAvailableError), e:
            languages = self.registryValue('languages', chan)
            if languages:
                languages = format('Valid languages include %L',
                                   sorted(languages))
            else:
                languages = 'I do not speak any other languages.'
            irc.errorInvalid('language', str(e), languages)
        except babelfish.BabelizerIOError, e:
            irc.error(str(e))
Example #18
0
class NewKarma(callbacks.Plugin):
    callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')
    def __init__(self, irc):
        self.__parent = super(NewKarma, self)
        self.__parent.__init__(irc)
        self.db = KarmaDB()
        self.alias_db = AliasDB()

    def die(self):
        self.__parent.die()
        self.db.close()
        self.alias_db.close()

    def _normalizeThing(self, thing):
        assert thing
        if thing[0] == '(' and thing[-1] == ')':
            thing = thing[1:-1]
        return thing

    def _respond(self, irc, channel, message=None):
        if self.registryValue('response', channel):
	    if message:
                irc.reply(message, prefixNick=False)
            else:
                irc.replySuccess()
        else:
            irc.noReply()

    def _parseKarmaMessage(self, name, total, channel, originalname, direction):
        if direction == "up":
            message = self.registryValue('karmaMessageUp', channel)
        elif direction == "down":
            message = self.registryValue('karmaMessageDown', channel)
        elif direction == "none":
            message = self.registryValue('karmaMessageNone', channel)
        else:
            message = "I have no idea what you are talking about."
        if total == 1 or total == -1:
            message = message.replace('points', 'point')
        if originalname:
            name = "%s (%s)" % (originalname, name)
        return  message.replace('USER', name).replace('TOTAL', str(total))

    def _doAlias(self, irc, channel, things):
      name = things.split('is also known as')[0].split()[-1]
      alias = things.split('is also known as')[1].split()[0]
      self.alias_db.alias(channel, name, alias)
      irc.reply("%s is also %s, got it!" % (name, alias))

    def _doUnalias(self, irc, channel, things):
      name = things.split('is no longer known as')[0].split()[-1]
      alias = things.split('is no longer known as')[1].split()[0]
      self.alias_db.unalias(channel, name, alias)
      irc.reply("Who?  I've forgotten that %s was ever %s!" % (name, alias))

    def _doKarma(self, irc, channel, things):
      for thing in things.split():
        originalthing = None
        #if thing.endswith('++'):
        if "++" in thing:
            thing = thing.split("++")[0]
            if thing:
              #see if what we are incrementing is an alias for someone
              aliasfor = self.alias_db.get(channel, self._normalizeThing(thing))
              if aliasfor:
                originalthing = thing
                thing = aliasfor
              if type(thing) != list:
                thing = [thing]
              for athing in thing:
                #Honor allowSelfRating unless this is a group alias
                if ircutils.strEqual(athing, irc.msg.nick) and \
                   not self.registryValue('allowSelfRating', channel) and \
                   len(thing) == 1:
                  irc.error('You\'re not allowed to adjust your own karma.')
                else:
                  self.db.increment(channel, self._normalizeThing(athing))
                  t = self.db.get(channel, athing)
                  if t is None:
                    total = 0
                  else:
                    (added, subtracted) = t
                    total = added - subtracted
                  if total == 0:
                    self._respond(irc, channel, self._parseKarmaMessage(athing, total, channel, originalthing, "none"))
	            self.db.garbageCollect(channel, athing)
                  else:
                    self._respond(irc, channel, self._parseKarmaMessage(athing, total, channel, originalthing, "up"))
        #decrement unless some person has "--" in their name in channel
        elif "--" in thing and thing not in irc.state.channels[channel].users:
            #Hack for users with "--" in their name being given negative karma
            if thing[0:-2] in irc.state.channels[channel].users:
                thing = thing[0:-2]
            else:
                thing = thing.split("--")[0]
            if thing:
              #see if what we are incrementing is an alias for someone
              aliasfor = self.alias_db.get(channel, self._normalizeThing(thing))
              if aliasfor:
                originalthing = thing
                thing = aliasfor
              if type(thing) != list:
                thing = [thing]
              for athing in thing:
                #Honor allowSelfRating unless this is a group alias
                if ircutils.strEqual(athing, irc.msg.nick) and \
                   not self.registryValue('allowSelfRating', channel) and \
                   len(thing) == 1:
                  irc.error('You\'re not allowed to adjust your own karma.')
                else:
                  self.db.decrement(channel, self._normalizeThing(athing))
                  t = self.db.get(channel, athing)
                  if t is None:
                    total = 0
                  else:
                    (added, subtracted) = t
                    total = added - subtracted
                  if total == 0:
                    self._respond(irc, channel, self._parseKarmaMessage(athing, total, channel, originalthing, "none"))
	            self.db.garbageCollect(channel, athing)
                  else:
                    self._respond(irc, channel, self._parseKarmaMessage(athing, total, channel, originalthing, "down"))

    def invalidCommand(self, irc, msg, tokens):
        channel = msg.args[0]
        if not irc.isChannel(channel):
            return
        if tokens[-1][-2:] in ('++', '--'):
            thing = ' '.join(tokens)
            self._doKarma(irc, channel, thing)

    def doPrivmsg(self, irc, msg):
        # We don't handle this if we've been addressed because invalidCommand
        # will handle it for us.  This prevents us from accessing the db twice
        # and therefore crashing.
        if not (msg.addressed or msg.repliedTo):
            channel = msg.args[0]
            if irc.isChannel(channel) and \
               not ircmsgs.isCtcp(msg) and \
               self.registryValue('allowUnaddressedKarma', channel):
                irc = callbacks.SimpleProxy(irc, msg)
                thing = msg.args[1].rstrip()
                if '++' in thing or '--' in thing:
                    self._doKarma(irc, channel, thing)
                if 'is also known as' in thing:
                    self._doAlias(irc, channel, thing)

                if 'is no longer known as' in thing:
                    self._doUnalias(irc, channel, thing)

    def showaliases(self, irc, msg, args, channel, name):
        """[<channel>] <word>

        Lists the karmaaliases for a given word.
        """
        if name:
          name = name[0]
          aliases = self.alias_db.get_aliases(channel, name)
          if aliases:
            sep = ", "
            if len(aliases) > 2:
              aliases[-1] = "and %s" % (aliases[-1])
            elif len(aliases) == 2:
              sep = " and "
            irc.reply("%s is known as %s." % (name, sep.join(aliases)))
          else:
            irc.reply("%s doesn't have any aliases!" % (name))
        else:
          irc.reply("Give me *something*!  A nick, a word, anything!")
    showaliases = wrap(showaliases, ['channel', any('something')])

    def karma(self, irc, msg, args, channel, things):
        """[<channel>] [<thing> ...]

        Returns the karma of <thing>.  If <thing> is not given, returns the top
        N karmas, where N is determined by the config variable
        supybot.plugins.Karma.rankingDisplay.  If one <thing> is given, returns
        the details of its karma; if more than one <thing> is given, returns
        the total karma of each of the the things. <channel> is only necessary
        if the message isn't sent on the channel itself.
        """
        if len(things) == 1:
            name = things[0]
            t = self.db.get(channel, name)
            if t is None:
                irc.reply(format('%s has neutral karma.', name))
            else:
                (added, subtracted) = t
                total = added - subtracted
                if self.registryValue('simpleOutput', channel):
                    s = format('%s: %i', name, total)
                else:
                    s = format('Karma for %q has been increased %n and '
                               'decreased %n for a total karma of %s.',
                               name, (added, 'time'), (subtracted, 'time'),
                               total)
                irc.reply(s)
        elif len(things) > 1:
            (L, neutrals) = self.db.gets(channel, things)
            if L:
                s = format('%L', [format('%s: %i', *t) for t in L])
                if neutrals:
                    neutral = format('.  %L %h neutral karma',
                                     neutrals, len(neutrals))
                    s += neutral
                irc.reply(s + '.')
            else:
                irc.reply('I didn\'t know the karma for any of those things.')
        else: # No name was given.  Return the top/bottom N karmas.
            limit = self.registryValue('rankingDisplay', channel)
            top = self.db.top(channel, limit)
            highest = [format('%q (%s)', s, t)
                       for (s, t) in self.db.top(channel, limit)]
            lowest = [format('%q (%s)', s, t)
                      for (s, t) in self.db.bottom(channel, limit)]
            if not (highest and lowest):
                irc.error('I have no karma for this channel.')
                return
            rank = self.db.rank(channel, msg.nick)
            if rank is not None:
                total = self.db.size(channel)
                rankS = format('  You (%s) are ranked %i out of %i.',
                               msg.nick, rank, total)
            else:
                rankS = ''
            s = format('Highest karma: %L.  Lowest karma: %L.%s',
                       highest, lowest, rankS)
            irc.reply(s, prefixNick=False)
    karma = wrap(karma, ['channel', any('something')])

    _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active'])
    def most(self, irc, msg, args, channel, kind):
        """[<channel>] {increased,decreased,active}

        Returns the most increased, the most decreased, or the most active
        (the sum of increased and decreased) karma things.  <channel> is only
        necessary if the message isn't sent in the channel itself.
        """
        L = self.db.most(channel, kind,
                         self.registryValue('mostDisplay', channel))
        if L:
            L = [format('%q: %i', name, i) for (name, i) in L]
            irc.reply(format('%L', L))
        else:
            irc.error('I have no karma for this channel.')
    most = wrap(most, ['channel',
                       ('literal', ['increased', 'decreased', 'active'])])

    def clear(self, irc, msg, args, channel, name):
        """[<channel>] <name>

        Resets the karma of <name> to 0.
        """
        self.db.clear(channel, name)
        irc.replySuccess()
    clear = wrap(clear, [('checkChannelCapability', 'op'), 'text'])

    def dump(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Dumps the Karma database for <channel> to <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.dump(channel, filename)
        irc.replySuccess()
    dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    def load(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Loads the Karma database for <channel> from <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.load(channel, filename)
        irc.replySuccess()
    load = wrap(load, [('checkCapability', 'owner'), 'channeldb', 'filename'])
Example #19
0
class Karma(callbacks.Plugin):
    """Provides a simple tracker for setting Karma (thing++, thing--)."""
    callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')
    def __init__(self, irc):
        self.__parent = super(Karma, self)
        self.__parent.__init__(irc)
        self.db = KarmaDB()

    def die(self):
        self.__parent.die()
        self.db.close()

    def _normalizeThing(self, thing):
        assert thing
        if thing[0] == '(' and thing[-1] == ')':
            thing = thing[1:-1]
        return thing

    def _respond(self, irc, channel, thing, karma):
        if self.registryValue('response', channel):
            irc.reply(_('%(thing)s\'s karma is now %(karma)i') %
                    {'thing': thing, 'karma': karma})
        else:
            irc.noReply()

    def _doKarma(self, irc, msg, channel, thing):
        def get_karma_word(thing, suffixes):
            words = thing.split()
            karma_words = [word for word in words if word.endswith(tuple(suffixes))]

            if len(karma_words != 1):
                return None

            return karma_words[0]

        inc = self.registryValue('incrementChars', channel)
        dec = self.registryValue('decrementChars', channel)
        inc_word = get_karma_word(thing, inc)
        dec_word = get_karma_word(thing, dec)

        # Ignore if there is not an unambigous op
        if (inc_word is None) != (dec_word is None):
            return

        word = inc_word or dec_word
        cmp_word = self._normalizeThing(ircutils.toLower(word))

        # Don't karma if self rating is disabled
        allow_self_rating = self.registryValue('allowSelfRating', channel)
        if not allow_self_rating and cmp_word == ircutils.toLower(msg.nick):
            irc.error(_('You\'re not allowed to adjust your own karma.'))
            return

        # Don't karma if the target isn't a nick
        only_nicks = self.registryValue('onlyNicks', channel)
        if only_nicks:
            users = [ircutils.toLower(user) for user in irc.state.channels[channel].users]
            if not cmp_word in users:
                return

        # Execute the karma op
        karma = ''
        if inc_word:
            self.db.increment(channel, cmp_word)
            karma = self.db.get(channel, cmp_word)
        else:
            self.db.decrement(channel, cmp_word)
            karma = self.db.get(channel, cmp_word)

        if karma:
            self._respond(irc, channel, thing, karma[0]-karma[1])

    def invalidCommand(self, irc, msg, tokens):
        channel = msg.args[0]
        if irc.isChannel(channel) and tokens:
            thing = ' '.join(tokens)
            self._doKarma(irc, msg, channel, thing)

    def doPrivmsg(self, irc, msg):
        # We don't handle this if we've been addressed because invalidCommand
        # will handle it for us.  This prevents us from accessing the db twice
        # and therefore crashing.
        if not (msg.addressed or msg.repliedTo):
            channel = msg.args[0]
            if irc.isChannel(channel) and \
               not ircmsgs.isCtcp(msg) and \
               self.registryValue('allowUnaddressedKarma', channel):
                irc = callbacks.SimpleProxy(irc, msg)
                thing = msg.args[1].rstrip()
                self._doKarma(irc, msg, channel, thing)

    @internationalizeDocstring
    def karma(self, irc, msg, args, channel, things):
        """[<channel>] [<thing> ...]

        Returns the karma of <thing>.  If <thing> is not given, returns the top
        N karmas, where N is determined by the config variable
        supybot.plugins.Karma.rankingDisplay.  If one <thing> is given, returns
        the details of its karma; if more than one <thing> is given, returns
        the total karma of each of the things. <channel> is only necessary
        if the message isn't sent on the channel itself.
        """
        if len(things) == 1:
            name = things[0]
            t = self.db.get(channel, name)
            if t is None:
                irc.reply(format(_('%s has neutral karma.'), name))
            else:
                (added, subtracted) = t
                total = added - subtracted
                if self.registryValue('simpleOutput', channel):
                    s = format('%s: %i', name, total)
                else:
                    s = format(_('Karma for %q has been increased %n and '
                               'decreased %n for a total karma of %s.'),
                               name, (added, _('time')),
                               (subtracted, _('time')),
                               total)
                irc.reply(s)
        elif len(things) > 1:
            (L, neutrals) = self.db.gets(channel, things)
            if L:
                s = format('%L', [format('%s: %i', *t) for t in L])
                if neutrals:
                    neutral = format('.  %L %h neutral karma',
                                     neutrals, len(neutrals))
                    s += neutral
                irc.reply(s + '.')
            else:
                irc.reply(_('I didn\'t know the karma for any of those '
                            'things.'))
        else: # No name was given.  Return the top/bottom N karmas.
            limit = self.registryValue('rankingDisplay', channel)
            top = self.db.top(channel, limit)
            highest = [format('%q (%s)', s, t)
                       for (s, t) in self.db.top(channel, limit)]
            lowest = [format('%q (%s)', s, t)
                      for (s, t) in self.db.bottom(channel, limit)]
            if not (highest and lowest):
                irc.error(_('I have no karma for this channel.'))
                return
            rank = self.db.rank(channel, msg.nick)
            if rank is not None:
                total = self.db.size(channel)
                rankS = format(_('  You (%s) are ranked %i out of %i.'),
                               msg.nick, rank, total)
            else:
                rankS = ''
            s = format(_('Highest karma: %L.  Lowest karma: %L.%s'),
                       highest, lowest, rankS)
            irc.reply(s)
    karma = wrap(karma, ['channel', any('something')])

    _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active'])
    @internationalizeDocstring
    def most(self, irc, msg, args, channel, kind):
        """[<channel>] {increased,decreased,active}

        Returns the most increased, the most decreased, or the most active
        (the sum of increased and decreased) karma things.  <channel> is only
        necessary if the message isn't sent in the channel itself.
        """
        L = self.db.most(channel, kind,
                         self.registryValue('mostDisplay', channel))
        if L:
            L = [format('%q: %i', name, i) for (name, i) in L]
            irc.reply(format('%L', L))
        else:
            irc.error(_('I have no karma for this channel.'))
    most = wrap(most, ['channel',
                       ('literal', ['increased', 'decreased', 'active'])])

    @internationalizeDocstring
    def clear(self, irc, msg, args, channel, name):
        """[<channel>] [<name>]

        Resets the karma of <name> to 0. If <name> is not given, resets
        everything.
        """
        self.db.clear(channel, name or None)
        irc.replySuccess()
    clear = wrap(clear, [('checkChannelCapability', 'op'), optional('text')])

    @internationalizeDocstring
    def dump(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Dumps the Karma database for <channel> to <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.dump(channel, filename)
        irc.replySuccess()
    dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename'])

    @internationalizeDocstring
    def load(self, irc, msg, args, channel, filename):
        """[<channel>] <filename>

        Loads the Karma database for <channel> from <filename> in the bot's
        data directory.  <channel> is only necessary if the message isn't sent
        in the channel itself.
        """
        self.db.load(channel, filename)
        irc.replySuccess()
    load = wrap(load, [('checkCapability', 'owner'), 'channeldb', 'filename'])
Example #20
0
try:
    # The 3rd party simplejson module was included in Python 2.6 and renamed to
    # json.  Unfortunately, this conflicts with the 3rd party json module.
    # Luckily, the 3rd party json module has a different interface so we test
    # to make sure we aren't using it.
    if simplejson is None or hasattr(simplejson, 'read'):
        simplejson = utils.python.universalImport('simplejson',
                                                  'local.simplejson')
except ImportError:
    raise callbacks.Error, \
            'You need Python2.6 or the simplejson module installed to use ' \
            'this plugin.  Download the module at ' \
            '<http://undefined.org/python/#simplejson>.'

unitAbbrevs = utils.abbrev(['Fahrenheit', 'Celsius', 'Centigrade', 'Kelvin'])
unitAbbrevs['C'] = 'Celsius'
unitAbbrevs['Ce'] = 'Celsius'

noLocationError = 'No such location could be found.'


class NoLocation(callbacks.Error):
    pass


class Weather(callbacks.Plugin):
    weatherCommands = ('wunder', 'wunder rss', 'cnn', 'ham')
    threaded = True

    def callCommand(self, method, irc, msg, *args, **kwargs):