]> jfr.im git - irc/evilnet/x3.git/blame - src/mod-python.c
mod-python: add emb_get_accounts function
[irc/evilnet/x3.git] / src / mod-python.c
CommitLineData
0b350353 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
4 *
5 * This file is part of x3.
6 *
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.
11 *
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.
16 *
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.
20 */
21
22#include "config.h"
23#ifdef WITH_PYTHON /* just disable this file if python doesnt exist */
24
d8f8d3b6 25#ifndef WITH_PROTOCOL_P10
26#error mod-python is only supported with p10 protocol enabled
27#endif /* WITH_PROTOCOL_P10 */
0b350353 28
413fd8ea 29#include <Python.h>
0b350353 30#include "chanserv.h"
31#include "conf.h"
32#include "modcmd.h"
33#include "nickserv.h"
34#include "opserv.h"
35#include "saxdb.h"
1136f709 36#include "mail.h"
0b350353 37#include "timeq.h"
039a6658 38#include "compat.h"
39d37f27 39#include "nickserv.h"
0b350353 40
a2c8c575 41/* TODO notes
42 *
f0e11521 43 * - Implement most of proto-p10 irc_* commands for calling from scripts
44 * - Implement functions to look up whois, channel, account, and reg-channel info for scripts
45 * - Implement x3.conf settings for python variables like include path, etc.
4c216694 46 * - modpython.py calls for everything you can reg_ a handler for in x3
a2c8c575 47 * - Some kind of system for getting needed binds bound automagicaly to make it easier
f0e11521 48 * to run peoples' scripts and mod-python in general.
039a6658 49 * - An interface to reading/writing data to x3.db. Maybe generic, or attached to account or channel reg records?
a2c8c575 50 */
0b350353 51
52static const struct message_entry msgtab[] = {
caf97651 53 { "PYMSG_RELOAD_SUCCESS", "Reloaded Python scripts successfully." },
54 { "PYMSG_RELOAD_FAILED", "Error reloading Python scripts." },
413fd8ea 55 { "PYMSG_RUN_UNKNOWN_EXCEPTION", "Error running python: unknown exception." },
46f628b1 56 { "PYMSG_RUN_EXCEPTION", "Error running python: %s: %s." },
f0e11521 57 { NULL, NULL } /* sentinel */
0b350353 58};
59
ef5e0305 60#define MODPYTHON_CONF_NAME "modules/python"
61
62static
63struct {
64 char const* scripts_dir;
ed8d873c 65 char const* main_module;
ef5e0305 66} modpython_conf;
67
0b350353 68static struct log_type *PY_LOG;
69const char *python_module_deps[] = { NULL };
70static struct module *python_module;
71
caf97651 72PyObject *base_module = NULL; /* Base python handling library */
f0e11521 73PyObject *handler_object = NULL; /* instance of handler class */
caf97651 74
4c216694 75
039a6658 76extern struct userNode *global, *chanserv, *opserv, *nickserv, *spamserv;
77
f0e11521 78/*
79Some hooks you can call from modpython.py to interact with the
80service. These emb_* functions are available as _svc.* in python. */
4c216694 81
6d94ce8b 82struct _tuple_dict_extra {
83 PyObject* data;
84 size_t* extra;
85};
86
39d37f27 87static int _dict_iter_fill_tuple(char const* key, UNUSED_ARG(void* data), void* extra) {
318ec177 88 PyObject* tmp;
dcc1df5e 89 struct _tuple_dict_extra* real_extra = (struct _tuple_dict_extra*)extra;
90
318ec177 91 if ((tmp = PyString_FromString(key)) == NULL)
92 return 1;
93
94 if (PyTuple_SetItem(real_extra->data, *(int*)real_extra->extra, tmp)) {
95 Py_DECREF(tmp);
96 return 1;
97 }
98
dcc1df5e 99 *real_extra->extra = *real_extra->extra + 1;
100 return 0;
101}
102
6d94ce8b 103/* get a tuple with all users in it */
104static PyObject*
105emb_get_users(UNUSED_ARG(PyObject *self), PyObject *args) {
106 PyObject* retval;
5345ea76 107 PyObject* tmp;
108 size_t num_clients, n = 0, i;
6d94ce8b 109 struct _tuple_dict_extra extra;
110
111 if (!PyArg_ParseTuple(args, ""))
112 return NULL;
113
114 num_clients = dict_size(clients);
115 retval = PyTuple_New(num_clients);
5345ea76 116 if (retval == NULL)
117 return NULL;
6d94ce8b 118
119 extra.extra = &n;
120 extra.data = retval;
121
39d37f27 122 if (dict_foreach(clients, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
5345ea76 123 for (i = 0; i < n; ++i) {
124 tmp = PyTuple_GetItem(retval, i);
125 PyTuple_SET_ITEM(retval, i, NULL);
126 Py_DECREF(tmp);
127 }
128 Py_DECREF(retval);
129 return NULL;
130 }
6d94ce8b 131
132 return retval;
133}
134
cc0b2b7f 135/* get a tuple with all channels in it */
136static PyObject*
137emb_get_channels(UNUSED_ARG(PyObject* self), PyObject* args) {
138 PyObject* retval;
d12756d7 139 PyObject* tmp;
140 size_t num_channels, n = 0, i;
cc0b2b7f 141 struct _tuple_dict_extra extra;
142
143 if (!PyArg_ParseTuple(args, ""))
144 return NULL;
145
146 num_channels = dict_size(channels);
147 retval = PyTuple_New(num_channels);
d12756d7 148 if (retval == NULL)
149 return NULL;
cc0b2b7f 150
151 extra.extra = &n;
152 extra.data = retval;
153
39d37f27 154 if (dict_foreach(channels, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
d12756d7 155 for (i = 0; i < n; ++i) {
156 tmp = PyTuple_GetItem(retval, i);
157 PyTuple_SET_ITEM(retval, i, NULL);
158 Py_DECREF(tmp);
159 }
160 Py_DECREF(retval);
161 return NULL;
162 }
cc0b2b7f 163
164 return retval;
165}
166
dcc1df5e 167static PyObject*
168emb_get_servers(UNUSED_ARG(PyObject* self), PyObject* args) {
169 PyObject* retval;
318ec177 170 PyObject* tmp;
171 size_t n = 0, i;
dcc1df5e 172 struct _tuple_dict_extra extra;
173
174 if (!PyArg_ParseTuple(args, ""))
175 return NULL;
176
177 retval = PyTuple_New(dict_size(servers));
39d37f27 178 if (retval == NULL)
179 return NULL;
180
181 extra.extra = &n;
182 extra.data = retval;
183
184 if (dict_foreach(servers, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
185 for (i = 0; i < n; ++i) {
186 tmp = PyTuple_GetItem(retval, i);
187 PyTuple_SET_ITEM(retval, i, NULL);
188 Py_DECREF(tmp);
189 }
190 Py_DECREF(retval);
191 return NULL;
192 }
193
194 return retval;
195}
196
197static PyObject*
198emb_get_accounts(UNUSED_ARG(PyObject* self), PyObject* args) {
199 PyObject* retval;
200 PyObject* tmp;
201 size_t n = 0, i;
202 struct _tuple_dict_extra extra;
203
204 if (!PyArg_ParseTuple(args, ""))
205 return NULL;
206
207 retval = PyTuple_New(dict_size(nickserv_handle_dict));
208 if (retval == NULL)
209 return NULL;
dcc1df5e 210
211 extra.extra = &n;
212 extra.data = retval;
213
39d37f27 214 if (dict_foreach(nickserv_handle_dict, _dict_iter_fill_tuple, (void*)&extra) != NULL) {
318ec177 215 for (i = 0; i < n; ++i) {
216 tmp = PyTuple_GetItem(retval, i);
217 PyTuple_SET_ITEM(retval, i, NULL);
218 Py_DECREF(tmp);
219 }
220 Py_DECREF(retval);
221 return NULL;
222 }
dcc1df5e 223
224 return retval;
225}
226
a2c8c575 227static PyObject*
039a6658 228emb_dump(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 229{
4c216694 230 /* Dump a raw string into the socket
50d61a79 231 usage: _svc.dump(<P10 string>)
4c216694 232 */
a2c8c575 233 char *buf;
234 int ret = 0;
235 char linedup[MAXLEN];
236
039a6658 237
a2c8c575 238 if(!PyArg_ParseTuple(args, "s:dump", &buf ))
239 return NULL;
a957511b 240
a2c8c575 241 safestrncpy(linedup, buf, sizeof(linedup));
a957511b 242
a2c8c575 243 if(parse_line(linedup, 1)) {
244 irc_raw(buf);
245 ret = 1;
a957511b 246 } else {
247 PyErr_SetString(PyExc_Exception, "invalid protocol message");
248 return NULL;
a2c8c575 249 }
a957511b 250
a2c8c575 251 return Py_BuildValue("i", ret);
252}
253
254static PyObject*
039a6658 255emb_send_target_privmsg(UNUSED_ARG(PyObject *self), PyObject *args)
a2c8c575 256{
4c216694 257 /* Send a privmsg
50d61a79 258 usage: _svc.send_target_privmsg(<servicenick_from>, <nick_to>, <message>)
4c216694 259 */
a2c8c575 260 int ret = 0;
261 char *servicenick;
262 char *channel;
263 char *buf;
264
265 struct service *service;
266
039a6658 267
a2c8c575 268 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &channel, &buf ))
269 return NULL;
e7af1e12 270
271 if (buf == NULL || strlen(buf) == 0) {
272 PyErr_SetString(PyExc_Exception, "invalid empty message");
273 return NULL;
274 }
275
a2c8c575 276 if(!(service = service_find(servicenick))) {
e7af1e12 277 PyErr_SetString(PyExc_Exception, "no such service nick");
8d670803 278 return NULL;
a2c8c575 279 }
e7af1e12 280
281 ret = send_target_message(5, channel, service->bot, "%s", buf);
a2c8c575 282 return Py_BuildValue("i", ret);
283}
284
d4e0f0c4 285static PyObject*
039a6658 286emb_send_target_notice(UNUSED_ARG(PyObject *self), PyObject *args)
d4e0f0c4 287{
4c216694 288 /* send a notice
50d61a79 289 usage: _svc.send_target_notice(<servicenick_from>, <nick_to>, <message>)
4c216694 290 */
d4e0f0c4 291 int ret = 0;
292 char *servicenick;
293 char *target;
294 char *buf;
295
296 struct service *service;
297
298 if(!PyArg_ParseTuple(args, "sss:reply", &servicenick, &target, &buf ))
299 return NULL;
66f68f65 300
301 if (buf == NULL || strlen(buf) == 0) {
302 PyErr_SetString(PyExc_Exception, "invalid empty message");
303 return NULL;
304 }
305
d4e0f0c4 306 if(!(service = service_find(servicenick))) {
66f68f65 307 PyErr_SetString(PyExc_Exception, "no such service nick");
d4e0f0c4 308 return NULL;
309 }
66f68f65 310
311 ret = send_target_message(4, target, service->bot, "%s", buf);
312
d4e0f0c4 313 return Py_BuildValue("i", ret);
314}
315
bfdfd1c3 316static PyObject*
317pyobj_from_usernode(struct userNode* user) {
82089e3f 318 unsigned int n, i;
bfdfd1c3 319 struct modeNode *mn;
82089e3f 320 PyObject* retval = NULL;
bfdfd1c3 321 PyObject* pChanList = PyTuple_New(user->channels.used);
322
82089e3f 323 if (pChanList == NULL)
324 return NULL;
325
bfdfd1c3 326 for (n=0; n < user->channels.used; n++) {
327 mn = user->channels.list[n];
82089e3f 328 if (PyTuple_SetItem(pChanList, n, Py_BuildValue("s", mn->channel->name)))
329 goto cleanup;
bfdfd1c3 330 }
331
82089e3f 332 retval = Py_BuildValue("{"
bfdfd1c3 333 "s: s, " /* nick */
334 "s: s, " /* ident */
335 "s: s, " /* info */
336 "s: s, " /* hostname */
337 "s: s, " /* ip */
338 "s: s, " /* fakehost */
339 "s: s, " /* sethost */
340 "s: s, " /* crypthost */
341 "s: s, " /* cryptip */
bfdfd1c3 342 "s: s, " /* numeric */
bfdfd1c3 343 "s: i, " /* loc */
344 "s: i, " /* no_notice */
345 "s: s, " /* mark */
346 "s: s, " /* version_reply */
347 "s: s, " /* account */
348 "s: O}", /* channels */
349 "nick", user->nick,
350 "ident", user->ident,
351 "info", user->info,
352 "hostname", user->hostname,
353 "ip", irc_ntoa(&user->ip),
354 "fakehost", user->fakehost,
355 "sethost", user->sethost,
356 "crypthost", user->crypthost,
357 "cryptip", user->cryptip,
bfdfd1c3 358 "numeric", user->numeric,
bfdfd1c3 359 "loc", user->loc,
360 "no_notice", user->no_notice,
361 "mark", user->mark,
362 "version_reply", user->version_reply,
363 "account", user->handle_info ? user->handle_info->handle : NULL,
364 "channels", pChanList);
82089e3f 365
366 if (retval == NULL)
367 goto cleanup;
368
369 return retval;
370
371cleanup:
372 Py_XDECREF(retval);
373 for (i = 0; i < n; ++i) {
374 retval = PyTuple_GetItem(pChanList, i);
375 PyTuple_SET_ITEM(pChanList, i, NULL);
376 Py_XDECREF(retval);
377 }
378 Py_DECREF(pChanList);
379
380 return NULL;
bfdfd1c3 381}
382
8d670803 383static PyObject*
039a6658 384emb_get_user(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 385{
4c216694 386 /* Get a python object containing everything x3 knows about a user, by nick.
50d61a79 387 usage: _svc.get_user(<nick>)
4c216694 388 */
54d2fd3d 389 char const* nick;
8d670803 390 struct userNode *user;
039a6658 391
8d670803 392 if(!PyArg_ParseTuple(args, "s", &nick))
393 return NULL;
bfdfd1c3 394
8d670803 395 if(!(user = GetUserH(nick))) {
54d2fd3d 396 PyErr_SetString(PyExc_Exception, "no such user");
8d670803 397 return NULL;
398 }
bfdfd1c3 399
400 return pyobj_from_usernode(user);
8d670803 401}
402
d8f8d3b6 403static PyObject*
404pyobj_from_server(struct server* srv) {
405 size_t n, idx;
406 PyObject* tmp = NULL;
407 PyObject* retval = NULL;
408 PyObject* users = PyTuple_New(srv->clients);
409
410 if (users == NULL)
411 return NULL;
412
413 idx = 0;
414 for (n = 0; n < srv->num_mask; ++n) {
415 if (srv->users[n] == NULL)
416 continue;
417
418 tmp = PyString_FromString(srv->users[n]->nick);
419 if (tmp == NULL)
420 goto cleanup;
421
422 if (PyTuple_SetItem(users, idx++, tmp))
423 goto cleanup;
424 }
425
426 retval = Py_BuildValue("{"
427 "s:s," /* name */
428 "s:l," /* boot */
429 "s:l," /* link_time */
430 "s:s," /* description */
431 "s:s," /* numeric */
432 "s:I," /* num_mask */
433 "s:I," /* hops */
434 "s:I," /* clients */
435 "s:I," /* max_clients */
436 "s:I," /* burst */
437 "s:I," /* self_burst */
438 "s:s" /* uplink */
439 "s:O" /* users */
440 /* TODO: Children */
441 "}",
442 "name", srv->name,
443 "boot", srv->boot,
444 "link_time", srv->link_time,
445 "description", srv->description,
446 "numeric", srv->numeric,
447 "num_mask", srv->num_mask,
448 "hops", srv->hops,
449 "clients", srv->clients,
450 "max_clients", srv->max_clients,
451 "burst", srv->burst,
452 "self_burst", srv->self_burst,
453 "uplink", srv->uplink ? srv->uplink->name : NULL,
454 "users", users
455 );
456
457 if (retval == NULL)
458 goto cleanup;
459
460 return retval;
461
462cleanup:
463 Py_XDECREF(retval);
464
465 for (n = 0; n < idx; ++n) {
466 tmp = PyTuple_GetItem(users, n);
467 PyTuple_SetItem(users, n, NULL);
468 Py_DECREF(tmp);
469 }
470 Py_DECREF(users);
471
472 return NULL;
473}
474
475static PyObject*
476emb_get_server(UNUSED_ARG(PyObject* self), PyObject* args) {
477 struct server* srv;
478 char const* name;
479
480 if (!PyArg_ParseTuple(args, "s", &name))
481 return NULL;
482
483 if (name == NULL || strlen(name) == 0) {
484 PyErr_SetString(PyExc_Exception, "invalid server name");
485 return NULL;
486 }
487
488 if ((srv = GetServerH(name)) == NULL) {
489 PyErr_SetString(PyExc_Exception, "unknown server");
490 return NULL;
491 }
492
493 return pyobj_from_server(srv);
494}
495
8d670803 496static PyObject*
039a6658 497emb_get_channel(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 498{
4c216694 499 /* Returns a python dict object with all sorts of info about a channel.
50d61a79 500 usage: _svc.get_channel(<name>)
4c216694 501 */
8d670803 502 char *name;
503 struct chanNode *channel;
cbfd323c 504 unsigned int n;
8d670803 505 PyObject *pChannelMembers;
506 PyObject *pChannelBans;
507 PyObject *pChannelExempts;
508
039a6658 509
8d670803 510 if(!PyArg_ParseTuple(args, "s", &name))
511 return NULL;
3f24e818 512
8d670803 513 if(!(channel = GetChannel(name))) {
3f24e818 514 PyErr_SetString(PyExc_Exception, "unknown channel");
8d670803 515 return NULL;
516 }
517
518 /* build tuple of nicks in channel */
519 pChannelMembers = PyTuple_New(channel->members.used);
520 for(n=0;n < channel->members.used;n++) {
521 struct modeNode *mn = channel->members.list[n];
522 PyTuple_SetItem(pChannelMembers, n, Py_BuildValue("s", mn->user->nick));
523 }
524
525 /* build tuple of bans */
526 pChannelBans = PyTuple_New(channel->banlist.used);
527 for(n=0; n < channel->banlist.used;n++) {
528 struct banNode *bn = channel->banlist.list[n];
529 PyTuple_SetItem(pChannelBans, n,
530 Py_BuildValue("{s:s,s:s,s:i}",
531 "ban", bn->ban,
532 "who", bn->who,
533 "set", bn->set)
534 );
535 }
536
8d670803 537 /* build tuple of exempts */
538 pChannelExempts = PyTuple_New(channel->exemptlist.used);
539 for(n=0; n < channel->exemptlist.used;n++) {
540 struct exemptNode *en = channel->exemptlist.list[n];
541 PyTuple_SetItem(pChannelExempts, n,
542 Py_BuildValue("{s:s,s:s,s:i}",
d4e0f0c4 543 "ban", en->exempt,
8d670803 544 "who", en->who,
545 "set", en->set)
546 );
547 }
548
8d670803 549 return Py_BuildValue("{s:s,s:s,s:s,s:i"
550 ",s:i,s:i,s:O,s:O,s:O}",
551
552 "name", channel->name,
553 "topic", channel->topic,
554 "topic_nick", channel->topic_nick,
555 "topic_time", channel->topic_time,
556
557 "timestamp", channel->timestamp,
558 "modes", channel->modes,
559 "members", pChannelMembers,
560 "bans", pChannelBans,
561 "exempts", pChannelExempts
562 );
563}
564
8d670803 565static PyObject*
039a6658 566emb_get_account(UNUSED_ARG(PyObject *self), PyObject *args)
8d670803 567{
4c216694 568 /* Returns a python dict object with all sorts of info about an account.
50d61a79 569 usage: _svc.get_account(<account name>)
4c216694 570 */
8d670803 571 char *name;
4c216694 572 struct handle_info *hi;
573
039a6658 574
8d670803 575 if(!PyArg_ParseTuple(args, "s", &name))
576 return NULL;
4c216694 577
578 hi = get_handle_info(name);
5b2b1df2 579
4c216694 580 if(!hi) {
5b2b1df2 581 PyErr_SetString(PyExc_Exception, "unknown account name");
4c216694 582 return NULL;
583 }
5b2b1df2 584
4c216694 585 return Py_BuildValue("{s:s,s:i,s:s,s:s,s:s"
586 ",s:s,s:s}",
587
588 "account", hi->handle,
589 "registered", hi->registered,
590 "last_seen", hi->lastseen,
591 "infoline", hi->infoline ? hi->infoline : "",
592 "email", hi->email_addr ? hi->email_addr : "",
593
594 "fakehost", hi->fakehost ? hi->fakehost : "",
595 "last_quit_host", hi->last_quit_host
596
597 /* TODO: */
598 /* users online authed to this account */
599 /* cookies */
600 /* nicks (nickserv nets only?) */
601 /* masks */
602 /* ignores */
603 /* channels */
604 );
8d670803 605}
8d670803 606
07559983 607static PyObject*
039a6658 608emb_get_info(UNUSED_ARG(PyObject *self), UNUSED_ARG(PyObject *args))
609{
610 /* return some info about the general setup
611 * of X3, such as what the chanserv's nickname
612 * is.
613 */
614
615
616 return Py_BuildValue("{s:s,s:s,s:s,s:s,s:s}",
617 "chanserv", chanserv? chanserv->nick : "ChanServ",
618 "nickserv", nickserv?nickserv->nick : "NickServ",
619 "opserv", opserv?opserv->nick : "OpServ",
620 "global", global?global->nick : "Global",
621 "spamserv", spamserv?spamserv->nick : "SpamServ");
622}
623
624static PyObject*
625emb_log_module(UNUSED_ARG(PyObject *self), PyObject *args)
07559983 626{
627 /* a gateway to standard X3 logging subsystem.
628 * level is a value 0 to 9 as defined by the log_severity enum in log.h.
07559983 629 *
630 * for now, all logs go to the PY_LOG log. In the future this will change.
631 */
632 char *message;
633 int ret = 0;
634 int level;
635
039a6658 636
07559983 637 if(!PyArg_ParseTuple(args, "is", &level, &message))
638 return NULL;
639
640 log_module(PY_LOG, level, "%s", message);
641
642 return Py_BuildValue("i", ret);
643}
8d670803 644
0076604e 645static PyObject*
646emb_kill(UNUSED_ARG(PyObject* self), PyObject* args) {
647 char const* from_nick, *target_nick, *message;
648 struct userNode *target;
649 struct service *service;
650
651 if (!PyArg_ParseTuple(args, "sss", &from_nick, &target_nick, &message))
652 return NULL;
653
654 if(!(service = service_find(from_nick))) {
655 PyErr_SetString(PyExc_Exception, "unknown service user specified as from user");
656 return NULL;
657 }
658
659 if ((target = GetUserH(target_nick)) == NULL) {
660 PyErr_SetString(PyExc_Exception, "unknown target user");
661 return NULL;
662 }
663
664 irc_kill(service->bot, target, message);
665
666 return Py_None;
667}
668
a2c8c575 669static PyMethodDef EmbMethods[] = {
07559983 670 /* Communication methods */
a2c8c575 671 {"dump", emb_dump, METH_VARARGS, "Dump raw P10 line to server"},
672 {"send_target_privmsg", emb_send_target_privmsg, METH_VARARGS, "Send a message to somewhere"},
d4e0f0c4 673 {"send_target_notice", emb_send_target_notice, METH_VARARGS, "Send a notice to somewhere"},
07559983 674 {"log_module", emb_log_module, METH_VARARGS, "Log something using the X3 log subsystem"},
039a6658 675//TODO: {"exec_cmd", emb_exec_cmd, METH_VARARGS, "execute x3 command provided"},
676// This should use environment from "python command" call to pass in, if available
0076604e 677 {"kill", emb_kill, METH_VARARGS, "Kill someone"},
039a6658 678//TODO: {"shun"
679//TODO: {"unshun"
680//TODO: {"gline", emb_gline, METH_VARARGS, "gline a mask"},
681//TODO: {"ungline", emb_ungline, METH_VARARGS, "remove a gline"},
682//TODO: {"kick", emb_kick, METH_VARARGS, "kick someone from a channel"},
683//TODO: {"channel_mode", emb_channel_mode, METH_VARARGS, "set modes on a channel"},
684//TODO: {"user_mode", emb_user_mode, METH_VARARGS, "Have x3 set usermodes on one of its own nicks"},
685//
686//TODO: {"get_config", emb_get_config, METH_VARARGS, "get x3.conf settings into a nested dict"},
687//TODO: {"config_set", emb_config_set, METH_VARARGS, "change a config setting 'on-the-fly'."},
688//
689//TODO: {"timeq_add", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
690//TODO: {"timeq_del", emb_timeq_new, METH_VARARGS, "some kind of interface to the timed event system."},
07559983 691 /* Information gathering methods */
8d670803 692 {"get_user", emb_get_user, METH_VARARGS, "Get details about a nickname"},
f0e11521 693 {"get_users", emb_get_users, METH_VARARGS, "Get all connected users"},
8d670803 694 {"get_channel", emb_get_channel, METH_VARARGS, "Get details about a channel"},
f0e11521 695 {"get_channels", emb_get_channels, METH_VARARGS, "Get all channels"},
d8f8d3b6 696 {"get_server", emb_get_server, METH_VARARGS, "Get details about a server"},
dcc1df5e 697 {"get_servers", emb_get_servers, METH_VARARGS, "Get all server names"},
4c216694 698 {"get_account", emb_get_account, METH_VARARGS, "Get details about an account"},
39d37f27 699 {"get_accounts", emb_get_accounts, METH_VARARGS, "Get all nickserv accounts"},
039a6658 700 {"get_info", emb_get_info, METH_VARARGS, "Get various misc info about x3"},
07559983 701 /* null terminator */
a2c8c575 702 {NULL, NULL, 0, NULL}
703};
704
705
f0e11521 706/*
707These functions set up the embedded environment for us to call out to
708modpython.py class methods.
4c216694 709 */
cbfd323c 710
07559983 711void python_log_module() {
712 /* Attempt to convert python errors to x3 log system */
713 PyObject *exc, *tb, *value, *tmp;
714 char *str_exc = "NONE";
715 char *str_value = "NONE";
716 char *str_tb = "NONE";
717
718 PyErr_Fetch(&exc, &value, &tb);
719
720 if(exc) {
721 if((tmp = PyObject_Str(exc)))
722 str_exc = PyString_AsString(tmp);
723 }
724 if(value) {
725 if((tmp = PyObject_Str(value)))
726 str_value = PyString_AsString(tmp);
727 }
728 if(tb) {
729 if((tmp = PyObject_Str(tb)))
730 str_tb = PyString_AsString(tmp);
731 }
732
733 /* Now restore it so we can print it (just in case)
734 * (should we do this only when running in debug mode?) */
735 PyErr_Restore(exc, value, tb);
736 PyErr_Print(); /* which of course, clears it again.. */
737
738 log_module(PY_LOG, LOG_WARNING, "PYTHON error: %s, value: %s", str_exc, str_value);
739
740 /* TODO: get the traceback using the traceback module via C api so we can add it to the X3 logs. See
741 * http://mail.python.org/pipermail/python-list/2003-March/192226.html */
742 // (this doesnt work, str_tb is just an object hashid) log_module(PY_LOG, LOG_INFO, "PYTHON error, traceback: %s", str_tb);
743}
744
745
e0f76584 746PyObject *python_build_handler_args(size_t argc, char *args[], PyObject *pIrcObj) {
4c216694 747 /* Sets up a python tuple with passed in arguments, prefixed by the Irc instance
f0e11521 748 which handlers use to interact with C.
4c216694 749 argc = number of args
750 args = array of args
751 pIrcObj = instance of the irc class
752 */
e0f76584 753 size_t i = 0, n;
754 PyObject *pArgs = NULL;
cbfd323c 755
e0f76584 756 pArgs = PyTuple_New(argc + 1);
757 Py_INCREF(pIrcObj);
758 PyTuple_SetItem(pArgs, i++, pIrcObj);
cbfd323c 759
e0f76584 760 if(args && argc) {
cbfd323c 761 PyObject *pValue;
e0f76584 762 for(n = 0; n < argc; ++n) {
763 pValue = PyString_FromString(args[n]);
cbfd323c 764 if(!pValue) {
e0f76584 765 Py_DECREF(pArgs);
766 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[n]);
767 return NULL;
cbfd323c 768 }
e0f76584 769 PyTuple_SetItem(pArgs, n+i, pValue);
cbfd323c 770 }
cbfd323c 771 }
e0f76584 772 return pArgs;
cbfd323c 773}
774
775PyObject *python_build_args(size_t argc, char *args[]) {
4c216694 776 /* Builds the passed in arguments into a python argument tuple.
777 argc = number of args
778 args = array of args
779 */
cbfd323c 780 size_t i;
781 PyObject *pArgs = NULL;
782
783 if(args && argc) {
784 pArgs = PyTuple_New(argc);
785 PyObject *pValue;
786 for(i = 0; i< argc; ++i) {
787 pValue = PyString_FromString(args[i]);
788 if(!pValue) {
789 Py_DECREF(pArgs);
790 log_module(PY_LOG, LOG_INFO, "Unable to convert '%s' to python string", args[i]);
791 return NULL;
792 }
793 PyTuple_SetItem(pArgs, i, pValue);
794 }
795 }
796 return pArgs;
d4e0f0c4 797}
caf97651 798
cbfd323c 799
e0f76584 800PyObject *new_irc_object(char *command_service, char *command_caller, char *command_target) {
4c216694 801 /* Creates a new instance of the irc class (from modpython.py) which is initalized
802 with current environment details like which service were talking to.
803 command_service = which service we are talking to, or empty string if none
804 command_caller = nick of user generating message, or empty string if none
805 command_target = If were reacting to something on a channel, this will
806 be set to the name of the channel. Otherwise empty
807 */
e0f76584 808 PyObject *pIrcArgs = NULL;
809 PyObject *pIrcClass;
810 PyObject *pIrcObj;
811
4c216694 812 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate irc class; %s %s %s", command_service, command_caller, command_target);
e0f76584 813 pIrcClass = PyObject_GetAttrString(base_module, "irc");
814 /* pIrcClass is a new reference */
815 if(pIrcClass && PyCallable_Check(pIrcClass)) {
816 //size_t i;
817 char *ircargs[] = {command_service, command_caller, command_target};
818 //PyObject *pValue;
819
820 pIrcArgs = python_build_args(3, ircargs);
e0f76584 821 pIrcObj = PyObject_CallObject(pIrcClass, pIrcArgs);
822 if(!pIrcObj) {
823 log_module(PY_LOG, LOG_ERROR, "IRC Class failed to load");
07559983 824 python_log_module();
825 //PyErr_Print();
e0f76584 826 }
827 if(pIrcArgs != NULL) {
828 Py_DECREF(pIrcArgs);
829 }
4c216694 830 Py_DECREF(pIrcClass);
e0f76584 831 return pIrcObj;
832 }
833 else {
4c216694 834 /* need to free pIrcClass here if it WAS found but was NOT callable? */
e0f76584 835 log_module(PY_LOG, LOG_ERROR, "Unable to find irc class");
836 return NULL;
837 }
838}
839
cbfd323c 840int python_call_handler(char *handler, char *args[], size_t argc, char *command_service, char *command_caller, char *command_target) {
4c216694 841 /* This is how we talk to modpython.c. First a new instance of the irc class is created using these
842 arguments to setup the current environment. Then the named method of the handler object is called
843 with the givin arguments.
cbfd323c 844 */
845 PyObject *pIrcObj;
846 PyObject *pArgs;
847 PyObject *pMethod;
848 PyObject *pValue;
849
e0f76584 850 log_module(PY_LOG, LOG_INFO, "attempting to call handler %s.", handler);
07559983 851 if(base_module != NULL && handler_object != NULL) {
cbfd323c 852 pIrcObj = new_irc_object(command_service, command_caller, command_target);
e0f76584 853 if(!pIrcObj) {
854 log_module(PY_LOG, LOG_INFO, "Can't get irc object. Bailing.");
855 return 0;
856 }
cbfd323c 857
e0f76584 858 pArgs = python_build_handler_args(argc, args, pIrcObj);
cbfd323c 859 pMethod = PyObject_GetAttrString(handler_object, handler);
860 if(pMethod && PyCallable_Check(pMethod)) {
07559983 861 /* Call the method, with the arguments */
cbfd323c 862 pValue = PyObject_CallObject(pMethod, pArgs);
863 if(pArgs) {
864 Py_DECREF(pArgs);
865 }
866 if(pValue != NULL) {
867 int ret;
868 ret = PyInt_AsLong(pValue);
869 if(ret == -1 && PyErr_Occurred()) {
07559983 870 //PyErr_Print();
cbfd323c 871 log_module(PY_LOG, LOG_INFO, "error converting return value of handler %s to type long. ", handler);
07559983 872 python_log_module();
cbfd323c 873 ret = 0;
874 }
875 log_module(PY_LOG, LOG_INFO, "handler %s was run successfully, returned %d.", handler, ret);
cbfd323c 876 Py_DECREF(pValue);
4c216694 877 Py_DECREF(pIrcObj);
878 Py_DECREF(pMethod);
cbfd323c 879 return ret;
880 }
881 else {
cbfd323c 882 /* TODO: instead of print errors, get them as strings
883 * and deal with them with normal x3 log system. */
cbfd323c 884 log_module(PY_LOG, LOG_WARNING, "call to handler %s failed", handler);
07559983 885 //PyErr_Print();
886 python_log_module();
4c216694 887 Py_DECREF(pIrcObj);
888 Py_DECREF(pMethod);
cbfd323c 889 return 0;
890 }
891 }
892 else { /* couldn't find handler methed */
4c216694 893 Py_DECREF(pArgs);
894 /* Free pMethod if it was found but not callable? */
cbfd323c 895 log_module(PY_LOG, LOG_ERROR, "Cannot find handler %s.", handler);
896 return 0;
897
898 }
899 }
900 else { /* No base module.. no python? */
e0f76584 901 log_module(PY_LOG, LOG_INFO, "Cannot handle %s, Python is not initialized.", handler);
cbfd323c 902 return 0;
903 }
904}
905
906PyObject *python_new_handler_object() {
4c216694 907 /* Create a new instance of the handler class.
908 This is called during python initilization (or reload)
909 and the result is saved and re-used.
910 */
cbfd323c 911 PyObject *pHandlerClass, *pHandlerObj;
912
913 log_module(PY_LOG, LOG_INFO, "Attempting to instanciate python class handler");
914 pHandlerClass = PyObject_GetAttrString(base_module, "handler");
915 /* Class is a new reference */
916 if(pHandlerClass && PyCallable_Check(pHandlerClass)) {
e0f76584 917 /*PyObject *pValue; */
cbfd323c 918
919 pHandlerObj = PyObject_CallObject(pHandlerClass, NULL);
07559983 920 if(pHandlerObj != NULL) {
921 log_module(PY_LOG, LOG_INFO, "Created new python handler object.");
922 return pHandlerObj;
923 }
924 else {
925 log_module(PY_LOG, LOG_ERROR, "Unable to instanciate handler object");
926 //PyErr_Print();
927 python_log_module();
928 return NULL;
929 }
cbfd323c 930 }
931 else {
932 log_module(PY_LOG, LOG_ERROR, "Unable to find handler class");
07559983 933 //PyErr_Print();
934 python_log_module();
935 if(pHandlerClass) {
936 Py_DECREF(pHandlerClass);
937 }
cbfd323c 938 return NULL;
939 }
940}
941
4c216694 942/* ------------------------------------------------------------------------------- *
943 Some gateway functions to convert x3 callbacks into modpython.py callbacks.
944 Mostly we just build relevant args and call the proper handler method
945
946 debate: do we just register these and check them in python
947 for every one (slow?) or actually work out if a plugin needs
948 it first? We will start by doing it every time.
cbfd323c 949 */
0b350353 950static int
951python_handle_join(struct modeNode *mNode)
952{
4c216694 953 /* callback for handle_join events.
954 */
0b350353 955 struct userNode *user = mNode->user;
956 struct chanNode *channel = mNode->channel;
957
a2c8c575 958
caf97651 959 log_module(PY_LOG, LOG_INFO, "python module handle_join");
a2c8c575 960 if(!channel||!user) {
4c216694 961 log_module(PY_LOG, LOG_WARNING, "Python code got join without channel or user!");
a2c8c575 962 return 0;
963 }
964 else {
965 char *args[] = {channel->name, user->nick};
e0f76584 966 return python_call_handler("join", args, 2, "", "", "");
a2c8c575 967 }
0b350353 968}
969
0ab7b4bc 970static int
971python_handle_server_link(struct server *server)
972{
973 log_module(PY_LOG, LOG_INFO, "python module handle_server_link");
974 if(!server) {
975 log_module(PY_LOG, LOG_WARNING, "Python code got server link without server!");
976 return 0;
977 }
978 else {
979 char *args[] = {server->name, server->description};
980 return python_call_handler("server_link", args, 2, "", "", "");
981 }
982}
983
984static int
985python_handle_new_user(struct userNode *user)
986{
987 log_module(PY_LOG, LOG_INFO, "Python module handle_new_user");
988 if(!user) {
989 log_module(PY_LOG, LOG_WARNING, "Python code got new_user without the user");
990 return 0;
991 }
992 else {
993 char *args[] = {user->nick, user->ident, user->hostname, user->info};
994 return python_call_handler("new_user", args, 4, "", "", "");
995 }
996}
997
998static void
999python_handle_nick_change(struct userNode *user, const char *old_nick)
1000{
1001 log_module(PY_LOG, LOG_INFO, "Python module handle_nick_change");
1002 if(!user) {
1003 log_module(PY_LOG, LOG_WARNING, "Python code got nick_change without the user!");
1004 }
1005 else {
1006 char *args[] = {user->nick, (char *)old_nick};
1007 python_call_handler("nick_change", args, 2, "", "", "");
1008 }
1009}
1010
4c216694 1011/* ----------------------------------------------------------------------------- */
1012
cbfd323c 1013
caf97651 1014int python_load() {
4c216694 1015 /* Init the python engine and do init work on modpython.py
1016 This is called during x3 startup, and on a python reload
1017 */
caf97651 1018 PyObject *pName;
ef5e0305 1019 char* buffer;
1020 char* env = getenv("PYTHONPATH");
1021
1022 if (env)
1023 env = strdup(env);
1024
1025 if (!env)
1026 setenv("PYTHONPATH", modpython_conf.scripts_dir, 1);
1027 else if (!strstr(env, modpython_conf.scripts_dir)) {
1028 buffer = (char*)malloc(strlen(env) + strlen(modpython_conf.scripts_dir) + 2);
1029 sprintf(buffer, "%s:%s", modpython_conf.scripts_dir, env);
1030 setenv("PYTHONPATH", buffer, 1);
1031 free(buffer);
1032 free(env);
1033 }
caf97651 1034
caf97651 1035 Py_Initialize();
50d61a79 1036 Py_InitModule("_svc", EmbMethods);
ed8d873c 1037 pName = PyString_FromString(modpython_conf.main_module);
caf97651 1038 base_module = PyImport_Import(pName);
1039 Py_DECREF(pName);
bc2f52df 1040
1041 Py_XDECREF(handler_object);
1042 handler_object = NULL;
1043
caf97651 1044 if(base_module != NULL) {
cbfd323c 1045 handler_object = python_new_handler_object();
1046 if(handler_object) {
e0f76584 1047 python_call_handler("init", NULL, 0, "", "", "");
cbfd323c 1048 return 1;
1049 }
1050 else {
1051 /* error handler class not found */
1052 log_module(PY_LOG, LOG_WARNING, "Failed to create handler object");
1053 return 0;
1054 }
caf97651 1055 }
1056 else {
07559983 1057 //PyErr_Print();
1058 python_log_module();
d4e0f0c4 1059 log_module(PY_LOG, LOG_WARNING, "Failed to load modpython.py");
a2c8c575 1060 return 0;
caf97651 1061 }
cbfd323c 1062 return 0;
caf97651 1063}
1064
caf97651 1065int
1066python_finalize(void) {
4c216694 1067 /* Called after X3 is fully up and running.
1068 Code can be put here that needs to run to init things, but
1069 which is sensitive to everything else in x3 being up and ready
1070 to go.
1071 */
caf97651 1072
1073 PyRun_SimpleString("print 'Hello, World of Python!'");
1074 log_module(PY_LOG, LOG_INFO, "python module finalize");
1075
1076 return 1;
1077}
1078
caf97651 1079static void
4c216694 1080python_cleanup(void) {
1081 /* Called on shutdown of the python module (or before reloading)
1082 */
413fd8ea 1083
caf97651 1084 log_module(PY_LOG, LOG_INFO, "python module cleanup");
413fd8ea 1085 if (PyErr_Occurred())
1086 PyErr_Clear();
f0e11521 1087 Py_Finalize(); /* Shut down python enterpreter */
caf97651 1088}
1089
4c216694 1090/* ---------------------------------------------------------------------------------- *
1091 Python module command handlers.
1092*/
caf97651 1093static MODCMD_FUNC(cmd_reload) {
4c216694 1094 /* reload the python system completely
1095 */
caf97651 1096 log_module(PY_LOG, LOG_INFO, "Shutting python down");
1097 python_cleanup();
1098 log_module(PY_LOG, LOG_INFO, "Loading python stuff");
1099 if(python_load()) {
a2c8c575 1100 reply("PYMSG_RELOAD_SUCCESS");
caf97651 1101 }
1102 else {
a2c8c575 1103 reply("PYMSG_RELOAD_FAILED");
caf97651 1104 }
1105 return 1;
1106}
1107
413fd8ea 1108static char* format_python_error(int space_nls) {
1109 PyObject* extype = NULL, *exvalue = NULL, *extraceback = NULL;
1110 PyObject* pextypestr = NULL, *pexvaluestr = NULL;
1111 char* extypestr = NULL, *exvaluestr = NULL;
1112 size_t retvallen = 0;
1113 char* retval = NULL, *tmp;
1114
1115 PyErr_Fetch(&extype, &exvalue, &extraceback);
1116 if (!extype)
1117 goto cleanup;
1118
1119 pextypestr = PyObject_Str(extype);
1120 if (!pextypestr)
1121 goto cleanup;
1122 extypestr = PyString_AsString(pextypestr);
1123 if (!extypestr)
1124 goto cleanup;
1125
1126 pexvaluestr = PyObject_Str(exvalue);
1127 if (pexvaluestr)
1128 exvaluestr = PyString_AsString(pexvaluestr);
1129
1130 retvallen = strlen(extypestr) + (exvaluestr ? strlen(exvaluestr) + 2 : 0) + 1;
1131 retval = (char*)malloc(retvallen);
1132 if (exvaluestr)
1133 snprintf(retval, retvallen, "%s: %s", extypestr, exvaluestr);
1134 else
1135 strncpy(retval, extypestr, retvallen);
1136
1137 if (space_nls) {
1138 tmp = retval;
1139 while (*tmp) {
1140 if (*tmp == '\n')
1141 *tmp = ' ';
1142 ++tmp;
1143 }
1144 }
1145
1146cleanup:
1147 if (PyErr_Occurred())
1148 PyErr_Clear(); /* ignore errors caused by formatting */
1149 Py_XDECREF(extype);
1150 Py_XDECREF(exvalue);
1151 Py_XDECREF(extraceback);
1152 Py_XDECREF(pextypestr);
1153 Py_XDECREF(pexvaluestr);
1154
1155 if (retval)
1156 return retval;
1157
1158 return strdup("unknown exception");
1159}
1160
8d670803 1161static MODCMD_FUNC(cmd_run) {
413fd8ea 1162 /* this method allows running arbitrary python commands.
1163 * use with care.
1164 */
1165 char* msg;
46f628b1 1166 PyObject* py_main_module;
1167 PyObject* py_globals;
1168 PyObject* py_locals;
1169 PyObject* py_retval;
1170 PyObject* extype, *exvalue, *extraceback;
1171 PyObject* exvaluestr = NULL;
1172 char* exmsg = NULL, *exmsgptr;
1173
1174 py_main_module = PyImport_AddModule("__main__");
1175 py_globals = py_locals = PyModule_GetDict(py_main_module);
413fd8ea 1176
a2c8c575 1177 msg = unsplit_string(argv + 1, argc - 1, NULL);
413fd8ea 1178
46f628b1 1179 py_retval = PyRun_String(msg, Py_file_input, py_globals, py_locals);
1180 if (py_retval == NULL) {
1181 PyErr_Fetch(&extype, &exvalue, &extraceback);
1182 if (exvalue != NULL) {
1183 exvaluestr = PyObject_Str(exvalue);
1184 exmsg = strdup(PyString_AS_STRING(exvaluestr));
1185 exmsgptr = exmsg;
1186 while (exmsgptr && *exmsgptr) {
1187 if (*exmsgptr == '\n' || *exmsgptr == '\r' || *exmsgptr == '\t')
1188 *exmsgptr = ' ';
1189 exmsgptr++;
1190 }
1191 }
1192 if (extype != NULL && exvalue != NULL && PyType_Check(extype)) {
1193 reply("PYMSG_RUN_EXCEPTION", ((PyTypeObject*)extype)->tp_name, exmsg);
413fd8ea 1194 } else
1195 reply("PYMSG_RUN_UNKNOWN_EXCEPTION");
46f628b1 1196
1197 if (extype != NULL)
1198 Py_DECREF(extype);
1199 if (exvalue != NULL)
1200 Py_DECREF(exvalue);
1201 if (extraceback != NULL)
1202 Py_DECREF(extraceback);
1203 if (exvaluestr != NULL)
1204 Py_DECREF(exvaluestr);
1205 if (exmsg)
1206 free(exmsg);
1207 } else {
1208 Py_DECREF(py_retval);
413fd8ea 1209 }
1210
a2c8c575 1211 return 1;
1212}
1213
07559983 1214#define numstrargs(X) sizeof(X) / sizeof(*X)
1215static MODCMD_FUNC(cmd_command) {
1216 char *plugin = argv[1];
1217 char *command = argv[2];
1218 char *msg; /* args */
1219 if(argc > 3) {
1220 msg = unsplit_string(argv + 3, argc - 3, NULL);
1221 }
1222 else {
1223 msg = "";
1224 }
1225 char *args[] = {plugin, command, msg};
1226 python_call_handler("cmd_command", args, numstrargs(args), cmd->parent->bot->nick, user?user->nick:"", channel?channel->name:"");
1227 return 1;
1228}
1229
ef5e0305 1230static void modpython_conf_read(void) {
1231 dict_t conf_node;
1232 char const* str;
1233
1234 if (!(conf_node = conf_get_data(MODPYTHON_CONF_NAME, RECDB_OBJECT))) {
1235 log_module(PY_LOG, LOG_ERROR, "config node '%s' is missing or has wrong type", MODPYTHON_CONF_NAME);
1236 return;
1237 }
1238
1239 str = database_get_data(conf_node, "scripts_dir", RECDB_QSTRING);
1240 modpython_conf.scripts_dir = str ? str : "./";
ed8d873c 1241
1242 str = database_get_data(conf_node, "main_module", RECDB_QSTRING);
1243 modpython_conf.main_module = str ? str : "modpython";
ef5e0305 1244}
1245
0b350353 1246int python_init(void) {
4c216694 1247 /* X3 calls this function on init of the module during startup. We use it to
1248 do all our setup tasks and bindings
1249 */
0b350353 1250
0b350353 1251 PY_LOG = log_register_type("Python", "file:python.log");
caf97651 1252 python_module = module_register("python", PY_LOG, "mod-python.help", NULL);
ef5e0305 1253 conf_register_reload(modpython_conf_read);
1254
caf97651 1255 log_module(PY_LOG, LOG_INFO, "python module init");
1256 message_register_table(msgtab);
1257
0b350353 1258/*
1259 reg_auth_func(python_check_messages);
1260 reg_handle_rename_func(python_rename_account);
1261 reg_unreg_func(python_unreg_account);
1262 conf_register_reload(python_conf_read);
0b350353 1263 saxdb_register("python", python_saxdb_read, python_saxdb_write);
0b350353 1264 modcmd_register(python_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
1265*/
caf97651 1266 modcmd_register(python_module, "reload", cmd_reload, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
8d670803 1267 modcmd_register(python_module, "run", cmd_run, 2, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
07559983 1268 modcmd_register(python_module, "command", cmd_command, 3, MODCMD_REQUIRE_STAFF, NULL);
039a6658 1269
f0e11521 1270// Please help us by implementing any of the callbacks listed as TODO below. They already exist
1271// in x3, they just need handle_ bridges implemented. (see python_handle_join for an example)
0ab7b4bc 1272 reg_server_link_func(python_handle_server_link);
1273 reg_new_user_func(python_handle_new_user);
1274 reg_nick_change_func(python_handle_nick_change);
039a6658 1275//TODO: reg_del_user_func(python_handle_del_user);
1276//TODO: reg_account_func(python_handle_account); /* stamping of account name to the ircd */
1277//TODO: reg_handle_rename_func(python_handle_handle_rename); /* handle used to ALSO mean account name */
1278//TODO: reg_failpw_func(python_handle_failpw);
1279//TODO: reg_allowauth_func(python_handle_allowauth);
1280//TODO: reg_handle_merge_func(python_handle_merge);
1281//
1282//TODO: reg_oper_func(python_handle_oper);
1283//TODO: reg_new_channel_func(python_handle_new_channel);
caf97651 1284 reg_join_func(python_handle_join);
039a6658 1285//TODO: reg_del_channel_func(python_handle_del_channel);
1286//TODO: reg_part_func(python_handle_part);
1287//TODO: reg_kick_func(python_handle_kick);
1288//TODO: reg_topic_func(python_handle_topic);
1289//TODO: reg_channel_mode_func(python_handle_channel_mode);
1290
1291//TODO: reg_privmsg_func(python_handle_privmsg);
1292//TODO: reg_notice_func
1293//TODO: reg_svccmd_unbind_func(python_handle_svccmd_unbind);
1294//TODO: reg_chanmsg_func(python_handle_chanmsg);
1295//TODO: reg_allchanmsg_func
1296//TODO: reg_user_mode_func
1297
caf97651 1298 reg_exit_func(python_cleanup);
0b350353 1299
caf97651 1300 python_load();
0b350353 1301 return 1;
1302}
1303
1304#endif /* WITH_PYTHON */