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

CommandManager as attr of Bot, plus cleanup

tags/v0.1^2
Ben Kurtovic 12 роки тому
джерело
коміт
abe58a07f6
6 змінених файлів з 74 додано та 63 видалено
  1. +13
    -9
      earwigbot/bot.py
  2. +34
    -41
      earwigbot/commands/__init__.py
  3. +4
    -4
      earwigbot/irc/frontend.py
  4. +5
    -3
      earwigbot/irc/watcher.py
  5. +14
    -4
      earwigbot/util.py
  6. +4
    -2
      setup.py

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

@@ -23,10 +23,13 @@
import threading
from time import sleep, time

from earwigbot.commands import CommandManager
from earwigbot.config import BotConfig
from earwigbot.irc import Frontend, Watcher
from earwigbot.tasks import task_manager

__all__ = ["Bot"]

class Bot(object):
"""
The Bot class is the core of EarwigBot, essentially responsible for
@@ -46,16 +49,14 @@ class Bot(object):
def __init__(self, root_dir):
self.config = BotConfig(root_dir)
self.logger = logging.getLogger("earwigbot")
self.commands = CommandManager(self)
self.tasks = None
self.frontend = None
self.watcher = None

self._keep_scheduling = True
self._lock = threading.Lock()

def _start_thread(self, name, target):
thread = threading.Thread(name=name, target=target)
thread.start()

def _wiki_scheduler(self):
while self._keep_scheduling:
time_start = time()
@@ -68,17 +69,18 @@ class Bot(object):
def _start_components(self):
if self.config.components.get("irc_frontend"):
self.logger.info("Starting IRC frontend")
self.frontend = Frontend(self.config)
self._start_thread(name, self.frontend.loop)
self.frontend = Frontend(self)
self.commands.load()
threading.Thread(name=name, target=self.frontend.loop).start()

if self.config.components.get("irc_watcher"):
self.logger.info("Starting IRC watcher")
self.watcher = Watcher(self.config, self.frontend)
self._start_thread(name, self.watcher.loop)
self.watcher = Watcher(self)
threading.Thread(name=name, target=self.watcher.loop).start()

if self.config.components.get("wiki_scheduler"):
self.logger.info("Starting wiki scheduler")
self._start_thread(name, self._wiki_scheduler)
threading.Thread(name=name, target=self._wiki_scheduler).start()

def _loop(self):
while 1:
@@ -104,6 +106,7 @@ class Bot(object):
with self._lock:
self.config.load()
#if self.config.components.get("irc_frontend"):
# self.commands.load()

def stop(self):
if self.frontend:
@@ -111,3 +114,4 @@ class Bot(object):
if self.watcher:
self.watcher.stop()
self._keep_scheduling = False
sleep(3) # Give a few seconds to finish closing IRC connections

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

@@ -25,17 +25,16 @@ EarwigBot's IRC Command Manager

This package provides the IRC "commands" used by the bot's front-end component.
This module contains the BaseCommand class (import with
`from earwigbot.commands import BaseCommand`) and an internal _CommandManager
class. This can be accessed through the `command_manager` singleton.
`from earwigbot.commands import BaseCommand`) and an internal CommandManager
class. This can be accessed through `bot.commands`.
"""

import imp
import logging
import os
import sys
from os import listdir, path
from re import sub

from earwigbot.config import config

__all__ = ["BaseCommand", "command_manager"]
__all__ = ["BaseCommand", "CommandManager"]

class BaseCommand(object):
"""A base class for commands on IRC.
@@ -88,32 +87,33 @@ class BaseCommand(object):
pass


class _CommandManager(object):
def __init__(self):
class CommandManager(object):
def __init__(self, bot):
self.bot = bot
self.logger = logging.getLogger("earwigbot.tasks")
self._base_dir = os.path.dirname(os.path.abspath(__file__))
self._connection = None
self._dirs = [path.dirname(__file__), bot.config.root_dir]
self._commands = {}

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

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.
We'll first try to import it using imp magic, 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]))
f, path, desc = imp.find_module(name, [path])
try:
__import__(name)
except:
self.logger.exception("Couldn't load file {0}".format(filename))
module = imp.load_module(name, f, path, desc)
except Exception:
e = "Couldn't load module {0} from {1}"
self.logger.exception(e.format(name, path))
return
finally:
f.close()

try:
command = sys.modules[name].Command(self._connection)
command = module.Command(self.bot.frontend)
except AttributeError:
return # No command in this module
if not isinstance(command, BaseCommand):
@@ -122,20 +122,16 @@ class _CommandManager(object):
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)
def load(self):
"""Load (or reload) all valid commands into self._commands."""
dirs = [path.join(path.dirname(__file__), "commands"),
path.join(bot.config.root_dir, "commands")]
for dir in dirs:
files = listdir(dir)
files = [sub("\.pyc?$", "", f) for f in files if f[0] != "_"]
files = list(set(files)) # Remove duplicates
for filename in sorted(files):
self._load_command(filename, dir)

msg = "Found {0} commands: {1}"
commands = ", ".join(self._commands.keys())
@@ -158,6 +154,3 @@ class _CommandManager(object):
e = "Error executing command '{0}'"
self.logger.exception(e.format(data.command))
break


command_manager = _CommandManager()

+ 4
- 4
earwigbot/irc/frontend.py Переглянути файл

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

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

__all__ = ["Frontend"]
@@ -40,14 +39,15 @@ class Frontend(IRCConnection):
"""
sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z")

def __init__(self, config):
self.config = config
def __init__(self, bot):
self.bot = bot
self.config = bot.config
self.logger = logging.getLogger("earwigbot.frontend")

cf = config.irc["frontend"]
base = super(Frontend, self)
base.__init__(cf["host"], cf["port"], cf["nick"], cf["ident"],
cf["realname"], self.logger)
command_manager.load(self)
self._connect()

def _process_message(self, line):


+ 5
- 3
earwigbot/irc/watcher.py Переглянути файл

@@ -38,14 +38,16 @@ class Watcher(IRCConnection):
to channels on the IRC frontend.
"""

def __init__(self, config, frontend=None):
self.config = config
def __init__(self, bot):
self.bot = bot
self.config = bot.config
self.frontend = bot.frontend
self.logger = logging.getLogger("earwigbot.watcher")

cf = config.irc["watcher"]
base = super(Watcher, self)
base.__init__(cf["host"], cf["port"], cf["nick"], cf["ident"],
cf["realname"], self.logger)
self.frontend = frontend
self._prepare_process_hook()
self._connect()



+ 14
- 4
earwigbot/util.py Переглянути файл

@@ -36,12 +36,22 @@ class BotUtility(object):
return __version__

def run(self):
print "EarwigBot v{0}\n".format(self.version())

def main(self):
root_dir = path.abspath(path.curdir())
bot = Bot(root_dir)
bot.run()
try:
bot.run()
finally:
bot.stop()

def main(self):
print "EarwigBot v{0}\n".format(self.version())
parser = argparse.ArgumentParser(description=BotUtility.__doc__)

parser.add_argument("-V", "--version", action="version",
version=self.version())

args = parser.parse_args()
# args.func(args)


main = BotUtility().main


+ 4
- 2
setup.py Переглянути файл

@@ -30,8 +30,10 @@ from setuptools import setup
setup(
name = "earwigbot",
entry_points = {"console_scripts": ["earwigbot = earwigbot.util:main"]},
install_requires = ["PyYAML>=3.10", "oursql>=0.9.3", "oauth2>=1.5.211",
"numpy>=1.6.1", "matplotlib>=1.1.0"],
install_requires = ["PyYAML>=3.10",
"oursql>=0.9.3",
"oauth2>=1.5.211",
"matplotlib>=1.1.0"],
version = "0.1.dev",
author = "Ben Kurtovic",
author_email = "ben.kurtovic@verizon.net",


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