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