Limnoria/plugins/Fun.py

396 lines
14 KiB
Python
Raw Normal View History

2003-03-12 07:26:59 +01:00
###
2004-08-23 20:08:21 +02:00
# Copyright (c) 2002-2004, Jeremiah Fincher
2003-03-12 07:26:59 +01:00
# 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 a multitude of fun, useless commands.
"""
2004-08-23 20:08:21 +02:00
__revision__ = '$Id$'
2004-07-24 07:18:26 +02:00
import supybot.plugins as plugins
2003-03-12 07:26:59 +01:00
import gc
import re
2003-04-02 09:27:00 +02:00
import sys
2003-03-12 07:26:59 +01:00
import md5
import sha
import random
import inspect
import mimetypes
from itertools import imap
2003-03-12 07:26:59 +01:00
2004-07-24 07:18:26 +02:00
import supybot.conf as conf
import supybot.utils as utils
2004-10-16 04:52:31 +02:00
from supybot.commands import *
2004-07-24 07:18:26 +02:00
import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils
2004-08-10 10:09:25 +02:00
import supybot.registry as registry
import supybot.webutils as webutils
2004-07-24 07:18:26 +02:00
import supybot.callbacks as callbacks
2003-03-12 07:26:59 +01:00
2004-08-10 10:09:25 +02:00
conf.registerPlugin('Fun')
conf.registerGroup(conf.supybot.plugins.Fun, 'levenshtein')
conf.registerGlobalValue(conf.supybot.plugins.Fun.levenshtein, 'max',
registry.PositiveInteger(256, """Determines the maximum size of a string
2004-08-18 22:08:51 +02:00
given to the levenshtein command. The levenshtein command uses an O(n**3)
2004-08-10 10:09:25 +02:00
algorithm, which means that with strings of length 256, it can take 1.5
seconds to finish; with strings of length 384, though, it can take 4
2004-08-18 20:44:18 +02:00
seconds to finish, and with strings of much larger lengths, it takes more
2004-08-10 10:09:25 +02:00
and more time. Using nested commands, strings can get quite large, hence
this variable, to limit the size of arguments passed to the levenshtein
command."""))
class MyFunProxy(object):
def reply(self, msg, s):
self.s = s
2004-07-21 21:36:35 +02:00
class Fun(callbacks.Privmsg):
2003-12-04 00:56:06 +01:00
def ping(self, irc, msg, args):
"""takes no arguments
Checks to see if the bot is alive.
"""
irc.reply('pong', prefixName=False)
2004-01-07 17:09:44 +01:00
2004-10-02 19:31:38 +02:00
def hexip(self, irc, msg, args, ip):
2003-08-28 00:43:29 +02:00
"""<ip>
Returns the hexadecimal IP for that IP.
"""
quads = ip.split('.')
ret = ""
for quad in quads:
i = int(quad)
ret += '%02x' % i
irc.reply(ret.upper())
2004-10-02 19:31:38 +02:00
hexip = wrap(hexip, ['ip'])
2004-07-21 21:36:35 +02:00
2004-10-02 19:31:38 +02:00
def ord(self, irc, msg, args, letter):
2003-03-15 12:18:28 +01:00
"""<letter>
2003-03-15 11:00:49 +01:00
2003-03-15 12:18:28 +01:00
Returns the 8-bit value of <letter>.
"""
2004-10-02 19:31:38 +02:00
irc.reply(str(ord(letter)))
ord = wrap(ord, ['letter'])
2003-03-15 11:00:49 +01:00
2004-10-02 19:31:38 +02:00
def chr(self, irc, msg, args, i):
2003-03-15 12:18:28 +01:00
"""<number>
2003-03-15 11:00:49 +01:00
2003-03-15 12:18:28 +01:00
Returns the character associated with the 8-bit value <number>
"""
try:
irc.reply(chr(i))
2003-03-15 12:18:28 +01:00
except ValueError:
irc.error('That number doesn\'t map to an 8-bit character.')
2004-10-02 19:31:38 +02:00
chr = wrap(chr, ['int'])
2003-08-20 18:26:23 +02:00
2004-10-02 19:31:38 +02:00
def encode(self, irc, msg, args, encoding, text):
2003-07-31 08:43:19 +02:00
"""<encoding> <text>
Returns an encoded form of the given text; the valid encodings are
available in the documentation of the Python codecs module:
2004-01-09 20:01:54 +01:00
<http://www.python.org/doc/lib/node127.html>.
2003-07-31 08:43:19 +02:00
"""
try:
irc.reply(text.encode(encoding))
except LookupError:
2004-10-02 19:31:38 +02:00
irc.errorInvalid('encoding', encoding)
encode = wrap(encode, ['something', 'text'])
2003-07-31 08:43:19 +02:00
2004-10-02 19:31:38 +02:00
def decode(self, irc, msg, args, encoding, text):
2003-07-31 08:43:19 +02:00
"""<encoding> <text>
Returns an un-encoded form of the given text; the valid encodings are
available in the documentation of the Python codecs module:
2004-01-09 20:01:54 +01:00
<http://www.python.org/doc/lib/node127.html>.
2003-07-31 08:43:19 +02:00
"""
try:
irc.reply(text.decode(encoding).encode('utf-8'))
except LookupError:
2004-10-02 19:31:38 +02:00
irc.errorInvalid('encoding', encoding)
decode = wrap(decode, ['something', 'text'])
2003-08-20 18:26:23 +02:00
2004-10-02 19:31:38 +02:00
def xor(self, irc, msg, args, password, text):
2003-04-16 10:26:58 +02:00
"""<password> <text>
Returns <text> XOR-encrypted with <password>. See
http://www.yoe.org/developer/xor.html for information about XOR
encryption.
"""
2003-03-12 07:26:59 +01:00
passwordlen = len(password)
i = 0
ret = []
for c in text:
ret.append(chr(ord(c) ^ ord(password[i])))
i = (i + 1) % passwordlen
irc.reply(''.join(ret))
2004-10-02 19:31:38 +02:00
xor = wrap(xor, ['something', 'text'])
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def mimetype(self, irc, msg, args, filename):
2003-04-16 10:26:58 +02:00
"""<filename>
Returns the mime type associated with <filename>
"""
2003-03-12 07:26:59 +01:00
(type, encoding) = mimetypes.guess_type(filename)
if type is not None:
irc.reply(type)
2003-03-12 07:26:59 +01:00
else:
s = 'I couldn\'t figure out that filename.'
irc.reply(s)
2004-11-06 20:05:53 +01:00
mimetype = wrap(mimetype, ['text'])
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def md5(self, irc, msg, args, text):
2003-04-16 10:26:58 +02:00
"""<text>
Returns the md5 hash of a given string. Read
http://www.rsasecurity.com/rsalabs/faq/3-6-6.html for more information
about md5.
"""
irc.reply(md5.md5(text).hexdigest())
2004-10-02 19:31:38 +02:00
md5 = wrap(md5, ['text'])
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def sha(self, irc, msg, args, text):
2003-04-16 10:26:58 +02:00
"""<text>
Returns the SHA hash of a given string. Read
http://www.secure-hash-algorithm-md5-sha-1.co.uk/ for more information
about SHA.
"""
irc.reply(sha.sha(text).hexdigest())
2004-10-02 19:31:38 +02:00
sha = wrap(sha, ['text'])
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def urlquote(self, irc, msg, args, text):
2003-04-16 10:26:58 +02:00
"""<text>
Returns the URL quoted form of the text.
"""
irc.reply(webutils.urlquote(text))
2004-10-02 19:31:38 +02:00
urlquote = wrap(urlquote, ['text'])
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def urlunquote(self, irc, msg, args, text):
2003-04-16 10:26:58 +02:00
"""<text>
Returns the text un-URL quoted.
"""
s = webutils.urlunquote(text)
irc.reply(s)
2004-10-02 19:31:38 +02:00
urlunquote = wrap(urlunquote, ['text'])
2003-03-12 07:26:59 +01:00
def coin(self, irc, msg, args):
2003-04-16 10:26:58 +02:00
"""takes no arguments
Flips a coin and returns the result.
"""
2003-03-12 07:26:59 +01:00
if random.randrange(0, 2):
irc.reply('heads')
2003-03-12 07:26:59 +01:00
else:
irc.reply('tails')
2004-10-02 19:31:38 +02:00
coin = wrap(coin)
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def dice(self, irc, msg, args, m):
2003-04-16 10:26:58 +02:00
"""<dice>d<sides>
Rolls a die with <sides> number of sides <dice> times.
For example, 2d6 will roll 2 six-sided dice; 10d10 will roll 10
ten-sided dice.
"""
2004-10-02 19:31:38 +02:00
(dice, sides) = imap(int, m.groups())
if dice > 6:
irc.error('You can\'t roll more than 6 dice.')
elif sides > 100:
irc.error('Dice can\'t have more than 100 sides.')
2003-03-12 07:26:59 +01:00
else:
2004-10-02 19:31:38 +02:00
L = [0] * dice
for i in xrange(dice):
L[i] = random.randrange(1, sides+1)
irc.reply(utils.commaAndify([str(x) for x in L]))
_dicere = re.compile(r'^(\d+)d(\d+)$')
dice = wrap(dice, [('matches', _dicere,
'Dice must be of the form <dice>d<sides>')])
2003-03-12 07:26:59 +01:00
def objects(self, irc, msg, args):
"""takes no arguments
2003-04-16 10:26:58 +02:00
2003-10-19 16:49:38 +02:00
Returns the number and types of Python objects in memory.
"""
2003-08-07 11:19:40 +02:00
classes = 0
functions = 0
modules = 0
strings = 0
2003-08-07 11:19:40 +02:00
dicts = 0
lists = 0
tuples = 0
refcounts = 0
2003-03-12 07:26:59 +01:00
objs = gc.get_objects()
2003-08-07 11:19:40 +02:00
for obj in objs:
if isinstance(obj, str):
strings += 1
2003-08-07 11:19:40 +02:00
if isinstance(obj, tuple):
tuples += 1
elif inspect.isroutine(obj):
functions += 1
elif isinstance(obj, dict):
dicts += 1
elif isinstance(obj, list):
lists += 1
elif inspect.isclass(obj):
classes += 1
elif inspect.ismodule(obj):
modules += 1
refcounts += sys.getrefcount(obj)
2003-03-12 07:26:59 +01:00
response = 'I have %s objects: %s modules, %s classes, %s functions, '\
'%s dictionaries, %s lists, %s tuples, %s strings, and a ' \
'few other odds and ends. ' \
'I have a total of %s references.' % \
2003-03-12 07:26:59 +01:00
(len(objs), modules, classes, functions,
dicts, lists, tuples, strings, refcounts)
irc.reply(response)
2004-10-02 19:31:38 +02:00
objects = wrap(objects)
2003-03-12 07:26:59 +01:00
2004-10-02 19:31:38 +02:00
def levenshtein(self, irc, msg, args, s1, s2):
2003-04-04 17:49:24 +02:00
"""<string1> <string2>
Returns the levenshtein distance (also known as the "edit distance"
2004-06-05 08:02:31 +02:00
between <string1> and <string2>)
2003-04-04 17:49:24 +02:00
"""
2004-08-10 10:09:25 +02:00
max = self.registryValue('levenshtein.max')
if len(s1) > max or len(s2) > max:
irc.error('Levenshtein distance is a complicated algorithm, try '
'it with some smaller inputs.')
else:
irc.reply(str(utils.distance(s1, s2)))
2004-11-06 20:05:53 +01:00
levenshtein = wrap(levenshtein, ['something', 'text'])
2003-04-04 17:49:24 +02:00
2004-10-02 19:31:38 +02:00
def soundex(self, irc, msg, args, text, length):
"""<string> [<length>]
Returns the Soundex hash to a given length. The length defaults to
4, since that's the standard length for a soundex hash. For unlimited
length, use 0.
"""
2004-10-02 19:31:38 +02:00
irc.reply(utils.soundex(text, length))
2004-11-06 20:05:53 +01:00
soundex = wrap(soundex, ['somethingWithoutSpaces', additional('int', 4)])
# The list of words and algorithm are pulled straight the mozbot
2004-10-02 19:31:38 +02:00
# MagicEightBall.bm module: http://tinyurl.com/7ytg7
2004-10-16 04:52:31 +02:00
_responses = {'positive': ['It is possible.', 'Yes!', 'Of course.',
'Naturally.', 'Obviously.', 'It shall be.',
'The outlook is good.', 'It is so.',
2004-10-16 04:52:31 +02:00
'One would be wise to think so.',
'The answer is certainly yes.'],
'negative': ['In your dreams.', 'I doubt it very much.',
2004-10-16 04:52:31 +02:00
'No chance.', 'The outlook is poor.',
'Unlikely.', 'About as likely as pigs flying.',
2004-10-16 04:52:31 +02:00
'You\'re kidding, right?', 'NO!', 'NO.', 'No.',
'The answer is a resounding no.', ],
2004-10-16 04:52:31 +02:00
'unknown' : ['Maybe...', 'No clue.', '_I_ don\'t know.',
'The outlook is hazy, please ask again later.',
'What are you asking me for?', 'Come again?',
2004-10-16 04:52:31 +02:00
'You know the answer better than I.',
'The answer is def-- oooh! shiny thing!'],
}
2004-10-16 04:52:31 +02:00
def _checkTheBall(self, questionLength):
if questionLength % 3 == 0:
category = 'positive'
elif questionLength % 3 == 1:
category = 'negative'
else:
category = 'unknown'
return random.choice(self._responses[category])
2004-10-02 19:31:38 +02:00
def eightball(self, irc, msg, args, text):
2003-08-29 09:49:11 +02:00
"""[<question>]
Ask a question and the answer shall be provided.
2003-08-29 09:49:11 +02:00
"""
if text:
irc.reply(self._checkTheBall(len(text)))
else:
2004-08-23 20:08:21 +02:00
irc.reply(self._checkTheBall(random.randint(0, 2)))
2004-10-19 14:03:18 +02:00
eightball = wrap(eightball, [additional('text')])
2003-08-29 09:49:11 +02:00
2004-07-31 09:01:46 +02:00
_rouletteChamber = random.randrange(0, 6)
_rouletteBullet = random.randrange(0, 6)
2004-10-02 19:31:38 +02:00
def roulette(self, irc, msg, args, spin):
2004-07-31 09:01:46 +02:00
"""[spin]
2003-12-03 02:43:38 +01:00
2004-07-31 09:01:46 +02:00
Fires the revolver. If the bullet was in the chamber, you're dead.
Tell me to spin the chambers and I will.
2003-12-03 02:43:38 +01:00
"""
2004-10-02 19:31:38 +02:00
if spin:
self._rouletteBullet = random.randrange(0, 6)
irc.reply('*SPIN* Are you feeling lucky?', prefixName=False)
2004-07-31 09:01:46 +02:00
return
2004-10-02 19:31:38 +02:00
channel = msg.args[0]
2004-07-31 09:01:46 +02:00
if self._rouletteChamber == self._rouletteBullet:
self._rouletteBullet = random.randrange(0, 6)
self._rouletteChamber = random.randrange(0, 6)
2004-01-31 08:31:11 +01:00
if irc.nick in irc.state.channels[channel].ops:
2004-10-02 19:31:38 +02:00
irc.queueMsg(ircmsgs.kick(channel, msg.nick, 'BANG!'))
2004-01-31 08:31:11 +01:00
else:
2004-07-31 09:01:46 +02:00
irc.reply('*BANG* Hey, who put a blank in here?!',
prefixName=False)
irc.reply('reloads and spins the chambers.', action=True)
2003-12-03 02:43:38 +01:00
else:
irc.reply('*click*')
2004-07-31 09:01:46 +02:00
self._rouletteChamber += 1
self._rouletteChamber %= 6
2004-10-19 14:03:18 +02:00
roulette = wrap(roulette, ['public', additional(('literal', 'spin'))])
2003-12-03 02:43:38 +01:00
2004-10-02 19:31:38 +02:00
def monologue(self, irc, msg, args, channel):
2004-04-20 13:19:31 +02:00
"""[<channel>]
Returns the number of consecutive lines you've sent in <channel>
without being interrupted by someone else (i.e. how long your current
'monologue' is). <channel> is only necessary if the message isn't sent
in the channel itself.
"""
i = 0
for m in reversed(irc.state.history):
2004-10-02 19:31:38 +02:00
if m.command != 'PRIVMSG':
continue
if not m.prefix:
continue
if not ircutils.strEqual(m.args[0], channel):
2004-04-20 13:19:31 +02:00
continue
2004-10-02 19:31:38 +02:00
if msg.prefix == m.prefix:
2004-04-20 13:19:31 +02:00
i += 1
else:
break
iS = utils.nItems('line', i)
irc.reply('Your current monologue is at least %s long.' % iS)
2004-10-02 19:31:38 +02:00
monologue = wrap(monologue, ['channel'])
2004-04-20 13:19:31 +02:00
2003-12-03 02:43:38 +01:00
Class = Fun
2003-04-03 10:24:52 +02:00
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: