Переглянути джерело

Restruturing codebase to be a bit more Pythonic.

tags/v0.1^2
Ben Kurtovic 12 роки тому
джерело
коміт
bff00f9b28
77 змінених файлів з 2031 додано та 826 видалено
  1. +70
    -0
      bot.py
  2. +0
    -0
     
  3. +0
    -5
      bot/classes/__init__.py
  4. +0
    -35
      bot/commands/chanops.py
  5. +0
    -38
      bot/commands/editcount.py
  6. +0
    -31
      bot/commands/praise.py
  7. +0
    -29
      bot/commands/replag.py
  8. +0
    -38
      bot/commands/rights.py
  9. +0
    -16
      bot/commands/test.py
  10. +0
    -228
      bot/config.py
  11. +0
    -14
      bot/tasks/afc_catdelink.py
  12. +0
    -15
      bot/tasks/afc_copyvios.py
  13. +0
    -14
      bot/tasks/afc_dailycats.py
  14. +0
    -13
      bot/tasks/afc_undated.py
  15. +0
    -14
      bot/tasks/blptag.py
  16. +0
    -13
      bot/tasks/feed_dailycats.py
  17. +0
    -14
      bot/tasks/wrongmime.py
  18. +0
    -24
      bot/wiki/__init__.py
  19. +0
    -35
      bot/wiki/constants.py
  20. +0
    -57
      earwigbot.py
  21. +37
    -0
      earwigbot/__init__.py
  22. +1
    -0
      earwigbot/blowfish.py
  23. +27
    -0
      earwigbot/classes/__init__.py
  24. +26
    -3
      earwigbot/classes/base_command.py
  25. +26
    -3
      earwigbot/classes/base_task.py
  26. +22
    -0
      earwigbot/classes/connection.py
  27. +22
    -0
      earwigbot/classes/data.py
  28. +22
    -0
      earwigbot/classes/rc.py
  29. +24
    -4
      earwigbot/commands/__init__.py
  30. +0
    -0
      earwigbot/commands/_old.py
  31. +23
    -3
      earwigbot/commands/afc_report.py
  32. +23
    -3
      earwigbot/commands/afc_status.py
  33. +21
    -1
      earwigbot/commands/calc.py
  34. +55
    -0
      earwigbot/commands/chanops.py
  35. +22
    -2
      earwigbot/commands/crypt.py
  36. +22
    -2
      earwigbot/commands/ctcp.py
  37. +58
    -0
      earwigbot/commands/editcount.py
  38. +22
    -2
      earwigbot/commands/git.py
  39. +22
    -2
      earwigbot/commands/help.py
  40. +21
    -1
      earwigbot/commands/link.py
  41. +51
    -0
      earwigbot/commands/praise.py
  42. +22
    -2
      earwigbot/commands/registration.py
  43. +21
    -1
      earwigbot/commands/remind.py
  44. +50
    -0
      earwigbot/commands/replag.py
  45. +58
    -0
      earwigbot/commands/rights.py
  46. +36
    -0
      earwigbot/commands/test.py
  47. +23
    -3
      earwigbot/commands/threads.py
  48. +335
    -0
      earwigbot/config.py
  49. +24
    -4
      earwigbot/frontend.py
  50. +39
    -43
      earwigbot/main.py
  51. +21
    -1
      earwigbot/rules.py
  52. +63
    -0
      earwigbot/runner.py
  53. +24
    -4
      earwigbot/tasks/__init__.py
  54. +34
    -0
      earwigbot/tasks/afc_catdelink.py
  55. +38
    -0
      earwigbot/tasks/afc_copyvios.py
  56. +34
    -0
      earwigbot/tasks/afc_dailycats.py
  57. +23
    -3
      earwigbot/tasks/afc_history.py
  58. +24
    -3
      earwigbot/tasks/afc_statistics.py
  59. +33
    -0
      earwigbot/tasks/afc_undated.py
  60. +34
    -0
      earwigbot/tasks/blptag.py
  61. +33
    -0
      earwigbot/tasks/feed_dailycats.py
  62. +34
    -0
      earwigbot/tasks/wrongmime.py
  63. +23
    -12
      earwigbot/tests/__init__.py
  64. +21
    -2
      earwigbot/tests/test_blowfish.py
  65. +58
    -0
      earwigbot/tests/test_calc.py
  66. +48
    -0
      earwigbot/tests/test_test.py
  67. +24
    -4
      earwigbot/watcher.py
  68. +44
    -0
      earwigbot/wiki/__init__.py
  69. +21
    -1
      earwigbot/wiki/category.py
  70. +55
    -0
      earwigbot/wiki/constants.py
  71. +20
    -0
      earwigbot/wiki/exceptions.py
  72. +25
    -5
      earwigbot/wiki/functions.py
  73. +21
    -1
      earwigbot/wiki/page.py
  74. +28
    -7
      earwigbot/wiki/site.py
  75. +23
    -3
      earwigbot/wiki/user.py
  76. +0
    -39
      tests/test_calc.py
  77. +0
    -29
      tests/test_test.py

+ 70
- 0
bot.py Переглянути файл

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


+ 0
- 5
bot/classes/__init__.py Переглянути файл

@@ -1,5 +0,0 @@
from base_command import *
from base_task import *
from connection import *
from data import *
from rc import *

+ 0
- 35
bot/commands/chanops.py Переглянути файл

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

+ 0
- 38
bot/commands/editcount.py Переглянути файл

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

+ 0
- 31
bot/commands/praise.py Переглянути файл

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

+ 0
- 29
bot/commands/replag.py Переглянути файл

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

+ 0
- 38
bot/commands/rights.py Переглянути файл

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

+ 0
- 16
bot/commands/test.py Переглянути файл

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

+ 0
- 228
bot/config.py Переглянути файл

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

+ 0
- 14
bot/tasks/afc_catdelink.py Переглянути файл

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

+ 0
- 15
bot/tasks/afc_copyvios.py Переглянути файл

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

+ 0
- 14
bot/tasks/afc_dailycats.py Переглянути файл

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

+ 0
- 13
bot/tasks/afc_undated.py Переглянути файл

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

+ 0
- 14
bot/tasks/blptag.py Переглянути файл

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

+ 0
- 13
bot/tasks/feed_dailycats.py Переглянути файл

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

+ 0
- 14
bot/tasks/wrongmime.py Переглянути файл

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

+ 0
- 24
bot/wiki/__init__.py Переглянути файл

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

+ 0
- 35
bot/wiki/constants.py Переглянути файл

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

+ 0
- 57
earwigbot.py Переглянути файл

@@ -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."

+ 37
- 0
earwigbot/__init__.py Переглянути файл

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

bot/blowfish.py → earwigbot/blowfish.py Переглянути файл

@@ -1,3 +1,4 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# blowfish.py

+ 27
- 0
earwigbot/classes/__init__.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 *

bot/classes/base_command.py → earwigbot/classes/base_command.py Переглянути файл

@@ -1,15 +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.

import logging

__all__ = ["BaseCommand"]

class BaseCommand(object):
"""A base class for commands on IRC.

This docstring is reported to the user when they use !help <command>.
"""
# This is the command's name, as reported to the user when they use !help:
name = "base_command"
name = None
# Hooks are "msg", "msg_private", "msg_public", and "join". "msg" is the
# default behavior; if you wish to override that, change the value in your
# command subclass:
@@ -24,7 +46,8 @@ class BaseCommand(object):
from within a method.
"""
self.connection = connection
self.logger = logging.getLogger(".".join(("commands", self.name)))
logger_name = ".".join(("earwigbot", "commands", self.name))
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(logging.DEBUG)

def check(self, data):

bot/classes/base_task.py → earwigbot/classes/base_task.py Переглянути файл

@@ -1,9 +1,31 @@
# -*- 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 config
import wiki
from earwigbot.config import config
from earwigbot import wiki

__all__ = ["BaseTask"]

class BaseTask(object):
"""A base class for bot tasks that edit Wikipedia."""
@@ -20,7 +42,8 @@ class BaseTask(object):

def _setup_logger(self):
"""Set up a basic module-level logger."""
self.logger = logging.getLogger(".".join(("tasks", self.name)))
logger_name = ".".join(("earwigbot", "tasks", self.name))
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(logging.DEBUG)

def run(self, **kwargs):

bot/classes/connection.py → earwigbot/classes/connection.py Переглянути файл

@@ -1,8 +1,30 @@
# -*- 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 threading

__all__ = ["BrokenSocketException", "Connection"]

class BrokenSocketException(Exception):
"""A socket has broken, because it is not sending data. Raised by
Connection.get()."""

bot/classes/data.py → earwigbot/classes/data.py Переглянути файл

@@ -1,7 +1,29 @@
# -*- 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

__all__ = ["KwargParseException", "Data"]

class KwargParseException(Exception):
"""Couldn't parse a certain keyword argument in self.args, probably because
it was given incorrectly: e.g., no value (abc), just a value (=xyz), just

