@@ -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) |