diff --git a/.gitignore b/.gitignore index bd69f72..0541584 100755 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ persist config +gitflow *.db *.log .*.swp *.pyc *.orig +.project +.pydevproject +.geany diff --git a/DOCUMENTATION b/DOCUMENTATION index 1675060..a5daa54 100755 --- a/DOCUMENTATION +++ b/DOCUMENTATION @@ -1 +1 @@ -Please see the wiki @ http://git.io/cloudbotwiki \ No newline at end of file +Please see the wiki @ http://git.io/cloudbotircwiki \ No newline at end of file diff --git a/README.md b/README.md index 9d8503c..f0f4b12 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CloudBot/1.1d +# CloudBot/1.2 ## About @@ -25,13 +25,13 @@ Unzip the resulting file, and continue to read this document. ## Install -Before you can run the bot, you need to install a few Python modules. These are `lXML`, `BeautifulSoup`, `MyGengo`, and `HTTPlib2`. These can be installed with PIP (The Python package manager): +Before you can run the bot, you need to install a few Python modules. These are `lXML`, `BeautifulSoup`, `psutil`, and `HTTPlib2`. These can be installed with PIP (The Python package manager): `sudo pip install lxml` `sudo pip install beautifulsoup` -`sudo pip install mygengo` +`sudo pip install psutil` `sudo pip install httplib2` @@ -55,7 +55,7 @@ For the wrapper to work best, install `screen`, or `daemon`: If you are a user of another Linux disto, use your package manager to install the dependencies, or, for other operating systems, use **Google** to locate source packages you can install. -Once you have installed the required dependencies, run the bot☩: +Once you have installed the required dependencies, run the bot¹: `./cloudbot start` @@ -87,22 +87,22 @@ They can both be found in [#CloudBot](irc://irc.esper.net/cloudbot "Connect via **mau5bot** is the stable bot, and runs on the latest release version of CloudBot. (mau5bot is running on **Ubuntu Server** *Oneric Ocelot/11.10* with **Python** *2.7.2*) -**neerbot** is the unstable bot, and runs on the latest development☩☩ version of CloudBot. (neerbot is running on **Debian** *Wheezy/Testing* with **Python** *2.7.2*) +**neerbot** is the unstable bot, and runs on the latest development² version of CloudBot. (neerbot is running on **Debian** *Wheezy/Testing* with **Python** *2.7.2*) ## Requirements -CloudBot runs on **Python** *2.7.x*. It is developed on **Debian** *Wheezy/Testing* with **Python** *2.7.2*. +CloudBot runs on **Python** *2.7.x*. It is developed on **Debian** *Wheezy/Testing* and **Ubuntu** *11.10* with **Python** *2.7.2*. -It **requires Python modules** `lXML`, `BeautifulSoup`, `Enchant`, `MyGengo`, and `HTTPlib2`. +It **requires Python modules** `lXML`, `BeautifulSoup`, `Enchant`, `psutil`, and `HTTPlib2`. The programs `screen` or `daemon` are recomended for the wrapper to run optimaly. -**Windows** users: Windows compatibility with the wrapper and some plugins is **broken** (such as the ping), but we do intend to add it.☩☩☩ +**Windows** users: Windows compatibility with the wrapper and some plugins is **broken** (such as the ping), but we do intend to add it.³ ## License CloudBot is **licensed** under the **GPL v3** license. The terms are as follows. - CloudBot/1.1d + CloudBot/1.2 Copyright © 2011 ClouDev - <[cloudev.github.com](http://cloudev.github.com)> @@ -119,10 +119,10 @@ CloudBot is **licensed** under the **GPL v3** license. The terms are as follows. You should have received a copy of the GNU General Public License along with CloudBot. If not, see . -## ☩ +## Notes -☩ if you prefer to run the bot with a custom backend/run it manually, or are on **Windows**, run the bot with `./bot.py` +¹ if you prefer to run the bot with a custom backend/run it manually, or are on **Windows**, run the bot with `./bot.py` -☩☩ or whatever version [neersighted](http://git.io/neersighted) is currently hacking on +² or whatever version [neersighted](http://git.io/neersighted) is currently hacking on -☩☩☩ eventually +³ eventually diff --git a/bot.py b/bot.py index e4bbeaa..470c577 100755 --- a/bot.py +++ b/bot.py @@ -13,9 +13,10 @@ os.chdir(sys.path[0] or '.') # do stuff relative to the install directory class Bot(object): pass -print 'Welcome to Cloudbot - Version 1.1c - http://git.io/cloudbot' +print 'Welcome to Cloudbot - Version 1.2 - http://git.io/cloudbotirc' bot = Bot() +bot.start_time = time.time() print 'Loading plugins...' @@ -28,7 +29,7 @@ config() if not hasattr(bot, 'config'): exit() -print 'Connecting to IRC' +print 'Connecting to IRC...' bot.conns = {} @@ -49,7 +50,7 @@ bot.persist_dir = os.path.abspath('persist') if not os.path.exists(bot.persist_dir): os.mkdir(bot.persist_dir) -print 'Running main loop' +print 'Connection(s) made, bot online...' while True: reload() # these functions only do things diff --git a/cloudbot b/cloudbot index 02f8d37..d6fbc09 100755 --- a/cloudbot +++ b/cloudbot @@ -5,7 +5,7 @@ echo " / ____/ /___ __ ______/ / __ )____ / /_" echo " / / / / __ \/ / / / __ / __ / __ \/ __/" echo "/ /___/ / /_/ / /_/ / /_/ / /_/ / /_/ / /_ " echo "\____/_/\____/\__,_/\__,_/_____/\____/\__/ " -echo " http://git.io/cloudbot by lukeroge " +echo " http://git.io/cloudbotirc by ClouDev " echo "" locatefiles() { botfile="/bot.py" @@ -23,10 +23,10 @@ running() { } checkbackend() { - if dpkg -l| grep ^ii|grep daemon|grep 'turns other' > /dev/null; then - backend="daemon" - elif dpkg -l| grep ^ii|grep screen|grep 'terminal multi' > /dev/null; then + if dpkg -l| grep ^ii|grep screen|grep 'terminal multi' > /dev/null; then backend="screen" + elif dpkg -l| grep ^ii|grep daemon|grep 'turns other' > /dev/null; then + backend="daemon" else backend="manual" fi @@ -77,6 +77,7 @@ processargs() { start) if running; then echo "Cannot start! Bot is already running!" + exit 1 else echo "Starting CloudBot... ($backend)" start @@ -88,15 +89,18 @@ processargs() { stop else echo "Cannot stop! Bot is not already running!" + exit 1 fi ;; restart) if running; then echo "Restarting CloudBot... ($backend)" stop + sleep 3 start else echo "Cannot restart! Bot is not already running!" + exit 1 fi ;; clear) @@ -107,8 +111,7 @@ processargs() { status ;; *) - echo "Please enter a command:" - usage="./cloudbot {start|stop|restart|clear|status}" + usage="usage: ./cloudbot {start|stop|restart|clear|status}" echo $usage ;; esac @@ -122,3 +125,4 @@ main() { } main $* +exit 0 \ No newline at end of file diff --git a/core/config.py b/core/config.py index 8ea8255..8eea815 100755 --- a/core/config.py +++ b/core/config.py @@ -17,7 +17,7 @@ if not os.path.exists('config'): "server": "irc.esper.net", "nick": "MyNewCloudBot", "user": "cloudbot", - "realname": "CloudBot - http://git.io/cloudbot", + "realname": "CloudBot - http://git.io/cloudbotirc", "nickserv_password": "", "channels": ["#cloudbot"], "invitejoin": true, @@ -38,14 +38,18 @@ if not os.path.exists('config'): "bitly_api": "INSERT API KEY FROM bitly.com HERE", "wolframalpha": "INSERT API KEY FROM wolframalpha.com HERE", "lastfm": "INSERT API KEY FROM lastfm HERE", - "mc_user": "INSERT MINECRAFT USERNAME HERE", - "mc_pass": "INSERT MINECRAFT PASSWORD HERE" + "mc_user": "INSERT minecraft USERNAME HERE", + "mc_pass": "INSERT minecraft PASSWORD HERE" }, "plugins": { "factoids": { "prefix": false + }, + "ignore": + { + "ignored": [] } }, "censored_strings": @@ -53,11 +57,11 @@ if not os.path.exists('config'): "mypass", "mysecret" ], - "admins": ["myname"] + "admins": ["myname@myhost"] }''') + '\n') print "Config generated!" print "Please edit the config now!" - print "For help, see http://git.io/cloudbotwiki" + print "For help, see http://git.io/cloudbotircwiki" print "Thank you for using CloudBot!" sys.exit() diff --git a/core/irc.py b/core/irc.py index b14c828..50ff379 100755 --- a/core/irc.py +++ b/core/irc.py @@ -163,6 +163,7 @@ class IRC(object): else: prefix, command, params = irc_noprefix_rem(msg).groups() nick, user, host = irc_netmask_rem(prefix).groups() + mask = user + "@" + host paramlist = irc_param_ref(params) lastparam = "" if paramlist: @@ -170,7 +171,7 @@ class IRC(object): paramlist[-1] = paramlist[-1][1:] lastparam = paramlist[-1] self.out.put([msg, prefix, command, params, nick, user, host, - paramlist, lastparam]) + mask, paramlist, lastparam]) if command == "PING": self.cmd("PONG", paramlist) @@ -183,6 +184,9 @@ class IRC(object): def join(self, channel): self.cmd("JOIN", [channel]) + + def part(self, channel): + self.cmd("PART", [channel]) def msg(self, target, text): self.cmd("PRIVMSG", [target, text]) @@ -225,6 +229,7 @@ class FakeIRC(IRC): else: prefix, command, params = irc_noprefix_rem(msg).groups() nick, user, host = irc_netmask_rem(prefix).groups() + mask = user + "@" + host paramlist = irc_param_ref(params) lastparam = "" if paramlist: @@ -232,7 +237,7 @@ class FakeIRC(IRC): paramlist[-1] = paramlist[-1][1:] lastparam = paramlist[-1] self.out.put([msg, prefix, command, params, nick, user, host, - paramlist, lastparam]) + mask, paramlist, lastparam]) if command == "PING": self.cmd("PONG", [params]) diff --git a/core/main.py b/core/main.py index 041d67e..df47cd4 100755 --- a/core/main.py +++ b/core/main.py @@ -7,7 +7,7 @@ thread.stack_size(1024 * 512) # reduce vm size class Input(dict): def __init__(self, conn, raw, prefix, command, params, - nick, user, host, paraml, msg): + nick, user, host, mask, paraml, msg): chan = paraml[0].lower() if chan == conn.nick.lower(): # is a PM @@ -25,9 +25,6 @@ class Input(dict): else: conn.msg(chan, '(' + nick + ') ' + msg) - def set_nick(nick): - conn.set_nick(nick) - def me(msg): conn.msg(chan, "\x01%s %s\x01" % ("ACTION", msg)) @@ -35,10 +32,10 @@ class Input(dict): conn.cmd('NOTICE', [nick, msg]) dict.__init__(self, conn=conn, raw=raw, prefix=prefix, command=command, - params=params, nick=nick, user=user, host=host, + params=params, nick=nick, user=user, host=host, mask=mask, paraml=paraml, msg=msg, server=conn.server, chan=chan, notice=notice, say=say, reply=reply, pm=pm, bot=bot, - me=me, set_nick=set_nick, lastparam=paraml[-1]) + me=me, lastparam=paraml[-1]) # make dict keys accessible as attributes def __getattr__(self, key): diff --git a/plugins/mtg.py b/disabled_plugins/mtg.py similarity index 100% rename from plugins/mtg.py rename to disabled_plugins/mtg.py diff --git a/plugins/repaste.py b/disabled_plugins/repaste.py similarity index 90% rename from plugins/repaste.py rename to disabled_plugins/repaste.py index 97aea23..1443345 100755 --- a/plugins/repaste.py +++ b/disabled_plugins/repaste.py @@ -51,21 +51,21 @@ autorepastes = {} #@hook.regex('(pastebin\.com)(/[^ ]+)') @hook.regex('(mibpaste\.com)(/[^ ]+)') -def autorepaste(inp, input=None, db=None, chan=None): +def autorepaste(inp, input=None, notice=None, db=None, chan=None, nick=None): db_init(db) - manual = input.db.execute("select manual from repaste where chan=?", (chan, )).fetchone() + manual = db.execute("select manual from repaste where chan=?", (chan, )).fetchone() if manual and len(manual) and manual[0]: return url = inp.group(1) + inp.group(2) urllib.unquote(url) if url in autorepastes: out = autorepastes[url] - input.notice("In the future, please use a less awful pastebin (e.g. pastebin.com)") + notice("In the future, please use a less awful pastebin (e.g. pastebin.com)") else: out = repaste("http://" + url, input, db, False) autorepastes[url] = out - input.notice("In the future, please use a less awful pastebin (e.g. pastebin.com) instead of %s." % inp.group(1)) - input.say("%s (repasted for %s)" % (out, input.nick)) + notice("In the future, please use a less awful pastebin (e.g. pastebin.com) instead of %s." % inp.group(1)) + input.say("%s (repasted for %s)" % (out, nick)) scrapers = { diff --git a/plugins/8ball.py b/plugins/8ball.py index 3f576d7..392c333 100755 --- a/plugins/8ball.py +++ b/plugins/8ball.py @@ -4,7 +4,7 @@ import re r = "\x02\x0305" # red g = "\x02\x0303" # green -y = "\x02\x0308" # yellow +y = "\x02" # yellow (not really) answers = [g + "As I see it, yes", g + "It is certain", @@ -36,7 +36,7 @@ answers = [g + "As I see it, yes", @hook.command('8ball') def eightball(inp, me=None): - ".8ball -- The all knowing magic eight ball, "\ + ".8ball -- The all knowing magic eight ball, " \ "in electronic form. Ask and it shall be answered!" global nextresponsenumber inp = inp.strip() diff --git a/plugins/admin.py b/plugins/admin.py index e91afd6..8582602 100755 --- a/plugins/admin.py +++ b/plugins/admin.py @@ -1,195 +1,166 @@ -# Shitty plugin made by iloveportalz0r -# Broken by The Noodle -# Improved by Lukeroge -# Further improved by neersighted +# Plugin made by iloveportalz0r, TheNoodle, Lukeroge and neersighted from util import hook import os +import re import sys -import subprocess +import json import time +import subprocess + -@hook.command("quit", autohelp=False) -@hook.command("exit", autohelp=False) @hook.command(autohelp=False) -def stop(inp, input=None, db=None, notice=None): - ".stop [reason] -- Kills the bot, with [reason] as its quit message." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - if inp: - input.conn.send("QUIT :Killed by " + input.nick + " (" + inp + ")") +def admins(inp, notice=None, bot=None): + ".admins -- Lists bot's admins." + adminlist = bot.config["admins"] + if adminlist: + notice("Admins are: %s." % ", ".join(adminlist)) else: - input.conn.send("QUIT :Killed by " + input.nick + " (no reason)") + notice("No users are admins!") + return + + +@hook.command(adminonly=True) +def admin(inp, notice=None, bot=None, config=None): + ".admin -- Make an admin." + target = inp.lower() + adminlist = bot.config["admins"] + if target in adminlist: + notice("%s is already an admin." % target) + else: + notice("%s is now an admin." % target) + adminlist.append(target) + adminlist.sort() + json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) + return + + +@hook.command(adminonly=True) +def unadmin(inp, notice=None, bot=None, config=None): + ".unadmin -- Make a non-admin." + target = inp.lower() + adminlist = bot.config["admins"] + if target in adminlist: + notice("%s is no longer an admin." % target) + adminlist.remove(target) + adminlist.sort() + json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) + else: + notice("%s is not an admin." % target) + return + + +@hook.command(autohelp=False) +def channels(inp, conn=None): + ".channels -- Lists the channels that the bot is in." + return "I am in these channels: %s" % ", ".join(conn.channels) + + +@hook.command("quit", autohelp=False, adminonly=True) +@hook.command(autohelp=False, adminonly=True) +def stop(inp, nick=None, conn=None): + ".stop [reason] -- Kills the bot with [reason] as its quit message." + if inp: + conn.cmd("QUIT", ["Killed by %s (%s)" % (nick, inp)]) + else: + conn.cmd("QUIT", ["Killed by %s." % nick]) time.sleep(5) - subprocess.call("./cloudbot stop", shell=True) + os.execl("./cloudbot", "stop") -@hook.command("reboot", autohelp=False) -@hook.command(autohelp=False) -def restart(inp, input=None, db=None, notice=None): - ".restart [reason] -- Restarts the bot, with [reason] as its quit message." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return +@hook.command(autohelp=False, adminonly=True) +def restart(inp, nick=None, conn=None): + ".restart [reason] -- Restarts the bot with [reason] as its quit message." if inp: - input.conn.send("QUIT :Restarted by " + input.nick + " (" + inp + ")") + conn.cmd("QUIT", ["Restarted by %s (%s)" % (nick, inp)]) else: - input.conn.send("QUIT :Restarted by " + input.nick + " (no reason)") + conn.cmd("QUIT", ["Restarted by %s." % nick]) time.sleep(5) os.execl("./cloudbot", "restart") -@hook.command("clearlogs", autohelp=False) -@hook.command(autohelp=False) -def clear(inp, input=None, db=None, notice=None): - ".clear -- Clears the bot's log(s)." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - time.sleep(5) - subprocess.call("./cloudbot clear", shell=True) + +@hook.command(autohelp=False, adminonly=True) +def clearlogs(inp, input=None): + ".clearlogs -- Clears the bots log(s)." + subprocess.call(["./cloudbot", "clear"]) -@hook.command -def join(inp, input=None, db=None, notice=None): +@hook.command(adminonly=True) +def join(inp, conn=None, notice=None): ".join -- Joins ." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - notice("Attempting to join " + inp + "...") - input.conn.send("JOIN " + inp) + notice("Attempting to join %s..." % inp) + conn.join(inp) -@hook.command -def cycle(inp, input=None, db=None, notice=None): +@hook.command(adminonly=True) +def part(inp, conn=None, notice=None): + ".part -- Leaves ." + notice("Attempting to part from %s..." % inp) + conn.part(inp) + + +@hook.command(adminonly=True) +def cycle(inp, conn=None, notice=None): ".cycle -- Cycles ." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - notice("Attempting to cycle " + inp + "...") - input.conn.send("PART " + inp) - input.conn.send("JOIN " + inp) + notice("Attempting to cycle %s..." % inp) + conn.part(inp) + conn.join(inp) -@hook.command -def part(inp, input=None, notice=None): - ".part -- Parts from ." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - notice("Attempting to part from " + inp + "...") - input.conn.send("PART " + inp) - - -@hook.command -def nick(inp, input=None, notice=None): +@hook.command(adminonly=True) +def nick(inp, input=None, notice=None, conn=None): ".nick -- Changes the bots nickname to ." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") + if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): + notice("Invalid username!") return - notice("Changing nick to " + inp + ".") - input.conn.send("NICK " + inp) + notice("Attempting to change nick to \"%s\"..." % inp) + conn.set_nick(inp) -@hook.command -def raw(inp, input=None, notice=None): +@hook.command(adminonly=True) +def raw(inp, conn=None, notice=None): ".raw -- Sends a RAW IRC command." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return notice("Raw command sent.") - input.conn.send(inp) + conn.send(inp) -@hook.command -def kick(inp, input=None, notice=None): - ".kick [channel] [reason] -- kicks a user." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - split = inp.split(" ") - if split[0][0] == "#": - chan = split[0] - user = split[1] - out = "KICK %s %s" % (chan, user) - if len(split) > 2: - reason = "" - for x in split[2:]: - reason = reason + x + " " - reason = reason[:-1] - out = out + " :" + reason - else: - chan = input.chan - user = split[0] - out = "KICK %s %s" % (input.chan, split[0]) - if len(split) > 1: - reason = "" - for x in split[1:]: - reason = reason + x + " " - reason = reason[:-1] - out = out + " :" + reason - - notice("Attempting to kick %s from %s..." % (user, chan)) - input.conn.send(out) - - -@hook.command -def say(inp, input=None, notice=None): +@hook.command(adminonly=True) +def say(inp, conn=None, chan=None, notice=None): ".say [channel] -- Makes the bot say in [channel]. "\ "If [channel] is blank the bot will say the in "\ "the channel the command was used in." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - split = inp.split(" ") - if split[0][0] == "#": + inp = inp.split(" ") + if inp[0][0] == "#": message = "" - for x in split[1:]: + for x in inp[1:]: message = message + x + " " message = message[:-1] - out = "PRIVMSG %s :%s" % (split[0], message) + out = "PRIVMSG %s :%s" % (inp[0], message) else: message = "" - for x in split[0:]: + for x in inp[0:]: message = message + x + " " message = message[:-1] - out = "PRIVMSG %s :%s" % (input.chan, message) - input.conn.send(out) + out = "PRIVMSG %s :%s" % (chan, message) + conn.send(out) -@hook.command("me") -@hook.command -def act(inp, input=None, notice=None): - ".act [channel] -- Makes the bot act out in [channel] "\ +@hook.command("act", adminonly=True) +@hook.command(adminonly=True) +def me(inp, conn=None, chan=None, notice=None): + ".me [channel] -- Makes the bot act out in [channel] "\ "If [channel] is blank the bot will act the in "\ "the channel the command was used in." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - split = inp.split(" ") - if split[0][0] == "#": + inp = inp.split(" ") + if inp[0][0] == "#": message = "" - for x in split[1:]: + for x in inp[1:]: message = message + x + " " message = message[:-1] - out = "PRIVMSG %s :\x01ACTION %s\x01" % (split[0], message) + out = "PRIVMSG %s :\x01ACTION %s\x01" % (inp[0], message) else: message = "" - for x in split[0:]: + for x in inp[0:]: message = message + x + " " message = message[:-1] - out = "PRIVMSG %s :\x01ACTION %s\x01" % (input.chan, message) - input.conn.send(out) - - -@hook.command -def topic(inp, input=None, notice=None): - ".topic [channel] -- Change the topic of a channel." - if not input.nick in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return - split = inp.split(" ") - if split[0][0] == "#": - out = "PRIVMSG %s :%s" % (split[0], message) - else: - out = "TOPIC %s :%s" % (input.chan, message) - input.conn.send(out) + out = "PRIVMSG %s :\x01ACTION %s\x01" % (chan, message) + conn.send(out) diff --git a/plugins/bf.py b/plugins/bf.py index a3bd724..32e9f3c 100755 --- a/plugins/bf.py +++ b/plugins/bf.py @@ -11,6 +11,7 @@ BUFFER_SIZE = 5000 MAX_STEPS = 1000000 +@hook.command('brainfuck') @hook.command def bf(inp): ".bf -- Executes as Brainfuck code." diff --git a/plugins/choose.py b/plugins/choose.py index 62e87d7..1ba5b2e 100755 --- a/plugins/choose.py +++ b/plugins/choose.py @@ -6,7 +6,7 @@ from util import hook @hook.command def choose(inp): - ".choose , [choice2], [choice3], [choice4], ... -- "\ + ".choose , [choice2], [choice3], [choice4], ... -- " \ "Randomly picks one of the given choices." c = re.findall(r'([^,]+)', inp) diff --git a/plugins/coin.py b/plugins/coin.py index ddbd853..6cd4ace 100755 --- a/plugins/coin.py +++ b/plugins/coin.py @@ -41,5 +41,5 @@ def coin(inp): return "You flip a coin and get " + sidename + "." else: flips = flip_simple(count) - return "You flip %s coins and get "\ + return "You flip %s coins and get " \ "%s heads and %s tails." % (str(count), str(flips[0]), str(flips[1])) diff --git a/plugins/ctcp.py b/plugins/ctcp.py index 083f294..83ea55e 100755 --- a/plugins/ctcp.py +++ b/plugins/ctcp.py @@ -1,22 +1,27 @@ +# Plugin by neersighted +import time +import getpass from util import hook # CTCP responses @hook.regex(r'^\x01VERSION\x01$') -def ctcpversion(inp, notice=None): - notice('\x01VERSION: CloudBot - http://git.io/cloudbot') +def ctcp_version(inp, notice=None): + notice('\x01VERSION: CloudBot - http://git.io/cloudbotirc') @hook.regex(r'^\x01PING\x01$') -def ctcpping(inp, notice=None): +def ctcp_ping(inp, notice=None): notice('\x01PING: PONG') @hook.regex(r'^\x01TIME\x01$') -def ctcptime(inp, notice=None): - notice('\x01TIME: GET A WATCH') +def ctcp_time(inp, notice=None): + the_time = time.strftime("%r", time.localtime()) + notice('\x01TIME: The time is: ' + the_time) @hook.regex(r'^\x01FINGER\x01$') -def ctcpfinger(inp, notice=None): - notice('\x01FINGER: WHERE ARE YOU PUTTING THAT') +def ctcp_finger(inp, notice=None): + user = getpass.getuser() + notice('\x01FINGER: Username is: ' + user) diff --git a/plugins/data/flirts.txt b/plugins/data/flirts.txt new file mode 100755 index 0000000..13e6710 --- /dev/null +++ b/plugins/data/flirts.txt @@ -0,0 +1,40 @@ +I bet your name's Mickey, 'cause you're so fine. +Hey, pretty mama. You smell kinda pretty, wanna smell me? +I better get out my library card, 'cause I'm checkin' you out. +If you were a booger, I'd pick you. +If I could rearrange the alphabet, I would put U and I together. +I've been bad, take me to your room. +I think Heaven's missing an angel. +That shirt looks good on you, it'd look better on my bedroom floor. +I cant help to notice but you look a lot like my next girlfriend. +Aren't your feet tired? Because you've been running through my mind all day. +I must be asleep, 'cause you are a dream come true. Also, I'm slightly damp. +I like large posteriors and I cannot prevaricate. +How you doin'? +If I said you had a good body, would you hold it against me? +Hey, baby cakes. +Nice butt. +I love you like a fat kid loves cake. +Do you believe in love at first sight? Or should I walk by again...? +Want to see my good side? Hahaha, that was a trick question, all I have are good sides. +You look like a woman who appreciates the finer things in life. Come over here and feel my velour bedspread. +Now you're officially my woman. Kudos! I can't say I don't envy you. +I find that the most erotic part of a woman is the boobies. +If you want to climb aboard the Love Train, you've got to stand on the Love Tracks. But you might just get smushed by a very sensual cow-catcher. +Lets say you and I knock some very /sensual/ boots. +I lost my phone number, can I have yours? +Does this rag smell like chloroform to you? +I'm here, where are your other two wishes? +Apart from being sexy, what do you do for a living? +Hi, I'm Mr. Right. Someone said you were looking for me. +You got something on your chest: My eyes. +Are you from Tennessee? Cause you're the only TEN I see. +Are you an alien? Because you just abducted my heart. +Excuse me, but I think you dropped something!!! MY JAW!!! +If I followed you home, would you keep me? +Where have you been all my life? +I'm just a love machine, and I don't work for nobody but you. +Do you live on a chicken farm? Because you sure know how to raise cocks. +Are you wearing space pants? Because your ass is out of this world. +Nice legs. What time do they open? +Your daddy must have been a baker, because you've got a nice set of buns. diff --git a/plugins/data/fortunes.txt b/plugins/data/fortunes.txt new file mode 100755 index 0000000..1c01b36 --- /dev/null +++ b/plugins/data/fortunes.txt @@ -0,0 +1,51 @@ +"Help! I'm stuck in the fortune cookie factory! +He who laughs at himself never runs out of things to laugh at. +The world is your oyster. +Today will be a good day. +Life's short, party naked. +Haters gonna hate. +You are amazing and let no one tell you otherwise. +A starship ride has been promised to you by the galactic wizard. +That wasn’t chicken. +Don’t fry bacon in the nude. +Take calculated risks. That is quite different from being rash. +DO THE IMPOSSIBLE, SEE THE INVISIBLE. +You cannot plough a field by turning it over in your mind. Unless you have telekinesis. +No one can make you feel inferior without your consent. +Never lose the ability to find beauty in ordinary things. +Ignore previous fortune. +Smile more. +You are the dancing queen. +YOU'RE THE BEST AROUND, NOTHIN'S GONNA EVER KEEP YA DOWN. +The cake is a lie. +Never take life seriously. Nobody gets out alive anyway. +Friendship is like peeing on yourself: everyone can see it, but only you get the warm feeling that it brings. +Never go to a doctor whose office plants have died. +Always remember you're unique, just like everyone else. +What if everything is an illusion and nothing exists? In that case, I definitely overpaid for my carpet. +Even if you are on the right track, you will get run over if you just sit there. +Think like a man of action, and act like a man of thought. +When in doubt, lubricate. +It is time for you to live up to your family name and face FULL LIFE CONSEQUENCES. +It's a good day to do what has to be done. +Move near the countryside and you will be friends of John Freeman. +If you can't beat 'em, mock 'em. +Use gun. And if that don't work, use more gun. +LOOK OUT BEHIND YOU +This message will self destruct in 10 seconds. +You'll never know what you can do until you try. +You are talented in many ways +Be both a speaker of words and a doer of deeds. +A visit to a strange place will bring you renewed perspective. +A passionate new romance will appear in your life when you least expect it. +If you care enough for a result, you will most certainly attain it. +To be loved, be loveable. +Step away from the power position for one day. +If you want to get a sure crop with a big yield, sow wild oats. +It doesn't take guts to quit. +You can expect a change for the better in job or status in the future. +As the wallet grows, so do the needs. +You have a reputation for being straightforward and honest. +Learn a new language and get a new soul. +A tall dark stranger will soon enter our life. +Keep staring. I'll do a trick. diff --git a/plugins/data/insults.txt b/plugins/data/insults.txt new file mode 100755 index 0000000..3a7cd71 --- /dev/null +++ b/plugins/data/insults.txt @@ -0,0 +1,32 @@ +You are the son of a motherless ogre. +Your mother was a hamster and your father smelled of elderberries. +I once owned a dog that was smarter than you. +Go climb a wall of dicks. +You fight like a dairy farmer. +I've spoken to apes more polite than you. +Go and boil your bottom! Son of a silly person! +I fart in your general direction. +Go away or I shall taunt you a second time. +Shouldn't you have a license for being that ugly? +Calling you an idiot would be an insult to all the stupid people. +Why don't you slip into something more comfortable...like a coma. +Well, they do say opposites attract...so I sincerely hope you meet somebody who is attractive, honest, intelligent, and cultured... +Are you always this stupid or are you just making a special effort today? +Yo momma so fat when she sits around the house she sits AROUND the house. +Yo momma so ugly she made an onion cry. +Is your name Maple Syrup? It should be, you sap. +Bite my shiny metal ass! +Up yours, meatbag. +Jam a bastard in it you crap! +Don't piss me off today, I'm running out of places to hide the bodies... +Why don't you go outside and play hide and go fuck yourself! +I'll use small words you're sure to understand, you warthog-faced buffoon. +You are a sad, strange little man, and you have my pity. +Sit your five dollar ass down before I make change. +What you've just said is one of the most insanely idiotic things I've ever heard. Everyone in this room is now dumber for having listened to it. May God have mercy on your soul. +Look up Idiot in the dictionary. Know what you'll find? The definition of the word IDIOT, which you are. +You're dumber than a bag of hammers. +Why don't you go back to your home on Whore Island? +If I had a dick this is when I'd tell you to suck it. +Go play in traffic. +The village called, they want their idiot back. diff --git a/plugins/data/itemids.txt b/plugins/data/itemids.txt new file mode 100755 index 0000000..917d88f --- /dev/null +++ b/plugins/data/itemids.txt @@ -0,0 +1,370 @@ +// obtained from +// edited by Lukeroge and _frozen +// Block id +1 Stone +2 Grass Block +3 Dirt +4 Cobblestone +5 Wooden Planks +6 Sapling +7 Bedrock +8 Water +9 Water +10 Lava +11 Lava +12 Sand +13 Gravel +14 Gold Ore +15 Iron Ore +16 Coal Ore +17 Wood +18 Leaves +19 Sponge +20 Glass +21 Lapis Lazuli Ore +22 Lapis Lazuli Block +23 Dispenser +24 Sandstone +25 Note Block +26 Bed +27 Powered Rail +28 Detector Rail +29 Sticky Piston +30 Cobweb +31 Grass +32 Dead Bush +33 Piston +34 Piston Extended +35 Wool +35:1 Orange Wool +35:2 Magenta Wool +35:3 Light Blue Wool +35:4 Yellow Wool +35:5 Lime Wool +35:6 Pink Wool +35:7 Gray Wool +35:8 Light Gray Wool +35:9 Cyan Wool +35:10 Purple Wool +35:11 Blue Wool +35:12 Brown Wool +35:13 Green Wool +35:14 Red Wool +35:15 Black Wool +35:0 White Wool +36 Block Moved by Piston +37 Flower +38 Rose +39 Brown Mushroom +40 Red Mushroom +41 Block of Gold +42 Block of Iron +43 Double Slabs +44 Slabs +45 Bricks +46 TNT +47 Bookshelf +48 Moss Stone +49 Obsidian +50 Torch +51 Fire +52 Monster Spawner +53 Wooden Stairs +54 Chest +55 Redstone Dust +56 Diamond Ore +57 Block of Diamond +58 Crafting Table +59 Crops +60 Farmland +61 Furnace +62 Furnace +63 Sign +64 Wooden Door +65 Ladder +66 Rail +67 Stone Stairs +68 Sign +69 Lever +70 Pressure Plate +71 Iron Door +72 Pressure Plate +73 Redstone Ore +74 Redstone Ore +75 Redstone Torch +76 Redstone Torch +77 Button +78 Snow +79 Ice +80 Snow +81 Cactus +82 Clay +83 Sugar cane +84 Jukebox +85 Fence +86 Pumpkin +87 Netherrack +88 Soul Sand +89 Glowstone +90 Portal +91 Jack 'o' Lantern +92 Cake +93 Redstone Repeater (off) +94 Redstone Repeater (on) +95 Locked chest +96 Trapdoor +97 Hidden Silverfish +98 Stone Bricks +99 Mushroom +100 Mushroom +101 Iron Bars +102 Glass Pane +103 Melon +104 Pumpkin Stem +105 Melon Stem +106 Vines +107 Fence Gate +108 Brick Stairs +109 Stone Brick Stairs +110 Mycelium +111 Lily Pad +112 Nether Brick +113 Nether Brick Fence +114 Nether Brick Stairs +115 Nether Wart +116 Enchantment Table +117 Brewing stand +118 Cauldron +119 End Portal +120 End Portal Frame +121 End Stone +122 Dragon Egg +123 Redstone Lamp (Off) +124 Redstone Lamp (On) +// Items Ids +256 Iron Shovel +257 Iron Pickaxe +258 Iron Axe +259 Flint and Steel +260 Apple +261 Bow +262 Arrow +263 Coal +264 Diamond +265 Iron Ingot +266 Gold Ingot +267 Iron Sword +268 Wooden Sword +269 Wooden Shovel +270 Wooden Pickaxe +271 Wooden Axe +272 Stone Sword +273 Stone Shovel +274 Stone Pickaxe +275 Stone Axe +276 Diamond Sword +277 Diamond Shovel +278 Diamond Pickaxe +279 Diamond Axe +280 Stick +281 Bowl +282 Mushroom Stew +283 Golden Sword +284 Golden Shovel +285 Golden Pickaxe +286 Golden Axe +287 String +288 Feather +289 Gunpowder +290 Wooden Hoe +291 Stone Hoe +292 Iron Hoe +293 Diamond Hoe +294 Golden Hoe +295 Seeds +296 Wheat +297 Bread +298 Leather Cap +299 Leather Tunic +300 Leather Pants +301 Leather Boots +302 Chain Helmet +303 Chain Chestplate +304 Chain Leggings +305 Chain Boots +306 Iron Helmet +307 Iron Chestplate +308 Iron Leggings +309 Iron Boots +310 Diamond Helmet +311 Diamond Chestplate +312 Diamond Leggings +313 Diamond Boots +314 Golden Helmet +315 Golden Chestplate +316 Golden Leggings +317 Golden boots +318 Flint +319 Raw Porkchop +320 Cooked Porkchop +321 Painting +322 Golden Apple +323 Sign +324 Wooden Door +325 Bucket +326 Water Bucket +327 Lava bucket +328 Minecart +329 Saddle +330 Iron Door +331 Redstone +332 Snowball +333 Boat +334 Leather +335 Milk +336 Brick +337 Clay +338 Sugar Canes +339 Paper +340 Book +341 Slimeball +342 Minecart with Chest +343 Minecart with Furnace +344 Egg +345 Compass +346 Fishing Rod +347 Clock +348 Glowstone Dust +349 Raw Fish +350 Cooked Fish +351 Dye +351:0 Ink Sac +351:1 Rose Red +351:2 Cactus Green +351:3 Cocoa Beans +351:4 Lapis Lazuli +351:5 Purple Dye +351:6 Cyan Dye +351:7 Light Gray Dye +351:8 Gray Dye +351:9 Pink Dye +351:10 Lime Dye +351:11 Dandelion Yellow +351:12 Light Blue Dye +351:13 Magenta Dye +351:14 Orange Dye +351:15 Bone Meal +352 Bone +353 Sugar +354 Cake +355 Bed +356 Redstone Repeater +357 Cookie +358 Map +359 Shears +360 Melon +361 Pumpkin Seeds +362 Melon Seeds +363 Raw Beef +364 Steak +365 Raw Chicken +366 Cooked Chicken +367 Rotten Flesh +368 Ender Pearl +369 Blaze Rod +370 Ghast Tear +371 Gold Nugget +372 Nether Wart +373 Potion +373:16 Awkward Potion +373:32 Thick Potion +373:64 Mundane Potion +373:8193 Regeneration Potion (0:45) +373:8194 Swiftness Potion (3:00) +373:8195 Fire Resistance Potion (3:00) +373:8196 Poison Potion (0:45) +373:8197 Healing Potion +373:8200 Weakness Potion (1:30) +373:8201 Strength Potion (3:00) +373:8202 Slowness Potion (1:30) +373:8204 Harming Potion +373:8225 Regeneration Potion II (0:22) +373:8226 Swiftness Potion II (1:30) +373:8228 Poison Potion II (0:22) +373:8229 Healing Potion II +373:8233 Strength Potion II (1:30) +373:8236 Harming Potion II +373:8257 Regeneration Potion (2:00) +373:8258 Swiftness Potion (8:00) +373:8259 Fire Resistance Potion (8:00) +373:8260 Poison Potion (2:00) +373:8264 Weakness Potion (4:00) +373:8265 Strength Potion (8:00) +373:8266 Slowness Potion (4:00) +373:16378 Fire Resistance Splash (2:15) +373:16385 Regeneration Splash (0:33) +373:16386 Swiftness Splash (2:15) +373:16388 Poison Splash (0:33) +373:16389 Healing Splash +373:16392 Weakness Splash (1:07) +373:16393 Strength Splash (2:15) +373:16394 Slowness Splash (1:07) +373:16396 Harming Splash +373:16418 Swiftness Splash II (1:07) +373:16420 Poison Splash II (0:16) +373:16421 Healing Splash II +373:16425 Strength Splash II (1:07) +373:16428 Harming Splash II +373:16449 Regeneration Splash (1:30) +373:16450 Swiftness Splash (6:00) +373:16451 Fire Resistance Splash (6:00) +373:16452 Poison Splash (1:30) +373:16456 Weakness Splash (3:00) +373:16457 Strength Splash (6:00) +373:16458 Slowness Splash (3:00) +373:16471 Regeneration Splash II (0:16) +374 Glass Bottle +375 Spider Eye +376 Fermented Spider Eye +377 Blaze Powder +378 Magma Cream +379 Brewing Stand +380 Cauldron +381 Eye of Ender +382 Glistering Melon +383 Spawn Egg +383:50 Creeper Egg +383:51 Skeleton Egg +383:52 Spider Egg +383:54 Zombie Egg +383:55 Slime Egg +383:56 Ghast Egg +383:57 Zombie Pigman Egg +383:58 Enderman Egg +383:59 Cave Spider Egg +383:60 Silverfish Egg +383:61 Blaze Egg +383:62 Magma Cube Egg +383:90 Pig Egg +383:91 Sheep Egg +383:92 Cow Egg +383:93 Chicken Egg +383:94 Squid Egg +383:95 Wolf Egg +383:96 Mooshroom Egg +383:98 Ocelot Egg +383:120 Villager Egg +384 Bottle Of Enchanting +385 Fire Charge +// Records +2256 Music Disc 13 +2257 Music Disc Cat +2258 Music Disc Blocks +2259 Music Disc Chirp +2260 Music Disc Far +2261 Music Disc Mall +2262 Music Disc Mellohi +2263 Music Disc Stal +2264 Music Disc Strad +2265 Music Disc Ward +2266 Music Disc 11 diff --git a/plugins/data/kill_bodyparts.txt b/plugins/data/kill_bodyparts.txt new file mode 100755 index 0000000..8477e68 --- /dev/null +++ b/plugins/data/kill_bodyparts.txt @@ -0,0 +1,9 @@ +head +arms +legs +arm +leg +toes +fingers +"special parts" +"man bits" diff --git a/plugins/data/kills.txt b/plugins/data/kills.txt new file mode 100755 index 0000000..c644ea4 --- /dev/null +++ b/plugins/data/kills.txt @@ -0,0 +1,22 @@ +rips off 's and leaves them to die. +grabs 's head and rips it clean off their body. +grabs a machine gun and riddles 's body with bullets. +sends The Terminator on a mission to retrieve 's . +gags and ties then throws them off a bridge. +crushes with a huge spiked boulder. +glares at until they die of boredom. +stuffs a few TNT blocks under 's bed and sets them off. +shivs in the . +rams a rocket launcher up 's ass and lets off a few rounds. +crushes 's skull in with a spiked mace. +unleashes the armies of Isengard on . +slices 's off with a Katana. +throws to Cthulu and watches them get ripped to shreads. +feeds to an owlbear, who proceeds to maul them. +turns into a snail and salts them. +snacks on 's . +puts into a sack, throws the sack in the river, and hurls the river into space. +goes bowling with 's disembodied head. +uses 's as a back-scratcher. +sends to /dev/null! +feeds coke and mentos till they violently explode. diff --git a/plugins/data/larts.txt b/plugins/data/larts.txt new file mode 100755 index 0000000..b4a831c --- /dev/null +++ b/plugins/data/larts.txt @@ -0,0 +1,105 @@ +swaps 's shampoo with glue. +installs Windows on 's computer. +forces to use perl for 3 weeks. +registers 's name with 50 known spammers. +resizes 's console to 40x24. +takes 's drink. +dispenses 's email address to a few hundred 'bulk mailing services'. +pokes in the eye. +beats senseless with a 50lb Linux manual. +cats /dev/random into 's ear. +signs up for AOL. +downvotes on Reddit. +enrolls in Visual Basic 101. +sporks . +drops a truckload of support tickets on . +judo chops . +sets 's resolution to 800x600. +formats 's harddrive to fat12. +rm -rf's . +stabs . +makes learn C++. +steals 's mojo. +strangles with a doohicky mouse cord. +whacks with the cluebat. +sells on EBay. +drops creepers on 's house. +throws all of 's diamond gear into lava. +uses as a biological warfare study. +uses the 'Customer Appreciation Bat' on . +puts in the Total Perspective Vortex. +casts into the fires of Mt. Doom. +gives a melvin. +turns over to the Fun Police. +turns over to Agent Smith to be 'bugged'. +takes away 's internet connection. +pushes past the Shoe Event Horizon. +counts '1, 2, 5... er... 3!' and hurls the Holy Handgrenade Of Antioch at . +puts in a nest of camel spiders. +makes read slashdot at -1. +puts 'alias vim=emacs' in 's /etc/profile. +uninstalls every web browser from 's system. +locks in the Chateau d'If. +signs up for getting hit on the head lessons. +makes try to set up a Lexmark printer. +fills 's eyedrop bottle with lime juice. +casts into the fires of Mt. Doom. +gives a Flying Dutchman. +rips off 's arm, and uses it to beat them to death. +pierces 's nose with a rusty paper hole puncher. +pokes with a rusty nail. +puts sugar between 's bedsheets. +pours sand into 's breakfast. +mixes epoxy into 's toothpaste. +puts Icy-Hot in 's lube container. +straps to a chair, and plays a endless low bitrate MP3 loop of \"the world's most annoying sound\" from \"Dumb and Dumber\". +tells Dr. Dre that was talking smack. +forces to use a Commodore 64 for all their word processing. +smacks in the face with a burlap sack full of broken glass. +puts in a room with several heavily armed manic depressives. +makes watch reruns of \"Blue's Clues\". +puts lye in 's coffee. +introduces to the clue-by-four. +tattoos the Windows symbol on 's ass. +lets Borg have his way with . +signs up for line dancing classes at the local senior center. +wakes out of a sound sleep with some brand new nipple piercings. +gives a 2 gauge Prince Albert. +forces to eat all their veggies. +covers 's toilet paper with lemon-pepper. +fills 's ketchup bottle with Dave's Insanity sauce. +forces to stare at an incredibly frustrating and seemingly never-ending IRC political debate. +knocks two of 's teeth out with a 2x4. +removes Debian from 's system. +switches over to CentOS. +uses 's iPod for skeet shooting practice. +gives 's phone number to Borg. +posts 's IP, username(s), and password(s) on 4chan. +forces to use words like 'irregardless' and 'administrate' (thereby sounding like a real dumbass). +tickles until they wet their pants and pass out. +replaces 's KY with elmer's clear wood glue. +replaces 's TUMS with alka-seltzer tablets. +squeezes habanero pepper juice into 's tub of vaseline. +forces to learn the Win32 API. +gives an atomic wedgie. +ties to a chair and forces them to listen to 'N Sync at full blast. +forces to use notepad for text editing. +frowns at really, really hard. +jabs a hot lighter into 's eye sockets. +forces to browse the web with IE6. +takes out at the knees with a broken pool cue. +forces to listen to emo music. +lets a few creepers into 's house. +signs up for the Iowa State Ferret Legging Championship. +attempts to hotswap 's RAM. +dragon punches . +puts railroad spikes into 's side. +replaces 's Astroglide with JB Weld. +replaces 's stress pills with rat poison pellets. +replaces 's crotch itch cream with Nair. +does the Australian Death Grip on . +dances upon the grave of 's ancestors. +farts in 's general direction. +flogs with stinging nettle. +intoduces to the Knights who say Ni. +hands a poison ivy joint. diff --git a/plugins/data/recipes.txt b/plugins/data/recipes.txt new file mode 100755 index 0000000..14ce8cf --- /dev/null +++ b/plugins/data/recipes.txt @@ -0,0 +1,217 @@ +//Minecraft Recipes List +//Created by _303 +//Obtained from https://github.com/ClouDev/CloudBot/blob/develop/plugins/data/recipes.txt +//Edited by _frozen +// +//Summary of Use: Each column is seperated by a comma (,) and rows by a vertical bar (|). Order of Recipes & Categories taken from +//www.minecraftwiki.net/wiki/Crafting for easier updating in the future (The Future!) +// +//Basic Recipes +// +4x Wooden Planks: Wood +4x Stick: Wooden Planks | Wooden Planks +4x Torch: Coal | Stick +4x Torch: Charcoal | Stick +1x Crafting Table: Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks +1x Furnace: Cobblestone, Cobblestone, Cobblestone | Cobblestone, None, Cobblestone | Cobblestone, Cobblestone, Cobblestone +1x Chest: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, None, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks +// +//Block Recipes +// +1x Block of Gold: Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot +1x Block of Iron: Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot +1x Block of Diamond: Diamond, Diamond, Diamond | Diamond, Diamond, Diamond | Diamond, Diamond, Diamond +1x Lapis Lazuli Block: Lapis Lazuli, Lapis Lazuli, Lapis Lazuli | Lapis Lazuli, Lapis Lazuli, Lapis Lazuli | Lapis Lazuli, Lapis Lazuli, Lapis Lazuli +1x Glowstone: Glowstone Dust, Glowstone Dust | Glowstone Dust, Glowstone Dust +1x Wool: String, String | String, String +1x TNT: Gunpowder, Sand, Gunpowder | Sand, Gunpowder, Sand | Gunpowder, Sand, Gunpowder +3x Cobblestone Slab: Cobblestone, Cobblestone, Cobblestone +3x Stone Slab: Stone, Stone, Stone +3x Sandstone Slab: Sandstone, Sandstone, Sandstone +3x Wooden Slab: Wooden Planks, Wooden Planks, Wooden Planks +3x Stone Bricks Slab: Stone Bricks, Stone Bricks, Stone Bricks +3x Bricks Slab: Bricks, Bricks, Bricks +4x Wooden Stairs: Wooden Planks, None, None | Wooden Planks, Wooden Planks, None | Wooden Planks, Wooden Planks, Wooden Planks +4x Stone Stairs: Cobblestone, None, None | Cobblestone, Cobblestone, None | Cobblestone, Cobblestone, Cobblestone +4x Brick Stairs: Bricks, None, None | Bricks, Bricks, None | Bricks, Bricks, Bricks +4x Nether Brick Stairs: Nether Bricks, None, None | Nether Bricks, Nether Bricks, None | Nether Bricks, Nether Bricks, Nether Bricks +4x Stone Brick Stairs: Stone Bricks, None, None | Stone Bricks, Stone Bricks, None | Stone Bricks, Stone Bricks, Stone Bricks +1x Snow: Snowball, Snowball | Snowball, Snowball +1x Clay Block: Clay, Clay | Clay, Clay +1x Brick Block: Brick, Brick | Brick, Brick +4x Stone Bricks: Stone, Stone | Stone, Stone +1x Bookshelf: Wooden Planks, Wooden Planks, Wooden Planks | Book, Book, Book | Wooden Planks, Wooden Planks, Wooden Planks +1x Sandstone: Sand, Sand | Sand, Sand +1x Jack 'o' Lantern: Pumpkin | Torch +// +//Tool Recipes +// +1x Wooden Pickaxe: Wooden Planks, Wooden Planks, Wooden Planks | None, Stick, None | None, Stick, None +1x Wooden Axe: Wooden Planks, Wooden Planks | Wooden Planks, Stick | None, Stick +1x Wooden Hoe: Wooden Planks, Wooden Planks | None, Stick | None, Stick +1x Wooden Shovel: Wooden Planks | Stick | Stick +1x Stone Pickaxe: Cobblestone, Cobblestone, Cobblestone | None, Stick, None | None, Stick, None +1x Stone Axe: Cobblestone, Cobblestone | Cobblestone, Stick | None, Stick +1x Stone Hoe: Cobblestone, Cobblestone | None, Stick | None, Stick +1x Stone Shovel: Cobblestone | Stick | Stick +1x Iron Pickaxe: Iron Ingot, Iron Ingot, Iron Ingot | None, Stick, None | None, Stick, None +1x Iron Axe: Iron Ingot, Iron Ingot | Iron Ingot, Stick | None, Stick +1x Iron Hoe: Iron Ingot, Iron Ingot | None, Stick | None, Stick +1x Iron Shovel: Iron Ingot | Stick | Stick +1x Diamond Pickaxe: Diamond, Diamond, Diamond | None, Stick, None | None, Stick, None +1x Diamond Axe: Diamond, Diamond | Diamond, Stick | None, Stick +1x Diamond Hoe: Diamond, Diamond | None, Stick | None, Stick +1x Diamond Shovel: Diamond | Stick | Stick +1x Golden Pickaxe: Gold Ingot, Gold Ingot, Gold Ingot | None, Stick, None | None, Stick, None +1x Golden Axe: Gold Ingot, Gold Ingot | Gold Ingot, Stick | None, Stick +1x Golden Hoe: Gold Ingot, Gold Ingot | None, Stick | None, Stick +1x Golden Shovel: Gold Ingot | Stick | Stick +1x Flint and Steel: Iron Ingot, None | None, Flint +1x Bucket: Iron Ingot, None, Iron Ingot | None, Iron Ingot, None +1x Compass: None, Iron Ingot, None | Iron Ingot, Redstone, Iron Ingot | None, Iron Ingot, None +1x Map: Paper, Paper, Paper | Paper, Compass, Paper | Paper, Paper, Paper +1x Clock: None, Gold Ingot, None | Gold Ingot, Redstone, Gold Ingot | None, Gold Ingot, None +1x Fishing Rod: None, None, Stick | None, Stick, String | Stick, None, String +1x Shears: None, Iron Ingot | Iron Ingot, None +3x Fire Charge: Gunpowder, None, None | Blaze Powder, Coal/Charcoal, None +// +//Weapon Recipes +// +1x Wooden Sword: Wooden Planks | Wooden Planks | Stick +1x Stone Sword: Cobblestone | Cobblestone | Stick +1x Iron Sword: Iron Ingot | Iron Ingot | Stick +1x Diamond Sword: Diamond | Diamond | Stick +1x Golden Sword: Gold Ingot | Gold Ingot | Stick +1x Bow: None, Stick, String | Stick, None, String | None, Stick, String +4x Arrow: Flint | Stick | Feather +// +//Armor Recipes +// +1x Leather Tunic: Leather, None, Leather | Leather, Leather, Leather | Leather, Leather, Leather +1x Leather Pants: Leather, Leather, Leather | Leather, None, Leather | Leather, None, Leather +1x Leather Cap: Leather, Leather, Leather | Leather, None, Leather +1x Leather Boots: Leather, None, Leather | Leather, None, Leather +1x Chain Chestplate: Fire, None, Fire | Fire, Fire, Fire | Fire, Fire, Fire +1x Chain Leggings: Fire, Fire, Fire | Fire, None, Fire | Fire, None, Fire +1x Chain Helmet: Fire, Fire, Fire | Fire, None, Fire +1x Chain Boots: Fire, None, Fire | Fire, None, Fire +1x Iron Chestplate: Iron Ingot, None, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot +1x Iron Leggings: Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, None, Iron Ingot | Iron Ingot, None, Iron Ingot +1x Iron Helmet: Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, None, Iron Ingot +1x Iron Boots: Iron Ingot, None, Iron Ingot | Iron Ingot, None, Iron Ingot +1x Diamond Chestplate: Diamond, None, Diamond | Diamond, Diamond, Diamond | Diamond, Diamond, Diamond +1x Diamond Leggings: Diamond, Diamond, Diamond | Diamond, None, Diamond | Diamond, None, Diamond +1x Diamond Helmet: Diamond, Diamond, Diamond | Diamond, None, Diamond +1x Diamond Boots: Diamond, None, Diamond | Diamond, None, Diamond +1x Golden Chestplate: Gold Ingot, None, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot +1x Golden Leggings: Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, None, Gold Ingot | Gold Ingot, None, Gold Ingot +1x Golden Helmet: Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, None, Gold Ingot +1x Golden Boots: Gold Ingot, None, Gold Ingot | Gold Ingot, None, Gold Ingot +// +//Transportation Recipes +// +1x Minecart: Iron Ingot, None, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot +1x Minecart with Chest: Chest | Minecart +1x Minecart with Furnace: Furnace | Minecart +16x Rail: Iron Ingot, None, Iron Ingot | Iron Ingot, Stick, Iron Ingot | Iron Ingot, None, Iron Ingot +6x Powered Rail: Gold Ingot, None, Gold Ingot | Gold Ingot, Stick, Gold Ingot | Gold Ingot, Redstone, Gold Ingot +6x Detector Rail: Iron Ingot, None, Iron Ingot | Iron Ingot, Pressure Plate, Iron Ingot | Iron Ingot, Redstone, Iron Ingot +1x Boat: Wooden Planks, None, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks +// +//Mechanism Recipes +// +1x Wooden Door: Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks +1x Iron Door: Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot +2x Trapdoor: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks +1x Stone Pressure Plate: Stone, Stone +1x Wooden Pressure Plate: Wooden Planks, Wooden Planks +1x Button: Stone | Stone +1x Redstone Torch: Redstone | Stick +1x Lever: Stick | Cobblestone +1x Note Block: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Redstone, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks +1x Jukebox: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Diamond, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks +1x Dispenser: Cobblestone, Cobblestone, Cobblestone | Cobblestone, Bow, Cobblestone | Cobblestone, Redstone, Cobblestone +1x Redstone Repeater: Redstone Torch, Redstone, Redstone Torch | Stone, Stone, Stone +1x Piston: Wooden Planks, Wooden Planks, Wooden Planks | Cobblestone, Iron Ingot, Cobblestone | Cobblestone, Redstone, Cobblestone +1x Sticky Piston: none, slime ball, none | none, piston, none +1x Redstone Lamp: none, redstone dust, none | redstone dust, glowstone block, redstone | none, redstone dust, none +// +//Food Recipes +// +4x Bowl: Wooden Planks, None, Wooden Planks | None, Wooden Planks, None +1x Mushroom Stew: Brown Mushroom, Red Mushroom | Bowl +1x Bread: Wheat, Wheat, Wheat +1x Sugar: Sugar Canes +1x Cake: Milk, Milk, Milk | Sugar, Egg, Sugar | Wheat, Wheat, Wheat +8x Cookie: Wheat, Cocoa Beans, Wheat +1x Golden Apple: Gold Nugget, Gold Nugget, Gold Nugget | Gold Nugget, Apple, Gold Nugget | Gold Nugget, Gold Nugget, Gold Nugget +1x Melon Block: Melon, Melon, Melon | Melon, Melon, Melon | Melon, Melon, Melon +1x Melon Seeds: Melon Slice +4x Pumpkin Seeds: Pumpkin +// +//Miscellaneous Recipes +// +9x Gold Ingot: Block of Gold +9x Iron Ingot: Block of Iron +9x Diamond: Block of Diamond +9x Lapis Lazuli: Lapis Lazuli Block +2x Ladder: Stick, None, Stick | Stick, Stick, Stick | Stick, None, Stick +1x Sign: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks | None, Stick, None +1x Painting: Stick, Stick, Stick | Stick, Black Wool, Stick | Stick, Stick, Stick +16x Iron Bars: Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot +16x Glass Pane: Glass, Glass, Glass | Glass, Glass, Glass +3x Paper: Sugar Canes, Sugar Canes, Sugar Canes +1x Book: Paper | Paper | Paper +2x Fence: Stick, Stick, Stick | Stick, Stick, Stick +2x Nether Brick Fence: Nether Brick, Nether Brick, Nether Brick | Nether Brick, Nether Brick, Nether Brick +1x Fence Gate: Stick, Wooden Planks, Stick | Stick, Wooden Planks, Stick +1x Bed: Wool, Wool, Wool | Wooden Planks, Wooden Planks, Wooden Planks +9x Gold Nugget: Gold Ingot +1x Gold Ingot: Gold Nugget, Gold Nugget, Gold Nugget | Gold Nugget, Gold Nugget, Gold Nugget | Gold Nugget, Gold Nugget, Gold Nugget +1x Eye of Ender: Ender Pearl | Blaze Powder +// +//Dye Recipes +// +3x Bone Meal: Bone +2x Light Gray Dye: Gray Dye, Bone Meal +2x Gray Dye: Ink Sac, Bone Meal +2x Rose Red: Rose +2x Orange Dye: Rose Red, Dandelion Yellow +2x Dandelion Yellow: Flower +2x Lime Dye: Cactus Green, Bone Meal +2x Light Blue Dye: Lapis Lazuli, Bone Meal +2x Cyan Dye: Lapis Lazuli, Cactus Green +2x Purple Dye: Lapis Lazuli, Rose Red +4x Magenta Dye: Lapis Lazuli, Rose Red, Rose Red, Bone Meal +2x Pink Dye: Rose Red, Bone Meal +// +//Wool Recipes +// +1x Light Gray Wool: Light Gray Dye, Wool +1x Gray Wool: Gray Dye, Wool +1x Black Wool: Ink Sac, Wool +1x Red Wool: Rose Red, Wool +1x Orange Wool: Orange Dye, Wool +1x Yellow Wool: Dandelion Yellow, Wool +1x Lime Wool: Lime Dye, Wool +1x Green Wool: Cactus Green, Wool +1x Light Blue Wool: Light Blue Dye, Wool +1x Cyan Wool: Cyan Dye, Wool +1x Blue Wool: Lapis Lazuli, Wool +1x Purple Wool: Purple Dye, Wool +1x Magenta Wool: Magenta Dye, Wool +1x Pink Wool: Pink Dye, Wool +1x Brown Wool: Cocoa Beans, Wool +1x Wool: Bone Meal, Wool +// +//Enchancement & Brewing Recipes +// +3x Glass Bottle: Glass, None, Glass | None, Glass, None +1x Cauldron: Iron Ingot, None, Iron Ingot | Iron Ingot, None, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot +1x Brewing Stand: None, Blaze Rod, None | Cobblestone, Cobblestone, Cobblestone +2x Blaze Powder: Blaze Rod +1x Magma Cream: Slimeball | Blaze Powder +1x Fermented Spider Eye: Spider Eye | Brown Mushroom, Sugar +1x Glistering Melon: Melon Slice, Gold Nugget +9x Gold Nugget: Gold Ingot +1x Enchantment Table: None, Book, None | Diamond, Obsidian, Diamond | Obsidian, Obsidian, Obsidian \ No newline at end of file diff --git a/plugins/data/slap_items.txt b/plugins/data/slap_items.txt new file mode 100755 index 0000000..2f53980 --- /dev/null +++ b/plugins/data/slap_items.txt @@ -0,0 +1,19 @@ +cast iron skillet +large trout +baseball bat +wooden cane +CRT monitor +diamond sword +physics textbook +television +mau5head +five ton truck +roll of duct tape +book +cobblestone block +lava bucket +rubber chicken +gold block +fire extinguisher +heavy rock +chunk of dirt diff --git a/plugins/data/slaps.txt b/plugins/data/slaps.txt new file mode 100755 index 0000000..d8ee0b2 --- /dev/null +++ b/plugins/data/slaps.txt @@ -0,0 +1,12 @@ +slaps with a . +slaps around a bit with a . +throws a at . +chucks a few s at . +grabs a and throws it in 's face. +launches a in 's general direction. +sits on 's face, while slamming a into their crotch. +holds down and repeatedly whacks them with a . +prods with a flaming . +picks up a and whacks with it. +ties to a chair and throws a at them. +hits on the head with a . diff --git a/plugins/dice.py b/plugins/dice.py index c70f7f6..2e688bd 100755 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -1,7 +1,5 @@ -""" -dice.py: written by Scaevolus 2008, updated 2009 -simulates dicerolls -""" +# Written by Scaevolus, updated by Lukeroge + import re import random @@ -9,7 +7,8 @@ from util import hook whitespace_re = re.compile(r'\s+') -valid_diceroll = r'^([+-]?(?:\d+|\d*d(?:\d+|F))(?:[+-](?:\d+|\d*d(?:\d+|F)))*)( .+)?$' +valid_diceroll = r'^([+-]?(?:\d+|\d*d(?:\d+|F))(?:[+-](?:\d+|\d*d(?:\d+|' \ + 'F)))*)( .+)?$' valid_diceroll_re = re.compile(valid_diceroll, re.I) sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I) split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I) @@ -23,21 +22,22 @@ def nrolls(count, n): if count < 100: return [random.randint(0, 1) for x in xrange(count)] else: # fake it - return [int(random.normalvariate(.5*count, (.75*count)**.5))] + return [int(random.normalvariate(.5 * count, (.75 * count) ** .5))] else: if count < 100: return [random.randint(1, n) for x in xrange(count)] else: # fake it - return [int(random.normalvariate(.5*(1+n)*count, - (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))] + return [int(random.normalvariate(.5 * (1 + n) * count, + (((n + 1) * (2 * n + 1) / 6. - + (.5 * (1 + n)) ** 2) * count) ** .5))] @hook.command('roll') #@hook.regex(valid_diceroll, re.I) @hook.command def dice(inp): - ".dice -- Simulates dicerolls. Example of : '.dice 2d20-d5+4 roll 2'." \ - "D20s, subtract 1D5, add 4" + ".dice -- Simulates dicerolls. Example of :" \ + " '.dice 2d20-d5+4 roll 2'. D20s, subtract 1D5, add 4" try: # if inp is a re.match object... (inp, desc) = inp.groups() diff --git a/plugins/dictionary.py b/plugins/dictionary.py index 84f9982..934a8b8 100755 --- a/plugins/dictionary.py +++ b/plugins/dictionary.py @@ -1,21 +1,41 @@ +# Plugin by GhettoWizard and Scaevolus import re - -from util import hook, http +from util import hook +from util import http @hook.command('u') @hook.command def urban(inp): - ".urban -- Looks up on urbandictionary.com." + ".urban [id] -- Looks up on urbandictionary.com." + + # set a default definition number + id = 1 + + # clean and split the input + input = inp.lower().strip() + parts = input.split() + + # if the last word is a number, set the ID to that number + if parts[-1].isdigit(): + id = int(parts[-1]) + del parts[-1] + input = " ".join(parts) url = 'http://www.urbandictionary.com/iphone/search/define' - page = http.get_json(url, term=inp) + page = http.get_json(url, term=input) defs = page['list'] if page['result_type'] == 'no_results': return 'Not found.' - out = defs[0]['word'] + ': ' + defs[0]['definition'] + # try getting the requested definition + try: + out = "[%s/%s] %s: %s" % \ + (str(id), str(len(defs)), defs[id - 1]['word'], + defs[id - 1]['definition']) + except IndexError: + return 'Not found.' if len(out) > 400: out = out[:out.rfind(' ', 0, 400)] + '...' @@ -23,7 +43,6 @@ def urban(inp): return out -# define plugin by GhettoWizard & Scaevolus @hook.command('dictionary') @hook.command def define(inp): diff --git a/plugins/drama.py b/plugins/drama.py index 67008bf..db586bc 100755 --- a/plugins/drama.py +++ b/plugins/drama.py @@ -7,7 +7,8 @@ ed_url = "http://encyclopediadramatica.ch/" @hook.command('ed') @hook.command def drama(inp): - ".drama -- Gets the first paragraph of Encyclopedia Dramatica article on ." + ".drama -- Gets the first paragraph of"\ + "the Encyclopedia Dramatica article on ." j = http.get_json(api_url, search=inp) if not j[1]: diff --git a/plugins/fact.py b/plugins/fact.py index 3a4ee55..82bec4e 100755 --- a/plugins/fact.py +++ b/plugins/fact.py @@ -1,7 +1,10 @@ import re -from util import hook, http, misc +from util import hook +from util import http +from util import misc from BeautifulSoup import BeautifulSoup + @hook.command(autohelp=False) def fact(inp, say=False, nick=False): ".fact -- Gets a random fact from OMGFACTS." @@ -15,10 +18,11 @@ def fact(inp, say=False, nick=False): return u"%s [ %s ]" % (fact, link) + def get_fact(): page = http.get('http://www.omg-facts.com/random') soup = BeautifulSoup(page) - container = soup.find('a', {'class' : 'surprise'}) + container = soup.find('a', {'class': 'surprise'}) link = container['href'] fact = misc.strip_html(container.renderContents()) diff --git a/plugins/factoids.py b/plugins/factoids.py index f26fab7..706cc06 100755 --- a/plugins/factoids.py +++ b/plugins/factoids.py @@ -6,7 +6,7 @@ from util import hook import re -# the dictionary has target_word : replacement_word pairs +# some simple "shortcodes" for formatting purposes shortcodes = { '': '\x02', '': '\x02', @@ -24,7 +24,8 @@ def db_init(db): def get_memory(db, word): - row = db.execute("select data from mem where word=lower(?)", [word]).fetchone() + row = db.execute("select data from mem where word=lower(?)", + [word]).fetchone() if row: return row[0] else: @@ -43,12 +44,11 @@ def multiwordReplace(text, wordDic): return rc.sub(translate, text) -@hook.command("r") +@hook.command("r", adminonly=True) +@hook.command(adminonly=True) def remember(inp, nick='', db=None, say=None, input=None, notice=None): - ".remember [+] -- Remembers with . Add + to to append." - if input.nick not in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return + ".remember [+] -- Remembers with . Add +" + " to to append." db_init(db) append = False @@ -80,20 +80,18 @@ def remember(inp, nick='', db=None, say=None, input=None, notice=None): if append: notice("Appending %s to %s" % (new, data.replace('"', "''"))) else: - notice('Forgetting existing data (%s), remembering this instead!' % \ - data.replace('"', "''")) + notice('Forgetting existing data (%s), remembering this instead!' + % data.replace('"', "''")) return else: notice('Remembered!') return -@hook.command("f") +@hook.command("f", adminonly=True) +@hook.command(adminonly=True) def forget(inp, db=None, input=None, notice=None): ".forget -- Forgets a remembered ." - if input.nick not in input.bot.config["admins"]: - notice("Only bot admins can use this command!") - return db_init(db) data = get_memory(db, inp) @@ -102,7 +100,7 @@ def forget(inp, db=None, input=None, notice=None): db.execute("delete from mem where word=lower(?)", [inp]) db.commit() - notice('`%s` has been forgotten.' % data.replace('`', "'")) + notice('"%s" has been forgotten.' % data.replace('`', "'")) return else: notice("I don't know about that.") @@ -111,10 +109,10 @@ def forget(inp, db=None, input=None, notice=None): @hook.command("info") @hook.regex(r'^\? ?(.+)') -def question(inp, say=None, db=None, bot=None): +def factoid(inp, say=None, db=None, bot=None): "? -- Shows what data is associated with ." try: - prefix_on = bot.config["plugins"]["factoids"]["prefix"] + prefix_on = bot.config["plugins"]["factoids"].get("prefix", False) except KeyError: prefix_on = False diff --git a/plugins/feelings.py b/plugins/feelings.py new file mode 100755 index 0000000..3141ceb --- /dev/null +++ b/plugins/feelings.py @@ -0,0 +1,56 @@ +from util import hook +import re +import random + +nick_re = re.compile(r"^[A-Za-z0-9_|.-\]\[]*$") + +insults = [] +flirts = [] + +with open("plugins/data/insults.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + insults.append(line.strip()) + +with open("plugins/data/flirts.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + flirts.append(line.strip()) + + +@hook.command +def insult(inp, nick=None, me=None, conn=None): + ".insult -- Makes the bot insult ." + target = inp.strip() + + if not re.match(nick_re, target): + notice("Invalid username!") + return + + if target == conn.nick.lower() or target == "itself": + target = nick + else: + target = inp + + out = 'insults %s... "%s"' % (target, random.choice(insults)) + me(out) + + +@hook.command +def flirt(inp, nick=None, me=None, conn=None): + ".flirt -- Make the bot flirt with ." + target = inp.strip() + + if not re.match(nick_re, target): + notice("Invalid username!") + return + + if target == conn.nick.lower() or target == "itself": + target = 'itself' + else: + target = inp + + out = 'insults %s... "%s"' % (target, random.choice(flirts)) + me(out) diff --git a/plugins/flip.py b/plugins/flip.py index d613e48..14ad83e 100755 --- a/plugins/flip.py +++ b/plugins/flip.py @@ -1,6 +1,7 @@ from util import hook import random + @hook.command def flip(inp, flip_count=0, say=None): ".flip -- Flips over." diff --git a/plugins/flirt.py b/plugins/flirt.py deleted file mode 100755 index 0706b04..0000000 --- a/plugins/flirt.py +++ /dev/null @@ -1,55 +0,0 @@ -from util import hook -import re -import random - -flirts = ["I bet your name's Mickey, 'cause you're so fine.", - "Hey, pretty mama. You smell kinda pretty, wanna smell me?", - "I better get out my library card, 'cause I'm checkin' you out.", - "If you were a booger, I'd pick you.", - "If I could rearrange the alphabet, I would put U and I together.", - "I've been bad, take me to your room.", - "I think Heaven's missing an angel.", - "That shirt looks good on you, it'd look better on my bedroom floor.", - "I cant help to notice but you look a lot like my next girlfriend", - "Aren't your feet tired? Because you've been running through my mind all day.", - "I must be asleep, 'cause you are a dream come true. Also, I'm slightly damp.", - "I like large posteriors and I cannot prevaricate.", - "How you doin'?", - "If I said you had a good body, would you hold it against me?", - "Hey, baby cakes.", - "Nice butt.", - "I love you like a fat kid loves cake.", - "Do you believe in love at first sight? Or should I walk by again...?", - "Want to see my good side? Hahaha, that was a trick question, all I have are good sides.", - "You look like a woman who appreciates the finer things in life. Come over here and feel my velour bedspread.", - "Now you're officially my woman. Kudos! I can't say I don't envy you.", - "I find that the most erotic part of a woman is the boobies.", - "If you want to climb aboard the Love Train, you've got to stand on the Love Tracks. But you might just get smushed by a very sensual cow-catcher.", - "Lets say you and I knock some very /sensual/ boots.", - "I lost my phone number, can I have yours?", - "Does this rag smell like chloroform to you? ", - "I'm here, where are your other two wishes?", - "Apart from being sexy, what do you do for a living?", - "Hi, I'm Mr. Right. Someone said you were looking for me. ", - "You got something on your chest: My eyes.", - "Are you from Tennessee? Cause you're the only TEN I see.", - "Are you an alien? Because you just abducted my heart.", - "Excuse me, but I think you dropped something!!! MY JAW!!", - "If I followed you home, would you keep me?", - "Where have you been all my life?", - "I'm just a love machine, and I don't work for nobody but you", - "Do you live on a chicken farm? Because you sure know how to raise cocks.", - "Are you wearing space pants? Because your ass is out of this world.", - "Nice legs. What time do they open?", - "Your daddy must have been a baker, because you've got a nice set of buns."] - - -@hook.command(autohelp=False) -def flirt(inp, nick=None, me=None, input=None): - ".flirt -- Make the bot flirt with ." - msg = "flirts with " + nick + "... \"" + random.choice(flirts) + "\"" - if re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()) and inp != "": - msg = "flirts with " + inp + "... \"" + random.choice(flirts) + "\"" - if inp == input.conn.nick.lower() or inp == "itself": - msg = "flirts with itself... \"" + random.choice(flirts) + "\"" - me(msg) diff --git a/plugins/fmylife.py b/plugins/fmylife.py index d0986b3..7c67b15 100755 --- a/plugins/fmylife.py +++ b/plugins/fmylife.py @@ -1,41 +1,38 @@ # Plugin by Lukeroge -# -import re - -from util import hook, http, misc -from urllib2 import HTTPError +from util import hook, http from urlparse import urljoin from BeautifulSoup import BeautifulSoup +from collections import defaultdict base_url = 'http://www.fmylife.com/' -@hook.command(autohelp=False) -def fml(inp): - ".fml [id] -- Gets a random quote from fmyfife.com. Optionally gets [id]." +fml_cache = defaultdict() - inp = inp.replace("#", "") - - if inp: - if not inp.isdigit(): - return "Invalid ID!" - try: - page = http.get(urljoin(base_url, inp)) - except (HTTPError, IOError): - return "Could not fetch #%s. FML" % inp - else: - try: - page = http.get(urljoin(base_url, 'random')) - except (HTTPError, IOError): - return "I tried to use .fml, but it was broken. FML" +def refresh_cache(): + """Gets a page of random FMLs and puts them into a dictionary""" + page = http.get(urljoin(base_url, 'random')) soup = BeautifulSoup(page) - soup.find('div', id='submit').extract() - post = soup.body.find('div', 'post') - try: - id = int(post.find('a', 'fmllink')['href'].split('/')[-1]) - except TypeError: - return "Could not fetch #%s. FML" % inp - body = misc.strip_html(' '.join(link.renderContents() for link in post('a', 'fmllink'))) - return '(#%d) %s' % (id, body) + for e in soup.findAll('div', {'class': 'post article'}): + id = int(e['id']) + text = ''.join(e.find('p').findAll(text=True)) + text = http.unescape(text) + fml_cache[id] = text + +# do an initial refresh of the cache +refresh_cache() + + +@hook.command(autohelp=False) +def fml(inp, reply=None): + ".fml -- Gets a random quote from fmyfife.com." + + # grab the last item in the fml cache and remove it + id, text = fml_cache.popitem() + # reply with the fml we grabbed + reply('(#%d) %s' % (id, text)) + # refresh fml cache if its getting empty + if len(fml_cache) < 3: + refresh_cache() diff --git a/plugins/fortune.py b/plugins/fortune.py index 7f1f4b4..c31a17d 100755 --- a/plugins/fortune.py +++ b/plugins/fortune.py @@ -2,64 +2,16 @@ from util import hook import re import random -fortunes = ["Help! I'm stuck in the fortune cookie factory!", - "He who laughs at himself never runs out of things to laugh at.", - "The world is your oyster.", - "Today will be a good day.", - "Life's short, party naked.", - "Haters gonna hate.", - "You are amazing and let no one tell you otherwise.", - "A starship ride has been promised to you by the galactic wizard.", - "That wasn’t chicken.", - "Don’t fry bacon in the nude.", - "Take calculated risks. That is quite different from being rash.", - "DO THE IMPOSSIBLE, SEE THE INVISIBLE.", - "You cannot plough a field by turning it over in your mind. Unless you have telekinesis.", - "No one can make you feel inferior without your consent.", - "Never lose the ability to find beauty in ordinary things.", - "Ignore previous fortune.", - "Smile more.", - "You are the dancing queen.", - "YOU'RE THE BEST AROUND, NOTHIN'S GONNA EVER KEEP YA DOWN.", - "The cake is a lie.", - "Never take life seriously. Nobody gets out alive anyway.", - "Friendship is like peeing on yourself: everyone can see it, but only you get the warm feeling that it brings.", - "Never go to a doctor whose office plants have died.", - "Always remember you're unique, just like everyone else.", - "What if everything is an illusion and nothing exists? In that case, I definitely overpaid for my carpet.", - "Even if you are on the right track, you will get run over if you just sit there.", - "Think like a man of action, and act like a man of thought.", - "When in doubt, lubricate.", - "It is time for you to live up to your family name and face FULL LIFE CONSEQUENCES.", - "It's a good day to do what has to be done.", - "Move near the countryside and you will be friends of John Freeman.", - "If you can't beat 'em, mock 'em.", - "Use gun. And if that don't work, use more gun.", - "LOOK OUT BEHIND YOU", - "This message will self destruct in 10 seconds.", - "You'll never know what you can do until you try.", - "You are talented in many ways", - "Be both a speaker of words and a doer of deeds.", - "A visit to a strange place will bring you renewed perspective.", - "A passionate new romance will appear in your life when you least expect it.", - "If you care enough for a result, you will most certainly attain it.", - "To be loved, be loveable.", - "Step away from the power position for one day.", - "If you want to get a sure crop with a big yield, sow wild oats.", - "It doesn't take guts to quit.", - "You can expect a change for the better in job or status in the future.", - "As the wallet grows, so do the needs.", - "You have a reputation for being straightforward and honest.", - "Learn a new language and get a new soul.", - "A tall dark stranger will soon enter our life.", - "Keep staring. I'll do a trick."] +fortunes = [] -@hook.command(autohelp=False) -def fortune(inp, nick=None, say=None, input=None): +with open("plugins/data/fortunes.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + fortunes.append(line.strip()) + + +@hook.command +def fortune(inp): ".fortune -- Fortune cookies on demand." - - msg = "(" + nick + ") " + random.choice(fortunes) - if re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()) and inp != "": - msg = "(@" + inp + ") " + random.choice(fortunes) - - say(msg) + return random.choice(fortunes) diff --git a/plugins/gcalc.py b/plugins/gcalc.py index a568f53..68333b1 100755 --- a/plugins/gcalc.py +++ b/plugins/gcalc.py @@ -3,8 +3,9 @@ import re from util import hook, http, misc from BeautifulSoup import BeautifulSoup -@hook.command("calc") + @hook.command("math") +@hook.command def calc(inp): ".calc -- Calculate with Google Calc." @@ -14,7 +15,7 @@ def calc(inp): soup = BeautifulSoup(page) - response = soup.find('h2', {'class' : 'r'}) + response = soup.find('h2', {'class': 'r'}) if response is None: return "Could not calculate " + inp diff --git a/plugins/geoip.py b/plugins/geoip.py index 97a3fc7..7912b1a 100755 --- a/plugins/geoip.py +++ b/plugins/geoip.py @@ -1,9 +1,11 @@ from util import hook + def find_location(ip, api): import string import urllib - response = urllib.urlopen("http://api.ipinfodb.com/v3/ip-city/?key="+api+"&ip="+ip).read() + response = urllib.urlopen("http://api.ipinfodb.com/v3/ip-city/?key=" \ + + api + "&ip=" + ip).read() response = response.split(";") give = {} give["country"] = response[4].title() @@ -13,6 +15,7 @@ def find_location(ip, api): give["timezone"] = response[10].title() return give + def timezone(ip): time = find_location(ip)["timezone"] time = time.replace(":", ".") @@ -33,8 +36,10 @@ def geoip(inp, say=None, bot=None): localstring = give["city"] else: localstring = give["city"] + ", " + give["state"] - say("That IP comes from " + give["country"] + " (" + give["country_short"] + ")") - say("I think it's in " + localstring + " with a timezone of " + give["timezone"] + "GMT") + say("That IP comes from " + give["country"] + + " (" + give["country_short"] + ")") + say("I think it's in " + localstring + + " with a timezone of " + give["timezone"] + "GMT") else: - say("Either that wasn't an IP or I cannot locate it in my database. :(") + say("Either that wasn't an IP or I cannot locate it in my database.") return diff --git a/plugins/gitio.py b/plugins/gitio.py index be40835..99d4e41 100755 --- a/plugins/gitio.py +++ b/plugins/gitio.py @@ -1,10 +1,12 @@ -# plugin created by neersighted and lukeroge +# Plugin by neersighted and Lukeroge from util import hook import urllib2 + @hook.command def gitio(inp): - ".gitio [code] -- Shorten Github URLs with git.io. [code] is a optional custom short code." + ".gitio [code] -- Shorten Github URLs with git.io. [code] is" \ + " a optional custom short code." split = inp.split(" ") url = split[0] @@ -13,13 +15,14 @@ def gitio(inp): except: code = None - # if the first 8 chars of "url" are not "https://" then append "https://" to the url, also convert "http://" to "https://" + # if the first 8 chars of "url" are not "https://" then append + # "https://" to the url, also convert "http://" to "https://" if url[:8] != "https://": if url[:7] != "http://": url = "https://" + url else: url = "https://" + url[7:] - url='url='+str(url) + url = 'url=' + str(url) if code: url = url + '&code=' + str(code) req = urllib2.Request(url='http://git.io', data=url) @@ -31,7 +34,8 @@ def gitio(inp): return "Failed to get URL!" urlinfo = str(f.info()) - # loop over the rows in urlinfo and pick out location and status (this is pretty odd code, but urllib2.Request is weird) + # loop over the rows in urlinfo and pick out location and + # status (this is pretty odd code, but urllib2.Request is weird) for row in urlinfo.split("\n"): if row.find("Status") != -1: status = row diff --git a/plugins/google.py b/plugins/google.py index 7293099..282649a 100755 --- a/plugins/google.py +++ b/plugins/google.py @@ -1,28 +1,32 @@ import random - -from util import hook, http +from util import hook +from util import http def api_get(kind, query): + """Use the RESTful Google Search API""" url = 'http://ajax.googleapis.com/ajax/services/search/%s?' \ 'v=1.0&safe=off' return http.get_json(url % kind, q=query) +@hook.command('image') +@hook.command('gis') @hook.command -def gis(inp): +def googleimage(inp): ".gis -- Returns first Google Image result (Safesearch off)." parsed = api_get('images', inp) if not 200 <= parsed['responseStatus'] < 300: - raise IOError('error searching for images: %d: %s' % ( - parsed['responseStatus'], '')) + raise IOError('error searching for images: %d: %s' % ( \ + parsed['responseStatus'], '')) if not parsed['responseData']['results']: return 'no images found' return random.choice(parsed['responseData']['results'][:10])\ - ['unescapedUrl'] # squares is dumb + + ['unescapedUrl'] +@hook.command('search') @hook.command('g') @hook.command def google(inp): @@ -31,7 +35,7 @@ def google(inp): parsed = api_get('web', inp) if not 200 <= parsed['responseStatus'] < 300: raise IOError('error searching for pages: %d: %s' % ( - parsed['responseStatus'], '')) + parsed['responseStatus'], '')) if not parsed['responseData']['results']: return 'No results found.' diff --git a/plugins/gtime.py b/plugins/gtime.py deleted file mode 100755 index bfa9336..0000000 --- a/plugins/gtime.py +++ /dev/null @@ -1,33 +0,0 @@ -import re - -from util import hook, http -from BeautifulSoup import BeautifulSoup - - -@hook.command("time") -def clock(inp, say=None): - ".time -- Gets the time in " - - white_re = re.compile(r'\s+') - tags_re = re.compile(r'<[^<]*?>') - - page = http.get('http://www.google.com/search', q="time in " + inp) - - soup = BeautifulSoup(page) - - response = soup.find('td', {'style' : 'font-size:medium'}) - - if response is None: - return "Could not get the time for " + inp + "!" - - output = response.renderContents() - - output = ' '.join(output.splitlines()) - output = output.replace("\xa0", ",") - - output = white_re.sub(' ', output.strip()) - output = tags_re.sub('\x02', output.strip()) - - output = output.decode('utf-8', 'ignore') - - return output diff --git a/plugins/hash.py b/plugins/hash.py index 48d291f..d00731c 100755 --- a/plugins/hash.py +++ b/plugins/hash.py @@ -1,26 +1,31 @@ import hashlib from util import hook + @hook.command def md5(inp): ".hash -- Returns a md5 hash of ." return hashlib.md5(inp).hexdigest() + @hook.command def sha1(inp): ".hash -- Returns a sha1 hash of ." return hashlib.sha1(inp).hexdigest() + @hook.command def sha256(inp): ".hash -- Returns a sha256 hash of ." return hashlib.sha256(inp).hexdigest() + @hook.command def sha512(inp): ".hash -- Returns a sha512 hash of ." return hashlib.sha512(inp).hexdigest() + @hook.command def hash(inp): ".hash -- Returns hashes of ." diff --git a/plugins/help.py b/plugins/help.py index 59e351f..ceb67c3 100755 --- a/plugins/help.py +++ b/plugins/help.py @@ -1,7 +1,7 @@ import re - from util import hook + @hook.command(autohelp=False) def help(inp, input=None, bot=None, say=None, notice=None): ".help -- Gives a list of commands/help for a command." @@ -12,13 +12,16 @@ def help(inp, input=None, bot=None, say=None, notice=None): for command, (func, args) in bot.commands.iteritems(): fn = re.match(r'^plugins.(.+).py$', func._filename) if fn.group(1).lower() not in disabled: - if command not in disabled_comm: - if func.__doc__ is not None: - if func in funcs: - if len(funcs[func]) < len(command): + if not args.get('adminonly', False) or\ + input.nick in bot.config["admins"] or\ + input.mask in bot.config["admins"]: + if command not in disabled_comm: + if func.__doc__ is not None: + if func in funcs: + if len(funcs[func]) < len(command): + funcs[func] = command + else: funcs[func] = command - else: - funcs[func] = command commands = dict((value, key) for key, value in funcs.iteritems()) @@ -30,17 +33,17 @@ def help(inp, input=None, bot=None, say=None, notice=None): well.append(x) well.sort() for x in well: - if len(out[0]) + len(str(x)) > 440: + if len(out[0]) + len(str(x)) > 405: out[1] += " " + str(x) else: out[0] += " " + str(x) - + notice("Commands I recognise: " + out[0][1:]) if out[1]: notice(out[1][1:]) - notice("For detailed help, do '.help ' where is the " + - "name of the command you want help for.") - + notice("For detailed help, do '.help ' where "\ + "is the name of the command you want help for.") + else: if inp in commands: notice(commands[inp].__doc__) diff --git a/plugins/ignore.py b/plugins/ignore.py new file mode 100755 index 0000000..662531f --- /dev/null +++ b/plugins/ignore.py @@ -0,0 +1,62 @@ +import json +from util import hook + + +@hook.sieve +def ignoresieve(bot, input, func, type, args): + """ blocks input from ignored channels/nicks """ + ignorelist = bot.config["plugins"]["ignore"]["ignored"] + # don't block input to event hooks + if type == "event": + return input + if input.chan in ignorelist or\ + input.nick in ignorelist or\ + input.host in ignorelist or\ + input.mask in ignorelist: + if input.command == "PRIVMSG" and input.lastparam[1:] == "unignore": + return input + else: + return None + return input + + +@hook.command(autohelp=False) +def ignored(inp, notice=None, bot=None): + ".ignored -- Lists ignored channels/nicks/hosts." + ignorelist = bot.config["plugins"]["ignore"]["ignored"] + if ignorelist: + notice("Ignored channels/nicks/hosts are: %s" % ", ".join(ignorelist)) + else: + notice("No channels/nicks/hosts are currently ignored.") + return + + +@hook.command(adminonly=True) +def ignore(inp, notice=None, bot=None, config=None): + ".ignore -- Makes the bot ignore ." + target = inp.lower() + ignorelist = bot.config["plugins"]["ignore"]["ignored"] + if target in ignorelist: + notice("%s is already ignored." % target) + else: + notice("%s has been ignored." % target) + ignorelist.append(target) + ignorelist.sort() + json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) + return + + +@hook.command(adminonly=True) +def unignore(inp, notice=None, bot=None, config=None): + ".unignore -- Makes the bot listen to"\ + " ." + target = inp.lower() + ignorelist = bot.config["plugins"]["ignore"]["ignored"] + if target in ignorelist: + notice("%s has been unignored." % target) + ignorelist.remove(target) + ignorelist.sort() + json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2) + else: + notice("%s is not ignored." % target) + return diff --git a/plugins/insult.py b/plugins/insult.py deleted file mode 100755 index 3f0c62d..0000000 --- a/plugins/insult.py +++ /dev/null @@ -1,49 +0,0 @@ -from util import hook -import re -import random - -insults = ["You are the son of a motherless ogre.", - "Your mother was a hamster and your father smelled of elderberries.", - "I once owned a dog that was smarter than you. ", - "Go climb a wall of dicks.", - "You fight like a dairy farmer.", - "I've spoken to apes more polite than you.", - "Go and boil your bottom! Son of a silly person! ", - "I fart in your general direction.", - "Go away or I shall taunt you a second time. ", - "Shouldn't you have a license for being that ugly?", - "Calling you an idiot would be an insult to all the stupid people.", - "Why don't you slip into something more comfortable...like a coma.", - "Well, they do say opposites attact...so I sincerely hope you meet somebody who is attractive, honest, intelligent, and cultured..", - "Are you always this stupid or are you just making a special effort today?", - "Yo momma so fat when she sits around the house she sits AROUND the house.", - "Yo momma so ugly she made an onion cry.", - "Is your name Maple Syrup? It should be, you sap.", - "Bite my shiny metal ass!", - "Up yours, meatbag.", - "Jam a bastard in it you crap!", - "Don't piss me off today, I'm running out of places to hide to bodies", - "Why don't you go outside and play hide and go fuck yourself", - "I'll use small words you're sure to understand, you warthog-faced buffoon.", - "You are a sad, strange little man, and you have my pity.", - "Sit your five dollar ass down before I make change.", - "What you've just said is one of the most insanely idiotic things I've ever heard. Everyone in this room is now dumber for having listened to it. May God have mercy on your soul.", - "Look up Idiot in the dictionary. Know what you'll find? The definition of the word IDIOT, which you are.", - "You're dumber than a bag of hammers.", - "Why don't you go back to your home on Whore Island?", - "If I had a dick this is when I'd tell you to suck it.", - "Go play in traffic.", - "The village called, they want their idiot back."] - -@hook.command(autohelp=False) -def insult(inp, nick=None, say=None, input=None): - ".insult -- Makes the bot insult ." - - msg = "(" + nick + ") " + random.choice(insults) - if re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()) and inp != "": - msg = "(@" + inp + ") " + random.choice(insults) - - if inp == input.conn.nick.lower() or inp == "itself": - msg = "*stares at " + nick + "*" - - say(msg) diff --git a/plugins/kill.py b/plugins/kill.py deleted file mode 100755 index e5dfb05..0000000 --- a/plugins/kill.py +++ /dev/null @@ -1,42 +0,0 @@ -from util import hook -import re -import random - -kills = ["rips off 's and leaves them to die.", - "grabs 's head and rips it clean off their body.", - "grabs a machine gun and riddles 's body with bullets.", - "gags and ties then throws them off a bridge.", - "crushes with a huge spiked boulder.", - "rams a rocket launcher up 's ass and lets off a few rounds.", - "crushes 's skull in with a spiked mace.", - "feeds to an owlbear.", - "puts into a sack, throws the sack in the river, and hurls the river into space.", - "goes bowling with 's head.", - "sends to /dev/null!", - "feeds coke and mentos till they pop!"] - -body = ['head', - 'arms', - 'leg', - 'arm', - '"special parts"'] - -@hook.command -def kill(inp, me=None, nick=None, input=None, notice=None): - ".kill -- Makes the bot kill ." - inp = inp.strip() - - if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): - notice("Invalid username!") - return - - if inp == input.conn.nick.lower() or inp == "itself": - kill = random.choice(kills) - kill = re.sub ('', nick, kill) - msg = re.sub ('', random.choice(body), kill) - else: - kill = random.choice(kills) - kill = re.sub ('', inp, kill) - msg = re.sub ('', random.choice(body), kill) - - me(msg) diff --git a/plugins/lart.py b/plugins/lart.py deleted file mode 100755 index 7fc0754..0000000 --- a/plugins/lart.py +++ /dev/null @@ -1,118 +0,0 @@ -from util import hook -import re -import random - -larts = ["swaps 's shampoo with glue", - "installs windows on 's machine", - "forces to use perl for 3 weeks", - "registers 's name with 50 known spammers", - "resizes 's console to 40x24", - "takes 's drink", - "dispenses 's email address to a few hundred 'bulk mailing services'", - "pokes in the eye", - "beats senseless with a 50lb Linux manual", - "cats /dev/random into 's ear", - "signs up for AOL", - "enrolls in Visual Basic 101", - "sporks ", - "drops a truckload of support tickets on ", - "judo chops ", - "sets 's resolution to 800x600", - "formats 's harddrive to fat12", - "rm -rf's ", - "stabs ", - "steals 's mojo", - "strangles with a doohicky mouse cord", - "whacks with the cluebat", - "sells on EBay", - "uses as a biological warfare study", - "uses the 'Customer Appreciation Bat' on ", - "puts in the Total Perspective Vortex", - "casts into the fires of Mt. Doom", - "gives a melvin", - "turns over to Agent Smith to be 'bugged'", - "takes away 's internet connection", - "pushes past the Shoe Event Horizon", - "counts '1, 2, 5... er... 3!' and hurls the Holy Handgrenade Of Antioch at ", - "puts in a nest of camel spiders", - "makes read slashdot at -1", - "puts 'alias vim=emacs' in 's /etc/profile", - "uninstalls every web browser from 's system", - "locks in the Chateau d'If", - "signs up for getting hit on the head lessons", - "makes try to set up a Lexmark printer", - "fills 's eyedrop bottle with lime juice", - "casts into the fires of Mt. Doom.", - "gives a Flying Dutchman", - "rips off 's arm, and uses it to beat them to death", - "pierces 's nose with a rusty paper hole puncher", - "pokes with a rusty nail", - "puts sugar between 's bedsheets", - "pours sand into 's breakfast", - "mixes epoxy into 's toothpaste", - "puts Icy-Hot in 's lube container", - "straps to a chair, and plays a endless low bitrate MP3 loop of \"the world's most annoying sound\" from \"Dumb and Dumber\"", - "tells Dr. Dre that was talking smack", - "forces to use a Commodore 64 for all their word processing", - "smacks in the face with a burlap sack full of broken glass", - "puts in a room with several heavily armed manic depressives", - "makes watch reruns of \"Blue's Clues\"", - "puts lye in 's coffee", - "tattoos the Windows symbol on 's ass", - "lets Borg have his way with ", - "signs up for line dancing classes at the local senior center", - "wakes out of a sound sleep with some brand new nipple piercings", - "gives a 2 guage Prince Albert", - "forces to eat all their veggies", - "covers 's toilet paper with lemon-pepper", - "fills 's ketchup bottle with Dave's Insanity sauce", - "forces to stare at an incredibly frustrating and seemingly neverending IRC political debate", - "knocks two of 's teeth out with a 2x4", - "removes debian from 's system", - "uses 's iPod for skeet shooting practice", - "gives 's phone number to Borg", - "posts 's IP, username, and password on 4chan", - "forces to use words like 'irregardless' and 'administrate' (thereby sounding like a real dumbass)", - "tickles until they wet their pants and pass out", - "replaces 's KY with elmer's clear wood glue", - "replaces 's TUMS with alka-seltzer tablets", - "squeezes habanero pepper juice into 's tub of vaseline", - "Forces to learn the Win32 API", - "gives an atomic wedgie", - "ties to a chair and forces them to listen to 'N Sync at full blast", - "forces to use notepad for text editing", - "frowns at really really hard", - "jabs a hot lighter into 's eye sockets", - "forces to browse the web with IE6", - "takes out at the knees with a broken pool cue", - "forces to listen to emo music", - "lets a few creepers into 's house", - "signs up for the Iowa State Ferret Legging Championship", - "attempts to hotswap 's RAM", - "dragon punches ", - "puts track spikes into 's side", - "replaces 's Astroglide with JB Weld", - "replaces 's stress pills with rat poison pellets", - "replaces s crotch itch cream with Nair", - "does the Australian Death Grip on ", - "dances upon the grave of 's ancestors.", - "farts in 's general direction", - "flogs with stinging neddle", - "assigns all of the permissions tickets on the BeastNode support system to ", - "hands a poison ivy joint"] - -@hook.command -def lart(inp, me=None, nick=None, input=None, notice=None): - ".lart -- Makes the bot LART ." - inp = inp.strip() - - if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): - notice("Invalid username!") - return - - if inp == input.conn.nick.lower() or inp == "itself": - msg = re.sub ('', nick, random.choice(larts)) - else: - msg = re.sub ('', inp, random.choice(larts)) - - me(msg) diff --git a/plugins/lastfm.py b/plugins/lastfm.py index 9e7d508..d910ea7 100755 --- a/plugins/lastfm.py +++ b/plugins/lastfm.py @@ -1,15 +1,16 @@ -# Upgraded with tables/cacheing by ChauffeR of #freebnc on irc.esper.net +# Upgraded with tables/caching by ChauffeR of #freebnc on irc.esper.net from util import hook, http -@hook.command('l') -@hook.command('lfm') -@hook.command + +@hook.command('l', autohelp=False) +@hook.command(autohelp=False) def lastfm(inp, nick='', say=None, db=None, bot=None): + ".lastfm [user] -- Displays the now playing (or last played) "\ + "track of LastFM user [user]." if inp: user = inp else: user = nick - ".lastfm -- Displays the now playing (or recent) tracks of LastFM user ." db.execute("create table if not exists lastfm(nick primary key, acc)") sql = db.execute("select acc from lastfm where nick=lower(?)", (nick,)).fetchone(); api_url = "http://ws.audioscrobbler.com/2.0/?format=json" @@ -37,7 +38,7 @@ def lastfm(inp, nick='', say=None, db=None, bot=None): if inp: # specified a user name return "error: %s" % response["message"] else: - return "your nick is not a LastFM account. try '.lastfm '" + return "your nick is not a LastFM account. try '.lastfm [user]'" tracks = response["recenttracks"]["track"] diff --git a/plugins/log.py b/plugins/log.py index f05129e..1d37563 100755 --- a/plugins/log.py +++ b/plugins/log.py @@ -10,11 +10,12 @@ import re from util import hook -log_fds = {} # '%(net)s %(chan)s' : (filename, fd) +log_fds = {} # '%(net)s %(chan)s': (filename, fd) timestamp_format = '%H:%M:%S' -formats = {'PRIVMSG': '<%(nick)s> %(msg)s', +formats = { + 'PRIVMSG': '<%(nick)s> %(msg)s', 'PART': '-!- %(nick)s [%(user)s@%(host)s] has left %(chan)s', 'JOIN': '-!- %(nick)s [%(user)s@%(host)s] has joined %(param0)s', 'MODE': '-!- mode/%(chan)s [%(param_tail)s] by %(nick)s', @@ -22,16 +23,22 @@ formats = {'PRIVMSG': '<%(nick)s> %(msg)s', 'TOPIC': '-!- %(nick)s changed the topic of %(chan)s to: %(msg)s', 'QUIT': '-!- %(nick)s has quit [%(msg)s]', 'PING': '', - 'NOTICE': '' + 'NOTICE': '-%(nick)s- %(msg)s' } -ctcp_formats = {'ACTION': '* %(nick)s %(ctcpmsg)s'} +ctcp_formats = { +'ACTION': '* %(nick)s %(ctcpmsg)s', +'VERSION': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s', +'PING': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s', +'TIME': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s', +'FINGER': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s' +} irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])') def get_log_filename(dir, server, chan): - return os.path.join(dir, 'log', gmtime('%Y'), server, chan, + return os.path.join(dir, 'log', gmtime('%Y'), server, chan, (gmtime('%%s.%m-%d.log') % chan).lower()) diff --git a/plugins/mcitems.py b/plugins/mcitems.py new file mode 100755 index 0000000..bcfb378 --- /dev/null +++ b/plugins/mcitems.py @@ -0,0 +1,105 @@ +""" plugin by _303 (?) +""" + +from util import hook +import re +import itertools + +pattern = re.compile(r'^(?P\d+)x (?P.+?): (?P.*)$') + +recipelist = [] + + +class Recipe(object): + __slots__ = 'output', 'count', 'ingredients', 'line' + + def __init__(self, output, count, ingredients, line): + self.output = output + self.count = count + self.ingredients = ingredients + self.line = line + + def __str__(self): + return self.line + + +with open("plugins/data/recipes.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + line = line.strip() + match = pattern.match(line) + if not match: + continue + recipelist.append(Recipe(line=line, + output=match.group("name").lower(), + ingredients=match.group("ingredients"), + count=match.group("count"))) + +ids = [] + +with open("plugins/data/itemids.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + parts = line.strip().split() + id = parts[0] + name = " ".join(parts[1:]) + ids.append((id, name)) + + +@hook.command +def itemid(input, reply=None): + ".itemid -- gets the id from an item or vice versa" + input = input.lower().strip() + + if input == "": + reply("error: no input.") + return + + results = [] + + for id, name in ids: + if input == id: + results = ["\x02[%s]\x02 %s" % (id, name)] + break + elif input in name.lower(): + results.append("\x02[%s]\x02 %s" % (id, name)) + + if not len(results): + reply("No matches found.") + return + + if len(results) > 12: + reply("There are too many options, please narrow your search." + "(%s)" % len(results)) + return + + out = ", ".join(results) + + return out + + +@hook.command("craft") +@hook.command +def recipe(input, reply=None): + ".recipe/.craft -- gets the crafting recipe for an item" + input = input.lower().strip() + + results = [] + + for recipe in recipelist: + if input in recipe.output: + results.append(recipe.line) + + if not len(results): + reply("No matches found.") + return + + if len(results) > 3: + reply("There are too many options, please narrow your search." + "(%s)" % len(results)) + return + + for result in results: + reply(result) diff --git a/plugins/mcping.py b/plugins/mcping.py deleted file mode 100755 index 5a0e0a1..0000000 --- a/plugins/mcping.py +++ /dev/null @@ -1,36 +0,0 @@ -import socket -import struct - - -from util import hook - -def get_info(host, port): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect((host, port)) - sock.send('\xfe') - response = sock.recv(1) - if response != '\xff': - return "Server gave invalid response: "+repr(response) - length = struct.unpack('!h', sock.recv(2))[0] - values = sock.recv(length*2).decode('utf-16be').split(u'\xa7') - sock.close() - return "%s - %d/%d players" % (values[0], int(values[1]), int(values[2])) - except: - return "Error pinging "+host+":"+str(port)+", is it up? Double-check your address!" - -@hook.command -def mcping(inp): - ".mcping server[:port] - Ping a Minecraft server to check status." - inp = inp.strip().split(" ")[0] - - if ":" in inp: - host, port = inp.split(":", 1) - try: - port = int(port) - except: - return "Invalid port!" - else: - host = inp - port = 25565 - return get_info(host, port) diff --git a/plugins/mctools.py b/plugins/mctools.py index fa2c934..19f1e95 100755 --- a/plugins/mctools.py +++ b/plugins/mctools.py @@ -1,5 +1,27 @@ -from util import hook, http +from util import hook +from util import http import string +import socket +import struct + + +def mcping_connect(host, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((host, port)) + sock.send('\xfe') + response = sock.recv(1) + if response != '\xff': + return "Server gave invalid response: " + repr(response) + length = struct.unpack('!h', sock.recv(2))[0] + values = sock.recv(length * 2).decode('utf-16be').split(u'\xa7') + sock.close() + return "%s - %d/%d players"\ + % (values[0], int(values[1]), int(values[2])) + except: + return "Error pinging " + host + ":" + str(port) +\ + ", is it up? Double-check your address!" + @hook.command(autohelp=False) def mcstatus(inp, bot=None): @@ -9,30 +31,58 @@ def mcstatus(inp, bot=None): if password is None: return "error: no login set" - login = http.get("https://login.minecraft.net/?user="+username+"&password="+password+"&version=13") + login = http.get("https://login.minecraft.net/?user="\ + + username + "&password=" + password + "&version=13") if username.lower() in login.lower(): return "Minecraft login servers appear to be online!" else: return "Minecraft login servers appear to be offline!" + @hook.command def mclogin(inp, say=None): - ".mclogin -- Attempts to log in to Minecrat with and (This is NOT logged)." + ".mclogin -- Attempts to log in to Minecraft with "\ + " and (This is NOT logged)." inp = inp.split(" ") username = inp[0] password = inp[1] say("Attempting to log in using " + username) - login = http.get("https://login.minecraft.net/?user=" + username + "&password=" + password + "&version=13") + login = http.get("https://login.minecraft.net/?user="\ + + username + "&password=" + password + "&version=13") if username.lower() in login.lower(): return "I logged in with " + username else: - return "I couldn't log in using " + username + ", either the password is wrong or minecraft login servers are down!" + return "I couldn't log in using " + username + ", either"\ + " the password is wrong or Minecraft login servers are down!" + @hook.command def mcpaid(inp): - ".mcpaid -- Checks if has a premium Minecraft account." + ".mcpaid -- Checks if has a "\ + "premium Minecraft account." login = http.get("http://www.minecraft.net/haspaid.jsp?user=" + inp) if "true" in login: - return "The account \'" + inp + "\' is a premium Minecraft account!" + return "The account \'" + inp + "\' is a "\ + "premium Minecraft account!" else: - return "The account \'" + inp + "\' is not a premium Minecraft account!" + return "The account \'" + inp + "\' is not a "\ + "premium Minecraft account!" + +from util import hook + + +@hook.command +def mcping(inp): + ".mcping [:port] - Ping a Minecraft server to check status." + inp = inp.strip().split(" ")[0] + + if ":" in inp: + host, port = inp.split(":", 1) + try: + port = int(port) + except: + return "error: invalid port!" + else: + host = inp + port = 25565 + return mcping_connect(host, port) diff --git a/plugins/mem.py b/plugins/mem.py deleted file mode 100755 index c480291..0000000 --- a/plugins/mem.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import re - -from util import hook - - -@hook.command(autohelp=False) -def mem(inp): - ".mem -- Display the bot's current memory usage." - - if os.name == 'posix': - status_file = open("/proc/%d/status" % os.getpid()).read() - line_pairs = re.findall(r"^(\w+):\s*(.*)\s*$", status_file, re.M) - status = dict(line_pairs) - keys = 'VmSize VmLib VmData VmExe VmRSS VmStk'.split() - return ', '.join(key + ':' + status[key] for key in keys) - - elif os.name == 'nt': - cmd = "tasklist /FI \"PID eq %s\" /FO CSV /NH" % os.getpid() - out = os.popen(cmd).read() - - total = 0 - for amount in re.findall(r'([,0-9]+) K', out): - total += int(amount.replace(',', '')) - - return 'memory usage: %d kB' % total - - return mem.__doc__ diff --git a/plugins/misc.py b/plugins/misc.py index 4a82252..1a25988 100755 --- a/plugins/misc.py +++ b/plugins/misc.py @@ -1,6 +1,7 @@ import re import socket import subprocess +import platform import time from util import hook, http @@ -51,7 +52,7 @@ def onjoin(paraml, conn=None, bot=None): time.sleep(1) # HTTP Useragent - http.ua_cloudbot = 'CloudBot - http://git.io/cloudbot' + http.ua_cloudbot = 'CloudBot - http://git.io/cloudbotirc' # Stay-alive code stayalive = conn.conf.get('stayalive') diff --git a/plugins/namegen.py b/plugins/namegen.py index e3d5683..ba82033 100755 --- a/plugins/namegen.py +++ b/plugins/namegen.py @@ -1,9 +1,9 @@ # Plugin by Lukeroge -# - -from util import hook, molecular +from util import hook +from util import molecular import unicodedata + @hook.command() def namegen(inp, say=None, nick=None, input=None, notice=None): ".namegen [modules] -- Generates some names using the chosen modules. '.namegen list' will display a list of all modules." diff --git a/plugins/op.py b/plugins/op.py new file mode 100755 index 0000000..28e96ad --- /dev/null +++ b/plugins/op.py @@ -0,0 +1,110 @@ +# Plugin made by Lukeroge and neersighted +from util import hook + + +@hook.command(adminonly=True) +def topic(inp, conn=None, chan=None, notice=None): + ".topic [channel] -- Change the topic of a channel." + inp = inp.split(" ") + if inp[0][0] == "#": + out = "PRIVMSG %s :%s" % (inp[0], message) + else: + out = "TOPIC %s :%s" % (chan, message) + conn.send(out) + + +@hook.command(adminonly=True) +def kick(inp, chan=None, conn=None, notice=None): + ".kick [channel] [reason] -- Makes the bot kick in [channel] "\ + "If [channel] is blank the bot will kick the in "\ + "the channel the command was used in." + inp = inp.split(" ") + if inp[0][0] == "#": + chan = inp[0] + user = inp[1] + out = "KICK %s %s" % (chan, user) + if len(inp) > 2: + reason = "" + for x in inp[2:]: + reason = reason + x + " " + reason = reason[:-1] + out = out + " :" + reason + else: + user = inp[0] + out = "KICK %s %s" % (chan, user) + if len(inp) > 1: + reason = "" + for x in inp[1:]: + reason = reason + x + " " + reason = reason[:-1] + out = out + " :" + reason + + notice("Attempting to kick %s from %s..." % (user, chan)) + conn.send(out) + + +@hook.command(adminonly=True) +def ban(inp, conn=None, chan=None, notice=None): + ".ban [channel] -- Makes the bot ban in [channel]. "\ + "If [channel] is blank the bot will ban in "\ + "the channel the command was used in." + inp = inp.split(" ") + if inp[0][0] == "#": + chan = inp[0] + user = inp[1] + out = "MODE %s +b %s" % (chan, user) + else: + user = inp[0] + out = "MODE %s +b %s" % (chan, user) + notice("Attempting to ban %s from %s..." % (user, chan)) + conn.send(out) + + +@hook.command(adminonly=True) +def unban(inp, conn=None, chan=None, notice=None): + ".unban [channel] -- Makes the bot unban in [channel]. "\ + "If [channel] is blank the bot will unban in "\ + "the channel the command was used in." + inp = inp.split(" ") + if inp[0][0] == "#": + chan = inp[0] + user = inp[1] + out = "MODE %s -b %s" % (chan, user) + else: + user = inp[0] + out = "MODE %s -b %s" % (chan, user) + notice("Attempting to unban %s from %s..." % (user, chan)) + conn.send(out) + + +@hook.command(adminonly=True) +def kickban(inp, chan=None, conn=None, notice=None): + ".kickban [channel] [reason] -- Makes the bot kickban in [channel] "\ + "If [channel] is blank the bot will kickban the in "\ + "the channel the command was used in." + inp = inp.split(" ") + if inp[0][0] == "#": + chan = inp[0] + user = inp[1] + out1 = "MODE %s +b %s" % (chan, user) + out2 = "KICK %s %s" % (chan, user) + if len(inp) > 2: + reason = "" + for x in inp[2:]: + reason = reason + x + " " + reason = reason[:-1] + out = out + " :" + reason + else: + user = inp[0] + out1 = "MODE %s +b %s" % (chan, user) + out2 = "KICK %s %s" % (chan, user) + if len(inp) > 1: + reason = "" + for x in inp[1:]: + reason = reason + x + " " + reason = reason[:-1] + out = out + " :" + reason + + notice("Attempting to kickban %s from %s..." % (user, chan)) + conn.send(out1) + conn.send(out2) diff --git a/plugins/password.py b/plugins/password.py index 08ea501..f1fcb08 100755 --- a/plugins/password.py +++ b/plugins/password.py @@ -40,7 +40,7 @@ def gen_password(types): okay.append(x) else: needs_def = 1 - #defualts to lowercase alpha password if no arguments are found + #defaults to lowercase alpha password if no arguments are found if needs_def == 1: for x in string.ascii_lowercase: okay.append(x) @@ -53,10 +53,6 @@ def gen_password(types): @hook.command def password(inp, notice=None): - ".password [types] -- Generates a password of (default 10). [types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the types, eg. 'numbers symbols'" + ".password [types] -- Generates a password of (default 10). [types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the types, eg. 'numbers symbols'" password = gen_password(inp) - short = "error: input too short" - penis = ["penis", "mypenis", "dick", "mydick"] - if inp in penis: - return short notice(password) diff --git a/plugins/ping.py b/plugins/ping.py index 310ace0..67f76dd 100755 --- a/plugins/ping.py +++ b/plugins/ping.py @@ -2,18 +2,24 @@ from util import hook import subprocess import re +import os + +ping_regex = re.compile(r"(\d+.\d+)/(\d+.\d+)/(\d+.\d+)/(\d+.\d+)") @hook.command def ping(inp, reply=None): ".ping [count] -- Pings [count] times." + if os.name == "nt": + return "Sorry, this command is not supported on Windows systems." + args = inp.split(' ') host = args[0] + # check for a seccond argument and set the ping count if len(args) > 1: - count = args[1] - count = int(count) + count = int(args[1]) if count > 20: count = 20 else: @@ -25,14 +31,10 @@ def ping(inp, reply=None): reply("Attempting to ping %s %s times..." % (host, count)) - pingcmd = subprocess.check_output("ping -c "\ - + count + " " + host, shell=True) - if 'request timed out' in pingcmd or 'unknown host' in pingcmd: + pingcmd = subprocess.check_output(["ping", "-c", count, host]) + if "request timed out" in pingcmd or "unknown host" in pingcmd: return "error: could not ping host" else: - m = re.search(r"rtt min/avg/max/mdev = "\ - "(\d+.\d+)/(\d+.\d+)/(\d+.\d+)/(\d+.\d+)", pingcmd) - return "min: %sms, max: %sms, average: %sms, range: %sms, count: %s"\ + m = re.search(ping_regex, pingcmd) + return "min: %sms, max: %sms, average: %sms, range: %sms, count: %s" \ % (m.group(1), m.group(3), m.group(2), m.group(4), count) - - diff --git a/plugins/potato.py b/plugins/potato.py index cf34686..5712a35 100755 --- a/plugins/potato.py +++ b/plugins/potato.py @@ -4,6 +4,7 @@ import random potatoes = ['AC Belmont', 'AC Blue Pride', 'AC Brador', 'AC Chaleur', 'AC Domino', 'AC Dubuc', 'AC Glacier Chip', 'AC Maple Gold', 'AC Novachip', 'AC Peregrine Red', 'AC Ptarmigan', 'AC Red Island', 'AC Saguenor', 'AC Stampede Russet', 'AC Sunbury', 'Abeille', 'Abnaki', 'Acadia', 'Acadia Russet', 'Accent', 'Adirondack Blue', 'Adirondack Red', 'Adora', 'Agria', 'All Blue', 'All Red', 'Alpha', 'Alta Russet', 'Alturas Russet', 'Amandine', 'Amisk', 'Andover', 'Anoka', 'Anson', 'Aquilon', 'Arran Consul', 'Asterix', 'Atlantic', 'Austrian Crescent', 'Avalanche', 'Banana', 'Bannock Russet', 'Batoche', 'BeRus', 'Belle De Fonteney', 'Belleisle', 'Bintje', 'Blossom', 'Blue Christie', 'Blue Mac', 'Brigus', 'Brise du Nord', 'Butte', 'Butterfinger', 'Caesar', 'CalWhite', 'CalRed', 'Caribe', 'Carlingford', 'Carlton', 'Carola', 'Cascade', 'Castile', 'Centennial Russet', 'Century Russet', 'Charlotte', 'Cherie', 'Cherokee', 'Cherry Red', 'Chieftain', 'Chipeta', 'Coastal Russet', 'Colorado Rose', 'Concurrent', 'Conestoga', 'Cowhorn', 'Crestone Russet', 'Crispin', 'Cupids', 'Daisy Gold', 'Dakota Pearl', 'Defender', 'Delikat', 'Denali', 'Desiree', 'Divina', 'Dundrod', 'Durango Red', 'Early Rose', 'Elba', 'Envol', 'Epicure', 'Eramosa', 'Estima', 'Eva', 'Fabula', 'Fambo', 'Fremont Russet', 'French Fingerling', 'Frontier Russet', 'Fundy', 'Garnet Chile', 'Gem Russet', 'GemStar Russet', 'Gemchip', 'German Butterball', 'Gigant', 'Goldrush', 'Granola', 'Green Mountain', 'Haida', 'Hertha', 'Hilite Russet', 'Huckleberry', 'Hunter', 'Huron', 'IdaRose', 'Innovator', 'Irish Cobbler', 'Island Sunshine', 'Ivory Crisp', 'Jacqueline Lee', 'Jemseg', 'Kanona', 'Katahdin', 'Kennebec', "Kerr's Pink", 'Keswick', 'Keuka Gold', 'Keystone Russet', 'King Edward VII', 'Kipfel', 'Klamath Russet', 'Krantz', 'LaRatte', 'Lady Rosetta', 'Latona', 'Lemhi Russet', 'Liberator', 'Lili', 'MaineChip', 'Marfona', 'Maris Bard', 'Maris Piper', 'Matilda', 'Mazama', 'McIntyre', 'Michigan Purple', 'Millenium Russet', 'Mirton Pearl', 'Modoc', 'Mondial', 'Monona', 'Morene', 'Morning Gold', 'Mouraska', 'Navan', 'Nicola', 'Nipigon', 'Niska', 'Nooksack', 'NorValley', 'Norchip', 'Nordonna', 'Norgold Russet', 'Norking Russet', 'Norland', 'Norwis', 'Obelix', 'Ozette', 'Peanut', 'Penta', 'Peribonka', 'Peruvian Purple', 'Pike', 'Pink Pearl', 'Prospect', 'Pungo', 'Purple Majesty', 'Purple Viking', 'Ranger Russet', 'Reba', 'Red Cloud', 'Red Gold', 'Red La Soda', 'Red Pontiac', 'Red Ruby', 'Red Thumb', 'Redsen', 'Rocket', 'Rose Finn Apple', 'Rose Gold', 'Roselys', 'Rote Erstling', 'Ruby Crescent', 'Russet Burbank', 'Russet Legend', 'Russet Norkotah', 'Russet Nugget', 'Russian Banana', 'Saginaw Gold', 'Sangre', 'Sant�', 'Satina', 'Saxon', 'Sebago', 'Shepody', 'Sierra', 'Silverton Russet', 'Simcoe', 'Snowden', 'Spunta', "St. John's", 'Summit Russet', 'Sunrise', 'Superior', 'Symfonia', 'Tolaas', 'Trent', 'True Blue', 'Ulla', 'Umatilla Russet', 'Valisa', 'Van Gogh', 'Viking', 'Wallowa Russet', 'Warba', 'Western Russet', 'White Rose', 'Willamette', 'Winema', 'Yellow Finn', 'Yukon Gold'] + @hook.command def potato(inp, me=None, input=None): ".potato - Makes a tasty little potato." diff --git a/plugins/quote.py b/plugins/quote.py index 04a5237..d1a49b6 100755 --- a/plugins/quote.py +++ b/plugins/quote.py @@ -4,30 +4,27 @@ import time from util import hook + def format_quote(q, num, n_quotes): - "Returns a formatted string of a quote" + """Returns a formatted string of a quote""" ctime, nick, msg = q return "[%d/%d] <%s> %s" % (num, n_quotes, nick, msg) + def create_table_if_not_exists(db): - "Creates an empty quote table if one does not already exist" - db.execute('''CREATE TABLE IF NOT EXISTS quote ( - chan, - nick, - add_nick, - msg, - time real, - deleted default 0, - PRIMARY KEY (chan, nick, msg) - )''') + """Creates an empty quote table if one does not already exist""" + db.execute("create table if not exists quote" + "(chan, nick, add_nick, msg, time real, deleted default 0, " + "primary key (chan, nick, msg))") db.commit() + def add_quote(db, chan, nick, add_nick, msg): - "Adds a quote to a nick, returns message string" + """Adds a quote to a nick, returns message string""" try: - db.execute('''INSERT OR FAIL INTO quote - (chan, nick, add_nick, msg, time) + db.execute('''INSERT OR FAIL INTO quote + (chan, nick, add_nick, msg, time) VALUES(?,?,?,?,?)''', (chan, nick, add_nick, msg, time.time())) db.commit() @@ -35,33 +32,53 @@ def add_quote(db, chan, nick, add_nick, msg): return "Message already stored, doing nothing." return "Quote added." + def del_quote(db, chan, nick, add_nick, msg): - "Deletes a quote from a nick" - db.execute('''UPDATE quote - SET deleted = 1 - WHERE chan=? - AND lower(nick)=lower(?) - AND msg=msg''') + """Deletes a quote from a nick""" + db.execute('''UPDATE quote SET deleted = 1 WHERE + chan=? AND lower(nick)=lower(?) AND msg=msg''') db.commit() + def get_quote_num(num, count, name): - "Returns the quote number desired from the database" + """Returns the quote number to fetch from the DB""" if num: # Make sure num is a number if it isn't false num = int(num) - if count == 0: # If there are no quotes in the database, raise an Exception. + if count == 0: # Error on no quotes raise Exception("No quotes found for %s." % name) - if num and num < 0: # If the selected quote is less than 0, count back if possible. + if num and num < 0: # Count back if possible num = count + num + 1 if num + count > -1 else count + 1 - if num and num > count: # If a number is given and and there are not enough quotes, raise an Exception. - raise Exception("I only have %d quote%s for %s." % (count, ('s', '')[count == 1], name)) + if num and num > count: # If there are not enough quotes, raise an error + raise Exception("I only have %d quote%s for %s."\ + % (count, ('s', '')[count == 1], name)) if num and num == 0: # If the number is zero, set it to one num = 1 if not num: # If a number is not given, select a random one num = random.randint(1, count) return num -def get_quote_by_nick(db, chan, nick, num=False): - "Returns a formatted quote from a nick, random or selected by number" + +def get_quote_by_nick(db, nick, num=False): + """Returns a formatted quote from a nick, random or selected by number""" + count = db.execute('''SELECT COUNT(*) FROM quote WHERE deleted != 1 + AND lower(nick) = lower(?)''', [nick]).fetchall()[0][0] + + try: + num = get_quote_num(num, count, nick) + except Exception as error_message: + return error_message + + quote = db.execute('''SELECT time, nick, msg + FROM quote + WHERE deleted != 1 + AND lower(nick) = lower(?) + ORDER BY time + LIMIT ?, 1''', (nick, (num - 1))).fetchall()[0] + return format_quote(quote, num, count) + + +def get_quote_by_nick_chan(db, chan, nick, num=False): + """Returns a formatted quote from a nick in a channel, random or selected by number""" count = db.execute('''SELECT COUNT(*) FROM quote WHERE deleted != 1 @@ -79,11 +96,12 @@ def get_quote_by_nick(db, chan, nick, num=False): AND chan = ? AND lower(nick) = lower(?) ORDER BY time - LIMIT ?, 1''', (chan, nick, (num-1))).fetchall()[0] + LIMIT ?, 1''', (chan, nick, (num - 1))).fetchall()[0] return format_quote(quote, num, count) -def get_quote_by_chan(db, chan, num=False): - "Returns a formatted quote from a channel, random or selected by number" + +def get_quote_by_chan(db, chan, num=False): + """Returns a formatted quote from a channel, random or selected by number""" count = db.execute('''SELECT COUNT(*) FROM quote WHERE deleted != 1 @@ -94,19 +112,20 @@ def get_quote_by_chan(db, chan, num=False): except Exception as error_message: return error_message - quote = db.execute('''SELECT time, nick, msg + quote = db.execute('''SELECT time, nick, msg FROM quote WHERE deleted != 1 - AND chan = ? + AND chan = ? ORDER BY time - LIMIT ?, 1''', (chan, (num -1))).fetchall()[0] + LIMIT ?, 1''', (chan, (num - 1))).fetchall()[0] return format_quote(quote, num, count) + @hook.command('q') @hook.command def quote(inp, nick='', chan='', db=None, notice=None): ".quote [#chan] [nick] [#n]/.quote add -- Gets " \ - "random or [#n]th quote by or from <#chan>/adds quote." + "random or [#n]th quote by or from <#chan>/adds quote." create_table_if_not_exists(db) add = re.match(r"add[^\w@]+(\S+?)>?\s+(.*)", inp, re.I) @@ -119,12 +138,12 @@ def quote(inp, nick='', chan='', db=None, notice=None): elif retrieve: select, num = retrieve.groups() by_chan = True if select.startswith('#') else False - if by_chan: + if by_chan: return get_quote_by_chan(db, select, num) else: - return get_quote_by_nick(db, chan, select, num) + return get_quote_by_nick(db, select, num) elif retrieve_chan: chan, nick, num = retrieve_chan.groups() - return get_quote_by_nick(db, chan, nick, num) - + return get_quote_by_nick_chan(db, chan, nick, num) + notice(quote.__doc__) diff --git a/plugins/seen.py b/plugins/seen.py index 4be75be..d26eb8f 100755 --- a/plugins/seen.py +++ b/plugins/seen.py @@ -8,7 +8,7 @@ from util import hook, timesince def db_init(db): "check to see that our db has the the seen table and return a connection." - db.execute("create table if not exists seen(name, time, quote, chan, " + db.execute("create table if not exists seen_user(name, time, quote, chan, host, " "primary key(name, chan))") db.commit() @@ -17,9 +17,9 @@ def db_init(db): @hook.event('PRIVMSG', ignorebots=False) def seeninput(paraml, input=None, db=None, bot=None): db_init(db) - db.execute("insert or replace into seen(name, time, quote, chan)" - "values(?,?,?,?)", (input.nick.lower(), time.time(), input.msg, - input.chan)) + db.execute("insert or replace into seen_user(name, time, quote, chan, host)" + "values(?,?,?,?,?)", (input.nick.lower(), time.time(), input.msg, + input.chan, input.host)) db.commit() @@ -35,11 +35,11 @@ def seen(inp, nick='', chan='', db=None, input=None): return "Have you looked in a mirror lately?" if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): - return "I cant look up that name, its impossible to use!" + return "I can't look up that name, its impossible to use!" db_init(db) - last_seen = db.execute("select name, time, quote from seen where name" + last_seen = db.execute("select name, time, quote from seen_user where name" " like ? and chan = ?", (inp, chan)).fetchone() if last_seen: diff --git a/plugins/shorten.py b/plugins/shorten.py index 270194b..7ab07e6 100755 --- a/plugins/shorten.py +++ b/plugins/shorten.py @@ -1,42 +1,40 @@ -# Plugin by Lukeroge -# +# Plugin by Lukeroge from util import hook, http from re import match from urllib2 import urlopen, Request, HTTPError from urllib import urlencode + class ShortenError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) - + + def bitly(url, user, apikey): try: - params = urlencode({'longUrl': url, 'login': user, 'apiKey': apikey, 'format': 'json'}) + if url[:8] == "https://": + pass + elif url[:7] != "http://": + url = "http://" + url + params = urlencode({'longUrl': url, 'login': user, 'apiKey': apikey, + 'format': 'json'}) j = http.get_json("http://api.bit.ly/v3/shorten?%s" % params) if j['status_code'] == 200: return j['data']['url'] - raise ShortenError('%s'%j['status_txt']) + raise ShortenError('%s' % j['status_txt']) except (HTTPError, ShortenError): return "Could not shorten %s!" % url + @hook.command def shorten(inp, bot=None): ".shorten - Makes an j.mp/bit.ly shortlink to the url provided." - api_user = bot.config.get("api_keys", {}).get("bitly_user", None) + api_user = bot.config.get("api_keys", {}).get("bitly_user", None) api_key = bot.config.get("api_keys", {}).get("bitly_api", None) if api_key is None: return "error: no api key set" return bitly(inp, api_user, api_key) - -@hook.command -def expand(inp, bot=None): - ".expand - Gets the original URL from a shortened link." - try: - url = http.get_url(inp) - except HTTPError, e: - return "Failed to expand URL." - return url diff --git a/plugins/sieve.py b/plugins/sieve.py index 108390e..ccc4174 100755 --- a/plugins/sieve.py +++ b/plugins/sieve.py @@ -5,18 +5,14 @@ from util import hook @hook.sieve def sieve_suite(bot, input, func, kind, args): - if input.command == 'PRIVMSG' and \ - input.nick.lower()[-3:] == 'bot' and args.get('ignorebots', True): + if input.command == 'PRIVMSG' and\ + input.nick.endswith('bot') and args.get('ignorebots', True): return None if kind == "command": if input.trigger in bot.config.get('disabled_commands', []): return None - ignored = bot.config.get('ignored', []) - if input.host in ignored or input.nick in ignored: - return None - fn = re.match(r'^plugins.(.+).py$', func._filename) disabled = bot.config.get('disabled_plugins', []) if fn and fn.group(1).lower() in disabled: @@ -36,7 +32,8 @@ def sieve_suite(bot, input, func, kind, args): if args.get('adminonly', False): admins = bot.config.get('admins', []) - if input.host not in admins and input.nick not in admins: + if input.nick not in admins and input.mask not in admins: + input.notice("Sorry, you are not allowed to use this command.") return None - return input + return input \ No newline at end of file diff --git a/plugins/slap.py b/plugins/slap.py deleted file mode 100755 index a4f7b23..0000000 --- a/plugins/slap.py +++ /dev/null @@ -1,50 +0,0 @@ -from util import hook -import re -import random - -slaps = ["slaps with a ", - "slaps around a bit with a ", - "throws a at ", - "grabs a and throws it in 's face", - "holds down and repeatedly whacks them with a ", - "prods with a flaming ", - "picks up a , and whacks with it", - "ties to a chair and throws a at them", - "hits on the head with a "] - -items = ["cast iron skillet", - "large trout", - "baseball bat", - "wooden cane", - "CRT monitor", - "physics textbook", - "television", - "mau5 head", - "five tonn truck", - "roll of duct tape", - "book", - "rubber chicken", - "gold block", - "fire extinguisher", - "heavy rock", - "chunk of dirt"] - -@hook.command -def slap(inp, me=None, nick=None, input=None, notice=None): - ".slap -- Makes the bot slap ." - inp = inp.strip() - - if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()): - notice("Invalid username!") - return - - if inp == input.conn.nick.lower() or inp == "itself": - slap = random.choice(slaps) - slap = re.sub ('', nick, slap) - msg = re.sub ('', random.choice(items), slap) - else: - slap = random.choice(slaps) - slap = re.sub ('', inp, slap) - msg = re.sub ('', random.choice(items), slap) - - me(msg) diff --git a/plugins/slogan.py b/plugins/slogan.py index bc31db6..711fe6c 100755 --- a/plugins/slogan.py +++ b/plugins/slogan.py @@ -2,10 +2,12 @@ from util import hook, http, misc import re import string + def sloganize(word): bytes = http.get('http://www.sloganizer.net/en/outbound.php', slogan=word) return bytes + @hook.command("slogan") def sloganizr(inp, nick=None, say=None, input=None): ".slogan -- Makes a slogan for ." diff --git a/plugins/stock.py b/plugins/stock.py index 0afdc13..c598d9e 100755 --- a/plugins/stock.py +++ b/plugins/stock.py @@ -20,7 +20,7 @@ def stock(inp): # if we dont get a company name back, the symbol doesn't match a company if results['company'] == '': - return "unknown ticker symbol %s" % inp + return "error: unknown ticker symbol (%s)" % inp if results['change'][0] == '-': results['color'] = "5" diff --git a/plugins/system.py b/plugins/system.py new file mode 100755 index 0000000..afd9233 --- /dev/null +++ b/plugins/system.py @@ -0,0 +1,66 @@ +import os +import re +import time +import platform +from util import hook +from datetime import timedelta + + +@hook.command(autohelp=False) +def system(inp): + ".system -- Retrieves information about the host system." + hostname = platform.node() + os = platform.platform() + python_imp = platform.python_implementation() + python_ver = platform.python_version() + architecture = '-'.join(platform.architecture()) + cpu = platform.machine() + return "Hostname: \x02%s\x02, Operating System: \x02%s\x02, Python " \ + "Version: \x02%s %s\x02, Architecture: \x02%s\x02, CPU: \x02%s" \ + "\x02" % (hostname, os, python_imp, python_ver, architecture, cpu) + + +@hook.command(autohelp=False) +def memory(inp): + ".memory -- Displays the bot's current memory usage." + if os.name == "posix": + # get process info + status_file = open('/proc/self/status').read() + s = dict(re.findall(r'^(\w+):\s*(.*)\s*$', status_file, re.M)) + # get the data we need and process it + data = s['VmRSS'], s['VmSize'], s['VmPeak'], s['VmStk'], s['VmData'] + data = [float(i.replace(' kB', '')) for i in data] + strings = [str(round(i / 1024, 2)) + ' MB' for i in data] + # prepare the output + out = "Threads: \x02%s\x02, Real Memory: \x02%s\x02, Allocated Memory: \x02%s\x02, Peak " \ + "Allocated Memory: \x02%s\x02, Stack Size: \x02%s\x02, Heap " \ + "Size: \x02%s\x02" % (s['Threads'], strings[0], strings[1], strings[2], + strings[3], strings[4]) + # return output + return out + + elif os.name == "nt": + cmd = 'tasklist /FI "PID eq %s" /FO CSV /NH' % os.getpid() + out = os.popen(cmd).read() + memory = 0 + for amount in re.findall(r'([,0-9]+) K', out): + memory += int(amount.replace(',', '')) + memory = str(round(float(memory) / 1024, 2)) + return "Memory Usage: \x02%s MB\x02" % memory + + else: + return "Sorry, this command is not supported on your OS." + + +@hook.command(autohelp=False) +def uptime(inp, bot=None): + ".uptime -- Shows the bot's uptime." + uptime_raw = round(time.time() - bot.start_time) + uptime = timedelta(seconds=uptime_raw) + return "Uptime: \x02%s\x02" % uptime + + +@hook.command(autohelp=False) +def pid(inp): + ".pid -- Prints the bot's PID." + return "PID: \x02%s\x02" % os.getpid() diff --git a/plugins/tell.py b/plugins/tell.py index 9c903ad..22e0599 100755 --- a/plugins/tell.py +++ b/plugins/tell.py @@ -25,13 +25,13 @@ def get_tells(db, user_to): @hook.singlethread @hook.event('PRIVMSG') -def tellinput(paraml, input=None, db=None, bot=None): +def tellinput(paraml, input=None, notice=None, db=None, bot=None, nick=None): if 'showtells' in input.msg.lower(): return db_init(db) - tells = get_tells(db, input.nick) + tells = get_tells(db, nick) if tells: user_from, message, time, chan = tells[0] @@ -43,9 +43,9 @@ def tellinput(paraml, input=None, db=None, bot=None): reply += " (+%d more, .showtells to view)" % (len(tells) - 1) db.execute("delete from tell where user_to=lower(?) and message=?", - (input.nick, message)) + (nick, message)) db.commit() - input.notice(reply) + notice(reply) @hook.command(autohelp=False) @@ -87,7 +87,7 @@ def tell(inp, nick='', chan='', db=None, input=None, notice=None): chan = 'a pm' if user_to == user_from.lower(): - notice("Nope.") + notice("Have you looked in a mirror lately?") return if user_to.lower() == input.conn.nick.lower(): diff --git a/plugins/time.py b/plugins/time.py new file mode 100755 index 0000000..d8cd9ed --- /dev/null +++ b/plugins/time.py @@ -0,0 +1,243 @@ +# Plugin by Lukeroge with some code from Phenny + +from util import hook +from util import http +import re +import time +import locale +import datetime +from BeautifulSoup import BeautifulSoup + +tags_re = re.compile(r'<[^<]*?>') + +TimeZones = {'KST': 9, 'CADT': 10.5, 'EETDST': 3, 'MESZ': 2, 'WADT': 9, + 'EET': 2, 'MST': -7, 'WAST': 8, 'IST': 5.5, 'B': 2, + 'MSK': 3, 'X': -11, 'MSD': 4, 'CETDST': 2, 'AST': -4, + 'HKT': 8, 'JST': 9, 'CAST': 9.5, 'CET': 1, 'CEST': 2, + 'EEST': 3, 'EAST': 10, 'METDST': 2, 'MDT': -6, 'A': 1, + 'UTC': 0, 'ADT': -3, 'EST': -5, 'E': 5, 'D': 4, 'G': 7, + 'F': 6, 'I': 9, 'H': 8, 'K': 10, 'PDT': -7, 'M': 12, + 'L': 11, 'O': -2, 'MEST': 2, 'Q': -4, 'P': -3, 'S': -6, + 'R': -5, 'U': -8, 'T': -7, 'W': -10, 'WET': 0, 'Y': -12, + 'CST': -6, 'EADT': 11, 'Z': 0, 'GMT': 0, 'WETDST': 1, + 'C': 3, 'WEST': 1, 'CDT': -5, 'MET': 1, 'N': -1, 'V': -9, + 'EDT': -4, 'UT': 0, 'PST': -8, 'MEZ': 1, 'BST': 1, + 'ACS': 9.5, 'ATL': -4, 'ALA': -9, 'HAW': -10, 'AKDT': -8, + 'AKST': -9, + 'BDST': 2} + +TZ1 = { + 'NDT': -2.5, + 'BRST': -2, + 'ADT': -3, + 'EDT': -4, + 'CDT': -5, + 'MDT': -6, + 'PDT': -7, + 'YDT': -8, + 'HDT': -9, + 'BST': 1, + 'MEST': 2, + 'SST': 2, + 'FST': 2, + 'CEST': 2, + 'EEST': 3, + 'WADT': 8, + 'KDT': 10, + 'EADT': 13, + 'NZD': 13, + 'NZDT': 13, + 'GMT': 0, + 'UT': 0, + 'UTC': 0, + 'WET': 0, + 'WAT': -1, + 'AT': -2, + 'FNT': -2, + 'BRT': -3, + 'MNT': -4, + 'EWT': -4, + 'AST': -4, + 'EST': -5, + 'ACT': -5, + 'CST': -6, + 'MST': -7, + 'PST': -8, + 'YST': -9, + 'HST': -10, + 'CAT': -10, + 'AHST': -10, + 'NT': -11, + 'IDLW': -12, + 'CET': 1, + 'MEZ': 1, + 'ECT': 1, + 'MET': 1, + 'MEWT': 1, + 'SWT': 1, + 'SET': 1, + 'FWT': 1, + 'EET': 2, + 'UKR': 2, + 'BT': 3, + 'ZP4': 4, + 'ZP5': 5, + 'ZP6': 6, + 'WST': 8, + 'HKT': 8, + 'CCT': 8, + 'JST': 9, + 'KST': 9, + 'EAST': 10, + 'GST': 10, + 'NZT': 12, + 'NZST': 12, + 'IDLE': 12 +} + +TZ2 = { + 'ACDT': 10.5, + 'ACST': 9.5, + 'ADT': 3, + 'AEDT': 11, + 'AEST': 10, + 'AHDT': 9, + 'AHST': 10, + 'AST': 4, + 'AT': 2, + 'AWDT': -9, + 'AWST': -8, + 'BAT': -3, + 'BDST': -2, + 'BET': 11, + 'BST': -1, + 'BT': -3, + 'BZT2': 3, + 'CADT': -10.5, + 'CAST': -9.5, + 'CAT': 10, + 'CCT': -8, + # 'CDT': 5, + 'CED': -2, + 'CET': -1, + 'CST': 6, + 'EAST': -10, + # 'EDT': 4, + 'EED': -3, + 'EET': -2, + 'EEST': -3, + 'EST': 5, + 'FST': -2, + 'FWT': -1, + 'GMT': 0, + 'GST': -10, + 'HDT': 9, + 'HST': 10, + 'IDLE': -12, + 'IDLW': 12, + # 'IST': -5.5, + 'IT': -3.5, + 'JST': -9, + 'JT': -7, + 'KST': -9, + 'MDT': 6, + 'MED': -2, + 'MET': -1, + 'MEST': -2, + 'MEWT': -1, + 'MST': 7, + 'MT': -8, + 'NDT': 2.5, + 'NFT': 3.5, + 'NT': 11, + 'NST': -6.5, + 'NZ': -11, + 'NZST': -12, + 'NZDT': -13, + 'NZT': -12, + 'PDT': 7, + 'PST': 8, + 'ROK': -9, + 'SAD': -10, + 'SAST': -9, + 'SAT': -9, + 'SDT': -10, + 'SST': -2, + 'SWT': -1, + 'USZ3': -4, + 'USZ4': -5, + 'USZ5': -6, + 'USZ6': -7, + 'UT': 0, + 'UTC': 0, + 'UZ10': -11, + 'WAT': 1, + 'WET': 0, + 'WST': -8, + 'YDT': 8, + 'YST': 9, + 'ZP4': -4, + 'ZP5': -5, + 'ZP6': -6 +} + +TZ3 = { + 'AEST': 10, + 'AEDT': 11 +} + +# TimeZones.update(TZ2) # do these have to be negated? +TimeZones.update(TZ1) +TimeZones.update(TZ3) + +r_local = re.compile(r'\([a-z]+_[A-Z]+\)') + + +# gets the time from a timezone (code from phenny) +def get_time(tz): + TZ = tz.upper() + + if (TZ == 'UTC') or (TZ == 'Z'): + msg = time.strftime("\x02%I:%M%p\x02 %A - \x02Time\x02 in \x02 " \ + "GMT", time.gmtime()) + return msg + elif r_local.match(tz): + locale.setlocale(locale.LC_TIME, (tz[1:-1], 'UTF-8')) + msg = time.strftime("\x02%I:%M%p\x02 %A - \x02Time\x02 in \x02" \ + + str(TZ), time.gmtime()) + return msg + elif TZ in TimeZones: + offset = TimeZones[TZ] * 3600 + timenow = time.gmtime(time.time() + offset) + msg = time.strftime("\x02%I:%M%p\x02 %A - \x02Time\x02 in \x02" \ + + str(TZ), timenow) + return msg + elif tz and tz[0] in ('+', '-') and 4 <= len(tz) <= 6: + timenow = time.gmtime(time.time() + (int(tz[:3]) * 3600)) + msg = time.strftime("\x02%I:%M%p\x02 %A - \x02Time\x02 in\x02" \ + " GMT" + str(tz), timenow) + return msg + else: + timenow = time.gmtime(time.time() + (int(tz) * 3600)) + msg = time.strftime("\x02%I:%M%p\x02 %A - \x02Time\x02 in\x02" \ + " GMT " + str(tz), timenow) + return msg + + +@hook.command("time") +def time_command(inp): + ".time -- Gets the time in " + + request = http.get('http://www.google.com/search', q="time in " + inp) + soup = BeautifulSoup(request) + response = soup.find('td', {'style': 'font-size:medium'}) + + if response: + output = response.renderContents() + output = tags_re.sub('\x02', output.strip()) + return output + else: + try: + return get_time(inp) + except ValueError: + return "Could not get the time for " + inp + "!" diff --git a/plugins/todo.py b/plugins/todo.py index 6a2d170..9239823 100755 --- a/plugins/todo.py +++ b/plugins/todo.py @@ -90,7 +90,7 @@ def db_search(db, nick, query): @hook.command def todo(inp, nick='', chan='', db=None, notice=None, bot=None): - ".todo (add|del|list|search) [@user] args -- Manipulates your list of todos." + ".todo (add|del|list|search) args -- Manipulates your list of todos." db_init(db) @@ -99,9 +99,11 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None): args = parts[1:] - if len(args) and args[0].startswith("@"): - nick = args[0][1:] - args = args[1:] + # code to allow users to access each others factoids and a copy of help + # ".todo (add|del|list|search) [@user] args -- Manipulates your list of todos." + #if len(args) and args[0].startswith("@"): + # nick = args[0][1:] + # args = args[1:] if cmd == 'add': if not len(args): diff --git a/plugins/translate.py b/plugins/translate.py index 0709f38..d61ab14 100755 --- a/plugins/translate.py +++ b/plugins/translate.py @@ -1,4 +1,4 @@ -# MyGengo translation plugin by lukeroge and neersighted +# BING translation plugin by Lukeroge and neersighted from util import hook from util import http import re diff --git a/plugins/twitter.py b/plugins/twitter.py index 076de72..6186823 100755 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -1,31 +1,26 @@ -""" -twitter.py: written by Scaevolus 2009, modified by Lukeroge 2012 -retrieves most recent tweets -""" +# written by Scaevolus, modified by Lukeroge import random import re -from time import strptime, strftime +from time import strftime +from time import strptime from datetime import datetime - -from util import hook, http, timesince +from util import hook +from util import http +from util import timesince def unescape_xml(string): - # unescape the 5 chars that might be escaped in xml - - # gratuitously functional - # return reduce(lambda x, y: x.replace(*y), (string, - # zip('> < ' "e; &'.split(), '> < \' " &'.split())) - - # boring, normal + """Unescapes XML""" return string.replace('>', '>').replace('<', '<').replace(''', "'").replace('"e;', '"').replace('&', '&') history = [] history_max_size = 250 + def parseDateTime(s): + """Parses the date from a string""" if s is None: return None m = re.match(r'(.*?)(?:\.(\d+))?(([-+]\d{1,2}):(\d{2}))?$', @@ -40,7 +35,7 @@ def parseDateTime(s): tzname = 'UTC' tz = FixedOffset(timedelta(hours=tzhour, minutes=tzmin), tzname) - + x = datetime.strptime(datestr, "%Y-%m-%d %H:%M:%S") if fractional is None: fractional = '0' @@ -154,9 +149,9 @@ def twitter(inp): strptime(time.text, '%a %b %d %H:%M:%S +0000 %Y')) - time_pretty = timesince.timesince(parseDateTime(time_raw), datetime.utcnow()) + time_nice = timesince.timesince(parseDateTime(time_raw), datetime.utcnow()) text = unescape_xml(tweet.find(text).text.replace('\n', '')) screen_name = tweet.find(screen_name).text - return "\x02@%s\x02: %s [ %s ago ]" % (screen_name, text, time_pretty) + return "\x02@%s\x02: %s (%s ago)" % (screen_name, text, time_nice) diff --git a/plugins/urlparse.py b/plugins/urlparse.py index d8ba986..b4f47d5 100755 --- a/plugins/urlparse.py +++ b/plugins/urlparse.py @@ -1,40 +1,36 @@ from util import hook, http, urlnorm -import urllib -from urllib2 import urlopen, Request, HTTPError import re -import BeautifulSoup -ignored_urls = ["http://google.com", "http://youtube.com", - "http://pastebin.com", "http://mibpaste.com", - "http://fpaste.com", "http://git.io"] +titler = re.compile(r'(?si)(.+?)') -def parse(match): - url = urlnorm.normalize(match.encode('utf-8')) - if url not in ignored_urls: - url = url.decode('utf-8') - try: - soup = BeautifulSoup.BeautifulSoup(http.get(url)) - return soup.title.string - except: - return "fail" -@hook.regex(r'(^[^\.])([a-zA-Z]://|www\.)?[^ ]+(\.[a-z]+)(\/)?(.*)') -def urlparser(match, say=None): - url = urlnorm.normalize(match.group().encode('utf-8')) - if url[:7] != "http://": - if url[:8] != "https://": - url = "http://" + url - for x in ignored_urls: - if x in url: - return - title = parse(url) - if title == "fail": - return +def get_title(url): + url = urlnorm.normalize(url.encode('utf-8')) + url = url.decode('utf-8') + # add http if its missing + if not url.startswith("http"): + url = "http://" + url + try: + # get the title + request = http.open(url) + real_url = request.geturl() + text = request.read() + text = text.decode('utf8') + match = titler.search(text) + title = match.group(1) + except: + return "Could not parse URL! Are you sure its valid?" + title = http.unescape(title) - realurl = http.get_url(url) - if realurl == url: - say("(Link) %s" % title) - return + + # if the url has been redirected, show us + if real_url == url: + return title else: - say("(Link) %s [%s]" % (title, realurl)) - return + return u"%s [%s]" % (title, real_url) + + +@hook.command +def title(inp): + ".title -- gets the title of a web page" + return get_title(inp) diff --git a/plugins/util/molecular.py b/plugins/util/molecular.py index 4094d4b..3de9c00 100755 --- a/plugins/util/molecular.py +++ b/plugins/util/molecular.py @@ -33,6 +33,8 @@ """molecular.py -- molecular (ngenoid) name generator +Modified for CloudBot by Lukeroge. + This module knows how to generate "random" names for RPG characters. It uses the same method as the "ngen" name generator by Kimmo Kulovesi, and in fact it can use the same name files. molecular.py knows how @@ -201,26 +203,3 @@ class Molecule: if len(self.nametbl["final"]) > 0: n.append(random.choice(self.nametbl["final"])) return string.join(n, "") - -if __name__ == "__main__": - - if len(sys.argv) <= 1: - sys.stderr.write( \ - "Usage: molecular.py [ -r file ] [ nn ]\n") - sys.exit(0) - - name = Molecule() - - i = 1 - - while i < len(sys.argv): - arg = sys.argv[i] - if arg == "-r": - i += 1 - name.load(sys.argv[i]) - else: - n = int(sys.argv[i]) - lst = [] - for i in range(n): - print name.name() - i += 1 diff --git a/plugins/violence.py b/plugins/violence.py new file mode 100755 index 0000000..9eda0e1 --- /dev/null +++ b/plugins/violence.py @@ -0,0 +1,105 @@ +from util import hook +import re +import random + +nick_re = re.compile(r"^[A-Za-z0-9_|.-\]\[]*$") + +# define lists for messages +larts = [] +kills = [] +kill_bodyparts = [] +slaps = [] +slap_items = [] + +with open("plugins/data/larts.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + larts.append(line.strip()) + +with open("plugins/data/slaps.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + slaps.append(line.strip()) + +with open("plugins/data/slap_items.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + slap_items.append(line.strip()) + +with open("plugins/data/kills.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + kills.append(line.strip()) + +with open("plugins/data/kill_bodyparts.txt") as f: + for line in f.readlines(): + if line.startswith("//"): + continue + kill_bodyparts.append(line.strip()) + + +@hook.command +def slap(inp, me=None, nick=None, conn=None, notice=None): + ".slap -- Makes the bot slap ." + target = inp.lower() + + if not re.match(nick_re, target): + notice("Invalid username!") + return + + # if the user is trying to make the bot slap itself, slap them + if target == conn.nick.lower() or target == "itself": + target = nick + else: + target = inp + + out = random.choice(slaps) + out = out.replace('', target) + out = out.replace('', random.choice(slap_items)) + + # act out the message + me(out) + + +@hook.command +def lart(inp, me=None, nick=None, conn=None, notice=None): + ".lart -- LARTs ." + target = inp.lower() + + if not re.match(nick_re, target): + notice("Invalid username!") + return + + if target == conn.nick.lower() or target == "itself": + target = nick + else: + target = inp + + out = random.choice(larts) + out = out.replace('', target) + out = out.replace('', random.choice(slap_items)) + me(out) + + +@hook.command +def kill(inp, me=None, nick=None, conn=None, notice=None): + ".kill -- Makes the bot kill ." + target = inp.lower() + + if not re.match(nick_re, target): + notice("Invalid username!") + return + + if target == conn.nick.lower() or target == "itself": + target = nick + else: + target = inp + + out = random.choice(kills) + out = out.replace('', target) + out = out.replace('', random.choice(kill_bodyparts)) + me(out) diff --git a/plugins/weather.py b/plugins/weather.py index 9f6193d..daff1df 100755 --- a/plugins/weather.py +++ b/plugins/weather.py @@ -5,8 +5,8 @@ from util import hook, http @hook.command(autohelp=False) def forecast(inp, nick='', server='', reply=None, db=None, notice=None, say=None): - ".forecast [dontsave] -- Gets a weather forecast "\ - "for from Google." + ".forecast [dontsave] -- Gets a weather forecast" \ + " for from Google." loc = inp dontsave = loc.endswith(" dontsave") diff --git a/plugins/wordoftheday.py b/plugins/wordoftheday.py index 3d4b631..ebf6c1c 100755 --- a/plugins/wordoftheday.py +++ b/plugins/wordoftheday.py @@ -10,8 +10,8 @@ def word(inp, say=False, nick=False): soup = BeautifulSoup(page) - word = soup.find('strong', {'class' : 'main_entry_word'}).renderContents() - function = soup.find('p', {'class' : 'word_function'}).renderContents() + word = soup.find('strong', {'class': 'main_entry_word'}).renderContents() + function = soup.find('p', {'class': 'word_function'}).renderContents() #definitions = re.findall(r':' # r' *([^<]+)', content) diff --git a/plugins/youtube.py b/plugins/youtube.py index 67abe70..91f1d30 100755 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -86,4 +86,4 @@ def youtube(inp): vid_id = j['data']['items'][0]['id'] - return get_video_description(vid_id) + " - " + video_url % vid_id \ No newline at end of file + return get_video_description(vid_id) + " - " + video_url % vid_id