bot/classes/rc.py → earwigbot/classes/rc.py Переглянути файл

@@ -1,7 +1,29 @@
# -*- 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

__all__ = ["RC"]

class RC(object):
"""A class to store data on an event received from our IRC watcher."""
re_color = re.compile("\x03([0-9]{1,2}(,[0-9]{1,2})?)?")

bot/commands/__init__.py → earwigbot/commands/__init__.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -11,20 +31,20 @@ import logging
import os
import sys

from classes import BaseCommand
import config
from earwigbot.classes import BaseCommand
from earwigbot.config import config

__all__ = ["load", "get_all", "check"]

# Base directory when searching for commands:
base_dir = os.path.join(config.root_dir, "bot", "commands")
base_dir = os.path.dirname(os.path.abspath(__file__))

# Store commands in a dict, where the key is the command's name and the value
# is an instance of the command's class:
_commands = {}

# Logger for this module:
logger = logging.getLogger("tasks")
logger = logging.getLogger("earwigbot.tasks")

def _load_command(connection, filename):
"""Try to load a specific command from a module, identified by file name.

bot/commands/_old.py → earwigbot/commands/_old.py Переглянути файл


bot/commands/afc_report.py → earwigbot/commands/afc_report.py Переглянути файл

@@ -1,10 +1,30 @@
# -*- 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

from classes import BaseCommand
import tasks
import wiki
from earwigbot.classes import BaseCommand
from earwigbot import tasks
from earwigbot import wiki

class Command(BaseCommand):
"""Get information about an AFC submission by name."""

bot/commands/afc_status.py → earwigbot/commands/afc_status.py Переглянути файл

@@ -1,10 +1,30 @@
# -*- 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

from classes import BaseCommand
import config
import wiki
from earwigbot import wiki
from earwigbot.classes import BaseCommand
from earwigbot.config import config

class Command(BaseCommand):
"""Get the number of pending AfC submissions, open redirect requests, and

bot/commands/calc.py → earwigbot/commands/calc.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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 urllib

from classes import BaseCommand
from earwigbot.classes import BaseCommand

class Command(BaseCommand):
"""A somewhat advanced calculator: see http://futureboy.us/fsp/frink.fsp

+ 55
- 0
earwigbot/commands/chanops.py Переглянути файл

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

bot/commands/crypt.py → earwigbot/commands/crypt.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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

from classes import BaseCommand
import blowfish
from earwigbot.classes import BaseCommand
from earwigbot import blowfish

class Command(BaseCommand):
"""Provides hash functions with !hash (!hash list for supported algorithms)

bot/commands/ctcp.py → earwigbot/commands/ctcp.py Переглянути файл

@@ -1,10 +1,30 @@
# -*- 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 time

from classes import BaseCommand
import config
from earwigbot.classes import BaseCommand
from earwigbot.config import config

class Command(BaseCommand):
"""Not an actual command, this module is used to respond to the CTCP

+ 58
- 0
earwigbot/commands/editcount.py Переглянути файл

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

bot/commands/git.py → earwigbot/commands/git.py Переглянути файл

@@ -1,11 +1,31 @@
# -*- 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 subprocess
import re

from classes import BaseCommand
import config
from earwigbot.classes import BaseCommand
from earwigbot.config import config

class Command(BaseCommand):
"""Commands to interface with the bot's git repository; use '!git' for a

bot/commands/help.py → earwigbot/commands/help.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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

from classes import BaseCommand, Data
import commands
from earwigbot.classes import BaseCommand, Data
from earwigbot import commands

class Command(BaseCommand):
"""Displays help information."""

bot/commands/link.py → earwigbot/commands/link.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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
from urllib import quote

from classes import BaseCommand
from earwigbot.classes import BaseCommand

class Command(BaseCommand):
"""Convert a Wikipedia page name into a URL."""

+ 51
- 0
earwigbot/commands/praise.py Переглянути файл

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

