| @@ -0,0 +1,70 @@ | |||||
| #! /usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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() | |||||
| @@ -1,5 +0,0 @@ | |||||
| from base_command import * | |||||
| from base_task import * | |||||
| from connection import * | |||||
| from data import * | |||||
| from rc import * | |||||
| @@ -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) | |||||
| @@ -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))) | |||||
| @@ -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) | |||||
| @@ -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)) | |||||
| @@ -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))) | |||||
| @@ -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) | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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 | |||||
| @@ -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." | |||||
| @@ -0,0 +1,37 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| ) | |||||
| @@ -1,3 +1,4 @@ | |||||
| #! /usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | # | ||||
| # blowfish.py | # blowfish.py | ||||
| @@ -0,0 +1,27 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 * | |||||
| @@ -1,15 +1,37 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 logging | ||||
| __all__ = ["BaseCommand"] | |||||
| class BaseCommand(object): | class BaseCommand(object): | ||||
| """A base class for commands on IRC. | """A base class for commands on IRC. | ||||
| This docstring is reported to the user when they use !help <command>. | This docstring is reported to the user when they use !help <command>. | ||||
| """ | """ | ||||
| # This is the command's name, as 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 | # 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 | # default behavior; if you wish to override that, change the value in your | ||||
| # command subclass: | # command subclass: | ||||
| @@ -24,7 +46,8 @@ class BaseCommand(object): | |||||
| from within a method. | from within a method. | ||||
| """ | """ | ||||
| self.connection = connection | 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) | self.logger.setLevel(logging.DEBUG) | ||||
| def check(self, data): | def check(self, data): | ||||
| @@ -1,9 +1,31 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 logging | ||||
| import config | |||||
| import wiki | |||||
| from earwigbot.config import config | |||||
| from earwigbot import wiki | |||||
| __all__ = ["BaseTask"] | |||||
| class BaseTask(object): | class BaseTask(object): | ||||
| """A base class for bot tasks that edit Wikipedia.""" | """A base class for bot tasks that edit Wikipedia.""" | ||||
| @@ -20,7 +42,8 @@ class BaseTask(object): | |||||
| def _setup_logger(self): | def _setup_logger(self): | ||||
| """Set up a basic module-level logger.""" | """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) | self.logger.setLevel(logging.DEBUG) | ||||
| def run(self, **kwargs): | def run(self, **kwargs): | ||||
| @@ -1,8 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 socket | ||||
| import threading | import threading | ||||
| __all__ = ["BrokenSocketException", "Connection"] | |||||
| class BrokenSocketException(Exception): | class BrokenSocketException(Exception): | ||||
| """A socket has broken, because it is not sending data. Raised by | """A socket has broken, because it is not sending data. Raised by | ||||
| Connection.get().""" | Connection.get().""" | ||||
| @@ -1,7 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 re | ||||
| __all__ = ["KwargParseException", "Data"] | |||||
| class KwargParseException(Exception): | class KwargParseException(Exception): | ||||
| """Couldn't parse a certain keyword argument in self.args, probably because | """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 | it was given incorrectly: e.g., no value (abc), just a value (=xyz), just | ||||
| @@ -1,7 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 re | ||||
| __all__ = ["RC"] | |||||
| class RC(object): | class RC(object): | ||||
| """A class to store data on an event received from our IRC watcher.""" | """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})?)?") | re_color = re.compile("\x03([0-9]{1,2}(,[0-9]{1,2})?)?") | ||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's IRC Command Manager | ||||
| @@ -11,20 +31,20 @@ import logging | |||||
| import os | import os | ||||
| import sys | import sys | ||||
| from classes import BaseCommand | |||||
| import config | |||||
| from earwigbot.classes import BaseCommand | |||||
| from earwigbot.config import config | |||||
| __all__ = ["load", "get_all", "check"] | __all__ = ["load", "get_all", "check"] | ||||
| # Base directory when searching for commands: | # 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 | # Store commands in a dict, where the key is the command's name and the value | ||||
| # is an instance of the command's class: | # is an instance of the command's class: | ||||
| _commands = {} | _commands = {} | ||||
| # Logger for this module: | # Logger for this module: | ||||
| logger = logging.getLogger("tasks") | |||||
| logger = logging.getLogger("earwigbot.tasks") | |||||
| def _load_command(connection, filename): | def _load_command(connection, filename): | ||||
| """Try to load a specific command from a module, identified by file name. | """Try to load a specific command from a module, identified by file name. | ||||
| @@ -1,10 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 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): | class Command(BaseCommand): | ||||
| """Get information about an AFC submission by name.""" | """Get information about an AFC submission by name.""" | ||||
| @@ -1,10 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 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): | class Command(BaseCommand): | ||||
| """Get the number of pending AfC submissions, open redirect requests, and | """Get the number of pending AfC submissions, open redirect requests, and | ||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 re | ||||
| import urllib | import urllib | ||||
| from classes import BaseCommand | |||||
| from earwigbot.classes import BaseCommand | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """A somewhat advanced calculator: see http://futureboy.us/fsp/frink.fsp | """A somewhat advanced calculator: see http://futureboy.us/fsp/frink.fsp | ||||
| @@ -0,0 +1,55 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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) | |||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | import hashlib | ||||
| from classes import BaseCommand | |||||
| import blowfish | |||||
| from earwigbot.classes import BaseCommand | |||||
| from earwigbot import blowfish | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Provides hash functions with !hash (!hash list for supported algorithms) | """Provides hash functions with !hash (!hash list for supported algorithms) | ||||
| @@ -1,10 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 platform | ||||
| import time | import time | ||||
| from classes import BaseCommand | |||||
| import config | |||||
| from earwigbot.classes import BaseCommand | |||||
| from earwigbot.config import config | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Not an actual command, this module is used to respond to the CTCP | """Not an actual command, this module is used to respond to the CTCP | ||||
| @@ -0,0 +1,58 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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))) | |||||
| @@ -1,11 +1,31 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 shlex | ||||
| import subprocess | import subprocess | ||||
| import re | import re | ||||
| from classes import BaseCommand | |||||
| import config | |||||
| from earwigbot.classes import BaseCommand | |||||
| from earwigbot.config import config | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Commands to interface with the bot's git repository; use '!git' for a | """Commands to interface with the bot's git repository; use '!git' for a | ||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 re | ||||
| from classes import BaseCommand, Data | |||||
| import commands | |||||
| from earwigbot.classes import BaseCommand, Data | |||||
| from earwigbot import commands | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Displays help information.""" | """Displays help information.""" | ||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 re | ||||
| from urllib import quote | from urllib import quote | ||||
| from classes import BaseCommand | |||||
| from earwigbot.classes import BaseCommand | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Convert a Wikipedia page name into a URL.""" | """Convert a Wikipedia page name into a URL.""" | ||||
| @@ -0,0 +1,51 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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) | |||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | import time | ||||
| from classes import BaseCommand | |||||
| import wiki | |||||
| from earwigbot.classes import BaseCommand | |||||
| from earwigbot import wiki | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Return when a user registered.""" | """Return when a user registered.""" | ||||
| @@ -1,9 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 threading | ||||
| import time | import time | ||||
| from classes import BaseCommand | |||||
| from earwigbot.classes import BaseCommand | |||||
| class Command(BaseCommand): | class Command(BaseCommand): | ||||
| """Set a message to be repeated to you in a certain amount of time.""" | """Set a message to be repeated to you in a certain amount of time.""" | ||||
| @@ -0,0 +1,50 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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)) | |||||
| @@ -0,0 +1,58 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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))) | |||||
| @@ -0,0 +1,36 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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) | |||||
| @@ -1,11 +1,31 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 threading | ||||
| import re | 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): | class Command(BaseCommand): | ||||
| """Manage wiki tasks from IRC, and check on thread status.""" | """Manage wiki tasks from IRC, and check on thread status.""" | ||||
| @@ -0,0 +1,335 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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() | |||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's IRC Frontend Component | ||||
| @@ -12,14 +32,14 @@ imported by irc/command_handler.py if they are in irc/commands. | |||||
| import logging | import logging | ||||
| import re | 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"] | __all__ = ["get_connection", "startup", "main"] | ||||
| connection = None | connection = None | ||||
| logger = logging.getLogger("frontend") | |||||
| logger = logging.getLogger("earwigbot.frontend") | |||||
| sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z") | sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z") | ||||
| def get_connection(): | def get_connection(): | ||||
| @@ -1,12 +1,27 @@ | |||||
| #! /usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | 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 | (irc, scheduler, etc) and making sure they are all happy. An explanation of the | ||||
| @@ -34,11 +49,12 @@ import logging | |||||
| import threading | import threading | ||||
| import time | 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 | f_conn = None | ||||
| w_conn = None | w_conn = None | ||||
| @@ -52,9 +68,9 @@ def irc_watcher(f_conn=None): | |||||
| try: | try: | ||||
| watcher.main(w_conn, f_conn) | watcher.main(w_conn, f_conn) | ||||
| except: | except: | ||||
| logging.exception("Watcher had an error") | |||||
| logger.exception("Watcher had an error") | |||||
| time.sleep(5) # sleep a bit before restarting watcher | 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(): | def wiki_scheduler(): | ||||
| """Function to handle the wiki scheduler as another thread, or as the | """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 | enable the wiki scheduler and IRC watcher on new threads if they are | ||||
| enabled.""" | enabled.""" | ||||
| global f_conn | global f_conn | ||||
| logging.info("Starting IRC frontend") | |||||
| logger.info("Starting IRC frontend") | |||||
| f_conn = frontend.get_connection() | f_conn = frontend.get_connection() | ||||
| frontend.startup(f_conn) | frontend.startup(f_conn) | ||||
| if "wiki_schedule" in config.components: | if "wiki_schedule" in config.components: | ||||
| logging.info("Starting wiki scheduler") | |||||
| logger.info("Starting wiki scheduler") | |||||
| tasks.load() | tasks.load() | ||||
| t_scheduler = threading.Thread(target=wiki_scheduler) | t_scheduler = threading.Thread(target=wiki_scheduler) | ||||
| t_scheduler.name = "wiki-scheduler" | t_scheduler.name = "wiki-scheduler" | ||||
| @@ -89,7 +104,7 @@ def irc_frontend(): | |||||
| t_scheduler.start() | t_scheduler.start() | ||||
| if "irc_watcher" in config.components: | 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 = threading.Thread(target=irc_watcher, args=(f_conn,)) | ||||
| t_watcher.name = "irc-watcher" | t_watcher.name = "irc-watcher" | ||||
| t_watcher.daemon = True | t_watcher.daemon = True | ||||
| @@ -101,50 +116,31 @@ def irc_frontend(): | |||||
| w_conn.close() | w_conn.close() | ||||
| f_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 | # Make the frontend run on our primary thread if enabled, and enable | ||||
| # additional components through that function | # additional components through that function | ||||
| irc_frontend() | 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 | # Run the scheduler on the main thread, but also run the IRC watcher on | ||||
| # another thread iff it is enabled | # another thread iff it is enabled | ||||
| logging.info("Starting wiki scheduler") | |||||
| logger.info("Starting wiki scheduler") | |||||
| tasks.load() | tasks.load() | ||||
| if "irc_watcher" in enabled: | if "irc_watcher" in enabled: | ||||
| logging.info("Starting IRC watcher") | |||||
| logger.info("Starting IRC watcher") | |||||
| t_watcher = threading.Thread(target=irc_watcher) | t_watcher = threading.Thread(target=irc_watcher) | ||||
| t_watcher.name = "irc-watcher" | t_watcher.name = "irc-watcher" | ||||
| t_watcher.daemon = True | t_watcher.daemon = True | ||||
| t_watcher.start() | t_watcher.start() | ||||
| wiki_scheduler() | 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 | # The IRC watcher is our only enabled component, so run its function | ||||
| # only and don't worry about anything else: | # only and don't worry about anything else: | ||||
| logging.info("Starting IRC watcher") | |||||
| logger.info("Starting IRC watcher") | |||||
| irc_watcher() | irc_watcher() | ||||
| else: # Nothing is enabled! | 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) | exit(1) | ||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's IRC Watcher Rules | ||||
| @@ -9,7 +29,7 @@ recieves an event from IRC. | |||||
| import re | import re | ||||
| import tasks | |||||
| from earwigbot import tasks | |||||
| afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation" | afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation" | ||||
| @@ -0,0 +1,63 @@ | |||||
| #! /usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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() | |||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's Wiki Task Manager | ||||
| @@ -13,20 +33,20 @@ import sys | |||||
| import threading | import threading | ||||
| import time | 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"] | __all__ = ["load", "schedule", "start", "get", "get_all"] | ||||
| # Base directory when searching for tasks: | # 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 | # Store loaded tasks as a dict where the key is the task name and the value is | ||||
| # an instance of the task class: | # an instance of the task class: | ||||
| _tasks = {} | _tasks = {} | ||||
| # Logger for this module: | # Logger for this module: | ||||
| logger = logging.getLogger("commands") | |||||
| logger = logging.getLogger("earwigbot.commands") | |||||
| def _load_task(filename): | def _load_task(filename): | ||||
| """Try to load a specific task from a module, identified by file name.""" | """Try to load a specific task from a module, identified by file name.""" | ||||
| @@ -0,0 +1,34 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -0,0 +1,38 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -0,0 +1,34 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 collections import OrderedDict | ||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||
| @@ -11,9 +31,9 @@ from matplotlib import pyplot as plt | |||||
| from numpy import arange | from numpy import arange | ||||
| import oursql | 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: | # Valid submission statuses: | ||||
| STATUS_NONE = 0 | STATUS_NONE = 0 | ||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | from datetime import datetime | ||||
| import logging | import logging | ||||
| @@ -9,9 +29,9 @@ from time import sleep | |||||
| import oursql | 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 status number constants: | ||||
| CHART_NONE = 0 | CHART_NONE = 0 | ||||
| @@ -178,6 +198,7 @@ class Task(BaseTask): | |||||
| if replag > 600 and not kwargs.get("ignore_replag"): | if replag > 600 and not kwargs.get("ignore_replag"): | ||||
| msg = "Sync canceled as replag ({0} secs) is greater than ten minutes." | msg = "Sync canceled as replag ({0} secs) is greater than ten minutes." | ||||
| self.logger.warn(msg.format(replag)) | self.logger.warn(msg.format(replag)) | ||||
| return | |||||
| with self.conn.cursor() as cursor: | with self.conn.cursor() as cursor: | ||||
| self.update_tracked(cursor) | self.update_tracked(cursor) | ||||
| @@ -0,0 +1,33 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -0,0 +1,34 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -0,0 +1,33 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -0,0 +1,34 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -1,12 +1,29 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | CommandTestCase is a subclass of unittest.TestCase that provides setUp() for | ||||
| creating a fake connection and some other helpful methods. It uses | 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. | instead of a socket for data. | ||||
| """ | """ | ||||
| from os import path | |||||
| import re | import re | ||||
| import sys | |||||
| from unittest import TestCase | 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): | class CommandTestCase(TestCase): | ||||
| re_sender = re.compile(":(.*?)!(.*?)@(.*?)\Z") | re_sender = re.compile(":(.*?)!(.*?)@(.*?)\Z") | ||||
| @@ -1,11 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 unittest | ||||
| import random | import random | ||||
| import string | import string | ||||
| import support | |||||
| import blowfish | |||||
| from earwigbot import blowfish | |||||
| class TestBlowfish(unittest.TestCase): | class TestBlowfish(unittest.TestCase): | ||||
| @@ -0,0 +1,58 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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) | |||||
| @@ -0,0 +1,48 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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) | |||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's IRC Watcher Component | ||||
| @@ -12,12 +32,12 @@ frontend. | |||||
| import logging | 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 | frontend_conn = None | ||||
| logger = logging.getLogger("watcher") | |||||
| logger = logging.getLogger("earwigbot.watcher") | |||||
| def get_connection(): | def get_connection(): | ||||
| """Return a new Connection() instance with connection information. | """Return a new Connection() instance with connection information. | ||||
| @@ -0,0 +1,44 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -1,6 +1,26 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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): | class Category(Page): | ||||
| """ | """ | ||||
| @@ -0,0 +1,55 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | |||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | EarwigBot's Wiki Toolset: Exceptions | ||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | 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(). | that are not methods of any class, like get_site(). | ||||
| There's no need to import this module explicitly. All functions here are | 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 | from cookielib import LWPCookieJar, LoadError | ||||
| @@ -17,11 +37,11 @@ from os import chmod, path | |||||
| import platform | import platform | ||||
| import stat | 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 | _cookiejar = None | ||||
| @@ -1,11 +1,31 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 | from hashlib import md5 | ||||
| import re | import re | ||||
| from time import gmtime, strftime | from time import gmtime, strftime | ||||
| from urllib import quote | from urllib import quote | ||||
| from wiki.exceptions import * | |||||
| from earwigbot.wiki.exceptions import * | |||||
| class Page(object): | class Page(object): | ||||
| """ | """ | ||||
| @@ -1,4 +1,24 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 cookielib import CookieJar | ||||
| from gzip import GzipFile | from gzip import GzipFile | ||||
| @@ -16,12 +36,12 @@ try: | |||||
| except ImportError: | except ImportError: | ||||
| oursql = None | 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): | class Site(object): | ||||
| """ | """ | ||||
| @@ -531,7 +551,8 @@ class Site(object): | |||||
| large number of edits (ideally, at least one per second), or the result | large number of edits (ideally, at least one per second), or the result | ||||
| may be larger than expected. | 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)) | result = list(self.sql_query(query)) | ||||
| return result[0][0] | return result[0][0] | ||||
| @@ -1,10 +1,30 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # | |||||
| # Copyright (C) 2009, 2010, 2011 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||||
| # | |||||
| # 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 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): | class User(object): | ||||
| """ | """ | ||||
| @@ -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) | |||||
| @@ -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) | |||||