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

redoing irc command management: rewriting triggers and creating a BaseCommand class, also some changes to Data and Connection

tags/v0.1
Ben Kurtovic 13 роки тому
джерело
коміт
e743734bac
6 змінених файлів з 107 додано та 36 видалено
  1. +2
    -1
      core/main.py
  2. +24
    -0
      irc/base_command.py
  3. +4
    -4
      irc/connection.py
  4. +10
    -2
      irc/data.py
  5. +12
    -5
      irc/frontend.py
  6. +55
    -24
      irc/triggers.py

+ 2
- 1
core/main.py Переглянути файл

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


+ 24
- 0
irc/base_command.py Переглянути файл

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

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

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


+ 10
- 2
irc/data.py Переглянути файл

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

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

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


+ 55
- 24
irc/triggers.py Переглянути файл

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

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