diff --git a/plugins/Games/README.txt b/plugins/Games/README.txt new file mode 100644 index 000000000..d60b47a97 --- /dev/null +++ b/plugins/Games/README.txt @@ -0,0 +1 @@ +Insert a description of your plugin here, with any notes, etc. about using it. diff --git a/plugins/Games/__init__.py b/plugins/Games/__init__.py new file mode 100644 index 000000000..026a15ebb --- /dev/null +++ b/plugins/Games/__init__.py @@ -0,0 +1,60 @@ +### +# Copyright (c) 2005, James Vega +# 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 various game-related commands. +""" + +import supybot +import supybot.world as world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "%%VERSION%%" + +__author__ = supybot.authors.jemfinch + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +import config +import plugin +reload(plugin) # In case we're being reloaded. +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/plugins/Games/config.py b/plugins/Games/config.py new file mode 100644 index 000000000..f063db39f --- /dev/null +++ b/plugins/Games/config.py @@ -0,0 +1,45 @@ +### +# Copyright (c) 2005, James Vega +# 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. +### + +import supybot.conf as conf +import supybot.registry as registry + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified himself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('Games', True) + + +Games = conf.registerPlugin('Games') + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78 diff --git a/plugins/Games/plugin.py b/plugins/Games/plugin.py new file mode 100644 index 000000000..a5f35c156 --- /dev/null +++ b/plugins/Games/plugin.py @@ -0,0 +1,167 @@ +### +# Copyright (c) 2003-2005, 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. +### + +import re +import random + +import supybot.utils as utils +from supybot.commands import * +import supybot.ircmsgs as ircmsgs +import supybot.ircutils as ircutils +import supybot.callbacks as callbacks + + +class Games(callbacks.Privmsg): + def coin(self, irc, msg, args): + """takes no arguments + + Flips a coin and returns the result. + """ + if random.randrange(0, 2): + irc.reply('heads') + else: + irc.reply('tails') + coin = wrap(coin) + + def dice(self, irc, msg, args, m): + """d + + Rolls a die with number of sides times. + For example, 2d6 will roll 2 six-sided dice; 10d10 will roll 10 + ten-sided dice. + """ + (dice, sides) = utils.iter.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.') + else: + L = [0] * dice + for i in xrange(dice): + L[i] = random.randrange(1, sides+1) + irc.reply(format('%L', [str(x) for x in L])) + _dicere = re.compile(r'^(\d+)d(\d+)$') + dice = wrap(dice, [('matches', _dicere, + 'Dice must be of the form d')]) + + # The list of words and algorithm are pulled straight the mozbot + # MagicEightBall.bm module: http://tinyurl.com/7ytg7 + _responses = {'positive': ['It is possible.', 'Yes!', 'Of course.', + 'Naturally.', 'Obviously.', 'It shall be.', + 'The outlook is good.', 'It is so.', + 'One would be wise to think so.', + 'The answer is certainly yes.'], + 'negative': ['In your dreams.', 'I doubt it very much.', + 'No chance.', 'The outlook is poor.', + 'Unlikely.', 'About as likely as pigs flying.', + 'You\'re kidding, right?', 'NO!', 'NO.', 'No.', + 'The answer is a resounding no.', ], + 'unknown' : ['Maybe...', 'No clue.', '_I_ don\'t know.', + 'The outlook is hazy, please ask again later.', + 'What are you asking me for?', 'Come again?', + 'You know the answer better than I.', + 'The answer is def-- oooh! shiny thing!'], + } + + def _checkTheBall(self, questionLength): + if questionLength % 3 == 0: + category = 'positive' + elif questionLength % 3 == 1: + category = 'negative' + else: + category = 'unknown' + return utils.iter.choice(self._responses[category]) + + def eightball(self, irc, msg, args, text): + """[] + + Ask a question and the answer shall be provided. + """ + if text: + irc.reply(self._checkTheBall(len(text))) + else: + irc.reply(self._checkTheBall(random.randint(0, 2))) + eightball = wrap(eightball, [additional('text')]) + + _rouletteChamber = random.randrange(0, 6) + _rouletteBullet = random.randrange(0, 6) + def roulette(self, irc, msg, args, spin): + """[spin] + + Fires the revolver. If the bullet was in the chamber, you're dead. + Tell me to spin the chambers and I will. + """ + if spin: + self._rouletteBullet = random.randrange(0, 6) + irc.reply('*SPIN* Are you feeling lucky?', prefixName=False) + return + channel = msg.args[0] + if self._rouletteChamber == self._rouletteBullet: + self._rouletteBullet = random.randrange(0, 6) + self._rouletteChamber = random.randrange(0, 6) + if irc.nick in irc.state.channels[channel].ops: + irc.queueMsg(ircmsgs.kick(channel, msg.nick, 'BANG!')) + else: + irc.reply('*BANG* Hey, who put a blank in here?!', + prefixName=False) + irc.reply('reloads and spins the chambers.', action=True) + else: + irc.reply('*click*') + self._rouletteChamber += 1 + self._rouletteChamber %= 6 + roulette = wrap(roulette, ['public', additional(('literal', 'spin'))]) + + def monologue(self, irc, msg, args, channel): + """[] + + Returns the number of consecutive lines you've sent in + without being interrupted by someone else (i.e. how long your current + 'monologue' is). is only necessary if the message isn't sent + in the channel itself. + """ + i = 0 + for m in reversed(irc.state.history): + if m.command != 'PRIVMSG': + continue + if not m.prefix: + continue + if not ircutils.strEqual(m.args[0], channel): + continue + if msg.prefix == m.prefix: + i += 1 + else: + break + irc.reply(format('Your current monologue is at least %n long.', + (i, 'line'))) + monologue = wrap(monologue, ['channel']) + +Class = Games + + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: diff --git a/plugins/Games/test.py b/plugins/Games/test.py new file mode 100644 index 000000000..643cecc0f --- /dev/null +++ b/plugins/Games/test.py @@ -0,0 +1,57 @@ +### +# Copyright (c) 2005, James Vega +# 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. +### + +from supybot.test import * + +class GamesTestCase(ChannelPluginTestCase): + plugins = ('Games',) + _nonKickRe = re.compile(r'bang|click|spin', re.I) + def testRoulette(self): + self.irc.feedMsg(ircmsgs.op(self.channel, self.irc.nick)) + sawKick = False + for i in xrange(100): + m = self.getMsg('roulette', frm='someoneElse') + if m.command == 'PRIVMSG': + self.failUnless(self._nonKickRe.search(m.args[1]), + 'Got a msg without bang|click|spin: %r' % m) + elif m.command == 'KICK': + sawKick = True + self.failUnless('bang' in m.args[2].lower(), + 'Got a KICK without bang in it.') + else: + self.fail('Got something other than a kick or a privmsg.') + self.failUnless(sawKick, 'Didn\'t get a kick in %s iterations!' % i) + + def testEightball(self): + self.assertNotError('eightball') + self.assertNotError('eightball a') + self.assertNotError('eightball ab') + self.assertNotError('eightball abc') + +# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: