Browse Source

loading commands now works, along with 'help' and 'test'; docstring fixes, etc

tags/v0.1^2
Ben Kurtovic 13 years ago
parent
commit
99d0e7588f
5 changed files with 66 additions and 46 deletions
  1. +10
    -3
      bot/classes/base_command.py
  2. +1
    -1
      bot/classes/base_task.py
  3. +48
    -34
      bot/commands/__init__.py
  4. +6
    -7
      bot/commands/help.py
  5. +1
    -1
      bot/tasks/__init__.py

+ 10
- 3
bot/classes/base_command.py View File

@@ -14,6 +14,13 @@ class BaseCommand(object):
hooks = ["msg"] hooks = ["msg"]


def __init__(self, connection): 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 self.connection = connection


def check(self, data): def check(self, data):
@@ -34,8 +41,8 @@ class BaseCommand(object):
"""Main entry point for doing a command. """Main entry point for doing a command.


Handle an activity (usually a message) on IRC. At this point, thanks Handle an activity (usually a message) on IRC. At this point, thanks
to self.check() which is called automatically by command_handler, we
know this is something we should respond to, so (usually) a
'if data.command != "command_name": return' is unnecessary.
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 pass

+ 1
- 1
bot/classes/base_task.py View File

@@ -8,7 +8,7 @@ class BaseTask(object):
"""Constructor for new tasks. """Constructor for new tasks.


This is called once immediately after the task class is loaded by This is called once immediately after the task class is loaded by
the task manager (in tasks._load_class_from_file()).
the task manager (in tasks._load_task()).
""" """
pass pass




+ 48
- 34
bot/commands/__init__.py View File

@@ -8,53 +8,67 @@ In __init__, you can find some functions used to load and run these commands.
""" """


import os import os
import sys
import traceback import traceback


from classes import BaseCommand
import config

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


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

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


def _load_class_from_file(connection, module):
"""Add."""
global commands
objects = dir(module)
def _load_command(connection, filename):
"""Try to load a specific command from a module, identified by file name.


for this_obj in objects: # go through everything in the file
obj = eval("module.%s" % this_obj) # this_obj is a string, so get the actual object corresponding to that string
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


try:
bases = obj.__bases__
except AttributeError: # object isn't a valid class, so ignore it
continue
# Strip .py from the end of the filename and join with our package name:
name = ".".join(("commands", filename[:-3]))
try:
__import__(name)
except:
print "Couldn't load file {0}:".format(filename)
traceback.print_exc()
return


for base in bases:
if base.__name__ == "BaseCommand": # this inherits BaseCommand, so it must be a command class
command = obj(connection) # initialize a new command object
_commands.append(command)
print "Added command class %s from %s..." % (this_obj, module.__name__)
continue
command = sys.modules[name].Command(connection)
if not isinstance(command, BaseCommand):
return
_commands[command.name] = command
print "Added command {0}...".format(command.name)


def load(connection): def load(connection):
"""Load all valid commands into the _commands global variable."""
files = os.listdir(os.path.join("bot", "commands"))
"""Load all valid commands into the _commands global variable.

`connection` is a Connection object that is given to each command's
constructor.
"""
files = os.listdir(base_dir)
files.sort() files.sort()


for f in files:
if f.startswith("_") or not f.endswith(".py"): # ignore non-python files or files beginning with "_"
for filename in files:
if filename.startswith("_") or not filename.endswith(".py"):
continue continue
module = f[:-3] # strip .py from end
try: try:
exec "from irc.commands import %s" % module
except: # importing the file failed for some reason...
print "Couldn't load file %s:" % f
traceback.print_exc()
continue
process_module(connection, eval(module)) # 'module' is a string, so get the actual object for processing by eval-ing it
_load_command(connection, filename)
except AttributeError:
pass # The file is doesn't contain a command, so just move on


pretty_cmnds = map(lambda c: c.__class__.__name__, commands)
print "Found %s command classes: %s." % (len(commands), ', '.join(pretty_cmnds))
msg = "Found {0} command classes: {1}."
print msg.format(len(_commands), ", ".join(_commands.keys()))


def get_all(): def get_all():
"""Return our dict of all loaded commands.""" """Return our dict of all loaded commands."""
@@ -62,15 +76,15 @@ def get_all():


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


for command in _commands:
if hook in command.get_hooks():
for command in _commands.values():
if hook in command.hooks:
if command.check(data): if command.check(data):
try: try:
command.process(data) command.process(data)
except: except:
print "Error executing command '{}':".format(data.command)
traceback.print_exc() # catch exceptions and print them
print "Error executing command '{0}':".format(data.command)
traceback.print_exc()
break break

+ 6
- 7
bot/commands/help.py View File

@@ -4,11 +4,11 @@ from classes import BaseCommand, Data
import commands import commands


class Command(BaseCommand): class Command(BaseCommand):
"""Generates help information."""
"""Displays help information."""
name = "help" name = "help"


def process(self, data): def process(self, data):
self.cmnds = commands.get_all().keys()
self.cmnds = commands.get_all()
if not data.args: if not data.args:
self.do_main_help(data) self.do_main_help(data)
else: else:
@@ -17,7 +17,7 @@ class Command(BaseCommand):
def do_main_help(self, data): def do_main_help(self, data):
"""Give the user a general help message with a list of all commands.""" """Give the user a general help message with a list of all commands."""
msg = "I am a bot! I have {0} commands loaded: {1}. You can get help for any command with '!help <command>'." msg = "I am a bot! I have {0} commands loaded: {1}. You can get help for any command with '!help <command>'."
msg.format(len(self.cmnds), ', '.join(self.cmnds))
msg = msg.format(len(self.cmnds.keys()), ', '.join(self.cmnds))
self.connection.reply(data, msg) self.connection.reply(data, msg)


def do_command_help(self, data): def do_command_help(self, data):
@@ -26,17 +26,16 @@ class Command(BaseCommand):


# Create a dummy message to test which commands pick up the user's # Create a dummy message to test which commands pick up the user's
# input: # input:
dummy = Data()
dummy = Data(1)
dummy.command = command.lower() dummy.command = command.lower()
dummy.is_command = True dummy.is_command = True


for cmnd in self.cmnds:
for cmnd in self.cmnds.values():
if cmnd.check(dummy): if cmnd.check(dummy):
doc = cmnd.__doc__ doc = cmnd.__doc__
if doc: if doc:
msg = "info for command \x0303{0}\x0301: \"{1}\"" msg = "info for command \x0303{0}\x0301: \"{1}\""
msg.format(command, doc)
self.connection.reply(data, msg)
self.connection.reply(data, msg.format(command, doc))
return return
break break




+ 1
- 1
bot/tasks/__init__.py View File

@@ -20,7 +20,7 @@ __all__ = ["load", "schedule", "start"]
# an instance of the task class: # an instance of the task class:
_tasks = {} _tasks = {}


def _load_class_from_file(f):
def _load_task(f):
"""Look in a given file for the task class.""" """Look in a given file for the task class."""
global _tasks global _tasks




Loading…
Cancel
Save