bot/commands/registration.py → earwigbot/commands/registration.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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

from classes import BaseCommand
import wiki
from earwigbot.classes import BaseCommand
from earwigbot import wiki

class Command(BaseCommand):
"""Return when a user registered."""

bot/commands/remind.py → earwigbot/commands/remind.py Переглянути файл

@@ -1,9 +1,29 @@
# -*- 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 time

from classes import BaseCommand
from earwigbot.classes import BaseCommand

class Command(BaseCommand):
"""Set a message to be repeated to you in a certain amount of time."""

+ 50
- 0
earwigbot/commands/replag.py Переглянути файл

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

+ 58
- 0
earwigbot/commands/rights.py Переглянути файл

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

+ 36
- 0
earwigbot/commands/test.py Переглянути файл

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

bot/commands/threads.py → earwigbot/commands/threads.py Переглянути файл

@@ -1,11 +1,31 @@
# -*- 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 re

from classes import BaseCommand, Data, KwargParseException
import tasks
import config
from earwigbot import tasks
from earwigbot.classes import BaseCommand, Data, KwargParseException
from earwigbot.config import config

class Command(BaseCommand):
"""Manage wiki tasks from IRC, and check on thread status."""

+ 335
- 0
earwigbot/config.py Переглянути файл

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

bot/frontend.py → earwigbot/frontend.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -12,14 +32,14 @@ imported by irc/command_handler.py if they are in irc/commands.
import logging
import re

import config
import commands
from classes import Connection, Data, BrokenSocketException
from earwigbot import commands
from earwigbot.classes import Connection, Data, BrokenSocketException
from earwigbot.config import config

__all__ = ["get_connection", "startup", "main"]

connection = None
logger = logging.getLogger("frontend")
logger = logging.getLogger("earwigbot.frontend")
sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z")

def get_connection():

bot/main.py → earwigbot/main.py Переглянути файл

@@ -1,12 +1,27 @@
#! /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's Core

This should not be run directly; the wrapper in "earwigbot.py" is preferred,
but it should work fine alone, as long as you enter the password-unlock key at
the initial hidden prompt if one is needed.
EarwigBot's Main Module

The core is essentially responsible for starting the various bot components
(irc, scheduler, etc) and making sure they are all happy. An explanation of the
@@ -34,11 +49,12 @@ import logging
import threading
import time

import config
import frontend
import tasks
import watcher
from earwigbot import frontend
from earwigbot import tasks
from earwigbot import watcher
from earwigbot.config import config

logger = logging.getLogger("earwigbot")
f_conn = None
w_conn = None

@@ -52,9 +68,9 @@ def irc_watcher(f_conn=None):
try:
watcher.main(w_conn, f_conn)
except:
logging.exception("Watcher had an error")
logger.exception("Watcher had an error")
time.sleep(5) # sleep a bit before restarting watcher
logging.warn("Watcher has stopped; restarting component")
logger.warn("Watcher has stopped; restarting component")

def wiki_scheduler():
"""Function to handle the wiki scheduler as another thread, or as the
@@ -75,13 +91,12 @@ def irc_frontend():
enable the wiki scheduler and IRC watcher on new threads if they are
enabled."""
global f_conn

logging.info("Starting IRC frontend")
logger.info("Starting IRC frontend")
f_conn = frontend.get_connection()
frontend.startup(f_conn)

if "wiki_schedule" in config.components:
logging.info("Starting wiki scheduler")
logger.info("Starting wiki scheduler")
tasks.load()
t_scheduler = threading.Thread(target=wiki_scheduler)
t_scheduler.name = "wiki-scheduler"
@@ -89,7 +104,7 @@ def irc_frontend():
t_scheduler.start()

if "irc_watcher" in config.components:
logging.info("Starting IRC watcher")
logger.info("Starting IRC watcher")
t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,))
t_watcher.name = "irc-watcher"
t_watcher.daemon = True
@@ -101,50 +116,31 @@ def irc_frontend():
w_conn.close()
f_conn.close()

def run():
config.load()
try:
# Wait for our password decrypt key from the bot's wrapper, then
# decrypt passwords:
key = raw_input()
except EOFError:
pass
else:
config.decrypt(key)

enabled = config.components

