소스 검색

Relocate BaseCommand to earwigbot.commands; _CommandManager class

tags/v0.1^2
Ben Kurtovic 12 년 전
부모
커밋
1d02af98fa
22개의 변경된 파일150개의 추가작업 그리고 175개의 파일을 삭제
  1. +0
    -1
      earwigbot/classes/__init__.py
  2. +0
    -75
      earwigbot/classes/base_command.py
  3. +123
    -71
      earwigbot/commands/__init__.py
  4. +1
    -1
      earwigbot/commands/afc_report.py
  5. +1
    -1
      earwigbot/commands/afc_status.py
  6. +1
    -1
      earwigbot/commands/calc.py
  7. +1
    -1
      earwigbot/commands/chanops.py
  8. +1
    -1
      earwigbot/commands/crypt.py
  9. +3
    -3
      earwigbot/commands/ctcp.py
  10. +1
    -1
      earwigbot/commands/editcount.py
  11. +1
    -1
      earwigbot/commands/git.py
  12. +2
    -3
      earwigbot/commands/help.py
  13. +1
    -1
      earwigbot/commands/link.py
  14. +1
    -1
      earwigbot/commands/praise.py
  15. +1
    -1
      earwigbot/commands/registration.py
  16. +1
    -1
      earwigbot/commands/remind.py
  17. +1
    -1
      earwigbot/commands/replag.py
  18. +1
    -1
      earwigbot/commands/restart.py
  19. +1
    -1
      earwigbot/commands/rights.py
  20. +1
    -1
      earwigbot/commands/test.py
  21. +1
    -1
      earwigbot/commands/threads.py
  22. +6
    -6
      earwigbot/irc/frontend.py

+ 0
- 1
earwigbot/classes/__init__.py 파일 보기

@@ -20,5 +20,4 @@
# 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 *

+ 0
- 75
earwigbot/classes/base_command.py 파일 보기

@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2012 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 = 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:
hooks = ["msg"]

def __init__(self, connection):
"""Constructor for new commands.

This is called once when the command is loaded (from
commands._load_command()). `connection` is a Connection object,
allowing us to do self.connection.say(), self.connection.send(), etc,
from within a method.
"""
self.connection = connection
logger_name = ".".join(("earwigbot", "commands", self.name))
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(logging.DEBUG)

def check(self, data):
"""Returns whether this command should be called in response to 'data'.

Given a Data() instance, return True if we should respond to this
activity, or False if we should ignore it or it doesn't apply to us.

Most commands return True if data.command == self.name, otherwise they
return False. This is the default behavior of check(); you need only
override it if you wish to change that.
"""
if data.is_command and data.command == self.name:
return True
return False

def process(self, data):
"""Main entry point for doing a command.

Handle an activity (usually a message) on IRC. At this point, thanks
to self.check() which is called automatically by the command handler,
we know this is something we should respond to, so (usually) something
like 'if data.command != "command_name": return' is unnecessary.
"""
pass

+ 123
- 71
earwigbot/commands/__init__.py 파일 보기

@@ -24,88 +24,140 @@
EarwigBot's IRC Command Manager

This package provides the IRC "commands" used by the bot's front-end component.
In __init__, you can find some functions used to load and run these commands.
This module contains the BaseCommand class (import with
`from earwigbot.commands import BaseCommand`) and an internal _CommandManager
class. This can be accessed through the singleton `command_manager`.
"""

import logging
import os
import sys

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

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

# Base directory when searching for commands:
base_dir = os.path.dirname(os.path.abspath(__file__))
class BaseCommand(object):
"""A base class for commands on IRC.

# 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("earwigbot.tasks")

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

Given a Connection object and a filename, we'll first try to import it,
and if that works, make an instance of the 'Command' class inside (assuming
it is an instance of BaseCommand), add it to _commands, and report the
addition to the user. Any problems along the way will either be ignored or
reported.
"""
global _commands

# Strip .py from the end of the filename and join with our package name:
name = ".".join(("commands", filename[:-3]))
try:
__import__(name)
except:
logger.exception("Couldn't load file {0}".format(filename))
return

command = sys.modules[name].Command(connection)
if not isinstance(command, BaseCommand):
return

_commands[command.name] = command
logger.debug("Added command {0}".format(command.name))

def load(connection):
"""Load all valid commands into the _commands global variable.

`connection` is a Connection object that is given to each command's
constructor.
This docstring is reported to the user when they use !help <command>.
"""
files = os.listdir(base_dir)
files.sort()
# This is the command's name, as reported to the user when they use !help:
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:
hooks = ["msg"]

def __init__(self, connection):
"""Constructor for new commands.

This is called once when the command is loaded (from
commands._load_command()). `connection` is a Connection object,
allowing us to do self.connection.say(), self.connection.send(), etc,
from within a method.
"""
self.connection = connection
logger_name = ".".join(("earwigbot", "commands", self.name))
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(logging.DEBUG)

def check(self, data):
"""Returns whether this command should be called in response to 'data'.

Given a Data() instance, return True if we should respond to this
activity, or False if we should ignore it or it doesn't apply to us.

Most commands return True if data.command == self.name, otherwise they
return False. This is the default behavior of check(); you need only
override it if you wish to change that.
"""
if data.is_command and data.command == self.name:
return True
return False

def process(self, data):
"""Main entry point for doing a command.

Handle an activity (usually a message) on IRC. At this point, thanks
to self.check() which is called automatically by the command handler,
we know this is something we should respond to, so (usually) something
like 'if data.command != "command_name": return' is unnecessary.
"""
pass


class _CommandManager(object):
def __init__(self):
self.logger = logging.getLogger("earwigbot.tasks")
self._base_dir = os.path.dirname(os.path.abspath(__file__))
self._connection = None
self._commands = {}

def _load_command(self, filename):
"""Load a specific command from a module, identified by filename.

Given a Connection object and a filename, we'll first try to import
it, and if that works, make an instance of the 'Command' class inside
(assuming it is an instance of BaseCommand), add it to self._commands,
and log the addition. Any problems along the way will either be
ignored or logged.
"""
# Strip .py from the filename's end and join with our package name:
name = ".".join(("commands", filename[:-3]))
try:
__import__(name)
except:
self.logger.exception("Couldn't load file {0}".format(filename))
return

for filename in files:
if filename.startswith("_") or not filename.endswith(".py"):
continue
try:
_load_command(connection, filename)
command = sys.modules[name].Command(self._connection)
except AttributeError:
pass # The file is doesn't contain a command, so just move on

msg = "Found {0} commands: {1}"
logger.info(msg.format(len(_commands), ", ".join(_commands.keys())))

def get_all():
"""Return our dict of all loaded commands."""
return _commands

def check(hook, data):
"""Given an event on IRC, check if there's anything we can respond to."""
# Parse command arguments into data.command and data.args:
data.parse_args()

for command in _commands.values():
if hook in command.hooks:
if command.check(data):
try:
command.process(data)
except:
logger.exception("Error executing command '{0}'".format(data.command))
break
return # No command in this module
if not isinstance(command, BaseCommand):
return

self._commands[command.name] = command
self.logger.debug("Added command {0}".format(command.name))

def load(self, connection):
"""Load all valid commands into self._commands.

`connection` is a Connection object that is given to each command's
constructor.
"""
self._connection = connection

files = os.listdir(self._base_dir)
files.sort()
for filename in files:
if filename.startswith("_") or not filename.endswith(".py"):
continue
self._load_command(filename)

msg = "Found {0} commands: {1}"
commands = ", ".join(self._commands.keys())
self.logger.info(msg.format(len(self._commands), commands))

def get_all(self):
"""Return our dict of all loaded commands."""
return self._commands

def check(self, hook, data):
"""Given an IRC event, check if there's anything we can respond to."""
# Parse command arguments into data.command and data.args:
data.parse_args()
for command in self._commands.values():
if hook in command.hooks:
if command.check(data):
try:
command.process(data)
except Exception:
e = "Error executing command '{0}'"
self.logger.exception(e.format(data.command))
break


command_manager = _CommandManager()

+ 1
- 1
earwigbot/commands/afc_report.py 파일 보기

@@ -22,9 +22,9 @@

import re

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

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


+ 1
- 1
earwigbot/commands/afc_status.py 파일 보기

@@ -23,7 +23,7 @@
import re

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

class Command(BaseCommand):


+ 1
- 1
earwigbot/commands/calc.py 파일 보기

@@ -23,7 +23,7 @@
import re
import urllib

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

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


+ 1
- 1
earwigbot/commands/chanops.py 파일 보기

@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

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

class Command(BaseCommand):


+ 1
- 1
earwigbot/commands/crypt.py 파일 보기

@@ -22,8 +22,8 @@

import hashlib

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

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


+ 3
- 3
earwigbot/commands/ctcp.py 파일 보기

@@ -23,8 +23,8 @@
import platform
import time

import earwigbot
from earwigbot.classes import BaseCommand
from earwigbot import __version__
from earwigbot.commands import BaseCommand
from earwigbot.config import config

class Command(BaseCommand):
@@ -64,6 +64,6 @@ class Command(BaseCommand):
elif command == "VERSION":
default = "EarwigBot - $1 - Python/$2 https://github.com/earwig/earwigbot"
vers = config.irc.get("version", default)
vers = vers.replace("$1", earwigbot.__version__)
vers = vers.replace("$1", __version__)
vers = vers.replace("$2", platform.python_version())
self.connection.notice(target, "\x01VERSION {0}\x01".format(vers))

+ 1
- 1
earwigbot/commands/editcount.py 파일 보기

@@ -22,8 +22,8 @@

from urllib import quote_plus

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

class Command(BaseCommand):
"""Return a user's edit count."""


+ 1
- 1
earwigbot/commands/git.py 파일 보기

@@ -24,7 +24,7 @@ import shlex
import subprocess
import re

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

class Command(BaseCommand):


+ 2
- 3
earwigbot/commands/help.py 파일 보기

@@ -22,8 +22,7 @@

import re

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

class Command(BaseCommand):
@@ -31,7 +30,7 @@ class Command(BaseCommand):
name = "help"

def process(self, data):
self.cmnds = commands.get_all()
self.cmnds = command_manager.get_all()
if not data.args:
self.do_main_help(data)
else:


+ 1
- 1
earwigbot/commands/link.py 파일 보기

@@ -23,7 +23,7 @@
import re
from urllib import quote

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

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


+ 1
- 1
earwigbot/commands/praise.py 파일 보기

@@ -22,7 +22,7 @@

import random

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

class Command(BaseCommand):
"""Praise people!"""


+ 1
- 1
earwigbot/commands/registration.py 파일 보기

@@ -22,8 +22,8 @@

import time

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

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


+ 1
- 1
earwigbot/commands/remind.py 파일 보기

@@ -23,7 +23,7 @@
import threading
import time

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

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


+ 1
- 1
earwigbot/commands/replag.py 파일 보기

@@ -24,7 +24,7 @@ from os.path import expanduser

import oursql

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

class Command(BaseCommand):
"""Return the replag for a specific database on the Toolserver."""


+ 1
- 1
earwigbot/commands/restart.py 파일 보기

@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

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

class Command(BaseCommand):


+ 1
- 1
earwigbot/commands/rights.py 파일 보기

@@ -20,8 +20,8 @@
# 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
from earwigbot.commands import BaseCommand

class Command(BaseCommand):
"""Retrieve a list of rights for a given username."""


+ 1
- 1
earwigbot/commands/test.py 파일 보기

@@ -22,7 +22,7 @@

import random

from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand

class Command(BaseCommand):
"""Test the bot!"""


+ 1
- 1
earwigbot/commands/threads.py 파일 보기

@@ -24,7 +24,7 @@ import threading
import re

from earwigbot import tasks
from earwigbot.classes import BaseCommand
from earwigbot.commands import BaseCommand
from earwigbot.config import config
from earwigbot.irc import KwargParseException



+ 6
- 6
earwigbot/irc/frontend.py 파일 보기

@@ -23,7 +23,7 @@
import logging
import re

from earwigbot import commands
from earwigbot.commands import command_manager
from earwigbot.irc import IRCConnection, Data, BrokenSocketException
from earwigbot.config import config

@@ -47,7 +47,7 @@ class Frontend(IRCConnection):
base = super(Frontend, self)
base.__init__(cf["host"], cf["port"], cf["nick"], cf["ident"],
cf["realname"], self.logger)
commands.load(self)
command_manager.load(self)
self._connect()

def _process_message(self, line):
@@ -59,7 +59,7 @@ class Frontend(IRCConnection):
data.nick, data.ident, data.host = self.sender_regex.findall(line[0])[0]
data.chan = line[2]
# Check for 'join' hooks in our commands:
commands.check("join", data)
command_manager.check("join", data)

elif line[1] == "PRIVMSG":
data.nick, data.ident, data.host = self.sender_regex.findall(line[0])[0]
@@ -70,13 +70,13 @@ class Frontend(IRCConnection):
# This is a privmsg to us, so set 'chan' as the nick of the
# sender, then check for private-only command hooks:
data.chan = data.nick
commands.check("msg_private", data)
command_manager.check("msg_private", data)
else:
# Check for public-only command hooks:
commands.check("msg_public", data)
command_manager.check("msg_public", data)

# Check for command hooks that apply to all messages:
commands.check("msg", data)
command_manager.check("msg", data)

# If we are pinged, pong back:
elif line[0] == "PING":


불러오는 중...
취소
저장