From bff00f9b2847f3c053e64c30b80231cee85bc62d Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sat, 10 Dec 2011 19:02:04 -0500 Subject: [PATCH] Restruturing codebase to be a bit more Pythonic. --- bot.py | 70 +++++ bot/__init__.py | 0 bot/classes/__init__.py | 5 - bot/commands/chanops.py | 35 --- bot/commands/editcount.py | 38 --- bot/commands/praise.py | 31 --- bot/commands/replag.py | 29 -- bot/commands/rights.py | 38 --- bot/commands/test.py | 16 -- bot/config.py | 228 ---------------- bot/tasks/afc_catdelink.py | 14 - bot/tasks/afc_copyvios.py | 15 -- bot/tasks/afc_dailycats.py | 14 - bot/tasks/afc_undated.py | 13 - bot/tasks/blptag.py | 14 - bot/tasks/feed_dailycats.py | 13 - bot/tasks/wrongmime.py | 14 - bot/wiki/__init__.py | 24 -- bot/wiki/constants.py | 35 --- earwigbot.py | 57 ---- earwigbot/__init__.py | 37 +++ {bot => earwigbot}/blowfish.py | 1 + earwigbot/classes/__init__.py | 27 ++ {bot => earwigbot}/classes/base_command.py | 29 +- {bot => earwigbot}/classes/base_task.py | 29 +- {bot => earwigbot}/classes/connection.py | 22 ++ {bot => earwigbot}/classes/data.py | 22 ++ {bot => earwigbot}/classes/rc.py | 22 ++ {bot => earwigbot}/commands/__init__.py | 28 +- {bot => earwigbot}/commands/_old.py | 0 {bot => earwigbot}/commands/afc_report.py | 26 +- {bot => earwigbot}/commands/afc_status.py | 26 +- {bot => earwigbot}/commands/calc.py | 22 +- earwigbot/commands/chanops.py | 55 ++++ {bot => earwigbot}/commands/crypt.py | 24 +- {bot => earwigbot}/commands/ctcp.py | 24 +- earwigbot/commands/editcount.py | 58 ++++ {bot => earwigbot}/commands/git.py | 24 +- {bot => earwigbot}/commands/help.py | 24 +- {bot => earwigbot}/commands/link.py | 22 +- earwigbot/commands/praise.py | 51 ++++ {bot => earwigbot}/commands/registration.py | 24 +- {bot => earwigbot}/commands/remind.py | 22 +- earwigbot/commands/replag.py | 50 ++++ earwigbot/commands/rights.py | 58 ++++ earwigbot/commands/test.py | 36 +++ {bot => earwigbot}/commands/threads.py | 26 +- earwigbot/config.py | 335 ++++++++++++++++++++++++ {bot => earwigbot}/frontend.py | 28 +- {bot => earwigbot}/main.py | 82 +++--- {bot => earwigbot}/rules.py | 22 +- earwigbot/runner.py | 63 +++++ {bot => earwigbot}/tasks/__init__.py | 28 +- earwigbot/tasks/afc_catdelink.py | 34 +++ earwigbot/tasks/afc_copyvios.py | 38 +++ earwigbot/tasks/afc_dailycats.py | 34 +++ {bot => earwigbot}/tasks/afc_history.py | 26 +- {bot => earwigbot}/tasks/afc_statistics.py | 27 +- earwigbot/tasks/afc_undated.py | 33 +++ earwigbot/tasks/blptag.py | 34 +++ earwigbot/tasks/feed_dailycats.py | 33 +++ earwigbot/tasks/wrongmime.py | 34 +++ tests/support.py => earwigbot/tests/__init__.py | 35 ++- {tests => earwigbot/tests}/test_blowfish.py | 23 +- earwigbot/tests/test_calc.py | 58 ++++ earwigbot/tests/test_test.py | 48 ++++ {bot => earwigbot}/watcher.py | 28 +- earwigbot/wiki/__init__.py | 44 ++++ {bot => earwigbot}/wiki/category.py | 22 +- earwigbot/wiki/constants.py | 55 ++++ {bot => earwigbot}/wiki/exceptions.py | 20 ++ {bot => earwigbot}/wiki/functions.py | 30 ++- {bot => earwigbot}/wiki/page.py | 22 +- {bot => earwigbot}/wiki/site.py | 35 ++- {bot => earwigbot}/wiki/user.py | 26 +- tests/test_calc.py | 39 --- tests/test_test.py | 29 -- 77 files changed, 2031 insertions(+), 826 deletions(-) create mode 100755 bot.py delete mode 100644 bot/__init__.py delete mode 100644 bot/classes/__init__.py delete mode 100644 bot/commands/chanops.py delete mode 100644 bot/commands/editcount.py delete mode 100644 bot/commands/praise.py delete mode 100644 bot/commands/replag.py delete mode 100644 bot/commands/rights.py delete mode 100644 bot/commands/test.py delete mode 100644 bot/config.py delete mode 100644 bot/tasks/afc_catdelink.py delete mode 100644 bot/tasks/afc_copyvios.py delete mode 100644 bot/tasks/afc_dailycats.py delete mode 100644 bot/tasks/afc_undated.py delete mode 100644 bot/tasks/blptag.py delete mode 100644 bot/tasks/feed_dailycats.py delete mode 100644 bot/tasks/wrongmime.py delete mode 100644 bot/wiki/__init__.py delete mode 100644 bot/wiki/constants.py delete mode 100755 earwigbot.py create mode 100644 earwigbot/__init__.py rename {bot => earwigbot}/blowfish.py (99%) mode change 100644 => 100755 create mode 100644 earwigbot/classes/__init__.py rename {bot => earwigbot}/classes/base_command.py (58%) rename {bot => earwigbot}/classes/base_task.py (70%) rename {bot => earwigbot}/classes/connection.py (72%) rename {bot => earwigbot}/classes/data.py (61%) rename {bot => earwigbot}/classes/rc.py (72%) rename {bot => earwigbot}/commands/__init__.py (66%) rename {bot => earwigbot}/commands/_old.py (100%) rename {bot => earwigbot}/commands/afc_report.py (71%) rename {bot => earwigbot}/commands/afc_status.py (83%) rename {bot => earwigbot}/commands/calc.py (60%) create mode 100644 earwigbot/commands/chanops.py rename {bot => earwigbot}/commands/crypt.py (63%) rename {bot => earwigbot}/commands/ctcp.py (54%) create mode 100644 earwigbot/commands/editcount.py rename {bot => earwigbot}/commands/git.py (84%) rename {bot => earwigbot}/commands/help.py (56%) rename {bot => earwigbot}/commands/link.py (62%) create mode 100644 earwigbot/commands/praise.py rename {bot => earwigbot}/commands/registration.py (58%) rename {bot => earwigbot}/commands/remind.py (58%) create mode 100644 earwigbot/commands/replag.py create mode 100644 earwigbot/commands/rights.py create mode 100644 earwigbot/commands/test.py rename {bot => earwigbot}/commands/threads.py (80%) create mode 100644 earwigbot/config.py rename {bot => earwigbot}/frontend.py (74%) rename {bot => earwigbot}/main.py (65%) rename {bot => earwigbot}/rules.py (67%) create mode 100644 earwigbot/runner.py rename {bot => earwigbot}/tasks/__init__.py (72%) create mode 100644 earwigbot/tasks/afc_catdelink.py create mode 100644 earwigbot/tasks/afc_copyvios.py create mode 100644 earwigbot/tasks/afc_dailycats.py rename {bot => earwigbot}/tasks/afc_history.py (86%) rename {bot => earwigbot}/tasks/afc_statistics.py (95%) create mode 100644 earwigbot/tasks/afc_undated.py create mode 100644 earwigbot/tasks/blptag.py create mode 100644 earwigbot/tasks/feed_dailycats.py create mode 100644 earwigbot/tasks/wrongmime.py rename tests/support.py => earwigbot/tests/__init__.py (64%) rename {tests => earwigbot/tests}/test_blowfish.py (72%) create mode 100644 earwigbot/tests/test_calc.py create mode 100644 earwigbot/tests/test_test.py rename {bot => earwigbot}/watcher.py (68%) create mode 100644 earwigbot/wiki/__init__.py rename {bot => earwigbot}/wiki/category.py (69%) create mode 100644 earwigbot/wiki/constants.py rename {bot => earwigbot}/wiki/exceptions.py (67%) rename {bot => earwigbot}/wiki/functions.py (84%) rename {bot => earwigbot}/wiki/page.py (95%) rename {bot => earwigbot}/wiki/site.py (94%) rename {bot => earwigbot}/wiki/user.py (87%) delete mode 100644 tests/test_calc.py delete mode 100644 tests/test_test.py diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..246301a --- /dev/null +++ b/bot.py @@ -0,0 +1,70 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot + +This is a thin wrapper for EarwigBot's main bot code, specified by bot_script. +The wrapper will automatically restart the bot when it shuts down (from +!restart, for example). It requests the bot's password at startup and reuses it +every time the bot restarts internally, so you do not need to re-enter the +password after using !restart. + +For information about the bot as a whole, see the attached README.md file (in +markdown format!), the docs/ directory, and the LICENSE file for licensing +information. EarwigBot is released under the MIT license. +""" +from getpass import getpass +from subprocess import Popen, PIPE +from os import path +from sys import executable +from time import sleep + +import earwigbot + +bot_script = path.join(earwigbot.__path__[0], "runner.py") + +def main(): + print "EarwigBot v{0}\n".format(earwigbot.__version__) + + is_encrypted = earwigbot.config.config.load() + if is_encrypted: # Passwords in the config file are encrypted + key = getpass("Enter key to unencrypt bot passwords: ") + else: + key = None + + while 1: + bot = Popen([executable, bot_script], stdin=PIPE) + print >> bot.stdin, path.dirname(path.abspath(__file__)) + if is_encrypted: + print >> bot.stdin, key + return_code = bot.wait() + if return_code == 1: + exit() # Let critical exceptions in the subprocess cause us to + # exit as well + else: + sleep(5) # Sleep between bot runs following a non-critical + # subprocess exit + +if __name__ == "__main__": + main() diff --git a/bot/__init__.py b/bot/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/bot/classes/__init__.py b/bot/classes/__init__.py deleted file mode 100644 index 95da576..0000000 --- a/bot/classes/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from base_command import * -from base_task import * -from connection import * -from data import * -from rc import * diff --git a/bot/commands/chanops.py b/bot/commands/chanops.py deleted file mode 100644 index fd8f009..0000000 --- a/bot/commands/chanops.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseCommand -import config - -class Command(BaseCommand): - """Voice, devoice, op, or deop users in the channel.""" - name = "chanops" - - def check(self, data): - commands = ["chanops", "voice", "devoice", "op", "deop"] - if data.is_command and data.command in commands: - return True - return False - - def process(self, data): - if data.command == "chanops": - msg = "available commands are !voice, !devoice, !op, and !deop." - self.connection.reply(data, msg) - return - - if data.host not in config.irc["permissions"]["admins"]: - msg = "you must be a bot admin to use this command." - self.connection.reply(data, msg) - return - - # If it is just !op/!devoice/whatever without arguments, assume they - # want to do this to themselves: - if not data.args: - target = data.nick - else: - target = data.args[0] - - msg = " ".join((data.command, data.chan, target)) - self.connection.say("ChanServ", msg) diff --git a/bot/commands/editcount.py b/bot/commands/editcount.py deleted file mode 100644 index 71e2492..0000000 --- a/bot/commands/editcount.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -from urllib import quote_plus - -from classes import BaseCommand -import wiki - -class Command(BaseCommand): - """Return a user's edit count.""" - name = "editcount" - - def check(self, data): - commands = ["ec", "editcount"] - if data.is_command and data.command in commands: - return True - return False - - def process(self, data): - if not data.args: - name = data.nick - else: - name = ' '.join(data.args) - - site = wiki.get_site() - site._maxlag = None - user = site.get_user(name) - - try: - count = user.editcount() - except wiki.UserNotFoundError: - msg = "the user \x0302{0}\x0301 does not exist." - self.connection.reply(data, msg.format(name)) - return - - safe = quote_plus(user.name()) - url = "http://toolserver.org/~soxred93/pcount/index.php?name={0}&lang=en&wiki=wikipedia" - msg = "\x0302{0}\x0301 has {1} edits ({2})." - self.connection.reply(data, msg.format(name, count, url.format(safe))) diff --git a/bot/commands/praise.py b/bot/commands/praise.py deleted file mode 100644 index 26fd4b6..0000000 --- a/bot/commands/praise.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- - -import random - -from classes import BaseCommand - -class Command(BaseCommand): - """Praise people!""" - name = "praise" - - def check(self, data): - commands = ["praise", "earwig", "leonard", "leonard^bloom", "groove", - "groovedog"] - return data.is_command and data.command in commands - - def process(self, data): - if data.command == "earwig": - msg = "\x02Earwig\x0F is the bestest Python programmer ever!" - elif data.command in ["leonard", "leonard^bloom"]: - msg = "\x02Leonard^Bloom\x0F is the biggest slacker ever!" - elif data.command in ["groove", "groovedog"]: - msg = "\x02GrooveDog\x0F is the bestest heh evar!" - else: - if not data.args: - msg = "You use this command to praise certain people. Who they are is a secret." - else: - msg = "You're doing it wrong." - self.connection.reply(data, msg) - return - - self.connection.say(data.chan, msg) diff --git a/bot/commands/replag.py b/bot/commands/replag.py deleted file mode 100644 index 2eebadc..0000000 --- a/bot/commands/replag.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- - -from os.path import expanduser - -import oursql - -from classes import BaseCommand - -class Command(BaseCommand): - """Return the replag for a specific database on the Toolserver.""" - name = "replag" - - def process(self, data): - args = {} - if not data.args: - args["db"] = "enwiki_p" - else: - args["db"] = data.args[0] - args["host"] = args["db"].replace("_", "-") + ".rrdb.toolserver.org" - args["read_default_file"] = expanduser("~/.my.cnf") - - conn = oursql.connect(**args) - with conn.cursor() as cursor: - cursor.execute("SELECT NOW() - MAX(rev_timestamp) FROM revision") - replag = int(cursor.fetchall()[0][0]) - conn.close() - - msg = "Replag on \x0302{0}\x0301 is \x02{1}\x0F seconds." - self.connection.reply(data, msg.format(args["db"], replag)) diff --git a/bot/commands/rights.py b/bot/commands/rights.py deleted file mode 100644 index b7011a4..0000000 --- a/bot/commands/rights.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseCommand -import wiki - -class Command(BaseCommand): - """Retrieve a list of rights for a given username.""" - name = "rights" - - def check(self, data): - commands = ["rights", "groups", "permissions", "privileges"] - if data.is_command and data.command in commands: - return True - return False - - def process(self, data): - if not data.args: - name = data.nick - else: - name = ' '.join(data.args) - - site = wiki.get_site() - site._maxlag = None - user = site.get_user(name) - - try: - rights = user.groups() - except wiki.UserNotFoundError: - msg = "the user \x0302{0}\x0301 does not exist." - self.connection.reply(data, msg.format(name)) - return - - try: - rights.remove("*") # Remove the '*' group given to everyone - except ValueError: - pass - msg = "the rights for \x0302{0}\x0301 are {1}." - self.connection.reply(data, msg.format(name, ', '.join(rights))) diff --git a/bot/commands/test.py b/bot/commands/test.py deleted file mode 100644 index 2bb59f8..0000000 --- a/bot/commands/test.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -import random - -from classes import BaseCommand - -class Command(BaseCommand): - """Test the bot!""" - name = "test" - - def process(self, data): - hey = random.randint(0, 1) - if hey: - self.connection.say(data.chan, "Hey \x02%s\x0F!" % data.nick) - else: - self.connection.say(data.chan, "'sup \x02%s\x0F?" % data.nick) diff --git a/bot/config.py b/bot/config.py deleted file mode 100644 index bf9b872..0000000 --- a/bot/config.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -EarwigBot's JSON Config File Parser - -This handles all tasks involving reading and writing to our config file, -including encrypting and decrypting passwords and making a new config file from -scratch at the inital bot run. - -Usually you'll just want to do "from core import config" and access config data -from within config's global variables and functions: - -* config.components - a list of enabled components -* config.wiki - a dict of information about wiki-editing -* config.tasks - a dict of information for bot tasks -* config.irc - a dict of information about IRC -* config.metadata - a dict of miscellaneous information -* config.schedule() - returns a list of tasks scheduled to run at a given time - -Additionally, there are functions used in config loading: -* config.load() - loads and parses our config file, returning True if - passwords are stored encrypted or False otherwise -* config.decrypt() - given a key, decrypts passwords inside our config - variables; won't work if passwords aren't encrypted -""" - -import json -import logging -import logging.handlers -from os import mkdir, path - -import blowfish - -script_dir = path.dirname(path.abspath(__file__)) -root_dir = path.split(script_dir)[0] -config_path = path.join(root_dir, "config.json") -log_dir = path.join(root_dir, "logs") - -_config = None # Holds data loaded from our config file - -# Set our easy-config-access global variables to None -components, wiki, tasks, irc, metadata = None, None, None, None, None - -def _load(): - """Load data from our JSON config file (config.json) into _config.""" - global _config - with open(config_path, 'r') as fp: - try: - _config = json.load(fp) - except ValueError as error: - print "Error parsing config file {0}:".format(config_path) - print error - exit(1) - -def _setup_logging(): - """Configures the logging module so it works the way we want it to.""" - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - - if metadata.get("enableLogging"): - hand = logging.handlers.TimedRotatingFileHandler - formatter = BotFormatter() - color_formatter = BotFormatter(color=True) - - logfile = lambda f: path.join(log_dir, f) - - if not path.isdir(log_dir): - if not path.exists(log_dir): - mkdir(log_dir, 0700) - else: - msg = "log_dir ({0}) exists but is not a directory!" - print msg.format(log_dir) - exit(1) - - main_handler = hand(logfile("bot.log"), "midnight", 1, 7) - error_handler = hand(logfile("error.log"), "W6", 1, 4) - debug_handler = hand(logfile("debug.log"), "H", 1, 6) - - main_handler.setLevel(logging.INFO) - error_handler.setLevel(logging.WARNING) - debug_handler.setLevel(logging.DEBUG) - - for h in (main_handler, error_handler, debug_handler): - h.setFormatter(formatter) - logger.addHandler(h) - - stream_handler = logging.StreamHandler() - stream_handler.setLevel(logging.DEBUG) - stream_handler.setFormatter(color_formatter) - logger.addHandler(stream_handler) - - else: - logger.addHandler(logging.NullHandler()) - -def _make_new(): - """Make a new config file based on the user's input.""" - encrypt = raw_input("Would you like to encrypt passwords stored in config.json? [y/n] ") - if encrypt.lower().startswith("y"): - is_encrypted = True - else: - is_encrypted = False - - return is_encrypted - -def is_loaded(): - """Return True if our config file has been loaded, otherwise False.""" - return _config is not None - -def load(): - """Load, or reload, our config file. - - First, check if we have a valid config file, and if not, notify the user. - If there is no config file at all, offer to make one, otherwise exit. - - Store data from our config file in five global variables (components, wiki, - tasks, irc, metadata) for easy access (as well as the internal _config - variable). - - If everything goes well, return True if stored passwords are - encrypted in the file, or False if they are not. - """ - global components, wiki, tasks, irc, metadata - - if not path.exists(config_path): - print "You haven't configured the bot yet!" - choice = raw_input("Would you like to do this now? [y/n] ") - if choice.lower().startswith("y"): - return _make_new() - else: - exit(1) - - _load() - - components = _config.get("components", []) - wiki = _config.get("wiki", {}) - tasks = _config.get("tasks", {}) - irc = _config.get("irc", {}) - metadata = _config.get("metadata", {}) - - _setup_logging() - - # Are passwords encrypted? - return metadata.get("encryptPasswords", False) - -def decrypt(key): - """Use the key to decrypt passwords in our config file. - - Call this if load() returns True. Catch password decryption errors and - report them to the user. - """ - global irc, wiki - - try: - item = wiki.get("password") - if item: - wiki["password"] = blowfish.decrypt(key, item) - - item = irc.get("frontend").get("nickservPassword") - if item: - irc["frontend"]["nickservPassword"] = blowfish.decrypt(key, item) - - item = irc.get("watcher").get("nickservPassword") - if item: - irc["watcher"]["nickservPassword"] = blowfish.decrypt(key, item) - - except blowfish.BlowfishError as error: - print "\nError decrypting passwords:" - print "{0}: {1}.".format(error.__class__.__name__, error) - exit(1) - -def schedule(minute, hour, month_day, month, week_day): - """Return a list of tasks scheduled to run at the specified time. - - The schedule data comes from our config file's 'schedule' field, which is - stored as _config["schedule"]. Call this function as config.schedule(args). - """ - # Tasks to run this turn, each as a list of either [task_name, kwargs], or - # just the task_name: - tasks = [] - - now = {"minute": minute, "hour": hour, "month_day": month_day, - "month": month, "week_day": week_day} - - data = _config.get("schedule", []) - for event in data: - do = True - for key, value in now.items(): - try: - requirement = event[key] - except KeyError: - continue - if requirement != value: - do = False - break - if do: - try: - tasks.extend(event["tasks"]) - except KeyError: - pass - - return tasks - - -class BotFormatter(logging.Formatter): - def __init__(self, color=False): - self._format = super(BotFormatter, self).format - if color: - fmt = "[%(asctime)s %(lvl)s] %(name)s: %(message)s" - self.format = lambda record: self._format(self.format_color(record)) - else: - fmt = "[%(asctime)s %(levelname)-8s] %(name)s: %(message)s" - self.format = self._format - datefmt = "%Y-%m-%d %H:%M:%S" - super(BotFormatter, self).__init__(fmt=fmt, datefmt=datefmt) - - def format_color(self, record): - l = record.levelname.ljust(8) - if record.levelno == logging.DEBUG: - record.lvl = l.join(("\x1b[34m", "\x1b[0m")) # Blue - if record.levelno == logging.INFO: - record.lvl = l.join(("\x1b[32m", "\x1b[0m")) # Green - if record.levelno == logging.WARNING: - record.lvl = l.join(("\x1b[33m", "\x1b[0m")) # Yellow - if record.levelno == logging.ERROR: - record.lvl = l.join(("\x1b[31m", "\x1b[0m")) # Red - if record.levelno == logging.CRITICAL: - record.lvl = l.join(("\x1b[1m\x1b[31m", "\x1b[0m")) # Bold red - return record diff --git a/bot/tasks/afc_catdelink.py b/bot/tasks/afc_catdelink.py deleted file mode 100644 index a3b11f4..0000000 --- a/bot/tasks/afc_catdelink.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to delink mainspace categories in declined [[WP:AFC]] - submissions.""" - name = "afc_catdelink" - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/afc_copyvios.py b/bot/tasks/afc_copyvios.py deleted file mode 100644 index 173c690..0000000 --- a/bot/tasks/afc_copyvios.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to check newly-edited [[WP:AFC]] submissions for copyright - violations.""" - name = "afc_copyvios" - number = 1 - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/afc_dailycats.py b/bot/tasks/afc_dailycats.py deleted file mode 100644 index 9377970..0000000 --- a/bot/tasks/afc_dailycats.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """ A task to create daily categories for [[WP:AFC]].""" - name = "afc_dailycats" - number = 3 - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/afc_undated.py b/bot/tasks/afc_undated.py deleted file mode 100644 index dbad88c..0000000 --- a/bot/tasks/afc_undated.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to clear [[Category:Undated AfC submissions]].""" - name = "afc_undated" - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/blptag.py b/bot/tasks/blptag.py deleted file mode 100644 index fed72a8..0000000 --- a/bot/tasks/blptag.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to add |blp=yes to {{WPB}} or {{WPBS}} when it is used along with - {{WP Biography}}.""" - name = "blptag" - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/feed_dailycats.py b/bot/tasks/feed_dailycats.py deleted file mode 100644 index 24be9ee..0000000 --- a/bot/tasks/feed_dailycats.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to create daily categories for [[WP:FEED]].""" - name = "feed_dailycats" - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/tasks/wrongmime.py b/bot/tasks/wrongmime.py deleted file mode 100644 index 69ac042..0000000 --- a/bot/tasks/wrongmime.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -from classes import BaseTask - -class Task(BaseTask): - """A task to tag files whose extensions do not agree with their MIME - type.""" - name = "wrongmime" - - def __init__(self): - pass - - def run(self, **kwargs): - pass diff --git a/bot/wiki/__init__.py b/bot/wiki/__init__.py deleted file mode 100644 index f795d77..0000000 --- a/bot/wiki/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -EarwigBot's Wiki Toolset - -This is a collection of classes and functions to read from and write to -Wikipedia and other wiki sites. No connection whatsoever to python-wikitools -written by Mr.Z-man, other than a similar purpose. We share no code. - -Import the toolset with `import wiki`. -""" - -import logging -logger = logging.getLogger("wiki") -logger.addHandler(logging.NullHandler()) - -from wiki.constants import * -from wiki.exceptions import * -from wiki.functions import * - -from wiki.category import Category -from wiki.page import Page -from wiki.site import Site -from wiki.user import User diff --git a/bot/wiki/constants.py b/bot/wiki/constants.py deleted file mode 100644 index 5705cc2..0000000 --- a/bot/wiki/constants.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -EarwigBot's Wiki Toolset: Constants - -This module defines some useful constants: -* USER_AGENT - our default User Agent when making API queries -* NS_* - default namespace IDs for easy lookup - -Import with `from wiki import constants` or `from wiki.constants import *`. -""" - -# Default User Agent when making API queries: -import platform -USER_AGENT = "EarwigBot/0.1-dev (Python/{0}; https://github.com/earwig/earwigbot)".format(platform.python_version()) - -# Default namespace IDs: -NS_MAIN = 0 -NS_TALK = 1 -NS_USER = 2 -NS_USER_TALK = 3 -NS_PROJECT = 4 -NS_PROJECT_TALK = 5 -NS_FILE = 6 -NS_FILE_TALK = 7 -NS_MEDIAWIKI = 8 -NS_MEDIAWIKI_TALK = 9 -NS_TEMPLATE = 10 -NS_TEMPLATE_TALK = 11 -NS_HELP = 12 -NS_HELP_TALK = 13 -NS_CATEGORY = 14 -NS_CATEGORY_TALK = 15 -NS_SPECIAL = -1 -NS_MEDIA = -2 diff --git a/earwigbot.py b/earwigbot.py deleted file mode 100755 index 0fcb314..0000000 --- a/earwigbot.py +++ /dev/null @@ -1,57 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -""" -EarwigBot - -A thin wrapper for EarwigBot's main bot code, specified by bot_script. This -wrapper will automatically restart the bot when it shuts down (from !restart, -for example). It requests the bot's password at startup and reuses it every -time the bot restarts internally, so you do not need to re-enter the password -after using !restart. - -For information about the bot as a whole, see the attached README.md file (in -markdown format!) and the LICENSE for licensing information. -""" - -from getpass import getpass -from subprocess import Popen, PIPE -from os import path -from sys import executable -from time import sleep - -from bot import config - -__author__ = "Ben Kurtovic" -__copyright__ = "Copyright (C) 2009, 2010, 2011 by Ben Kurtovic" -__license__ = "MIT License" -__version__ = "0.1-dev" -__email__ = "ben.kurtovic@verizon.net" - -bot_script = path.join(path.dirname(path.abspath(__file__)), "bot", "main.py") - -def main(): - print "EarwigBot v{0}\n".format(__version__) - - is_encrypted = config.load() - if is_encrypted: # passwords in the config file are encrypted - key = getpass("Enter key to unencrypt bot passwords: ") - else: - key = None - - while 1: - bot = Popen([executable, bot_script], stdin=PIPE) - bot.communicate(key) # give the key to core.config.decrypt() - return_code = bot.wait() - if return_code == 1: - exit() # let critical exceptions in the subprocess cause us to - # exit as well - else: - sleep(5) # sleep between bot runs following a non-critical - # subprocess exit - -if __name__ == "__main__": - try: - main() - except KeyboardInterrupt: - print "\nKeyboardInterrupt: stopping bot wrapper." diff --git a/earwigbot/__init__.py b/earwigbot/__init__.py new file mode 100644 index 0000000..83fb901 --- /dev/null +++ b/earwigbot/__init__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot - http://earwig.github.com/earwig/earwigbot +See README.md for a basic overview, or the docs/ directory for details. +""" + +__author__ = "Ben Kurtovic" +__copyright__ = "Copyright (C) 2009, 2010, 2011 by Ben Kurtovic" +__license__ = "MIT License" +__version__ = "0.1.dev" +__email__ = "ben.kurtovic@verizon.net" + +from earwigbot import ( + blowfish, config, classes, commands, config, frontend, main, rules, tasks, + tests, watcher, wiki +) diff --git a/bot/blowfish.py b/earwigbot/blowfish.py old mode 100644 new mode 100755 similarity index 99% rename from bot/blowfish.py rename to earwigbot/blowfish.py index 84feab1..e426211 --- a/bot/blowfish.py +++ b/earwigbot/blowfish.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python # -*- coding: utf-8 -*- # # blowfish.py diff --git a/earwigbot/classes/__init__.py b/earwigbot/classes/__init__.py new file mode 100644 index 0000000..ef7f118 --- /dev/null +++ b/earwigbot/classes/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes.base_command import * +from earwigbot.classes.base_task import * +from earwigbot.classes.connection import * +from earwigbot.classes.data import * +from earwigbot.classes.rc import * diff --git a/bot/classes/base_command.py b/earwigbot/classes/base_command.py similarity index 58% rename from bot/classes/base_command.py rename to earwigbot/classes/base_command.py index b5db540..c94d6ea 100644 --- a/bot/classes/base_command.py +++ b/earwigbot/classes/base_command.py @@ -1,15 +1,37 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import logging +__all__ = ["BaseCommand"] + class BaseCommand(object): """A base class for commands on IRC. This docstring is reported to the user when they use !help . """ # This is the command's name, as reported to the user when they use !help: - name = "base_command" - + name = None + # Hooks are "msg", "msg_private", "msg_public", and "join". "msg" is the # default behavior; if you wish to override that, change the value in your # command subclass: @@ -24,7 +46,8 @@ class BaseCommand(object): from within a method. """ self.connection = connection - self.logger = logging.getLogger(".".join(("commands", self.name))) + logger_name = ".".join(("earwigbot", "commands", self.name)) + self.logger = logging.getLogger(logger_name) self.logger.setLevel(logging.DEBUG) def check(self, data): diff --git a/bot/classes/base_task.py b/earwigbot/classes/base_task.py similarity index 70% rename from bot/classes/base_task.py rename to earwigbot/classes/base_task.py index d568bb8..1a0d763 100644 --- a/bot/classes/base_task.py +++ b/earwigbot/classes/base_task.py @@ -1,9 +1,31 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import logging -import config -import wiki +from earwigbot.config import config +from earwigbot import wiki + +__all__ = ["BaseTask"] class BaseTask(object): """A base class for bot tasks that edit Wikipedia.""" @@ -20,7 +42,8 @@ class BaseTask(object): def _setup_logger(self): """Set up a basic module-level logger.""" - self.logger = logging.getLogger(".".join(("tasks", self.name))) + logger_name = ".".join(("earwigbot", "tasks", self.name)) + self.logger = logging.getLogger(logger_name) self.logger.setLevel(logging.DEBUG) def run(self, **kwargs): diff --git a/bot/classes/connection.py b/earwigbot/classes/connection.py similarity index 72% rename from bot/classes/connection.py rename to earwigbot/classes/connection.py index ab97cb5..f55df6c 100644 --- a/bot/classes/connection.py +++ b/earwigbot/classes/connection.py @@ -1,8 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import socket import threading +__all__ = ["BrokenSocketException", "Connection"] + class BrokenSocketException(Exception): """A socket has broken, because it is not sending data. Raised by Connection.get().""" diff --git a/bot/classes/data.py b/earwigbot/classes/data.py similarity index 61% rename from bot/classes/data.py rename to earwigbot/classes/data.py index 7638878..19e6171 100644 --- a/bot/classes/data.py +++ b/earwigbot/classes/data.py @@ -1,7 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re +__all__ = ["KwargParseException", "Data"] + class KwargParseException(Exception): """Couldn't parse a certain keyword argument in self.args, probably because it was given incorrectly: e.g., no value (abc), just a value (=xyz), just diff --git a/bot/classes/rc.py b/earwigbot/classes/rc.py similarity index 72% rename from bot/classes/rc.py rename to earwigbot/classes/rc.py index a2d23b6..557a0c4 100644 --- a/bot/classes/rc.py +++ b/earwigbot/classes/rc.py @@ -1,7 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re +__all__ = ["RC"] + class RC(object): """A class to store data on an event received from our IRC watcher.""" re_color = re.compile("\x03([0-9]{1,2}(,[0-9]{1,2})?)?") diff --git a/bot/commands/__init__.py b/earwigbot/commands/__init__.py similarity index 66% rename from bot/commands/__init__.py rename to earwigbot/commands/__init__.py index a17c32c..4f93b8d 100644 --- a/bot/commands/__init__.py +++ b/earwigbot/commands/__init__.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's IRC Command Manager @@ -11,20 +31,20 @@ import logging import os import sys -from classes import BaseCommand -import config +from earwigbot.classes import BaseCommand +from earwigbot.config import config __all__ = ["load", "get_all", "check"] # Base directory when searching for commands: -base_dir = os.path.join(config.root_dir, "bot", "commands") +base_dir = os.path.dirname(os.path.abspath(__file__)) # Store commands in a dict, where the key is the command's name and the value # is an instance of the command's class: _commands = {} # Logger for this module: -logger = logging.getLogger("tasks") +logger = logging.getLogger("earwigbot.tasks") def _load_command(connection, filename): """Try to load a specific command from a module, identified by file name. diff --git a/bot/commands/_old.py b/earwigbot/commands/_old.py similarity index 100% rename from bot/commands/_old.py rename to earwigbot/commands/_old.py diff --git a/bot/commands/afc_report.py b/earwigbot/commands/afc_report.py similarity index 71% rename from bot/commands/afc_report.py rename to earwigbot/commands/afc_report.py index 855f810..e296891 100644 --- a/bot/commands/afc_report.py +++ b/earwigbot/commands/afc_report.py @@ -1,10 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re -from classes import BaseCommand -import tasks -import wiki +from earwigbot.classes import BaseCommand +from earwigbot import tasks +from earwigbot import wiki class Command(BaseCommand): """Get information about an AFC submission by name.""" diff --git a/bot/commands/afc_status.py b/earwigbot/commands/afc_status.py similarity index 83% rename from bot/commands/afc_status.py rename to earwigbot/commands/afc_status.py index c2e219f..e502023 100644 --- a/bot/commands/afc_status.py +++ b/earwigbot/commands/afc_status.py @@ -1,10 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re -from classes import BaseCommand -import config -import wiki +from earwigbot import wiki +from earwigbot.classes import BaseCommand +from earwigbot.config import config class Command(BaseCommand): """Get the number of pending AfC submissions, open redirect requests, and diff --git a/bot/commands/calc.py b/earwigbot/commands/calc.py similarity index 60% rename from bot/commands/calc.py rename to earwigbot/commands/calc.py index 9fcb63a..c23937c 100644 --- a/bot/commands/calc.py +++ b/earwigbot/commands/calc.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re import urllib -from classes import BaseCommand +from earwigbot.classes import BaseCommand class Command(BaseCommand): """A somewhat advanced calculator: see http://futureboy.us/fsp/frink.fsp diff --git a/earwigbot/commands/chanops.py b/earwigbot/commands/chanops.py new file mode 100644 index 0000000..86e11f7 --- /dev/null +++ b/earwigbot/commands/chanops.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseCommand +from earwigbot.config import config + +class Command(BaseCommand): + """Voice, devoice, op, or deop users in the channel.""" + name = "chanops" + + def check(self, data): + commands = ["chanops", "voice", "devoice", "op", "deop"] + if data.is_command and data.command in commands: + return True + return False + + def process(self, data): + if data.command == "chanops": + msg = "available commands are !voice, !devoice, !op, and !deop." + self.connection.reply(data, msg) + return + + if data.host not in config.irc["permissions"]["admins"]: + msg = "you must be a bot admin to use this command." + self.connection.reply(data, msg) + return + + # If it is just !op/!devoice/whatever without arguments, assume they + # want to do this to themselves: + if not data.args: + target = data.nick + else: + target = data.args[0] + + msg = " ".join((data.command, data.chan, target)) + self.connection.say("ChanServ", msg) diff --git a/bot/commands/crypt.py b/earwigbot/commands/crypt.py similarity index 63% rename from bot/commands/crypt.py rename to earwigbot/commands/crypt.py index 2261112..d873576 100644 --- a/bot/commands/crypt.py +++ b/earwigbot/commands/crypt.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import hashlib -from classes import BaseCommand -import blowfish +from earwigbot.classes import BaseCommand +from earwigbot import blowfish class Command(BaseCommand): """Provides hash functions with !hash (!hash list for supported algorithms) diff --git a/bot/commands/ctcp.py b/earwigbot/commands/ctcp.py similarity index 54% rename from bot/commands/ctcp.py rename to earwigbot/commands/ctcp.py index 4b86b96..81073a8 100644 --- a/bot/commands/ctcp.py +++ b/earwigbot/commands/ctcp.py @@ -1,10 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import platform import time -from classes import BaseCommand -import config +from earwigbot.classes import BaseCommand +from earwigbot.config import config class Command(BaseCommand): """Not an actual command, this module is used to respond to the CTCP diff --git a/earwigbot/commands/editcount.py b/earwigbot/commands/editcount.py new file mode 100644 index 0000000..72f635d --- /dev/null +++ b/earwigbot/commands/editcount.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from urllib import quote_plus + +from earwigbot.classes import BaseCommand +from earwigbot import wiki + +class Command(BaseCommand): + """Return a user's edit count.""" + name = "editcount" + + def check(self, data): + commands = ["ec", "editcount"] + if data.is_command and data.command in commands: + return True + return False + + def process(self, data): + if not data.args: + name = data.nick + else: + name = ' '.join(data.args) + + site = wiki.get_site() + site._maxlag = None + user = site.get_user(name) + + try: + count = user.editcount() + except wiki.UserNotFoundError: + msg = "the user \x0302{0}\x0301 does not exist." + self.connection.reply(data, msg.format(name)) + return + + safe = quote_plus(user.name()) + url = "http://toolserver.org/~soxred93/pcount/index.php?name={0}&lang=en&wiki=wikipedia" + msg = "\x0302{0}\x0301 has {1} edits ({2})." + self.connection.reply(data, msg.format(name, count, url.format(safe))) diff --git a/bot/commands/git.py b/earwigbot/commands/git.py similarity index 84% rename from bot/commands/git.py rename to earwigbot/commands/git.py index 23dde86..dcec4fc 100644 --- a/bot/commands/git.py +++ b/earwigbot/commands/git.py @@ -1,11 +1,31 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import shlex import subprocess import re -from classes import BaseCommand -import config +from earwigbot.classes import BaseCommand +from earwigbot.config import config class Command(BaseCommand): """Commands to interface with the bot's git repository; use '!git' for a diff --git a/bot/commands/help.py b/earwigbot/commands/help.py similarity index 56% rename from bot/commands/help.py rename to earwigbot/commands/help.py index b35d205..394294e 100644 --- a/bot/commands/help.py +++ b/earwigbot/commands/help.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re -from classes import BaseCommand, Data -import commands +from earwigbot.classes import BaseCommand, Data +from earwigbot import commands class Command(BaseCommand): """Displays help information.""" diff --git a/bot/commands/link.py b/earwigbot/commands/link.py similarity index 62% rename from bot/commands/link.py rename to earwigbot/commands/link.py index 749da14..3ed9acf 100644 --- a/bot/commands/link.py +++ b/earwigbot/commands/link.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import re from urllib import quote -from classes import BaseCommand +from earwigbot.classes import BaseCommand class Command(BaseCommand): """Convert a Wikipedia page name into a URL.""" diff --git a/earwigbot/commands/praise.py b/earwigbot/commands/praise.py new file mode 100644 index 0000000..115c325 --- /dev/null +++ b/earwigbot/commands/praise.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import random + +from earwigbot.classes import BaseCommand + +class Command(BaseCommand): + """Praise people!""" + name = "praise" + + def check(self, data): + commands = ["praise", "earwig", "leonard", "leonard^bloom", "groove", + "groovedog"] + return data.is_command and data.command in commands + + def process(self, data): + if data.command == "earwig": + msg = "\x02Earwig\x0F is the bestest Python programmer ever!" + elif data.command in ["leonard", "leonard^bloom"]: + msg = "\x02Leonard^Bloom\x0F is the biggest slacker ever!" + elif data.command in ["groove", "groovedog"]: + msg = "\x02GrooveDog\x0F is the bestest heh evar!" + else: + if not data.args: + msg = "You use this command to praise certain people. Who they are is a secret." + else: + msg = "You're doing it wrong." + self.connection.reply(data, msg) + return + + self.connection.say(data.chan, msg) diff --git a/bot/commands/registration.py b/earwigbot/commands/registration.py similarity index 58% rename from bot/commands/registration.py rename to earwigbot/commands/registration.py index 4547872..d6fed87 100644 --- a/bot/commands/registration.py +++ b/earwigbot/commands/registration.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import time -from classes import BaseCommand -import wiki +from earwigbot.classes import BaseCommand +from earwigbot import wiki class Command(BaseCommand): """Return when a user registered.""" diff --git a/bot/commands/remind.py b/earwigbot/commands/remind.py similarity index 58% rename from bot/commands/remind.py rename to earwigbot/commands/remind.py index fd0234d..ddaf254 100644 --- a/bot/commands/remind.py +++ b/earwigbot/commands/remind.py @@ -1,9 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import threading import time -from classes import BaseCommand +from earwigbot.classes import BaseCommand class Command(BaseCommand): """Set a message to be repeated to you in a certain amount of time.""" diff --git a/earwigbot/commands/replag.py b/earwigbot/commands/replag.py new file mode 100644 index 0000000..f71ebb6 --- /dev/null +++ b/earwigbot/commands/replag.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from os.path import expanduser + +import oursql + +from earwigbot.classes import BaseCommand + +class Command(BaseCommand): + """Return the replag for a specific database on the Toolserver.""" + name = "replag" + + def process(self, data): + args = {} + if not data.args: + args["db"] = "enwiki_p" + else: + args["db"] = data.args[0] + args["host"] = args["db"].replace("_", "-") + ".rrdb.toolserver.org" + args["read_default_file"] = expanduser("~/.my.cnf") + + conn = oursql.connect(**args) + with conn.cursor() as cursor: + query = "SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(rc_timestamp) FROM recentchanges ORDER BY rc_timestamp DESC LIMIT 1" + cursor.execute(query) + replag = int(cursor.fetchall()[0][0]) + conn.close() + + msg = "Replag on \x0302{0}\x0301 is \x02{1}\x0F seconds." + self.connection.reply(data, msg.format(args["db"], replag)) diff --git a/earwigbot/commands/rights.py b/earwigbot/commands/rights.py new file mode 100644 index 0000000..465596f --- /dev/null +++ b/earwigbot/commands/rights.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseCommand +from earwigbot import wiki + +class Command(BaseCommand): + """Retrieve a list of rights for a given username.""" + name = "rights" + + def check(self, data): + commands = ["rights", "groups", "permissions", "privileges"] + if data.is_command and data.command in commands: + return True + return False + + def process(self, data): + if not data.args: + name = data.nick + else: + name = ' '.join(data.args) + + site = wiki.get_site() + site._maxlag = None + user = site.get_user(name) + + try: + rights = user.groups() + except wiki.UserNotFoundError: + msg = "the user \x0302{0}\x0301 does not exist." + self.connection.reply(data, msg.format(name)) + return + + try: + rights.remove("*") # Remove the '*' group given to everyone + except ValueError: + pass + msg = "the rights for \x0302{0}\x0301 are {1}." + self.connection.reply(data, msg.format(name, ', '.join(rights))) diff --git a/earwigbot/commands/test.py b/earwigbot/commands/test.py new file mode 100644 index 0000000..8975524 --- /dev/null +++ b/earwigbot/commands/test.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import random + +from earwigbot.classes import BaseCommand + +class Command(BaseCommand): + """Test the bot!""" + name = "test" + + def process(self, data): + hey = random.randint(0, 1) + if hey: + self.connection.say(data.chan, "Hey \x02%s\x0F!" % data.nick) + else: + self.connection.say(data.chan, "'sup \x02%s\x0F?" % data.nick) diff --git a/bot/commands/threads.py b/earwigbot/commands/threads.py similarity index 80% rename from bot/commands/threads.py rename to earwigbot/commands/threads.py index b3e6c36..346b4f5 100644 --- a/bot/commands/threads.py +++ b/earwigbot/commands/threads.py @@ -1,11 +1,31 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import threading import re -from classes import BaseCommand, Data, KwargParseException -import tasks -import config +from earwigbot import tasks +from earwigbot.classes import BaseCommand, Data, KwargParseException +from earwigbot.config import config class Command(BaseCommand): """Manage wiki tasks from IRC, and check on thread status.""" diff --git a/earwigbot/config.py b/earwigbot/config.py new file mode 100644 index 0000000..9374444 --- /dev/null +++ b/earwigbot/config.py @@ -0,0 +1,335 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot's JSON Config File Parser + +This handles all tasks involving reading and writing to our config file, +including encrypting and decrypting passwords and making a new config file from +scratch at the inital bot run. + +Usually you'll just want to do "from earwigbot.config import config", which +returns a singleton _BotConfig object, with data accessible from various +attributes and functions: + +* config.components - enabled components +* config.wiki - information about wiki-editing +* config.tasks - information for bot tasks +* config.irc - information about IRC +* config.metadata - miscellaneous information +* config.schedule() - tasks scheduled to run at a given time + +Additionally, _BotConfig has some functions used in config loading: +* config.load() - loads and parses our config file, returning True if + passwords are stored encrypted or False otherwise +* config.decrypt() - given a key, decrypts passwords inside our config + variables; won't work if passwords aren't encrypted +""" + +import json +import logging +import logging.handlers +from os import mkdir, path + +from earwigbot import blowfish + +__all__ = ["config"] + +class _ConfigNode(object): + def __iter__(self): + for key in self.__dict__.iterkeys(): + yield key + + def __getitem__(self, item): + return self.__dict__.__getitem__(item) + + def _dump(self): + data = self.__dict__.copy() + for key, val in data.iteritems(): + if isinstance(val, _ConfigNode): + data[key] = val.dump() + return data + + def _load(self, data): + self.__dict__ = data.copy() + + def _decrypt(self, key, intermediates, item): + base = self.__dict__ + try: + for inter in intermediates: + base = base[inter] + except KeyError: + return + if item in base: + base[item] = blowfish.decrypt(key, base[item]) + + def get(self, *args, **kwargs): + return self.__dict__.get(*args, **kwargs) + + +class _BotConfig(object): + def __init__(self): + self._script_dir = path.dirname(path.abspath(__file__)) + self._root_dir = path.split(self._script_dir)[0] + self._config_path = path.join(self._root_dir, "config.json") + self._log_dir = path.join(self._root_dir, "logs") + self._decryption_key = None + self._data = None + + self._components = _ConfigNode() + self._wiki = _ConfigNode() + self._tasks = _ConfigNode() + self._irc = _ConfigNode() + self._metadata = _ConfigNode() + + self._nodes = [self._components, self._wiki, self._tasks, self._irc, + self._metadata] + + def _load(self): + """Load data from our JSON config file (config.json) into _config.""" + filename = self._config_path + with open(filename, 'r') as fp: + try: + self._data = json.load(fp) + except ValueError as error: + print "Error parsing config file {0}:".format(filename) + print error + exit(1) + + def _setup_logging(self): + """Configures the logging module so it works the way we want it to.""" + log_dir = self._log_dir + logger = logging.getLogger("earwigbot") + logger.setLevel(logging.DEBUG) + + if self.metadata.get("enableLogging"): + hand = logging.handlers.TimedRotatingFileHandler + formatter = _BotFormatter() + color_formatter = _BotFormatter(color=True) + + logfile = lambda f: path.join(log_dir, f) + + if not path.isdir(log_dir): + if not path.exists(log_dir): + mkdir(log_dir, 0700) + else: + msg = "log_dir ({0}) exists but is not a directory!" + print msg.format(log_dir) + exit(1) + + main_handler = hand(logfile("bot.log"), "midnight", 1, 7) + error_handler = hand(logfile("error.log"), "W6", 1, 4) + debug_handler = hand(logfile("debug.log"), "H", 1, 6) + + main_handler.setLevel(logging.INFO) + error_handler.setLevel(logging.WARNING) + debug_handler.setLevel(logging.DEBUG) + + for h in (main_handler, error_handler, debug_handler): + h.setFormatter(formatter) + logger.addHandler(h) + + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.DEBUG) + stream_handler.setFormatter(color_formatter) + logger.addHandler(stream_handler) + + else: + logger.addHandler(logging.NullHandler()) + + def _make_new(self): + """Make a new config file based on the user's input.""" + encrypt = raw_input("Would you like to encrypt passwords stored in config.json? [y/n] ") + if encrypt.lower().startswith("y"): + is_encrypted = True + else: + is_encrypted = False + + return is_encrypted + + @property + def script_dir(self): + return self._script_dir + + @property + def root_dir(self): + return self._root_dir + + @property + def config_path(self): + return self._config_path + + @property + def log_dir(self): + return self._log_dir + + @property + def components(self): + """A dict of enabled components.""" + return self._components + + @property + def wiki(self): + """A dict of information about wiki-editing.""" + return self._wiki + + @property + def tasks(self): + """A dict of information for bot tasks.""" + return self._tasks + + @property + def irc(self): + """A dict of information about IRC.""" + return self._irc + + @property + def metadata(self): + """A dict of miscellaneous information.""" + return self._metadata + + def is_loaded(self): + """Return True if our config file has been loaded, otherwise False.""" + return self._data is not None + + def is_encrypted(self): + """Return True if passwords are encrypted, otherwise False.""" + return self.metadata.get("encryptPasswords", False) + + def load(self, config_path=None, log_dir=None): + """Load, or reload, our config file. + + First, check if we have a valid config file, and if not, notify the + user. If there is no config file at all, offer to make one, otherwise + exit. + + Store data from our config file in five _ConfigNodes (components, + wiki, tasks, irc, metadata) for easy access (as well as the internal + _data variable). + + If everything goes well, return True if stored passwords are + encrypted in the file, or False if they are not. + """ + if config_path: + self._config_path = config_path + if log_dir: + self._log_dir = log_dir + + if not path.exists(self._config_path): + print "You haven't configured the bot yet!" + choice = raw_input("Would you like to do this now? [y/n] ") + if choice.lower().startswith("y"): + return self._make_new() + else: + exit(1) + + self._load() + data = self._data + self.components._load(data.get("components", {})) + self.wiki._load(data.get("wiki", {})) + self.tasks._load(data.get("tasks", {})) + self.irc._load(data.get("irc", {})) + self.metadata._load(data.get("metadata", {})) + + self._setup_logging() + return self.is_encrypted() + + def decrypt(self, node, *nodes): + """Use self._decryption_key to decrypt an object in our config tree. + + If this is called when passwords are not encrypted (check with + config.is_encrypted()), nothing will happen. + + An example usage would be: + config.decrypt(config.irc, "frontend", "nickservPassword") + """ + if not self.is_encrypted(): + return + try: + node._decrypt(self._decryption_key, nodes[:-1], nodes[-1]) + except blowfish.BlowfishError as error: + print "\nError decrypting passwords:" + print "{0}: {1}.".format(error.__class__.__name__, error) + exit(1) + + def schedule(self, minute, hour, month_day, month, week_day): + """Return a list of tasks scheduled to run at the specified time. + + The schedule data comes from our config file's 'schedule' field, which + is stored as self._data["schedule"]. Call this function as + config.schedule(args). + """ + # Tasks to run this turn, each as a list of either [task_name, kwargs], + # or just the task_name: + tasks = [] + + now = {"minute": minute, "hour": hour, "month_day": month_day, + "month": month, "week_day": week_day} + + data = self._data.get("schedule", []) + for event in data: + do = True + for key, value in now.items(): + try: + requirement = event[key] + except KeyError: + continue + if requirement != value: + do = False + break + if do: + try: + tasks.extend(event["tasks"]) + except KeyError: + pass + + return tasks + + +class _BotFormatter(logging.Formatter): + def __init__(self, color=False): + self._format = super(_BotFormatter, self).format + if color: + fmt = "[%(asctime)s %(lvl)s] %(name)s: %(message)s" + self.format = lambda record: self._format(self.format_color(record)) + else: + fmt = "[%(asctime)s %(levelname)-8s] %(name)s: %(message)s" + self.format = self._format + datefmt = "%Y-%m-%d %H:%M:%S" + super(_BotFormatter, self).__init__(fmt=fmt, datefmt=datefmt) + + def format_color(self, record): + l = record.levelname.ljust(8) + if record.levelno == logging.DEBUG: + record.lvl = l.join(("\x1b[34m", "\x1b[0m")) # Blue + if record.levelno == logging.INFO: + record.lvl = l.join(("\x1b[32m", "\x1b[0m")) # Green + if record.levelno == logging.WARNING: + record.lvl = l.join(("\x1b[33m", "\x1b[0m")) # Yellow + if record.levelno == logging.ERROR: + record.lvl = l.join(("\x1b[31m", "\x1b[0m")) # Red + if record.levelno == logging.CRITICAL: + record.lvl = l.join(("\x1b[1m\x1b[31m", "\x1b[0m")) # Bold red + return record + + +config = _BotConfig() diff --git a/bot/frontend.py b/earwigbot/frontend.py similarity index 74% rename from bot/frontend.py rename to earwigbot/frontend.py index 1e98d50..0bfcaab 100644 --- a/bot/frontend.py +++ b/earwigbot/frontend.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's IRC Frontend Component @@ -12,14 +32,14 @@ imported by irc/command_handler.py if they are in irc/commands. import logging import re -import config -import commands -from classes import Connection, Data, BrokenSocketException +from earwigbot import commands +from earwigbot.classes import Connection, Data, BrokenSocketException +from earwigbot.config import config __all__ = ["get_connection", "startup", "main"] connection = None -logger = logging.getLogger("frontend") +logger = logging.getLogger("earwigbot.frontend") sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z") def get_connection(): diff --git a/bot/main.py b/earwigbot/main.py similarity index 65% rename from bot/main.py rename to earwigbot/main.py index a400e77..7f0c6f7 100644 --- a/bot/main.py +++ b/earwigbot/main.py @@ -1,12 +1,27 @@ -#! /usr/bin/env python # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ -EarwigBot's Core - -This should not be run directly; the wrapper in "earwigbot.py" is preferred, -but it should work fine alone, as long as you enter the password-unlock key at -the initial hidden prompt if one is needed. +EarwigBot's Main Module The core is essentially responsible for starting the various bot components (irc, scheduler, etc) and making sure they are all happy. An explanation of the @@ -34,11 +49,12 @@ import logging import threading import time -import config -import frontend -import tasks -import watcher +from earwigbot import frontend +from earwigbot import tasks +from earwigbot import watcher +from earwigbot.config import config +logger = logging.getLogger("earwigbot") f_conn = None w_conn = None @@ -52,9 +68,9 @@ def irc_watcher(f_conn=None): try: watcher.main(w_conn, f_conn) except: - logging.exception("Watcher had an error") + logger.exception("Watcher had an error") time.sleep(5) # sleep a bit before restarting watcher - logging.warn("Watcher has stopped; restarting component") + logger.warn("Watcher has stopped; restarting component") def wiki_scheduler(): """Function to handle the wiki scheduler as another thread, or as the @@ -75,13 +91,12 @@ def irc_frontend(): enable the wiki scheduler and IRC watcher on new threads if they are enabled.""" global f_conn - - logging.info("Starting IRC frontend") + logger.info("Starting IRC frontend") f_conn = frontend.get_connection() frontend.startup(f_conn) if "wiki_schedule" in config.components: - logging.info("Starting wiki scheduler") + logger.info("Starting wiki scheduler") tasks.load() t_scheduler = threading.Thread(target=wiki_scheduler) t_scheduler.name = "wiki-scheduler" @@ -89,7 +104,7 @@ def irc_frontend(): t_scheduler.start() if "irc_watcher" in config.components: - logging.info("Starting IRC watcher") + logger.info("Starting IRC watcher") t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,)) t_watcher.name = "irc-watcher" t_watcher.daemon = True @@ -101,50 +116,31 @@ def irc_frontend(): w_conn.close() f_conn.close() -def run(): - config.load() - try: - # Wait for our password decrypt key from the bot's wrapper, then - # decrypt passwords: - key = raw_input() - except EOFError: - pass - else: - config.decrypt(key) - - enabled = config.components - - if "irc_frontend" in enabled: +def main(): + if "irc_frontend" in config.components: # Make the frontend run on our primary thread if enabled, and enable # additional components through that function irc_frontend() - elif "wiki_schedule" in enabled: + elif "wiki_schedule" in config.components: # Run the scheduler on the main thread, but also run the IRC watcher on # another thread iff it is enabled - logging.info("Starting wiki scheduler") + logger.info("Starting wiki scheduler") tasks.load() if "irc_watcher" in enabled: - logging.info("Starting IRC watcher") + logger.info("Starting IRC watcher") t_watcher = threading.Thread(target=irc_watcher) t_watcher.name = "irc-watcher" t_watcher.daemon = True t_watcher.start() wiki_scheduler() - elif "irc_watcher" in enabled: + elif "irc_watcher" in config.components: # The IRC watcher is our only enabled component, so run its function # only and don't worry about anything else: - logging.info("Starting IRC watcher") + logger.info("Starting IRC watcher") irc_watcher() else: # Nothing is enabled! - logging.critical("No bot parts are enabled; stopping") - exit(1) - -if __name__ == "__main__": - try: - run() - except KeyboardInterrupt: - logging.critical("KeyboardInterrupt: stopping main bot loop") + logger.critical("No bot parts are enabled; stopping") exit(1) diff --git a/bot/rules.py b/earwigbot/rules.py similarity index 67% rename from bot/rules.py rename to earwigbot/rules.py index 5c55f9d..ce851cc 100644 --- a/bot/rules.py +++ b/earwigbot/rules.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's IRC Watcher Rules @@ -9,7 +29,7 @@ recieves an event from IRC. import re -import tasks +from earwigbot import tasks afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation" diff --git a/earwigbot/runner.py b/earwigbot/runner.py new file mode 100644 index 0000000..048c8b7 --- /dev/null +++ b/earwigbot/runner.py @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot Runner + +This is a very simple script that can be run from anywhere. It will add the +'earwigbot' package to sys.path if it's not already in there (i.e., it hasn't +been "installed"), accept a root_dir (the directory in which bot.py is located) +and a decryption key from raw_input (if passwords are encrypted), then call +config.load() and decrypt any passwords, and finally call the main() function +of earwigbot.main. +""" + +from os import path +import sys + +def run(): + pkg_dir = path.split(path.dirname(path.abspath(__file__)))[0] + if pkg_dir not in sys.path: + sys.path.insert(0, pkg_dir) + + from earwigbot.config import config + from earwigbot import main + + root_dir = raw_input() + config_path = path.join(root_dir, "config.json") + log_dir = path.join(root_dir, "logs") + is_encrypted = config.load(config_path, log_dir) + if is_encrypted: + config._decryption_key = raw_input() + config.decrypt(config.wiki, "password") + config.decrypt(config.irc, "frontend", "nickservPassword") + config.decrypt(config.irc, "watcher", "nickservPassword") + + try: + main.main() + except KeyboardInterrupt: + main.logger.critical("KeyboardInterrupt: stopping main bot loop") + exit(1) + +if __name__ == "__main__": + run() diff --git a/bot/tasks/__init__.py b/earwigbot/tasks/__init__.py similarity index 72% rename from bot/tasks/__init__.py rename to earwigbot/tasks/__init__.py index da91cc1..8cb0bfb 100644 --- a/bot/tasks/__init__.py +++ b/earwigbot/tasks/__init__.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's Wiki Task Manager @@ -13,20 +33,20 @@ import sys import threading import time -from classes import BaseTask -import config +from earwigbot.classes import BaseTask +from earwigbot.config import config __all__ = ["load", "schedule", "start", "get", "get_all"] # Base directory when searching for tasks: -base_dir = os.path.join(config.root_dir, "bot", "tasks") +base_dir = os.path.dirname(os.path.abspath(__file__)) # Store loaded tasks as a dict where the key is the task name and the value is # an instance of the task class: _tasks = {} # Logger for this module: -logger = logging.getLogger("commands") +logger = logging.getLogger("earwigbot.commands") def _load_task(filename): """Try to load a specific task from a module, identified by file name.""" diff --git a/earwigbot/tasks/afc_catdelink.py b/earwigbot/tasks/afc_catdelink.py new file mode 100644 index 0000000..17d9f9b --- /dev/null +++ b/earwigbot/tasks/afc_catdelink.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """A task to delink mainspace categories in declined [[WP:AFC]] + submissions.""" + name = "afc_catdelink" + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/earwigbot/tasks/afc_copyvios.py b/earwigbot/tasks/afc_copyvios.py new file mode 100644 index 0000000..2868cfe --- /dev/null +++ b/earwigbot/tasks/afc_copyvios.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask +from earwigbot.config import config + +class Task(BaseTask): + """A task to check newly-edited [[WP:AFC]] submissions for copyright + violations.""" + name = "afc_copyvios" + number = 1 + + def __init__(self): + self.cfg = cfg = config.tasks.get(self.name, {}) + config.decrypt(config.tasks, self.name, "search", "credentials", "key") + config.decrypt(config.tasks, self.name, "search", "credentials", "secret") + + def run(self, **kwargs): + pass diff --git a/earwigbot/tasks/afc_dailycats.py b/earwigbot/tasks/afc_dailycats.py new file mode 100644 index 0000000..99545e3 --- /dev/null +++ b/earwigbot/tasks/afc_dailycats.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """ A task to create daily categories for [[WP:AFC]].""" + name = "afc_dailycats" + number = 3 + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/bot/tasks/afc_history.py b/earwigbot/tasks/afc_history.py similarity index 86% rename from bot/tasks/afc_history.py rename to earwigbot/tasks/afc_history.py index a5c3611..0e7d855 100644 --- a/bot/tasks/afc_history.py +++ b/earwigbot/tasks/afc_history.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from collections import OrderedDict from datetime import datetime, timedelta @@ -11,9 +31,9 @@ from matplotlib import pyplot as plt from numpy import arange import oursql -from classes import BaseTask -import config -import wiki +from earwigbot import wiki +from earwigbot.classes import BaseTask +from earwigbot.config import config # Valid submission statuses: STATUS_NONE = 0 diff --git a/bot/tasks/afc_statistics.py b/earwigbot/tasks/afc_statistics.py similarity index 95% rename from bot/tasks/afc_statistics.py rename to earwigbot/tasks/afc_statistics.py index abb3582..0328bce 100644 --- a/bot/tasks/afc_statistics.py +++ b/earwigbot/tasks/afc_statistics.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from datetime import datetime import logging @@ -9,9 +29,9 @@ from time import sleep import oursql -from classes import BaseTask -import config -import wiki +from earwigbot import wiki +from earwigbot.classes import BaseTask +from earwigbot.config import config # Chart status number constants: CHART_NONE = 0 @@ -178,6 +198,7 @@ class Task(BaseTask): if replag > 600 and not kwargs.get("ignore_replag"): msg = "Sync canceled as replag ({0} secs) is greater than ten minutes." self.logger.warn(msg.format(replag)) + return with self.conn.cursor() as cursor: self.update_tracked(cursor) diff --git a/earwigbot/tasks/afc_undated.py b/earwigbot/tasks/afc_undated.py new file mode 100644 index 0000000..5282544 --- /dev/null +++ b/earwigbot/tasks/afc_undated.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """A task to clear [[Category:Undated AfC submissions]].""" + name = "afc_undated" + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/earwigbot/tasks/blptag.py b/earwigbot/tasks/blptag.py new file mode 100644 index 0000000..0ed70d0 --- /dev/null +++ b/earwigbot/tasks/blptag.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """A task to add |blp=yes to {{WPB}} or {{WPBS}} when it is used along with + {{WP Biography}}.""" + name = "blptag" + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/earwigbot/tasks/feed_dailycats.py b/earwigbot/tasks/feed_dailycats.py new file mode 100644 index 0000000..4e83908 --- /dev/null +++ b/earwigbot/tasks/feed_dailycats.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """A task to create daily categories for [[WP:FEED]].""" + name = "feed_dailycats" + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/earwigbot/tasks/wrongmime.py b/earwigbot/tasks/wrongmime.py new file mode 100644 index 0000000..c7f0bca --- /dev/null +++ b/earwigbot/tasks/wrongmime.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from earwigbot.classes import BaseTask + +class Task(BaseTask): + """A task to tag files whose extensions do not agree with their MIME + type.""" + name = "wrongmime" + + def __init__(self): + pass + + def run(self, **kwargs): + pass diff --git a/tests/support.py b/earwigbot/tests/__init__.py similarity index 64% rename from tests/support.py rename to earwigbot/tests/__init__.py index a2dadb5..678b288 100644 --- a/tests/support.py +++ b/earwigbot/tests/__init__.py @@ -1,12 +1,29 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ -EarwigBot's Unit Test Support +EarwigBot's Unit Tests -This module provides some support code for unit tests. - -Importing this module will "fix" your path so that EarwigBot code from bot/ can -be imported normally. +This module __init__ file provides some support code for unit tests. CommandTestCase is a subclass of unittest.TestCase that provides setUp() for creating a fake connection and some other helpful methods. It uses @@ -14,16 +31,10 @@ FakeConnection, a subclass of classes.Connection, but with an internal string instead of a socket for data. """ -from os import path import re -import sys from unittest import TestCase -root_dir = path.split(path.dirname(path.abspath(__file__)))[0] -code_dir = path.join(root_dir, "bot") -sys.path.insert(0, code_dir) - -from classes import Connection, Data +from earwigbot.classes import Connection, Data class CommandTestCase(TestCase): re_sender = re.compile(":(.*?)!(.*?)@(.*?)\Z") diff --git a/tests/test_blowfish.py b/earwigbot/tests/test_blowfish.py similarity index 72% rename from tests/test_blowfish.py rename to earwigbot/tests/test_blowfish.py index 9eb6184..dbb335f 100644 --- a/tests/test_blowfish.py +++ b/earwigbot/tests/test_blowfish.py @@ -1,11 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import unittest import random import string -import support -import blowfish +from earwigbot import blowfish class TestBlowfish(unittest.TestCase): diff --git a/earwigbot/tests/test_calc.py b/earwigbot/tests/test_calc.py new file mode 100644 index 0000000..4c175c5 --- /dev/null +++ b/earwigbot/tests/test_calc.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import unittest + +from earwigbot.commands.calc import Command + +class TestCalc(support.CommandTestCase): + + def setUp(self): + super(TestCalc, self).setUp(Command) + + def test_check(self): + self.assertFalse(self.command.check(self.make_msg("bloop"))) + self.assertFalse(self.command.check(self.make_join())) + + self.assertTrue(self.command.check(self.make_msg("calc"))) + self.assertTrue(self.command.check(self.make_msg("CALC", "foo"))) + + def test_ignore_empty(self): + self.command.process(self.make_msg("calc")) + self.assertReply("what do you want me to calculate?") + + def test_maths(self): + tests = [ + ("2 + 2", "2 + 2 = 4"), + ("13 * 5", "13 * 5 = 65"), + ("80 / 42", "80 / 42 = 40/21 (approx. 1.9047619047619047)"), + ("2/0", "2/0 = undef"), + ("π", "π = 3.141592653589793238"), + ] + + for test in tests: + q = test[0].strip().split() + self.command.process(self.make_msg("calc", *q)) + self.assertReply(test[1]) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/earwigbot/tests/test_test.py b/earwigbot/tests/test_test.py new file mode 100644 index 0000000..be077cd --- /dev/null +++ b/earwigbot/tests/test_test.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import unittest + +from earwigbot.commands.test import Command + +class TestTest(support.CommandTestCase): + + def setUp(self): + super(TestTest, self).setUp(Command) + + def test_check(self): + self.assertFalse(self.command.check(self.make_msg("bloop"))) + self.assertFalse(self.command.check(self.make_join())) + + self.assertTrue(self.command.check(self.make_msg("test"))) + self.assertTrue(self.command.check(self.make_msg("TEST", "foo"))) + + def test_process(self): + def _test(): + self.command.process(self.make_msg("test")) + self.assertSaidIn(["Hey \x02Foo\x0F!", "'sup \x02Foo\x0F?"]) + + for i in xrange(64): + _test() + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/bot/watcher.py b/earwigbot/watcher.py similarity index 68% rename from bot/watcher.py rename to earwigbot/watcher.py index 577cc6e..8c9c4e3 100644 --- a/bot/watcher.py +++ b/earwigbot/watcher.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's IRC Watcher Component @@ -12,12 +32,12 @@ frontend. import logging -import config -from classes import Connection, RC, BrokenSocketException -import rules +from earwigbot import rules +from earwigbot.classes import Connection, RC, BrokenSocketException +from earwigbot.config import config frontend_conn = None -logger = logging.getLogger("watcher") +logger = logging.getLogger("earwigbot.watcher") def get_connection(): """Return a new Connection() instance with connection information. diff --git a/earwigbot/wiki/__init__.py b/earwigbot/wiki/__init__.py new file mode 100644 index 0000000..c8c7e6b --- /dev/null +++ b/earwigbot/wiki/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot's Wiki Toolset + +This is a collection of classes and functions to read from and write to +Wikipedia and other wiki sites. No connection whatsoever to python-wikitools +written by Mr.Z-man, other than a similar purpose. We share no code. + +Import the toolset with `from earwigbot import wiki`. +""" + +import logging as _log +logger = _log.getLogger("earwigbot.wiki") +logger.addHandler(_log.NullHandler()) + +from earwigbot.wiki.constants import * +from earwigbot.wiki.exceptions import * +from earwigbot.wiki.functions import * + +from earwigbot.wiki.category import Category +from earwigbot.wiki.page import Page +from earwigbot.wiki.site import Site +from earwigbot.wiki.user import User diff --git a/bot/wiki/category.py b/earwigbot/wiki/category.py similarity index 69% rename from bot/wiki/category.py rename to earwigbot/wiki/category.py index c1a9301..0c279ed 100644 --- a/bot/wiki/category.py +++ b/earwigbot/wiki/category.py @@ -1,6 +1,26 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. -from wiki.page import Page +from earwigbot.wiki.page import Page class Category(Page): """ diff --git a/earwigbot/wiki/constants.py b/earwigbot/wiki/constants.py new file mode 100644 index 0000000..85e8118 --- /dev/null +++ b/earwigbot/wiki/constants.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +EarwigBot's Wiki Toolset: Constants + +This module defines some useful constants: +* USER_AGENT - our default User Agent when making API queries +* NS_* - default namespace IDs for easy lookup + +Import with `from earwigbot.wiki import constants` or `from earwigbot.wiki.constants import *`. +""" + +# Default User Agent when making API queries: +from platform import python_version as _p +USER_AGENT = "EarwigBot/0.1-dev (Python/{0}; https://github.com/earwig/earwigbot)".format(_p()) + +# Default namespace IDs: +NS_MAIN = 0 +NS_TALK = 1 +NS_USER = 2 +NS_USER_TALK = 3 +NS_PROJECT = 4 +NS_PROJECT_TALK = 5 +NS_FILE = 6 +NS_FILE_TALK = 7 +NS_MEDIAWIKI = 8 +NS_MEDIAWIKI_TALK = 9 +NS_TEMPLATE = 10 +NS_TEMPLATE_TALK = 11 +NS_HELP = 12 +NS_HELP_TALK = 13 +NS_CATEGORY = 14 +NS_CATEGORY_TALK = 15 +NS_SPECIAL = -1 +NS_MEDIA = -2 diff --git a/bot/wiki/exceptions.py b/earwigbot/wiki/exceptions.py similarity index 67% rename from bot/wiki/exceptions.py rename to earwigbot/wiki/exceptions.py index 3e964cf..c23a743 100644 --- a/bot/wiki/exceptions.py +++ b/earwigbot/wiki/exceptions.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's Wiki Toolset: Exceptions diff --git a/bot/wiki/functions.py b/earwigbot/wiki/functions.py similarity index 84% rename from bot/wiki/functions.py rename to earwigbot/wiki/functions.py index eae18a8..5648976 100644 --- a/bot/wiki/functions.py +++ b/earwigbot/wiki/functions.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ EarwigBot's Wiki Toolset: Misc Functions @@ -7,7 +27,7 @@ This module, a component of the wiki package, contains miscellaneous functions that are not methods of any class, like get_site(). There's no need to import this module explicitly. All functions here are -automatically available from wiki. +automatically available from earwigbot.wiki. """ from cookielib import LWPCookieJar, LoadError @@ -17,11 +37,11 @@ from os import chmod, path import platform import stat -import config -from wiki.exceptions import SiteNotFoundError -from wiki.site import Site +from earwigbot.config import config +from earwigbot.wiki.exceptions import SiteNotFoundError +from earwigbot.wiki.site import Site -__all__ = ["get_site"] +__all__ = ["get_site", "add_site", "del_site"] _cookiejar = None diff --git a/bot/wiki/page.py b/earwigbot/wiki/page.py similarity index 95% rename from bot/wiki/page.py rename to earwigbot/wiki/page.py index 970197b..5fca120 100644 --- a/bot/wiki/page.py +++ b/earwigbot/wiki/page.py @@ -1,11 +1,31 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from hashlib import md5 import re from time import gmtime, strftime from urllib import quote -from wiki.exceptions import * +from earwigbot.wiki.exceptions import * class Page(object): """ diff --git a/bot/wiki/site.py b/earwigbot/wiki/site.py similarity index 94% rename from bot/wiki/site.py rename to earwigbot/wiki/site.py index 3f76d05..806d711 100644 --- a/bot/wiki/site.py +++ b/earwigbot/wiki/site.py @@ -1,4 +1,24 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from cookielib import CookieJar from gzip import GzipFile @@ -16,12 +36,12 @@ try: except ImportError: oursql = None -from wiki import logger -from wiki.category import Category -from wiki.constants import * -from wiki.exceptions import * -from wiki.page import Page -from wiki.user import User +from earwigbot.wiki import logger +from earwigbot.wiki.category import Category +from earwigbot.wiki.constants import * +from earwigbot.wiki.exceptions import * +from earwigbot.wiki.page import Page +from earwigbot.wiki.user import User class Site(object): """ @@ -531,7 +551,8 @@ class Site(object): large number of edits (ideally, at least one per second), or the result may be larger than expected. """ - query = "SELECT NOW() - MAX(rev_timestamp) FROM revision" + query = """SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(rc_timestamp) FROM + recentchanges ORDER BY rc_timestamp DESC LIMIT 1""" result = list(self.sql_query(query)) return result[0][0] diff --git a/bot/wiki/user.py b/earwigbot/wiki/user.py similarity index 87% rename from bot/wiki/user.py rename to earwigbot/wiki/user.py index f65b9fc..f308569 100644 --- a/bot/wiki/user.py +++ b/earwigbot/wiki/user.py @@ -1,10 +1,30 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2009, 2010, 2011 by Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from time import gmtime, strptime -from wiki.constants import * -from wiki.exceptions import UserNotFoundError -from wiki.page import Page +from earwigbot.wiki.constants import * +from earwigbot.wiki.exceptions import UserNotFoundError +from earwigbot.wiki.page import Page class User(object): """ diff --git a/tests/test_calc.py b/tests/test_calc.py deleted file mode 100644 index 981fda3..0000000 --- a/tests/test_calc.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -import support -from commands.calc import Command - -class TestCalc(support.CommandTestCase): - - def setUp(self): - super(TestCalc, self).setUp(Command) - - def test_check(self): - self.assertFalse(self.command.check(self.make_msg("bloop"))) - self.assertFalse(self.command.check(self.make_join())) - - self.assertTrue(self.command.check(self.make_msg("calc"))) - self.assertTrue(self.command.check(self.make_msg("CALC", "foo"))) - - def test_ignore_empty(self): - self.command.process(self.make_msg("calc")) - self.assertReply("what do you want me to calculate?") - - def test_maths(self): - tests = [ - ("2 + 2", "2 + 2 = 4"), - ("13 * 5", "13 * 5 = 65"), - ("80 / 42", "80 / 42 = 40/21 (approx. 1.9047619047619047)"), - ("2/0", "2/0 = undef"), - ("π", "π = 3.141592653589793238"), - ] - - for test in tests: - q = test[0].strip().split() - self.command.process(self.make_msg("calc", *q)) - self.assertReply(test[1]) - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/tests/test_test.py b/tests/test_test.py deleted file mode 100644 index 540e1eb..0000000 --- a/tests/test_test.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -import support -from commands.test import Command - -class TestTest(support.CommandTestCase): - - def setUp(self): - super(TestTest, self).setUp(Command) - - def test_check(self): - self.assertFalse(self.command.check(self.make_msg("bloop"))) - self.assertFalse(self.command.check(self.make_join())) - - self.assertTrue(self.command.check(self.make_msg("test"))) - self.assertTrue(self.command.check(self.make_msg("TEST", "foo"))) - - def test_process(self): - def _test(): - self.command.process(self.make_msg("test")) - self.assertSaidIn(["Hey \x02Foo\x0F!", "'sup \x02Foo\x0F?"]) - - for i in xrange(64): - _test() - -if __name__ == "__main__": - unittest.main(verbosity=2)