if "irc_frontend" in enabled:
def main():
if "irc_frontend" in config.components:
# Make the frontend run on our primary thread if enabled, and enable
# additional components through that function
irc_frontend()

elif "wiki_schedule" in enabled:
elif "wiki_schedule" in config.components:
# Run the scheduler on the main thread, but also run the IRC watcher on
# another thread iff it is enabled
logging.info("Starting wiki scheduler")
logger.info("Starting wiki scheduler")
tasks.load()
if "irc_watcher" in enabled:
logging.info("Starting IRC watcher")
logger.info("Starting IRC watcher")
t_watcher = threading.Thread(target=irc_watcher)
t_watcher.name = "irc-watcher"
t_watcher.daemon = True
t_watcher.start()
wiki_scheduler()

elif "irc_watcher" in enabled:
elif "irc_watcher" in config.components:
# The IRC watcher is our only enabled component, so run its function
# only and don't worry about anything else:
logging.info("Starting IRC watcher")
logger.info("Starting IRC watcher")
irc_watcher()

else: # Nothing is enabled!
logging.critical("No bot parts are enabled; stopping")
exit(1)

if __name__ == "__main__":
try:
run()
except KeyboardInterrupt:
logging.critical("KeyboardInterrupt: stopping main bot loop")
logger.critical("No bot parts are enabled; stopping")
exit(1)

bot/rules.py → earwigbot/rules.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -9,7 +29,7 @@ recieves an event from IRC.

import re

import tasks
from earwigbot import tasks

afc_prefix = "wikipedia( talk)?:(wikiproject )?articles for creation"


+ 63
- 0
earwigbot/runner.py Переглянути файл

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

bot/tasks/__init__.py → earwigbot/tasks/__init__.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -13,20 +33,20 @@ import sys
import threading
import time

from classes import BaseTask
import config
from earwigbot.classes import BaseTask
from earwigbot.config import config

__all__ = ["load", "schedule", "start", "get", "get_all"]

# Base directory when searching for tasks:
base_dir = os.path.join(config.root_dir, "bot", "tasks")
base_dir = os.path.dirname(os.path.abspath(__file__))

# Store loaded tasks as a dict where the key is the task name and the value is
# an instance of the task class:
_tasks = {}

# Logger for this module:
logger = logging.getLogger("commands")
logger = logging.getLogger("earwigbot.commands")

def _load_task(filename):
"""Try to load a specific task from a module, identified by file name."""

+ 34
- 0
earwigbot/tasks/afc_catdelink.py Переглянути файл

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

+ 38
- 0
earwigbot/tasks/afc_copyvios.py Переглянути файл

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

+ 34
- 0
earwigbot/tasks/afc_dailycats.py Переглянути файл

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

bot/tasks/afc_history.py → earwigbot/tasks/afc_history.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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 datetime import datetime, timedelta
@@ -11,9 +31,9 @@ from matplotlib import pyplot as plt
from numpy import arange
import oursql

from classes import BaseTask
import config
import wiki
from earwigbot import wiki
from earwigbot.classes import BaseTask
from earwigbot.config import config

# Valid submission statuses:
STATUS_NONE = 0

bot/tasks/afc_statistics.py → earwigbot/tasks/afc_statistics.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
import logging
@@ -9,9 +29,9 @@ from time import sleep

import oursql

from classes import BaseTask
import config
import wiki
from earwigbot import wiki
from earwigbot.classes import BaseTask
from earwigbot.config import config

# Chart status number constants:
CHART_NONE = 0
@@ -178,6 +198,7 @@ class Task(BaseTask):
if replag > 600 and not kwargs.get("ignore_replag"):
msg = "Sync canceled as replag ({0} secs) is greater than ten minutes."
self.logger.warn(msg.format(replag))
return

with self.conn.cursor() as cursor:
self.update_tracked(cursor)

+ 33
- 0
earwigbot/tasks/afc_undated.py Переглянути файл

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

+ 34
- 0
earwigbot/tasks/blptag.py Переглянути файл

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

+ 33
- 0
earwigbot/tasks/feed_dailycats.py Переглянути файл

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

+ 34
- 0
earwigbot/tasks/wrongmime.py Переглянути файл

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

tests/support.py → earwigbot/tests/__init__.py Переглянути файл

