1 /* mod-python.c - Script module for x3
2 * Copyright 2003-2004 Martijn Smit and srvx Development Team
3 * Copyright 2005-2006 X3 Development Team
5 * This file is part of x3.
7 * x3 is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with srvx; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 #ifdef WITH_PYTHON /* just disable this file if python doesnt exist */
39 * - Impliment most of proto-p10 irc_* commands for calling from scripts
40 * - Impliment functions to look up whois, channel, account, and reg-channel info for scripts
41 * - Impliment x3.conf settings for python variables like include path, etc.
42 * - modpython.py calls for everything you can reg_ a handler for in x3
43 * - Some kind of system for getting needed binds bound automagicaly to make it easier
44 * to run peoples scripts and mod-python in general.
45 * - An interface to reading/writing data to x3.db. Maybe generic, or attached to account or channel reg records?
48 static const struct message_entry msgtab
[] = {
49 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
50 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
51 { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
52 { "PYMSG_RUN_EXCEPTION", "Error running python: %s: %s." },
53 { NULL
, NULL
} /* sentenal */
56 #define MODPYTHON_CONF_NAME "modules/python"
60 char const* scripts_dir
;
61 char const* main_module
;
64 static struct log_type
*PY_LOG
;
65 const char *python_module_deps
[] = { NULL
};
66 static struct module *python_module
;
68 PyObject
*base_module
= NULL
; /* Base python handling library */
69 PyObject
*handler_object
= NULL
; /* instanciation of handler class */
72 extern struct userNode
*global
, *chanserv
, *opserv
, *nickserv
, *spamserv
;
74 /* ---------------------------------------------------------------------- *
75 Some hooks you can call from modpython.py to interact with the
76 service, and IRC. These emb_* functions are available as _svc.*
81 emb_dump(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
83 /* Dump a raw string into the socket
84 usage: _svc.dump(<P10 string>)
91 if(!PyArg_ParseTuple(args
, "s:dump", &buf
))
94 safestrncpy(linedup
, buf
, sizeof(linedup
));
96 if(parse_line(linedup
, 1)) {
100 PyErr_SetString(PyExc_Exception
, "invalid protocol message");
104 return Py_BuildValue("i", ret
);
108 emb_send_target_privmsg(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
111 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
118 struct service
*service
;
121 if(!PyArg_ParseTuple(args
, "sss:reply", &servicenick
, &channel
, &buf
))
123 if(!(service
= service_find(servicenick
))) {
124 /* TODO: generate python exception here */
127 send_target_message(5, channel
, service
->bot
, "%s", buf
);
128 return Py_BuildValue("i", ret
);
132 emb_send_target_notice(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
135 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
142 struct service
*service
;
145 if(!PyArg_ParseTuple(args
, "sss:reply", &servicenick
, &target
, &buf
))
147 if(!(service
= service_find(servicenick
))) {
148 /* TODO: generate python exception here */
151 send_target_message(4, target
, service
->bot
, "%s", buf
);
152 return Py_BuildValue("i", ret
);
156 pyobj_from_usernode(struct userNode
* user
) {
159 PyObject
* pChanList
= PyTuple_New(user
->channels
.used
);
161 for (n
=0; n
< user
->channels
.used
; n
++) {
162 mn
= user
->channels
.list
[n
];
163 PyTuple_SetItem(pChanList
, n
, Py_BuildValue("s", mn
->channel
->name
));
166 return Py_BuildValue("{"
170 "s: s, " /* hostname */
172 "s: s, " /* fakehost */
173 "s: s, " /* sethost */
174 "s: s, " /* crypthost */
175 "s: s, " /* cryptip */
176 #ifdef WITH_PROTOCOL_P10
177 "s: s, " /* numeric */
178 #endif /* WITH_PROTOCOL_P10 */
180 "s: i, " /* no_notice */
182 "s: s, " /* version_reply */
183 "s: s, " /* account */
184 "s: O}", /* channels */
186 "ident", user
->ident
,
188 "hostname", user
->hostname
,
189 "ip", irc_ntoa(&user
->ip
),
190 "fakehost", user
->fakehost
,
191 "sethost", user
->sethost
,
192 "crypthost", user
->crypthost
,
193 "cryptip", user
->cryptip
,
194 #ifdef WITH_PROTOCOL_P10
195 "numeric", user
->numeric
,
196 #endif /* WITH_PROTOCOL_P10 */
198 "no_notice", user
->no_notice
,
200 "version_reply", user
->version_reply
,
201 "account", user
->handle_info
? user
->handle_info
->handle
: NULL
,
202 "channels", pChanList
);
206 emb_get_user(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
208 /* Get a python object containing everything x3 knows about a user, by nick.
209 usage: _svc.get_user(<nick>)
212 struct userNode
*user
;
214 if(!PyArg_ParseTuple(args
, "s", &nick
))
217 if(!(user
= GetUserH(nick
))) {
218 PyErr_SetString(PyExc_Exception
, "no such user");
222 return pyobj_from_usernode(user
);
226 emb_get_channel(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
228 /* Returns a python dict object with all sorts of info about a channel.
229 usage: _svc.get_channel(<name>)
232 struct chanNode
*channel
;
234 PyObject
*pChannelMembers
;
235 PyObject
*pChannelBans
;
236 PyObject
*pChannelExempts
;
239 if(!PyArg_ParseTuple(args
, "s", &name
))
241 if(!(channel
= GetChannel(name
))) {
242 /* TODO: generate py exception here */
246 /* build tuple of nicks in channel */
247 pChannelMembers
= PyTuple_New(channel
->members
.used
);
248 for(n
=0;n
< channel
->members
.used
;n
++) {
249 struct modeNode
*mn
= channel
->members
.list
[n
];
250 PyTuple_SetItem(pChannelMembers
, n
, Py_BuildValue("s", mn
->user
->nick
));
253 /* build tuple of bans */
254 pChannelBans
= PyTuple_New(channel
->banlist
.used
);
255 for(n
=0; n
< channel
->banlist
.used
;n
++) {
256 struct banNode
*bn
= channel
->banlist
.list
[n
];
257 PyTuple_SetItem(pChannelBans
, n
,
258 Py_BuildValue("{s:s,s:s,s:i}",
265 /* build tuple of exempts */
266 pChannelExempts
= PyTuple_New(channel
->exemptlist
.used
);
267 for(n
=0; n
< channel
->exemptlist
.used
;n
++) {
268 struct exemptNode
*en
= channel
->exemptlist
.list
[n
];
269 PyTuple_SetItem(pChannelExempts
, n
,
270 Py_BuildValue("{s:s,s:s,s:i}",
277 return Py_BuildValue("{s:s,s:s,s:s,s:i"
278 ",s:i,s:i,s:O,s:O,s:O}",
280 "name", channel
->name
,
281 "topic", channel
->topic
,
282 "topic_nick", channel
->topic_nick
,
283 "topic_time", channel
->topic_time
,
285 "timestamp", channel
->timestamp
,
286 "modes", channel
->modes
,
287 "members", pChannelMembers
,
288 "bans", pChannelBans
,
289 "exempts", pChannelExempts
294 emb_get_account(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
296 /* Returns a python dict object with all sorts of info about an account.
297 usage: _svc.get_account(<account name>)
300 struct handle_info
*hi
;
303 if(!PyArg_ParseTuple(args
, "s", &name
))
306 hi
= get_handle_info(name
);
310 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
313 "account", hi
->handle
,
314 "registered", hi
->registered
,
315 "last_seen", hi
->lastseen
,
316 "infoline", hi
->infoline
? hi
->infoline
: "",
317 "email", hi
->email_addr
? hi
->email_addr
: "",
319 "fakehost", hi
->fakehost
? hi
->fakehost
: "",
320 "last_quit_host", hi
->last_quit_host
323 /* users online authed to this account */
325 /* nicks (nickserv nets only?) */
333 emb_get_info(UNUSED_ARG(PyObject
*self
), UNUSED_ARG(PyObject
*args
))
335 /* return some info about the general setup
336 * of X3, such as what the chanserv's nickname
341 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
342 "chanserv", chanserv
? chanserv
->nick
: "ChanServ",
343 "nickserv", nickserv
?nickserv
->nick
: "NickServ",
344 "opserv", opserv
?opserv
->nick
: "OpServ",
345 "global", global
?global
->nick
: "Global",
346 "spamserv", spamserv
?spamserv
->nick
: "SpamServ");
350 emb_log_module(UNUSED_ARG(PyObject
*self
), PyObject
*args
)
352 /* a gateway to standard X3 logging subsystem.
353 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
354 * LOG_INFO is 3, LOG_WARNING is 6, LOG_ERROR is 7.
356 * for now, all logs go to the PY_LOG log. In the future this will change.
363 if(!PyArg_ParseTuple(args
, "is", &level
, &message
))
366 log_module(PY_LOG
, level
, "%s", message
);
368 return Py_BuildValue("i", ret
);
371 static PyMethodDef EmbMethods
[] = {
372 /* Communication methods */
373 {"dump", emb_dump
, METH_VARARGS
, "Dump raw P10 line to server"},
374 {"send_target_privmsg", emb_send_target_privmsg
, METH_VARARGS
, "Send a message to somewhere"},
375 {"send_target_notice", emb_send_target_notice
, METH_VARARGS
, "Send a notice to somewhere"},
376 {"log_module", emb_log_module
, METH_VARARGS
, "Log something using the X3 log subsystem"},
377 //TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
378 // This should use environment from "python command" call to pass in, if available
382 //TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
383 //TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
384 //TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
385 //TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
386 //TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
388 //TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
389 //TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
391 //TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
392 //TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
393 /* Information gathering methods */
394 {"get_user", emb_get_user
, METH_VARARGS
, "Get details about a nickname"},
395 {"get_channel", emb_get_channel
, METH_VARARGS
, "Get details about a channel"},
396 {"get_account", emb_get_account
, METH_VARARGS
, "Get details about an account"},
397 {"get_info", emb_get_info
, METH_VARARGS
, "Get various misc info about x3"},
398 /* null terminator */
399 {NULL
, NULL
, 0, NULL
}
403 /* ------------------------------------------------------------------------------------------------ *
404 Thes functions set up the embedded environment for us to call out to modpython.py class
408 void python_log_module() {
409 /* Attempt to convert python errors to x3 log system */
410 PyObject
*exc
, *tb
, *value
, *tmp
;
411 char *str_exc
= "NONE";
412 char *str_value
= "NONE";
413 char *str_tb
= "NONE";
415 PyErr_Fetch(&exc
, &value
, &tb
);
418 if((tmp
= PyObject_Str(exc
)))
419 str_exc
= PyString_AsString(tmp
);
422 if((tmp
= PyObject_Str(value
)))
423 str_value
= PyString_AsString(tmp
);
426 if((tmp
= PyObject_Str(tb
)))
427 str_tb
= PyString_AsString(tmp
);
430 /* Now restore it so we can print it (just in case)
431 * (should we do this only when running in debug mode?) */
432 PyErr_Restore(exc
, value
, tb
);
433 PyErr_Print(); /* which of course, clears it again.. */
435 log_module(PY_LOG
, LOG_WARNING
, "PYTHON error: %s, value: %s", str_exc
, str_value
);
437 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
438 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
439 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
443 PyObject
*python_build_handler_args(size_t argc
, char *args
[], PyObject
*pIrcObj
) {
444 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
445 which handlers use to interact with c.
446 argc = number of args
448 pIrcObj = instance of the irc class
451 PyObject
*pArgs
= NULL
;
453 pArgs
= PyTuple_New(argc
+ 1);
455 PyTuple_SetItem(pArgs
, i
++, pIrcObj
);
459 for(n
= 0; n
< argc
; ++n
) {
460 pValue
= PyString_FromString(args
[n
]);
463 log_module(PY_LOG
, LOG_INFO
, "Unable to convert '%s' to python string", args
[n
]);
466 PyTuple_SetItem(pArgs
, n
+i
, pValue
);
472 PyObject
*python_build_args(size_t argc
, char *args
[]) {
473 /* Builds the passed in arguments into a python argument tuple.
474 argc = number of args
478 PyObject
*pArgs
= NULL
;
481 pArgs
= PyTuple_New(argc
);
483 for(i
= 0; i
< argc
; ++i
) {
484 pValue
= PyString_FromString(args
[i
]);
487 log_module(PY_LOG
, LOG_INFO
, "Unable to convert '%s' to python string", args
[i
]);
490 PyTuple_SetItem(pArgs
, i
, pValue
);
497 PyObject
*new_irc_object(char *command_service
, char *command_caller
, char *command_target
) {
498 /* Creates a new instance of the irc class (from modpython.py) which is initalized
499 with current environment details like which service were talking to.
500 command_service = which service we are talking to, or empty string if none
501 command_caller = nick of user generating message, or empty string if none
502 command_target = If were reacting to something on a channel, this will
503 be set to the name of the channel. Otherwise empty
505 PyObject
*pIrcArgs
= NULL
;
509 log_module(PY_LOG
, LOG_INFO
, "Attempting to instanciate irc class; %s %s %s", command_service
, command_caller
, command_target
);
510 pIrcClass
= PyObject_GetAttrString(base_module
, "irc");
511 /* pIrcClass is a new reference */
512 if(pIrcClass
&& PyCallable_Check(pIrcClass
)) {
514 char *ircargs
[] = {command_service
, command_caller
, command_target
};
517 pIrcArgs
= python_build_args(3, ircargs
);
518 pIrcObj
= PyObject_CallObject(pIrcClass
, pIrcArgs
);
520 log_module(PY_LOG
, LOG_ERROR
, "IRC Class failed to load");
524 if(pIrcArgs
!= NULL
) {
527 Py_DECREF(pIrcClass
);
531 /* need to free pIrcClass here if it WAS found but was NOT callable? */
532 log_module(PY_LOG
, LOG_ERROR
, "Unable to find irc class");
537 int python_call_handler(char *handler
, char *args
[], size_t argc
, char *command_service
, char *command_caller
, char *command_target
) {
538 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
539 arguments to setup the current environment. Then the named method of the handler object is called
540 with the givin arguments.
547 log_module(PY_LOG
, LOG_INFO
, "attempting to call handler %s.", handler
);
548 if(base_module
!= NULL
&& handler_object
!= NULL
) {
549 pIrcObj
= new_irc_object(command_service
, command_caller
, command_target
);
551 log_module(PY_LOG
, LOG_INFO
, "Can't get irc object. Bailing.");
555 pArgs
= python_build_handler_args(argc
, args
, pIrcObj
);
556 pMethod
= PyObject_GetAttrString(handler_object
, handler
);
557 if(pMethod
&& PyCallable_Check(pMethod
)) {
558 /* Call the method, with the arguments */
559 pValue
= PyObject_CallObject(pMethod
, pArgs
);
565 ret
= PyInt_AsLong(pValue
);
566 if(ret
== -1 && PyErr_Occurred()) {
568 log_module(PY_LOG
, LOG_INFO
, "error converting return value of handler %s to type long. ", handler
);
572 log_module(PY_LOG
, LOG_INFO
, "handler %s was run successfully, returned %d.", handler
, ret
);
579 /* TODO: instead of print errors, get them as strings
580 * and deal with them with normal x3 log system. */
581 log_module(PY_LOG
, LOG_WARNING
, "call to handler %s failed", handler
);
589 else { /* couldn't find handler methed */
591 /* Free pMethod if it was found but not callable? */
592 log_module(PY_LOG
, LOG_ERROR
, "Cannot find handler %s.", handler
);
597 else { /* No base module.. no python? */
598 log_module(PY_LOG
, LOG_INFO
, "Cannot handle %s, Python is not initialized.", handler
);
603 PyObject
*python_new_handler_object() {
604 /* Create a new instance of the handler class.
605 This is called during python initilization (or reload)
606 and the result is saved and re-used.
608 PyObject
*pHandlerClass
, *pHandlerObj
;
610 log_module(PY_LOG
, LOG_INFO
, "Attempting to instanciate python class handler");
611 pHandlerClass
= PyObject_GetAttrString(base_module
, "handler");
612 /* Class is a new reference */
613 if(pHandlerClass
&& PyCallable_Check(pHandlerClass
)) {
614 /*PyObject *pValue; */
616 pHandlerObj
= PyObject_CallObject(pHandlerClass
, NULL
);
617 if(pHandlerObj
!= NULL
) {
618 log_module(PY_LOG
, LOG_INFO
, "Created new python handler object.");
622 log_module(PY_LOG
, LOG_ERROR
, "Unable to instanciate handler object");
629 log_module(PY_LOG
, LOG_ERROR
, "Unable to find handler class");
633 Py_DECREF(pHandlerClass
);
639 /* ------------------------------------------------------------------------------- *
640 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
641 Mostly we just build relevant args and call the proper handler method
643 debate: do we just register these and check them in python
644 for every one (slow?) or actually work out if a plugin needs
645 it first? We will start by doing it every time.
648 python_handle_join(struct modeNode
*mNode
)
650 /* callback for handle_join events.
652 struct userNode
*user
= mNode
->user
;
653 struct chanNode
*channel
= mNode
->channel
;
656 log_module(PY_LOG
, LOG_INFO
, "python module handle_join");
657 if(!channel
||!user
) {
658 log_module(PY_LOG
, LOG_WARNING
, "Python code got join without channel or user!");
662 char *args
[] = {channel
->name
, user
->nick
};
663 return python_call_handler("join", args
, 2, "", "", "");
668 python_handle_server_link(struct server
*server
)
670 log_module(PY_LOG
, LOG_INFO
, "python module handle_server_link");
672 log_module(PY_LOG
, LOG_WARNING
, "Python code got server link without server!");
676 char *args
[] = {server
->name
, server
->description
};
677 return python_call_handler("server_link", args
, 2, "", "", "");
682 python_handle_new_user(struct userNode
*user
)
684 log_module(PY_LOG
, LOG_INFO
, "Python module handle_new_user");
686 log_module(PY_LOG
, LOG_WARNING
, "Python code got new_user without the user");
690 char *args
[] = {user
->nick
, user
->ident
, user
->hostname
, user
->info
};
691 return python_call_handler("new_user", args
, 4, "", "", "");
696 python_handle_nick_change(struct userNode
*user
, const char *old_nick
)
698 log_module(PY_LOG
, LOG_INFO
, "Python module handle_nick_change");
700 log_module(PY_LOG
, LOG_WARNING
, "Python code got nick_change without the user!");
703 char *args
[] = {user
->nick
, (char *)old_nick
};
704 python_call_handler("nick_change", args
, 2, "", "", "");
708 /* ----------------------------------------------------------------------------- */
712 /* Init the python engine and do init work on modpython.py
713 This is called during x3 startup, and on a python reload
717 char* env
= getenv("PYTHONPATH");
723 setenv("PYTHONPATH", modpython_conf
.scripts_dir
, 1);
724 else if (!strstr(env
, modpython_conf
.scripts_dir
)) {
725 buffer
= (char*)malloc(strlen(env
) + strlen(modpython_conf
.scripts_dir
) + 2);
726 sprintf(buffer
, "%s:%s", modpython_conf
.scripts_dir
, env
);
727 setenv("PYTHONPATH", buffer
, 1);
733 Py_InitModule("_svc", EmbMethods
);
734 pName
= PyString_FromString(modpython_conf
.main_module
);
735 base_module
= PyImport_Import(pName
);
738 Py_XDECREF(handler_object
);
739 handler_object
= NULL
;
741 if(base_module
!= NULL
) {
742 handler_object
= python_new_handler_object();
744 python_call_handler("init", NULL
, 0, "", "", "");
748 /* error handler class not found */
749 log_module(PY_LOG
, LOG_WARNING
, "Failed to create handler object");
756 log_module(PY_LOG
, LOG_WARNING
, "Failed to load modpython.py");
763 python_finalize(void) {
764 /* Called after X3 is fully up and running.
765 Code can be put here that needs to run to init things, but
766 which is sensitive to everything else in x3 being up and ready
770 PyRun_SimpleString("print 'Hello, World of Python!'");
771 log_module(PY_LOG
, LOG_INFO
, "python module finalize");
777 python_cleanup(void) {
778 /* Called on shutdown of the python module (or before reloading)
781 log_module(PY_LOG
, LOG_INFO
, "python module cleanup");
782 if (PyErr_Occurred())
784 Py_Finalize(); /* Shut down python enterpriter */
787 /* ---------------------------------------------------------------------------------- *
788 Python module command handlers.
790 static MODCMD_FUNC(cmd_reload
) {
791 /* reload the python system completely
793 log_module(PY_LOG
, LOG_INFO
, "Shutting python down");
795 log_module(PY_LOG
, LOG_INFO
, "Loading python stuff");
797 reply("PYMSG_RELOAD_SUCCESS");
800 reply("PYMSG_RELOAD_FAILED");
805 static char* format_python_error(int space_nls
) {
806 PyObject
* extype
= NULL
, *exvalue
= NULL
, *extraceback
= NULL
;
807 PyObject
* pextypestr
= NULL
, *pexvaluestr
= NULL
;
808 char* extypestr
= NULL
, *exvaluestr
= NULL
;
809 size_t retvallen
= 0;
810 char* retval
= NULL
, *tmp
;
812 PyErr_Fetch(&extype
, &exvalue
, &extraceback
);
816 pextypestr
= PyObject_Str(extype
);
819 extypestr
= PyString_AsString(pextypestr
);
823 pexvaluestr
= PyObject_Str(exvalue
);
825 exvaluestr
= PyString_AsString(pexvaluestr
);
827 retvallen
= strlen(extypestr
) + (exvaluestr
? strlen(exvaluestr
) + 2 : 0) + 1;
828 retval
= (char*)malloc(retvallen
);
830 snprintf(retval
, retvallen
, "%s: %s", extypestr
, exvaluestr
);
832 strncpy(retval
, extypestr
, retvallen
);
844 if (PyErr_Occurred())
845 PyErr_Clear(); /* ignore errors caused by formatting */
848 Py_XDECREF(extraceback
);
849 Py_XDECREF(pextypestr
);
850 Py_XDECREF(pexvaluestr
);
855 return strdup("unknown exception");
858 static MODCMD_FUNC(cmd_run
) {
859 /* this method allows running arbitrary python commands.
863 PyObject
* py_main_module
;
864 PyObject
* py_globals
;
867 PyObject
* extype
, *exvalue
, *extraceback
;
868 PyObject
* exvaluestr
= NULL
;
869 char* exmsg
= NULL
, *exmsgptr
;
871 py_main_module
= PyImport_AddModule("__main__");
872 py_globals
= py_locals
= PyModule_GetDict(py_main_module
);
874 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
876 py_retval
= PyRun_String(msg
, Py_file_input
, py_globals
, py_locals
);
877 if (py_retval
== NULL
) {
878 PyErr_Fetch(&extype
, &exvalue
, &extraceback
);
879 if (exvalue
!= NULL
) {
880 exvaluestr
= PyObject_Str(exvalue
);
881 exmsg
= strdup(PyString_AS_STRING(exvaluestr
));
883 while (exmsgptr
&& *exmsgptr
) {
884 if (*exmsgptr
== '\n' || *exmsgptr
== '\r' || *exmsgptr
== '\t')
889 if (extype
!= NULL
&& exvalue
!= NULL
&& PyType_Check(extype
)) {
890 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject
*)extype
)->tp_name
, exmsg
);
892 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
898 if (extraceback
!= NULL
)
899 Py_DECREF(extraceback
);
900 if (exvaluestr
!= NULL
)
901 Py_DECREF(exvaluestr
);
905 Py_DECREF(py_retval
);
911 #define numstrargs(X) sizeof(X) / sizeof(*X)
912 static MODCMD_FUNC(cmd_command
) {
913 char *plugin
= argv
[1];
914 char *command
= argv
[2];
915 char *msg
; /* args */
917 msg
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
922 char *args
[] = {plugin
, command
, msg
};
923 python_call_handler("cmd_command", args
, numstrargs(args
), cmd
->parent
->bot
->nick
, user
?user
->nick
:"", channel
?channel
->name
:"");
927 static void modpython_conf_read(void) {
931 if (!(conf_node
= conf_get_data(MODPYTHON_CONF_NAME
, RECDB_OBJECT
))) {
932 log_module(PY_LOG
, LOG_ERROR
, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME
);
936 str
= database_get_data(conf_node
, "scripts_dir", RECDB_QSTRING
);
937 modpython_conf
.scripts_dir
= str
? str
: "./";
939 str
= database_get_data(conf_node
, "main_module", RECDB_QSTRING
);
940 modpython_conf
.main_module
= str
? str
: "modpython";
943 int python_init(void) {
944 /* X3 calls this function on init of the module during startup. We use it to
945 do all our setup tasks and bindings
948 PY_LOG
= log_register_type("Python", "file:python.log");
949 python_module
= module_register("python", PY_LOG
, "mod-python.help", NULL
);
950 conf_register_reload(modpython_conf_read
);
952 log_module(PY_LOG
, LOG_INFO
, "python module init");
953 message_register_table(msgtab
);
956 reg_auth_func(python_check_messages);
957 reg_handle_rename_func(python_rename_account);
958 reg_unreg_func(python_unreg_account);
959 conf_register_reload(python_conf_read);
960 saxdb_register("python", python_saxdb_read, python_saxdb_write);
961 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
963 modcmd_register(python_module
, "reload", cmd_reload
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+oper", NULL
);
964 modcmd_register(python_module
, "run", cmd_run
, 2, MODCMD_REQUIRE_AUTHED
, "flags", "+oper", NULL
);
965 modcmd_register(python_module
, "command", cmd_command
, 3, MODCMD_REQUIRE_STAFF
, NULL
);
967 // Please help us by implimenting any of the callbacks listed as TODO below. They already exist
968 // in x3, they just need handle_ bridges implimented. (see python_handle_join for an example)
969 reg_server_link_func(python_handle_server_link
);
970 reg_new_user_func(python_handle_new_user
);
971 reg_nick_change_func(python_handle_nick_change
);
972 //TODO: reg_del_user_func(python_handle_del_user);
973 //TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
974 //TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
975 //TODO: reg_failpw_func(python_handle_failpw);
976 //TODO: reg_allowauth_func(python_handle_allowauth);
977 //TODO: reg_handle_merge_func(python_handle_merge);
979 //TODO: reg_oper_func(python_handle_oper);
980 //TODO: reg_new_channel_func(python_handle_new_channel);
981 reg_join_func(python_handle_join
);
982 //TODO: reg_del_channel_func(python_handle_del_channel);
983 //TODO: reg_part_func(python_handle_part);
984 //TODO: reg_kick_func(python_handle_kick);
985 //TODO: reg_topic_func(python_handle_topic);
986 //TODO: reg_channel_mode_func(python_handle_channel_mode);
988 //TODO: reg_privmsg_func(python_handle_privmsg);
989 //TODO: reg_notice_func
990 //TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
991 //TODO: reg_chanmsg_func(python_handle_chanmsg);
992 //TODO: reg_allchanmsg_func
993 //TODO: reg_user_mode_func
995 reg_exit_func(python_cleanup
);
1001 #endif /* WITH_PYTHON */