]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-track.c
added levels command
[irc/evilnet/x3.git] / src / mod-track.c
1 /* mod-track.c - User surveillance module
2 * Copyright 2002-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21 /* Adds new section to srvx.conf:
22 * "modules" {
23 * "track" {
24 * // What data to show.
25 * "snomask" "nick,join,part,kick,new,del,auth,chanmode,umode";
26 * // Where to send track messages?
27 * "channel" "#wherever";
28 * // Which bot?
29 * "bot" "OpServ";
30 * // Show new users and joins from net joins? (off by default)
31 * "show_bursts" "0";
32 * };
33 * };
34 */
35
36 #include "conf.h"
37 #include "chanserv.h"
38 #include "helpfile.h"
39 #include "nickserv.h"
40 #include "modcmd.h"
41 #include "proto.h"
42 #include "dict.h"
43 #include "hash.h"
44
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51
52 /* track snomask definitions */
53 #define TRACK_NICK 0x0001 /* report nickchanges */
54 #define TRACK_JOIN 0x0002 /* report join/part */
55 #define TRACK_PART 0x0004 /* report parts */
56 #define TRACK_KICK 0x0008 /* report kicks */
57 #define TRACK_NEW 0x0010 /* report new users */
58 #define TRACK_DEL 0x0020 /* report quits */
59 #define TRACK_AUTH 0x0040 /* report auths */
60 #define TRACK_CHANMODE 0x0080 /* report channel modes */
61 #define TRACK_UMODE 0x0100 /* report user modes */
62
63 /* check track status */
64 #define check_track_nick(x) ((x).snomask & TRACK_NICK)
65 #define check_track_join(x) ((x).snomask & TRACK_JOIN)
66 #define check_track_part(x) ((x).snomask & TRACK_PART)
67 #define check_track_kick(x) ((x).snomask & TRACK_KICK)
68 #define check_track_new(x) ((x).snomask & TRACK_NEW)
69 #define check_track_del(x) ((x).snomask & TRACK_DEL)
70 #define check_track_auth(x) ((x).snomask & TRACK_AUTH)
71 #define check_track_chanmode(x) ((x).snomask & TRACK_CHANMODE)
72 #define check_track_umode(x) ((x).snomask & TRACK_UMODE)
73
74 /* set track status */
75 #define set_track_nick(x) ((x).snomask |= TRACK_NICK)
76 #define set_track_join(x) ((x).snomask |= TRACK_JOIN|TRACK_PART)
77 #define set_track_part(x) ((x).snomask |= TRACK_PART)
78 #define set_track_kick(x) ((x).snomask |= TRACK_KICK)
79 #define set_track_new(x) ((x).snomask |= TRACK_NEW|TRACK_DEL)
80 #define set_track_del(x) ((x).snomask |= TRACK_DEL)
81 #define set_track_auth(x) ((x).snomask |= TRACK_AUTH)
82 #define set_track_chanmode(x) ((x).snomask |= TRACK_CHANMODE)
83 #define set_track_umode(x) ((x).snomask |= TRACK_UMODE)
84 #define set_track_all(x) ((x).snomask |= TRACK_NICK|TRACK_JOIN|TRACK_PART|TRACK_KICK|TRACK_NEW|TRACK_DEL|TRACK_AUTH|TRACK_CHANMODE|TRACK_UMODE)
85
86 /* clear track status */
87 #define clear_track_nick(x) ((x).snomask &= ~TRACK_NICK)
88 #define clear_track_join(x) ((x).snomask &= ~(TRACK_JOIN|TRACK_PART))
89 #define clear_track_part(x) ((x).snomask &= ~TRACK_PART)
90 #define clear_track_kick(x) ((x).snomask &= ~TRACK_KICK)
91 #define clear_track_new(x) ((x).snomask &= ~(TRACK_NEW|TRACK_DEL))
92 #define clear_track_del(x) ((x).snomask &= ~TRACK_DEL)
93 #define clear_track_auth(x) ((x).snomask &= ~TRACK_AUTH)
94 #define clear_track_chanmode(x) ((x).snomask &= ~TRACK_CHANMODE)
95 #define clear_track_umode(x) ((x).snomask &= ~TRACK_UMODE)
96 #define clear_track_all(x) ((x).snomask &= ~(TRACK_NICK|TRACK_JOIN|TRACK_PART|TRACK_KICK|TRACK_NEW|TRACK_DEL|TRACK_AUTH|TRACK_CHANMODE|TRACK_UMODE))
97
98 extern struct modcmd *opserv_define_func(const char *name, modcmd_func_t *func, int min_level, int reqchan, int min_argc);
99
100 extern time_t now;
101 static struct {
102 struct chanNode *channel;
103 struct userNode *bot;
104 unsigned int snomask;
105 unsigned int show_bursts : 1;
106 unsigned int enabled : 1;
107 } track_cfg;
108 static char timestamp[16];
109 static dict_t track_db = NULL;
110
111 const char *track_module_deps[] = { NULL };
112
113 static int finalized;
114 int track_finalize(void);
115
116 #define TRACK(FORMAT, ARGS...) send_channel_message(track_cfg.channel, track_cfg.bot, "%s "FORMAT, timestamp , ## ARGS)
117 #define UPDATE_TIMESTAMP() strftime(timestamp, sizeof(timestamp), "[%H:%M:%S]", localtime(&now))
118
119 void add_track_user(struct userNode *user) { dict_insert(track_db, (const char *)user->nick, user); }
120 static void del_track_user(struct userNode *user) { dict_remove2(track_db, (const char *)user->nick, 1); }
121 static int
122 check_track_user(struct userNode *user)
123 {
124 int found;
125 if(!user)
126 return 0;
127 dict_find(track_db, (const char *)user->nick, &found);
128 return found;
129 }
130
131 static void
132 parse_track_conf(char *line)
133 {
134 char *t = NULL, *s = line;
135
136 while(s)
137 {
138 if ((t = strchr(s, ',')))
139 *t++ = 0;
140
141 switch (tolower(s[0]))
142 {
143 case 'a':
144 if(!strcasecmp(s, "auth"))
145 set_track_auth(track_cfg);
146 break;
147 case 'c':
148 if(!strcasecmp(s, "chanmode"))
149 set_track_chanmode(track_cfg);
150 break;
151 case 'd':
152 if(!strcasecmp(s, "del"))
153 set_track_del(track_cfg);
154 break;
155 case 'j':
156 if(!strcasecmp(s, "join"))
157 set_track_join(track_cfg);
158 break;
159 case 'k':
160 if(!strcasecmp(s, "kick"))
161 set_track_kick(track_cfg);
162 break;
163 case 'n':
164 if(!strcasecmp(s, "new"))
165 set_track_new(track_cfg);
166 if(!strcasecmp(s, "nick"))
167 set_track_nick(track_cfg);
168 break;
169 case 'p':
170 if(!strcasecmp(s, "part"))
171 set_track_nick(track_cfg);
172 break;
173 case 'u':
174 if(!strcasecmp(s, "umode"))
175 set_track_umode(track_cfg);
176 break;
177 }
178 s = t;
179 }
180 }
181
182 static void
183 track_nick_change(struct userNode *user, const char *old_nick) {
184 if (!track_cfg.enabled) return;
185 if (check_track_nick(track_cfg) && check_track_user(user))
186 {
187 UPDATE_TIMESTAMP();
188 TRACK("$bNICK$b change %s -> %s", old_nick, user->nick);
189 }
190 }
191
192 static int
193 track_join(struct modeNode *mNode) {
194 struct userNode *user = mNode->user;
195 struct chanNode *chan = mNode->channel;
196 if (!track_cfg.enabled) return 0;
197 if (user->uplink->burst && !track_cfg.show_bursts) return 0;
198 if (check_track_join(track_cfg) && check_track_user(user))
199 {
200 UPDATE_TIMESTAMP();
201 if (chan->members.used == 1) {
202 TRACK("$bCREATE$b %s by %s", chan->name, user->nick);
203 } else {
204 TRACK("$bJOIN$b %s by %s", chan->name, user->nick);
205 }
206 }
207 return 0;
208 }
209
210 static void
211 track_part(struct modeNode *mn, const char *reason) {
212 if (!track_cfg.enabled) return;
213 if (mn->user->dead) return;
214 if (check_track_part(track_cfg) && check_track_user(mn->user))
215 {
216 UPDATE_TIMESTAMP();
217 TRACK("$bPART$b %s by %s (%s)", mn->channel->name, mn->user->nick, reason ? reason : "");
218 }
219 }
220
221 static void
222 track_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *chan) {
223 if (!track_cfg.enabled) return;
224 if (check_track_kick(track_cfg) && ((check_track_user(kicker) || check_track_user(victim))))
225 {
226 UPDATE_TIMESTAMP();
227 TRACK("$bKICK$b %s from %s by %s", victim->nick, chan->name, (kicker ? kicker->nick : "some server"));
228 }
229 }
230
231 static int
232 track_new_user(struct userNode *user) {
233 if (!track_cfg.enabled) return 0;
234 if (user->uplink->burst && !track_cfg.show_bursts) return 0;
235 if (check_track_new(track_cfg) && check_track_user(user))
236 {
237 UPDATE_TIMESTAMP();
238 TRACK("$bNICK$b %s %s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name);
239 }
240 return 0;
241 }
242
243 static void
244 track_del_user(struct userNode *user, struct userNode *killer, const char *why) {
245 if (!track_cfg.enabled) return;
246 if (check_track_del(track_cfg) && (check_track_user(user) || (killer && check_track_user(killer))))
247 {
248 UPDATE_TIMESTAMP();
249 if (killer) {
250 TRACK("$bKILL$b %s (%s@%s, on %s) by %s (%s)", user->nick, user->ident, user->hostname, user->uplink->name, killer->nick, why);
251 } else {
252 TRACK("$bQUIT$b %s (%s@%s, on %s) (%s)", user->nick, user->ident, user->hostname, user->uplink->name, why);
253 }
254 del_track_user(user);
255 }
256 }
257
258 static void
259 track_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) {
260 if (!track_cfg.enabled) return;
261 if (user->uplink->burst && !track_cfg.show_bursts) return;
262 if (user->handle_info && (check_track_auth(track_cfg) && check_track_user(user))) {
263 UPDATE_TIMESTAMP();
264 TRACK("$bAUTH$b %s!%s@%s [%s] on %s as %s", user->nick, user->ident, user->hostname,
265 irc_ntoa(&user->ip), user->uplink->name, user->handle_info->handle);
266 }
267 }
268
269 static void
270 track_user_mode(struct userNode *user, const char *mode_change) {
271 if (!track_cfg.enabled) return;
272 if (user->uplink->burst && !track_cfg.show_bursts) return;
273 if (!mode_change[1]) return; /* warning there has to be atleast one char in the buffer */
274 if(check_track_umode(track_cfg) && check_track_user(user))
275 {
276 UPDATE_TIMESTAMP();
277 TRACK("$bUMODE$b %s %s", user->nick, mode_change);
278 }
279 }
280
281 static void
282 track_oper(struct userNode *user) {
283 if (!track_cfg.enabled) return;
284 if (user->uplink->burst && !track_cfg.show_bursts) return;
285 UPDATE_TIMESTAMP();
286 TRACK("$bOPER$b %s!%s@%s [%s] on %s", user->nick, user->ident, user->hostname, irc_ntoa(&user->ip), user->uplink->name);
287 }
288
289 static void
290 track_channel_mode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc)
291 {
292 if (!track_cfg.enabled) return;
293 if(who)
294 {
295 if (who->uplink->burst && !track_cfg.show_bursts) return;
296 if (!check_track_chanmode(track_cfg) || !check_track_user(who)) return;
297 } else
298 return;
299
300 static char targets[MAXLEN], string[MAXLEN];
301 struct userNode *un = NULL;
302 char *tmp = NULL, *tg = NULL, *md = NULL;
303 int add = 0;
304
305 string[0] = 0;
306 targets[0] = 0;
307
308 if (argc > 0)
309 unsplit_string(modes, argc, string);
310 else
311 strcpy(string, *modes);
312
313 if((tg = strchr(string, ' ')))
314 {
315 *tg++ = 0;
316 for(md = string; *md; md++)
317 {
318 if (*md == '+')
319 {
320 add = 1;
321 md++;
322 }
323 if (*md == '-')
324 {
325 add = 0;
326 md++;
327 }
328 switch(*md)
329 {
330 case 'k':
331 {
332 strcat(targets, " ");
333 if ((tmp = strchr(tg, ' ')))
334 *tmp++ = 0;
335 strcat(targets, tg);
336 if(tmp)
337 tg = tmp;
338 break;
339 }
340 case 'l':
341 {
342 if(add)
343 {
344 strcat(targets, " ");
345 if ((tmp = strchr(tg, ' ')))
346 *tmp++ = 0;
347 strcat(targets, tg);
348 if(tmp)
349 tg = tmp;
350 break;
351 }
352 }
353 case 'b':
354 {
355 strcat(targets, " ");
356 if ((tmp = strchr(tg, ' ')))
357 *tmp++ = 0;
358 strcat(targets, tg);
359 if(tmp)
360 tg = tmp;
361 break;
362 }
363 case 'e':
364 {
365 strcat(targets, " ");
366 if ((tmp = strchr(tg, ' ')))
367 *tmp++ = 0;
368 strcat(targets, tg);
369 if(tmp)
370 tg = tmp;
371 break;
372 }
373 case 'o':
374 {
375 strcat(targets, " ");
376 if ((tmp = strchr(tg, ' ')))
377 *tmp++ = 0;
378 if((un = GetUserN(tg)))
379 strcat(targets, un->nick);
380 else
381 strcat(targets, tg);
382 if(tmp)
383 tg = tmp;
384 break;
385 }
386 case 'v':
387 {
388 strcat(targets, " ");
389 if ((tmp = strchr(tg, ' ')))
390 *tmp++ = 0;
391 if((un = GetUserN(tg)))
392 strcat(targets, un->nick);
393 else
394 strcat(targets, tg);
395 if(tmp)
396 tg = tmp;
397 break;
398 }
399 }
400 }
401 }
402 UPDATE_TIMESTAMP();
403 if (who)
404 TRACK("$bMODE$b %s %s%s by %s", channel->name, string, targets, who->nick);
405 else
406 TRACK("$bMODE$b %s %s%s", channel->name, string, targets);
407 }
408
409 static void
410 check_track_state(struct userNode *user)
411 {
412 send_message_type(4, user, track_cfg.bot, "TRACK is tracking: %s%s%s%s%s%s%s%s%s",
413 check_track_nick(track_cfg) ? " nick":"", check_track_join(track_cfg) ? " join":"",
414 check_track_part(track_cfg) ? " part":"", check_track_kick(track_cfg) ? " kick":"",
415 check_track_new(track_cfg) ? " new":"", check_track_del(track_cfg) ? " del":"",
416 check_track_auth(track_cfg) ? " auth":"", check_track_chanmode(track_cfg) ? " chanmode":"",
417 check_track_umode(track_cfg) ? " umode":"");
418 }
419
420 MODCMD_FUNC(cmd_track)
421 {
422 unsigned int i, add;
423 const char *data;
424 char changed = false;
425
426 if(argc == 1)
427 {
428 svccmd_send_help_brief(user, track_cfg.bot, cmd);
429 check_track_state(user);
430 return 0;
431 }
432
433 for(i = 1; i < argc; i++)
434 {
435 data = argv[i];
436 add = 2;
437 changed = true;
438
439 if(data[0] == '+')
440 add = 1;
441 if(data[0] == '-')
442 add = 0;
443
444 if(add == 2)
445 {
446 if ((!strcasecmp(data, "all")))
447 {
448 set_track_all(track_cfg);
449 check_track_state(user);
450 TRACK("$bALERT$b TRACK fully enabled by %s", user->nick);
451 }
452 else if (!strcasecmp(data, "none"))
453 {
454 clear_track_all(track_cfg);
455 check_track_state(user);
456 TRACK("$bALERT$b TRACK disabled by %s", user->nick);
457 }
458 else
459 {
460 send_message_type(4, user, track_cfg.bot, "Unrecognised parameter: %s", data);
461 svccmd_send_help_brief(user, track_cfg.bot, cmd);
462 }
463 return 0;
464 }
465
466 *data++;
467
468 switch(tolower(data[0]))
469 {
470 case 'a':
471 if(!strcasecmp(data, "auth"))
472 {
473 if (add)
474 set_track_auth(track_cfg);
475 else
476 clear_track_auth(track_cfg);
477 }
478 break;
479 case 'c':
480 if(!strcasecmp(data, "chanmode"))
481 {
482 if (add)
483 set_track_chanmode(track_cfg);
484 else
485 clear_track_chanmode(track_cfg);
486 }
487 break;
488 case 'd':
489 if(!strcasecmp(data, "del"))
490 {
491 if (add)
492 set_track_del(track_cfg);
493 else
494 clear_track_del(track_cfg);
495 }
496 break;
497 case 'j':
498 if(!strcasecmp(data, "join"))
499 {
500 if(add)
501 set_track_join(track_cfg);
502 else
503 clear_track_join(track_cfg);
504 }
505 break;
506 case 'k':
507 if(!strcasecmp(data, "kick"))
508 {
509 if(add)
510 set_track_kick(track_cfg);
511 else
512 clear_track_kick(track_cfg);
513 }
514 break;
515 case 'n':
516 if(!strcasecmp(data, "new"))
517 {
518 if(add)
519 set_track_new(track_cfg);
520 else
521 clear_track_new(track_cfg);
522 }
523 if(!strcasecmp(data, "nick"))
524 {
525 if(add)
526 set_track_nick(track_cfg);
527 else
528 clear_track_nick(track_cfg);
529 }
530 break;
531 case 'p':
532 if(!strcasecmp(data, "part"))
533 {
534 if(add)
535 set_track_part(track_cfg);
536 else
537 clear_track_part(track_cfg);
538 }
539 break;
540 case 'u':
541 if(!strcasecmp(data, "umode"))
542 {
543 if(add)
544 set_track_umode(track_cfg);
545 else
546 clear_track_umode(track_cfg);
547 }
548 break;
549 default:
550 TRACK("Error, Unknown value %s", data);
551 break;
552 }
553 }
554 check_track_state(user);
555
556 if(changed)
557 {
558 char buf[256];
559 unsigned int pos = 0;
560 memset(buf, 0, sizeof(buf));
561 for(i = 1; i < argc; i++)
562 {
563 unsigned int len;
564 data = argv[i];
565 len = strlen(data);
566 if(pos + len > sizeof(buf))
567 break;
568 strcpy(&buf[pos], data);
569 pos += len;
570 }
571
572 UPDATE_TIMESTAMP();
573 TRACK("$bALERT$b TRACK command called with parameters '%s' by %s",
574 buf, user->nick);
575 }
576 return 0;
577 }
578
579 MODCMD_FUNC(cmd_deltrack)
580 {
581 struct userNode *un = NULL;
582
583 if((argc > 1) && (un = dict_find(clients, argv[1], NULL)))
584 {
585 if(check_track_user(un))
586 {
587 del_track_user(un);
588 UPDATE_TIMESTAMP();
589 TRACK("$bALERT$b No longer monitoring %s!%s@%s on %s requested by %s",
590 un->nick, un->ident, un->hostname, un->uplink->name, user->nick);
591 }
592 else
593 send_message_type(4, user, track_cfg.bot, "This nick isn't monitored.");
594 }
595 else
596 {
597 send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified.");
598 svccmd_send_help_brief(user, track_cfg.bot, cmd);
599 }
600 return 0;
601 }
602
603 MODCMD_FUNC(cmd_addtrack)
604 {
605 struct userNode *un = NULL;
606
607 if((argc > 1) && (un = dict_find(clients, argv[1], NULL)))
608 {
609 add_track_user(un);
610 UPDATE_TIMESTAMP();
611 TRACK("$bALERT$b Manually enabled monitoring of %s!%s@%s on %s requested by %s",
612 un->nick, un->ident, un->hostname, un->uplink->name, user->nick);
613 send_message_type(4, user, track_cfg.bot, "Now tracking %s!%s@%s on %s", un->nick,un->ident,un->hostname, un->uplink->name);
614 }
615 else
616 {
617 send_message_type(4, user, track_cfg.bot, "No nick or invalid nick specified.");
618 svccmd_send_help_brief(user, track_cfg.bot, cmd);
619 }
620 return 0;
621 }
622
623 MODCMD_FUNC(cmd_listtrack)
624 {
625 dict_iterator_t it, next;
626 if (track_db == NULL) return 0;
627 struct userNode *un = NULL;
628 send_message_type(4, user, track_cfg.bot, "Currently tracking:");
629 for (it=dict_first(track_db); it; it=next) {
630 next = iter_next(it);
631 un = it->data;
632 send_message_type(4, user, track_cfg.bot, "%s!%s@%s [%s] on %s",
633 un->nick, un->ident, un->hostname, irc_ntoa(&un->ip), un->uplink->name);
634 }
635 send_message_type(4, user, track_cfg.bot, "End of track list.");
636 return 0;
637 }
638
639 static void
640 track_conf_read(void) {
641 dict_t node;
642 char *str;
643
644 node = conf_get_data("modules/track", RECDB_OBJECT);
645 if (!node)
646 return;
647 str = database_get_data(node, "snomask", RECDB_QSTRING);
648 if (!str)
649 track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH;
650 else
651 parse_track_conf(str);
652 str = database_get_data(node, "channel", RECDB_QSTRING);
653 if (!str)
654 return;
655 // XXX - dont do addchannel if the channel is being shared with
656 // another module:
657 track_cfg.channel = AddChannel(str, now, "+sntOm", NULL, NULL);
658 if (!track_cfg.channel)
659 return;
660 str = database_get_data(node, "show_bursts", RECDB_QSTRING);
661 track_cfg.show_bursts = str ? enabled_string(str) : 0;
662 track_cfg.enabled = 1;
663 if (finalized)
664 track_finalize();
665 }
666
667 void
668 track_cleanup(void) {
669 track_cfg.enabled = 0;
670 unreg_del_user_func(track_del_user);
671 }
672
673 int
674 track_init(void) {
675 track_db = dict_new();
676
677 reg_exit_func(track_cleanup);
678 conf_register_reload(track_conf_read);
679 reg_nick_change_func(track_nick_change);
680 reg_join_func(track_join);
681 reg_part_func(track_part);
682 reg_kick_func(track_kick);
683 reg_new_user_func(track_new_user);
684 reg_del_user_func(track_del_user);
685 reg_auth_func(track_auth);
686 reg_channel_mode_func(track_channel_mode);
687 reg_user_mode_func(track_user_mode);
688 reg_oper_func(track_oper);
689 opserv_define_func("TRACK", cmd_track, 800, 0, 0);
690 opserv_define_func("DELTRACK", cmd_deltrack, 800, 0, 0);
691 opserv_define_func("ADDTRACK", cmd_addtrack, 800, 0, 0);
692 opserv_define_func("LISTTRACK", cmd_listtrack, 800, 0, 0);
693 return 1;
694 }
695
696 int
697 track_finalize(void) {
698 struct mod_chanmode change;
699 dict_t node;
700 char *str;
701
702 finalized = 1;
703 node = conf_get_data("modules/track", RECDB_OBJECT);
704 if (!node)
705 return 0;
706 str = database_get_data(node, "snomask", RECDB_QSTRING);
707 if (!str)
708 track_cfg.snomask = TRACK_NICK|TRACK_KICK|TRACK_JOIN|TRACK_PART|TRACK_CHANMODE|TRACK_NEW|TRACK_DEL|TRACK_AUTH;
709 else
710 parse_track_conf(str);
711 str = database_get_data(node, "bot", RECDB_QSTRING);
712 if (!str)
713 return 0;
714 track_cfg.bot = GetUserH(str);
715 if (!track_cfg.bot)
716 return 0;
717 mod_chanmode_init(&change);
718 change.argc = 1;
719 change.args[0].mode = MODE_CHANOP;
720 change.args[0].u.member = AddChannelUser(track_cfg.bot, track_cfg.channel);
721 mod_chanmode_announce(track_cfg.bot, track_cfg.channel, &change);
722 return 1;
723 }
724
725