| @@ -35,12 +35,13 @@ def irc_watcher(f_conn): | |||
| def run(): | |||
| global f_conn | |||
| f_conn = frontend.get_connection() | |||
| frontend.startup(f_conn) | |||
| t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,)) | |||
| t_watcher.daemon = True | |||
| t_watcher.start() | |||
| frontend.main(f_conn) | |||
| frontend.main() | |||
| w_conn.close() | |||
| f_conn.close() | |||
| @@ -0,0 +1,24 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # A base class for commands on IRC. | |||
| class BaseCommand(object): | |||
| def __init__(self, connection): | |||
| """docstring""" | |||
| self.connection = connection | |||
| def get_hook(self): | |||
| """Hooks are: 'msg', 'msg_private', 'msg_public', and 'join'.""" | |||
| return None | |||
| def get_help(self, command): | |||
| """docstring""" | |||
| return None | |||
| def check(self, data): | |||
| """docstring""" | |||
| return False | |||
| def process(self, data): | |||
| """docstring""" | |||
| pass | |||
| @@ -5,8 +5,8 @@ | |||
| import socket | |||
| import threading | |||
| class Connection: | |||
| def __init__(self, host, port, nick, ident, realname): | |||
| class Connection(object): | |||
| def __init__(self, host=None, port=None, nick=None, ident=None, realname=None): | |||
| """a class to interface with IRC""" | |||
| self.host = host | |||
| self.port = port | |||
| @@ -50,9 +50,9 @@ class Connection: | |||
| """send a message""" | |||
| self.send("PRIVMSG %s :%s" % (target, msg)) | |||
| def reply(self, target, nick, msg): | |||
| def reply(self, data, msg): | |||
| """send a message as a reply""" | |||
| self.say(target, "%s%s%s: %s" % (chr(2), nick, chr(0x0f), msg)) | |||
| self.say(data.chan, "%s%s%s: %s" % (chr(2), data.nick, chr(0x0f), msg)) | |||
| def action(self, target, msg): | |||
| """send a message as an action""" | |||
| @@ -2,7 +2,7 @@ | |||
| # A class to store data from an individual line received on IRC. | |||
| class Data: | |||
| class Data(object): | |||
| def __init__(self): | |||
| """store data from an individual line received on IRC""" | |||
| self.chan = str() | |||
| @@ -17,9 +17,17 @@ class Data: | |||
| while '' in args: # remove any empty arguments | |||
| args.remove('') | |||
| self.args = args[1:] # the command arguments | |||
| self.is_command = False # whether this is a real command or not | |||
| try: | |||
| self.command = args[0] # the command itself | |||
| except IndexError: | |||
| self.command = None | |||
| self.args = args[1:] # the command arguments | |||
| try: | |||
| if self.command.startswith('!') or self.command.startswith('.'): | |||
| self.is_command = True | |||
| self.command = self.command[1:] # strip '!' or '.' | |||
| except AttributeError: | |||
| pass | |||
| @@ -10,12 +10,19 @@ from irc import triggers | |||
| from irc.connection import Connection | |||
| from irc.data import Data | |||
| connection = None | |||
| def get_connection(): | |||
| connection = Connection(HOST, PORT, NICK, IDENT, REALNAME) | |||
| return connection | |||
| def main(connection): | |||
| def startup(conn): | |||
| global connection | |||
| connection = conn | |||
| triggers.init_commands(connection) | |||
| connection.connect() | |||
| def main(): | |||
| read_buffer = str() | |||
| while 1: | |||
| @@ -36,7 +43,7 @@ def main(connection): | |||
| data.nick, data.ident, data.host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||
| data.chan = line[2][1:] | |||
| triggers.check(connection, data, "join") # check if there's anything we can respond to, and if so, respond | |||
| triggers.check("join", data) # check if there's anything we can respond to, and if so, respond | |||
| if line[1] == "PRIVMSG": | |||
| data.nick, data.ident, data.host = re.findall(":(.*?)!(.*?)@(.*?)\Z", line[0])[0] | |||
| @@ -45,11 +52,11 @@ def main(connection): | |||
| if data.chan == NICK: # this is a privmsg to us, so set 'chan' as the nick of the sender | |||
| data.chan = data.nick | |||
| triggers.check(connection, data, "msg_private") # only respond if it's a private message | |||
| triggers.check("msg_private", data) # only respond if it's a private message | |||
| else: | |||
| triggers.check(connection, data, "msg_public") # only respond if it's a public (channel) message | |||
| triggers.check("msg_public", data) # only respond if it's a public (channel) message | |||
| triggers.check(connection, data, "msg") # check for general messages | |||
| triggers.check("msg", data) # check for general messages | |||
| if data.msg.startswith("!restart"): # hardcode the !restart command (we can't restart from within an ordinary command) | |||
| if data.host in OWNERS: | |||
| @@ -1,36 +1,67 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # Check what events on IRC we can respond to. | |||
| # A module to manage IRC commands. | |||
| from irc.commands import test, help, git, link, chanops | |||
| import os | |||
| import traceback | |||
| def check(connection, data, hook): | |||
| data.parse_args() # parse command arguments into data.command and data.args | |||
| commands = [] | |||
| def init_commands(connection, silent=False): | |||
| """load all valid command classes from irc/commmands/ into the commands variable""" | |||
| files = os.listdir(os.path.join("irc", "commands")) # get all files in irc/commands/ | |||
| for f in files: | |||
| if f.startswith("_") or not f.endswith(".py"): # ignore non-python files or files beginning with "_" | |||
| continue | |||
| module = f[:-3] # strip .py from end | |||
| if hook == "join": | |||
| pass | |||
| try: | |||
| exec "from irc.commands import %s" % module | |||
| except: # importing the file failed for some reason... | |||
| if not silent: | |||
| print "Couldn't load file %s:" % f | |||
| traceback.print_exc() | |||
| continue | |||
| if hook == "msg_private": | |||
| pass | |||
| m = eval(module) # 'module' is a string, so get the actual object for processing | |||
| process_module(connection, m) | |||
| if hook == "msg_public": | |||
| pass | |||
| if not silent: | |||
| pretty_cmnds = map(lambda c: c.__class__.__name__, commands) | |||
| print "Found %s command classes: %s." % (len(commands), ', '.join(pretty_cmnds)) | |||
| if hook == "msg": | |||
| if data.command == "!test": | |||
| test.call(connection, data) | |||
| def process_module(connection, module): | |||
| """go through all objects in a module and add valid command classes to the commands variable""" | |||
| global commands | |||
| objects = dir(module) | |||
| elif data.command == "!help": | |||
| help.call(connection, data) | |||
| 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 | |||
| elif data.command == "!git": | |||
| git.call(connection, data) | |||
| try: | |||
| bases = obj.__bases__ | |||
| except AttributeError: # object isn't a valid class, so ignore it | |||
| continue | |||
| elif (data.command == "!link" or | |||
| ("[[" in data.msg and "]]" in data.msg) or | |||
| ("{{" in data.msg and "}}" in data.msg)): | |||
| link.call(connection, data) | |||
| 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 | |||
| def get_commands(): | |||
| """get our commands""" | |||
| return commands | |||
| def check(hook, data): | |||
| """given an event on IRC, check if there's anything we can respond to by calling each command class""" | |||
| data.parse_args() # parse command arguments into data.command and data.args | |||
| elif (data.command == "!voice" or data.command == "!devoice" or | |||
| data.command == "!op" or data.command == "!deop"): | |||
| chanops.call(connection, data) | |||
| for command in commands: | |||
| if command.get_hook() == hook: | |||
| if command.check(data): | |||
| command.process(data) | |||
| break | |||