]> jfr.im git - irc/evilnet/x3.git/commitdiff
more python: x3 logging, callback reg hook filters, install py scripts
authorrubin <redacted>
Mon, 26 Jan 2009 06:02:53 +0000 (06:02 +0000)
committerrubin <redacted>
Mon, 26 Jan 2009 06:02:53 +0000 (06:02 +0000)
14 files changed:
ChangeLog
Makefile.am
Makefile.in
configure
configure.in
install-r.sh [new file with mode: 0755]
rx/Makefile.in
src/Makefile.in
src/mod-python.c
src/modpython.py
src/plugins/README [new file with mode: 0644]
src/plugins/__init__.py [new file with mode: 0644]
src/plugins/annoy/__init__.py [new file with mode: 0644]
src/plugins/annoy/plugin.py [new file with mode: 0644]

index b841f894bae2c3e52eedbfa148d8942bd9c306cb..36ad57ea70906d07d74de8c3a68237a03e03e596 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,17 @@
 /***********************************************************************
 X3 ChangeLog
 
+2009-01-24  Alex Schumann  <rubin@afternet.org>
+
+       * src/mod-python.c: added plugin hook to log to x3 logs, and worked
+       out the basics of callback registration.
+
+       * src/modpython.py: x3 logging hook and new callback registration
+
+       * Makefile.am: install modpython.py and plugins/ during make install
+
+       * install-r.sh: shell script to do recursive copy with exclusions
+
 2009-01-23  Alex Schumann  <rubin@afternet.org>
 
        * src/mod-python.c: refactor to use class-based hook manager. Seems to
index 4687f0b89b7e38034179fe232675aadcc1a1a8ad..63175d878b90b958ab33cdffa9453a5612a1ac08 100644 (file)
@@ -17,6 +17,7 @@ install-exec-local:
        $(INSTALL) -m 644 $(srcdir)/src/*.help $(prefix)
        $(INSTALL) -m 600 $(srcdir)/x3.conf.example $(prefix)
        $(INSTALL) -m 644 $(srcdir)/sockcheck.conf.example $(prefix)
+       ./install-r.sh $(srcdir)/src/plugins $(prefix)
        @echo
        @echo X3-$(VERSION) has been installed to $(prefix)
        @echo Remember to edit x3.conf.example and sockcheck.conf.example
index d1bf7e8c21ae0130ade2c2b4c11cb5e91798dc05..69e3cd52bbc1ca2517e4b47abd737513f8b5d39e 100644 (file)
@@ -80,6 +80,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CP = @CP@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CYGPATH_W = @CYGPATH_W@
@@ -613,6 +614,7 @@ install-exec-local:
        $(INSTALL) -m 644 $(srcdir)/src/*.help $(prefix)
        $(INSTALL) -m 600 $(srcdir)/x3.conf.example $(prefix)
        $(INSTALL) -m 644 $(srcdir)/sockcheck.conf.example $(prefix)
+       ./install-r.sh $(srcdir)/src/plugins $(prefix)
        @echo
        @echo X3-$(VERSION) has been installed to $(prefix)
        @echo Remember to edit x3.conf.example and sockcheck.conf.example
index 3b6219b7dc3a195b6e04a6a93e79ec4468af7398..518f3e5b2eff3d4401012011297f91061138d949 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.in Id: configure.in 2384 2008-12-25 06:49:44Z sirvulcan .
+# From configure.in Id: configure.in 2427 2009-01-23 23:27:32Z rubin .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.61 for X3 1.6.
 #
@@ -706,6 +706,7 @@ AMDEPBACKSLASH
 CCDEPMODE
 am__fastdepCC_TRUE
 am__fastdepCC_FALSE
+CP
 RANLIB
 LN_S
 CPP
@@ -4649,6 +4650,47 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_CP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $CP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_CP="$CP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+CP=$ac_cv_path_CP
+if test -n "$CP"; then
+  { echo "$as_me:$LINENO: result: $CP" >&5
+echo "${ECHO_T}$CP" >&6; }
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+
 if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
 set dummy ${ac_tool_prefix}ranlib; ac_word=$2
@@ -10854,13 +10896,13 @@ AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim
 CCDEPMODE!$CCDEPMODE$ac_delim
 am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim
 am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim
+CP!$CP$ac_delim
 RANLIB!$RANLIB$ac_delim
 LN_S!$LN_S$ac_delim
 CPP!$CPP$ac_delim
 GREP!$GREP$ac_delim
 EGREP!$EGREP$ac_delim
 MAKER!$MAKER$ac_delim
-ALLOCA!$ALLOCA$ac_delim
 _ACEOF
 
   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -10902,6 +10944,7 @@ _ACEOF
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+ALLOCA!$ALLOCA$ac_delim
 pythonpath!$pythonpath$ac_delim
 DO_PYTHON_TRUE!$DO_PYTHON_TRUE$ac_delim
 DO_PYTHON_FALSE!$DO_PYTHON_FALSE$ac_delim
@@ -10916,7 +10959,7 @@ LIBOBJS!$LIBOBJS$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 12; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 13; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
index 5625b43cfed65466321b038ee711e28ca8ae23d5..1b540c08fd9ff3df450f1f53f0ddfe1320ac0050 100644 (file)
@@ -22,6 +22,8 @@ AC_PROG_AWK
 AC_PROG_CC
 AC_PROG_INSTALL
 
+AC_PATH_PROG(CP, cp)
+
 AC_PROG_RANLIB
 dnl AC_PROG_LIBTOOL
 
diff --git a/install-r.sh b/install-r.sh
new file mode 100755 (executable)
index 0000000..c687834
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+# This is a hacky solution to the problem of INSTALL not knowing how to recursively copy files
+# and our need to recursively copy with exceptions the plugin tree. It works on linux,
+# but needs serious work to be more portable. Please Help. -Rubin
+SRC=$1
+DST=$2
+
+# TODO: find banaries like 'find' and 'cp' in common locations and/or path, and
+# use them there instead of assuming they are in path.
+
+if [ "_$DST" = _ ]; then
+    exit
+fi
+
+cd `dirname "$SRC"`
+SRCDIR=`basename "$SRC"`
+find "$SRCDIR" \! -path '*/.*' | \
+    while read f; do \
+        d="$DST/${f#foo/}"; \
+        mkdir -p "$(dirname "$d")"; \
+        if [ -f $f ]; then cp -v $f $d; fi; \
+    done
+# this will break if SRCDIR has hidden directories in it :/
+#find "$SRCDIR" \! -path '*/.*'
+
index bc2a5cb2923080b640eaf8dbe903a2586cd25c41..de99971ccb749a6ad678be2a6d3688cb571a7cc9 100644 (file)
@@ -77,6 +77,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CP = @CP@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CYGPATH_W = @CYGPATH_W@
index 26fb9ab75e49a8719f933a70f8419ff5b33d93b9..66b847fee10a508f985c3b68fc340de258fbbc27 100644 (file)
@@ -92,6 +92,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CP = @CP@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CYGPATH_W = @CYGPATH_W@
index c86cfefd5e5733c49f3c52ec29e746c9565546bd..e99fa2d7f7c103520035a9f151c9a94a223b037e 100644 (file)
@@ -281,14 +281,38 @@ emb_get_account(PyObject *self, PyObject *args)
                            );
 }
 