@@ -1,12 +1,29 @@
# -*- 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
creating a fake connection and some other helpful methods. It uses
@@ -14,16 +31,10 @@ FakeConnection, a subclass of classes.Connection, but with an internal string
instead of a socket for data.
"""

from os import path
import re
import sys
from unittest import TestCase

root_dir = path.split(path.dirname(path.abspath(__file__)))[0]
code_dir = path.join(root_dir, "bot")
sys.path.insert(0, code_dir)

from classes import Connection, Data
from earwigbot.classes import Connection, Data

class CommandTestCase(TestCase):
re_sender = re.compile(":(.*?)!(.*?)@(.*?)\Z")

tests/test_blowfish.py → earwigbot/tests/test_blowfish.py Переглянути файл

@@ -1,11 +1,30 @@
# -*- 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 random
import string

import support
import blowfish
from earwigbot import blowfish

class TestBlowfish(unittest.TestCase):


+ 58
- 0
earwigbot/tests/test_calc.py Переглянути файл

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

+ 48
- 0
earwigbot/tests/test_test.py Переглянути файл

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

bot/watcher.py → earwigbot/watcher.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -12,12 +32,12 @@ frontend.

import logging

import config
from classes import Connection, RC, BrokenSocketException
import rules
from earwigbot import rules
from earwigbot.classes import Connection, RC, BrokenSocketException
from earwigbot.config import config

frontend_conn = None
logger = logging.getLogger("watcher")
logger = logging.getLogger("earwigbot.watcher")

def get_connection():
"""Return a new Connection() instance with connection information.

+ 44
- 0
earwigbot/wiki/__init__.py Переглянути файл

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

bot/wiki/category.py → earwigbot/wiki/category.py Переглянути файл

@@ -1,6 +1,26 @@
# -*- 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):
"""

+ 55
- 0
earwigbot/wiki/constants.py Переглянути файл

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

bot/wiki/exceptions.py → earwigbot/wiki/exceptions.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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

bot/wiki/functions.py → earwigbot/wiki/functions.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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
@@ -7,7 +27,7 @@ This module, a component of the wiki package, contains miscellaneous functions
that are not methods of any class, like get_site().

There's no need to import this module explicitly. All functions here are
automatically available from wiki.
automatically available from earwigbot.wiki.
"""

from cookielib import LWPCookieJar, LoadError
@@ -17,11 +37,11 @@ from os import chmod, path
import platform
import stat

import config
from wiki.exceptions import SiteNotFoundError
from wiki.site import Site
from earwigbot.config import config
from earwigbot.wiki.exceptions import SiteNotFoundError
from earwigbot.wiki.site import Site

__all__ = ["get_site"]
__all__ = ["get_site", "add_site", "del_site"]

_cookiejar = None


bot/wiki/page.py → earwigbot/wiki/page.py Переглянути файл

@@ -1,11 +1,31 @@
# -*- 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
import re
from time import gmtime, strftime
from urllib import quote

from wiki.exceptions import *
from earwigbot.wiki.exceptions import *

class Page(object):
"""

bot/wiki/site.py → earwigbot/wiki/site.py Переглянути файл

@@ -1,4 +1,24 @@
# -*- 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 gzip import GzipFile
@@ -16,12 +36,12 @@ try:
except ImportError:
oursql = None

from wiki import logger
from wiki.category import Category
from wiki.constants import *
from wiki.exceptions import *
from wiki.page import Page
from wiki.user import User
from earwigbot.wiki import logger
from earwigbot.wiki.category import Category
from earwigbot.wiki.constants import *
from earwigbot.wiki.exceptions import *
from earwigbot.wiki.page import Page
from earwigbot.wiki.user import User

class Site(object):
"""
@@ -531,7 +551,8 @@ class Site(object):
large number of edits (ideally, at least one per second), or the result
may be larger than expected.
"""
query = "SELECT NOW() - MAX(rev_timestamp) FROM revision"
query = """SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(rc_timestamp) FROM
recentchanges ORDER BY rc_timestamp DESC LIMIT 1"""
result = list(self.sql_query(query))
return result[0][0]


bot/wiki/user.py → earwigbot/wiki/user.py Переглянути файл

@@ -1,10 +1,30 @@
# -*- 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 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):
"""

+ 0
- 39
tests/test_calc.py Переглянути файл

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

+ 0
- 29
tests/test_test.py Переглянути файл

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

Завантаження…
Відмінити
Зберегти