+static PyObject*
+emb_log_module(PyObject *self, PyObject *args)
+{
+    /* a gateway to standard X3 logging subsystem.
+     * level is a value 0 to 9 as defined by the log_severity enum in log.h.
+     * LOG_INFO is 3, LOG_WARNING is 6, LOG_ERROR is 7.
+     *
+     * for now, all logs go to the PY_LOG log. In the future this will change.
+     */
+    char *message;
+    int ret = 0;
+    int level;
+
+    if(!PyArg_ParseTuple(args, "is", &level, &message))
+        return NULL;
+
+    log_module(PY_LOG, level, "%s", message);
+
+    return Py_BuildValue("i", ret);
+}
 
 static PyMethodDef EmbMethods[] = {
+    /* Communication methods */
     {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
     {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
     {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
+    {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
+    /* Information gathering methods */
     {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
     {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
     {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
+    /* null terminator */
     {NULL, NULL, 0, NULL}
 };
 
@@ -298,6 +322,41 @@ static PyMethodDef EmbMethods[] = {
      methods.  
  */
 
+void python_log_module() {
+    /* Attempt to convert python errors to x3 log system */
+    PyObject *exc, *tb, *value, *tmp;
+    char *str_exc = "NONE";
+    char *str_value = "NONE";
+    char *str_tb = "NONE";
+
+    PyErr_Fetch(&exc, &value, &tb);
+
+    if(exc) {
+        if((tmp = PyObject_Str(exc)))
+            str_exc = PyString_AsString(tmp);
+    }
+    if(value) {
+        if((tmp = PyObject_Str(value)))
+            str_value = PyString_AsString(tmp);
+    }
+    if(tb) {
+        if((tmp = PyObject_Str(tb)))
+            str_tb = PyString_AsString(tmp);
+    }
+
+    /* Now restore it so we can print it (just in case) 
+     *   (should we do this only when running in debug mode?) */
+    PyErr_Restore(exc, value, tb);
+    PyErr_Print(); /* which of course, clears it again.. */
+
+    log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
+
+    /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
+     * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
+    // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
+}
+
+
 PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
     /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
        which handlers use to interact with c.
@@ -376,7 +435,8 @@ PyObject *new_irc_object(char *command_service, char *command_caller, char *comm
         pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
         if(!pIrcObj) {
             log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
-            PyErr_Print();
+            python_log_module();
+            //PyErr_Print();
         }
         if(pIrcArgs != NULL)  {
            Py_DECREF(pIrcArgs);
@@ -391,7 +451,6 @@ PyObject *new_irc_object(char *command_service, char *command_caller, char *comm
     }
 }
 
-
 int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
     /*  This is how we talk to modpython.c.  First a new instance of the irc class is created using these
         arguments to setup the current environment. Then the named method of the handler object is called
@@ -403,7 +462,7 @@ int python_call_handler(char *handler, char *args[], size_t argc, char *command_
     PyObject *pValue;
 
     log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
-    if(base_module != NULL) {
+    if(base_module != NULL && handler_object != NULL) {
         pIrcObj = new_irc_object(command_service, command_caller, command_target);
         if(!pIrcObj) {
             log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
@@ -413,6 +472,7 @@ int python_call_handler(char *handler, char *args[], size_t argc, char *command_
         pArgs = python_build_handler_args(argc, args, pIrcObj);
         pMethod = PyObject_GetAttrString(handler_object, handler);
         if(pMethod && PyCallable_Check(pMethod)) {
+            /* Call the method, with the arguments */
             pValue = PyObject_CallObject(pMethod, pArgs);
             if(pArgs) {
                 Py_DECREF(pArgs);
@@ -421,8 +481,9 @@ int python_call_handler(char *handler, char *args[], size_t argc, char *command_
                 int ret;
                 ret = PyInt_AsLong(pValue);
                 if(ret == -1 && PyErr_Occurred()) {
-                    PyErr_Print();
+                    //PyErr_Print();
                     log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
+                    python_log_module();
                     ret = 0;
                 }
                 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
@@ -434,8 +495,9 @@ int python_call_handler(char *handler, char *args[], size_t argc, char *command_
             else {
                 /* TODO: instead of print errors, get them as strings
                  * and deal with them with normal x3 log system. */
-                PyErr_Print();
                 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
+                //PyErr_Print();
+                python_log_module();
                 Py_DECREF(pIrcObj);
                 Py_DECREF(pMethod);
                 return 0;
@@ -469,10 +531,24 @@ PyObject *python_new_handler_object() {
         /*PyObject *pValue; */
 
         pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
-        return pHandlerObj;
+        if(pHandlerObj != NULL) {
+            log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
+            return pHandlerObj;
+        }
+        else {
+            log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
+            //PyErr_Print();
+            python_log_module();
+            return NULL;
+        }
     }
     else {
         log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
+        //PyErr_Print();
+        python_log_module();
+        if(pHandlerClass) {
+            Py_DECREF(pHandlerClass);
+        }
         return NULL;
     }
 }
@@ -534,7 +610,8 @@ int python_load() {
         }
     }
     else {
-        PyErr_Print();
+        //PyErr_Print();
+        python_log_module();
         log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
         return 0;
     }
@@ -593,6 +670,22 @@ static MODCMD_FUNC(cmd_run) {
     return 1;
 }
 
+#define numstrargs(X)   sizeof(X) / sizeof(*X)
+static MODCMD_FUNC(cmd_command) {
+    char *plugin = argv[1];
+    char *command = argv[2];
+    char *msg; /* args */
+    if(argc > 3) {
+       msg = unsplit_string(argv + 3, argc - 3, NULL);
+    }
+    else {
+        msg = "";
+    }
+    char *args[] = {plugin, command, msg};
+    python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
+    return 1;
+}
+
 int python_init(void) {
     /* X3 calls this function on init of the module during startup. We use it to
        do all our setup tasks and bindings 
@@ -613,6 +706,7 @@ int python_init(void) {
 */
     modcmd_register(python_module, "reload",  cmd_reload,  1,  MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
     modcmd_register(python_module, "run",  cmd_run,  2,  MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
+    modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
     reg_join_func(python_handle_join);
     reg_exit_func(python_cleanup);
 
index b5e6d2f6e18fd057add790f5bbe66cd8907c9c32..75e0a58d46725e348a7951fe24cd0fcd452141fd 100644 (file)
@@ -38,39 +38,111 @@ class irc:
     def reply(self, message):
         """ Send a private reply to the user using convenience values"""
         print "DEBUG: sending a message from %s to %s: %s"%(self.service, self.caller, message)
-        if(self.target):
+        if(len(self.target)):
             self.send_target_privmsg(self.service, self.target, "%s: %s"%(self.caller, message))
         else:
             self.send_target_privmsg(self.service, self.caller, message)
 
 class handler:
     """ Main hub of python system. Handle callbacks from c. """
-    modules = None  #module object to deal with 
+
+    def __init__(self):
+        print "DEBUG: constructor for handler initing"
+        self.plugins = plugins(self)
+        if(not self.plugins):
+            print "DEBUG: unable to make self.plugins!?!"
 
     def init(self, irc): # not to be confused with __init__!
-        print "DEBUG: This is x3init in python"
-        self.modules = modules()
+        """ This gets called once all the objects are up and running. Otherwise,
+        were not done initing this own instance to be able to start calling it """
+        print "DEBUG: in handler.init()"
+        self.plugins.init()
         return 0
 
     def join(self, irc, channel, nick):
         user = svc.get_user(nick)
         print "DEBUG: handler.join()"
-        irc.send_target_privmsg("x3", channel, "%s joined %s: %s "%(nick, channel, user))
+        self.plugins.callhandler("join", irc, [channel, nick], [channel, nick])
         return 0
         
     def cmd_run(self, irc, cmd):
         print "DEBUG: handler.cmd_run: %s"%cmd
-        eval(cmd);
-        return 0;
+        eval(cmd)
+        return 0
 
-class modules:
-    """Class to handle loading/unloading of modules"""
-    loaded_modules = {}
+    def addhook(self, event, method, filter=[None], data=None):
+        self.plugins.addhook(event, method, filter, data)
+        return 0
 
-    def __init__(self):
+    def addcommand(self, plugin, command, method):
+        self.addhook("command", method, [plugin, command])
+
+    def cmd_command(self, irc, plugin, cmd, args):
+        print "DEBUG: handlec.cmd_command; %s %s; args= %s"%(plugin, cmd, args)
+        self.plugins.callhandler("command", irc, [plugin, cmd], [args])
+        return 0
+
+class plugins:
+    """Class to handle loading/unloading of plugins"""
+    loaded_plugins = {}
+    hooks = []
+
+    class hook:
+        """ This is a request from a plugin to be called on an event """
+        event = ""     # Event to be called on (eg "join")
+        method = None  # Method to call
+        filter = None  # Arguments to filter
+        data = ""      # plugin-supplied data for plugin use
+        
+        def __init__(self, event, method, filter, data):
+            self.event = event
+            self.method = method
+            self.filter = filter
+            self.data = data
+
+        def event_is(self, event, evdata):
+            if(self.event == event):
+                for i in range(len(self.filter)):
+                    if( self.filter[i] != None 
+                      and self.filter[i] != evdata[i]): # should be case insensitive? or how to compare?
+                        print "DEBUG: rejecting event, %s is not %s"%(self.filter[i], evdata[i])
+                        return False
+                return True
+            else:
+                return False
+
+        def trigger(self, irc, args):
+            print "DEBUG: Triggering %s event. with '%s' arguments."%(self.event, args)
+            self.method(irc, *args)
+
+    def __init__(self, handler):
+        """ Constructor """
+        print "DEBUG: constructor for plugins initing"
+        self.handler = handler
+
+    def init(self):
+        print "DEBUG: in plugins.init()"
         self.load("annoy")
 
+    def addhook(self, event, method, filter=[None], data=None):
+        print "DEBUG: Adding hook for %s."%event
+        self.hooks.append(self.hook(event, method, filter, data))
+
+    def findhooksforevent(self, event, data):
+        ret = []
+        print "DEBUG: findhooksforevent() looking..."
+        for hook in self.hooks:
+            print "DEBUG: looking at a %s hook..."%hook.event
+            if(hook.event_is(event, data)):
+                ret.append(hook)
+        return ret
+
+    def callhandler(self, event, irc, filter, args):
+        for hook in self.findhooksforevent(event, filter):
+            hook.trigger(irc, args)
+
     def load(self, name):
+        """ Loads a plugin by name """
         mod_name = "plugins.%s"%name
         need_reload = False
         if(sys.modules.has_key(mod_name)):
@@ -83,7 +155,6 @@ class modules:
         if(need_reload == True):
             reload(module) # to ensure its read fresh
         Class = module.Class
-        pluginObj = Class(irc())
-        self.loaded_modules[mod_name] = pluginObj
+        pluginObj = Class(self.handler, irc())
+        self.loaded_plugins[mod_name] = pluginObj
 
-       
diff --git a/src/plugins/README b/src/plugins/README
new file mode 100644 (file)
index 0000000..09eb48f
--- /dev/null
@@ -0,0 +1,6 @@
+These are mod-python plugins distributed with x3. If you wish to make customizations, you should do so in the 
+plugins/ directory of your x3 runtime location, rather than the x3 source tree. 
+
+Make a copy of the plugin directory you wish to change to your own plugin name, so your changes won't be overwritten on the next make install.
+
+
diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py
new file mode 100644 (file)
index 0000000..7b1dc9c
--- /dev/null
@@ -0,0 +1 @@
+__all__ = [ "annoy" ]
diff --git a/src/plugins/annoy/__init__.py b/src/plugins/annoy/__init__.py
new file mode 100644 (file)
index 0000000..daad326
--- /dev/null
@@ -0,0 +1,6 @@
+
+import plugin
+reload(plugin)
+
+Class = plugin.Class
+
diff --git a/src/plugins/annoy/plugin.py b/src/plugins/annoy/plugin.py
new file mode 100644 (file)
index 0000000..fcdc773
--- /dev/null
@@ -0,0 +1,22 @@
+# anoy module
+
+import svc
+
+class Annoy:
+
+    def __init__(self, handler, irc):
+        self.handler = handler
+        self.name = "annoy"
+
+        irc.send_target_privmsg("O3", "#TheOPS", "%s is loaded"%self.name)
+        handler.addhook("join", self.on_join, "foobar")
+        handler.addcommand(self.name, "dance", self.dance)
+        self.test = "footest"
+
+    def on_join(self, irc, channel, nick):
+        irc.send_target_privmsg("x3", channel, "%s joined %s:%s "%(nick, channel, self.test))
+
+    def dance(self, irc, args):
+        irc.reply("Ok, %s, we can dance %s."%(irc.caller, args))
+
+Class = Annoy