]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-memoserv.c
Fixed header comments
[irc/evilnet/x3.git] / src / mod-memoserv.c
1 /* mod-memoserv.c - MemoServ module for srvx
2 * Copyright 2003-2004 Martijn Smit and 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 * "memoserv" {
24 * "bot" "NickServ";
25 * "message_expiry" "30d"; // age when messages are deleted; set
26 * // to 0 to disable message expiration
27 * };
28 * };
29 *
30 * After that, to make the module active on an existing bot:
31 * /msg opserv bind nickserv * *memoserv.*
32 *
33 * If you want a dedicated MemoServ bot, make sure the service control
34 * commands are bound to OpServ:
35 * /msg opserv bind opserv service *modcmd.joiner
36 * /msg opserv bind opserv service\ add *modcmd.service\ add
37 * /msg opserv bind opserv service\ rename *modcmd.service\ rename
38 * /msg opserv bind opserv service\ trigger *modcmd.service\ trigger
39 * /msg opserv bind opserv service\ remove *modcmd.service\ remove
40 * Add the bot:
41 * /msg opserv service add MemoServ User-to-user Memorandum Service
42 * /msg opserv bind memoserv help *modcmd.help
43 * Restart srvx with the updated conf file (as above, butwith "bot"
44 * "MemoServ"), and bind the commands to it:
45 * /msg opserv bind memoserv * *memoserv.*
46 * /msg opserv bind memoserv set *modcmd.joiner
47 */
48
49 #include "chanserv.h"
50 #include "conf.h"
51 #include "modcmd.h"
52 #include "saxdb.h"
53 #include "timeq.h"
54
55 #define KEY_SENT "sent"
56 #define KEY_RECIPIENT "to"
57 #define KEY_FROM "from"
58 #define KEY_MESSAGE "msg"
59 #define KEY_READ "read"
60
61 static const struct message_entry msgtab[] = {
62 { "MSMSG_CANNOT_SEND", "You cannot send to account $b%s$b." },
63 { "MSMSG_MEMO_SENT", "Message sent to $b%s$b." },
64 { "MSMSG_NO_MESSAGES", "You have no messages." },
65 { "MSMSG_MEMOS_FOUND", "Found $b%d$b matches.\nUse /msg $S READ <ID> to read a message." },
66 { "MSMSG_CLEAN_INBOX", "You have $b%d$b or more messages, please clean out your inbox.\nUse /msg $S READ <ID> to read a message." },
67 { "MSMSG_LIST_HEAD", "$bID$b $bFrom$b $bTime Sent$b" },
68 { "MSMSG_LIST_FORMAT", "%-2u %s %s" },
69 { "MSMSG_MEMO_HEAD", "Memo %u From $b%s$b, received on %s:" },
70 { "MSMSG_BAD_MESSAGE_ID", "$b%s$b is not a valid message ID (it should be a number between 0 and %u)." },
71 { "MSMSG_NO_SUCH_MEMO", "You have no memo with that ID." },
72 { "MSMSG_MEMO_DELETED", "Memo $b%d$b deleted." },
73 { "MSMSG_EXPIRY_OFF", "I am currently not expiring messages. (turned off)" },
74 { "MSMSG_EXPIRY", "Messages will be expired when they are %s old (%d seconds)." },
75 { "MSMSG_MESSAGES_EXPIRED", "$b%lu$b message(s) expired." },
76 { "MSMSG_MEMOS_INBOX", "You have $b%d$b new message(s) in your inbox and %d old messages. Use /msg $S LIST to list them." },
77 { "MSMSG_NEW_MESSAGE", "You have a new message from $b%s$b. /msg $S LIST" },
78 { "MSMSG_DELETED_ALL", "Deleted all of your messages." },
79 { "MSMSG_USE_CONFIRM", "Please use /msg $S DELETE * $bCONFIRM$b to delete $uall$u of your messages." },
80 { "MSMSG_STATUS_TOTAL", "I have $b%u$b memos in my database." },
81 { "MSMSG_STATUS_EXPIRED", "$b%ld$b memos expired during the time I am awake." },
82 { "MSMSG_STATUS_SENT", "$b%ld$b memos have been sent." },
83 { "MSMSG_SET_NOTIFY", "$bNotify: $b %s" },
84 { "MSMSG_SET_AUTHNOTIFY", "$bAuthNotify: $b %s" },
85 { "MSMSG_SET_PRIVATE", "$bPrivate: $b %s" },
86 { NULL, NULL }
87 };
88
89 struct memo {
90 struct memo_account *recipient;
91 struct memo_account *sender;
92 char *message;
93 time_t sent;
94 unsigned int is_read : 1;
95 };
96
97 DECLARE_LIST(memoList, struct memo*);
98 DEFINE_LIST(memoList, struct memo*);
99
100 /* memo_account.flags fields */
101 #define MEMO_NOTIFY_NEW 1
102 #define MEMO_NOTIFY_LOGIN 2
103 #define MEMO_DENY_NONCHANNEL 4
104
105 struct memo_account {
106 struct handle_info *handle;
107 unsigned int flags;
108 struct memoList sent;
109 struct memoList recvd;
110 };
111
112 static struct {
113 struct userNode *bot;
114 int message_expiry;
115 } memoserv_conf;
116
117 const char *memoserv_module_deps[] = { NULL };
118 static struct module *memoserv_module;
119 static struct log_type *MS_LOG;
120 static unsigned long memosSent, memosExpired;
121 static struct dict *memos; /* memo_account->handle->handle -> memo_account */
122
123 static struct memo_account *
124 memoserv_get_account(struct handle_info *hi)
125 {
126 struct memo_account *ma;
127 if (!hi)
128 return NULL;
129 ma = dict_find(memos, hi->handle, NULL);
130 if (ma)
131 return ma;
132 ma = calloc(1, sizeof(*ma));
133 if (!ma)
134 return ma;
135 ma->handle = hi;
136 ma->flags = MEMO_NOTIFY_NEW | MEMO_NOTIFY_LOGIN;
137 dict_insert(memos, ma->handle->handle, ma);
138 return ma;
139 }
140
141 static void
142 delete_memo(struct memo *memo)
143 {
144 memoList_remove(&memo->recipient->recvd, memo);
145 memoList_remove(&memo->sender->sent, memo);
146 free(memo->message);
147 free(memo);
148 }
149
150 static void
151 delete_memo_account(void *data)
152 {
153 struct memo_account *ma = data;
154
155 while (ma->recvd.used)
156 delete_memo(ma->recvd.list[0]);
157 while (ma->sent.used)
158 delete_memo(ma->sent.list[0]);
159 memoList_clean(&ma->recvd);
160 memoList_clean(&ma->sent);
161 free(ma);
162 }
163
164 void
165 do_expire(void)
166 {
167 dict_iterator_t it;
168 for (it = dict_first(memos); it; it = iter_next(it)) {
169 struct memo_account *acct = iter_data(it);
170 unsigned int ii;
171 for (ii = 0; ii < acct->sent.used; ++ii) {
172 struct memo *memo = acct->sent.list[ii];
173 if ((now - memo->sent) > memoserv_conf.message_expiry) {
174 delete_memo(memo);
175 memosExpired++;
176 ii--;
177 }
178 }
179 }
180 }
181
182 static void
183 expire_memos(UNUSED_ARG(void *data))
184 {
185 if (memoserv_conf.message_expiry) {
186 do_expire();
187 timeq_add(now + memoserv_conf.message_expiry, expire_memos, NULL);
188 }
189 }
190
191 static struct memo*
192 add_memo(time_t sent, struct memo_account *recipient, struct memo_account *sender, char *message)
193 {
194 struct memo *memo;
195
196 memo = calloc(1, sizeof(*memo));
197 if (!memo)
198 return NULL;
199
200 memo->recipient = recipient;
201 memoList_append(&recipient->recvd, memo);
202 memo->sender = sender;
203 memoList_append(&sender->sent, memo);
204 memo->sent = sent;
205 memo->message = strdup(message);
206 memosSent++;
207 return memo;
208 }
209
210 static int
211 memoserv_can_send(struct userNode *bot, struct userNode *user, struct memo_account *acct)
212 {
213 extern struct userData *_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended);
214 struct userData *dest;
215
216 if (!user->handle_info)
217 return 0;
218 if (!(acct->flags & MEMO_DENY_NONCHANNEL))
219 return 1;
220 for (dest = acct->handle->channels; dest; dest = dest->u_next)
221 if (_GetChannelUser(dest->channel, user->handle_info, 1, 0))
222 return 1;
223 send_message(user, bot, "MSMSG_CANNOT_SEND", acct->handle->handle);
224 return 0;
225 }
226
227 static struct memo *find_memo(struct userNode *user, struct svccmd *cmd, struct memo_account *ma, const char *msgid, unsigned int *id)
228 {
229 unsigned int memoid;
230 if (!isdigit(msgid[0])) {
231 if (ma->recvd.used)
232 reply("MSMSG_BAD_MESSAGE_ID", msgid, ma->recvd.used - 1);
233 else
234 reply("MSMSG_NO_MESSAGES");
235 return NULL;
236 }
237 memoid = atoi(msgid);
238 if (memoid >= ma->recvd.used) {
239 reply("MSMSG_NO_SUCH_MEMO");
240 return NULL;
241 }
242 return ma->recvd.list[*id = memoid];
243 }
244
245 static MODCMD_FUNC(cmd_send)
246 {
247 char *message;
248 struct handle_info *hi;
249 struct memo_account *ma, *sender;
250
251 if (!(hi = modcmd_get_handle_info(user, argv[1])))
252 return 0;
253 if (!(sender = memoserv_get_account(user->handle_info))
254 || !(ma = memoserv_get_account(hi))) {
255 reply("MSG_INTERNAL_FAILURE");
256 return 0;
257 }
258 if (!(memoserv_can_send(cmd->parent->bot, user, ma)))
259 return 0;
260 message = unsplit_string(argv + 2, argc - 2, NULL);
261 add_memo(now, ma, sender, message);
262 if (ma->flags & MEMO_NOTIFY_NEW) {
263 struct userNode *other;
264 for (other = ma->handle->users; other; other = other->next_authed)
265 send_message(other, cmd->parent->bot, "MSMSG_NEW_MESSAGE", user->nick);
266 }
267 reply("MSMSG_MEMO_SENT", ma->handle->handle);
268 return 1;
269 }
270
271 static MODCMD_FUNC(cmd_list)
272 {
273 struct memo_account *ma;
274 struct memo *memo;
275 unsigned int ii;
276 char posted[24];
277 struct tm tm;
278
279 if (!(ma = memoserv_get_account(user->handle_info)))
280 return 0;
281 reply("MSMSG_LIST_HEAD");
282 for (ii = 0; (ii < ma->recvd.used) && (ii < 15); ++ii) {
283 memo = ma->recvd.list[ii];
284 localtime_r(&memo->sent, &tm);
285 strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
286 reply("MSMSG_LIST_FORMAT", ii, memo->sender->handle->handle, posted);
287 }
288 if (ii == 0)
289 reply("MSG_NONE");
290 else if (ii == 15)
291 reply("MSMSG_CLEAN_INBOX", ii);
292 else
293 reply("MSMSG_MEMOS_FOUND", ii);
294 return 1;
295 }
296
297 static MODCMD_FUNC(cmd_read)
298 {
299 struct memo_account *ma;
300 unsigned int memoid;
301 struct memo *memo;
302 char posted[24];
303 struct tm tm;
304
305 if (!(ma = memoserv_get_account(user->handle_info)))
306 return 0;
307 if (!(memo = find_memo(user, cmd, ma, argv[1], &memoid)))
308 return 0;
309 localtime_r(&memo->sent, &tm);
310 strftime(posted, sizeof(posted), "%I:%M %p, %m/%d/%Y", &tm);
311 reply("MSMSG_MEMO_HEAD", memoid, memo->sender->handle->handle, posted);
312 send_message_type(4, user, cmd->parent->bot, "%s", memo->message);
313 memo->is_read = 1;
314 return 1;
315 }
316
317 static MODCMD_FUNC(cmd_delete)
318 {
319 struct memo_account *ma;
320 struct memo *memo;
321 unsigned int memoid;
322
323 if (!(ma = memoserv_get_account(user->handle_info)))
324 return 0;
325 if (!irccasecmp(argv[1], "*") || !irccasecmp(argv[1], "all")) {
326 if ((argc < 3) || irccasecmp(argv[2], "confirm")) {
327 reply("MSMSG_USE_CONFIRM");
328 return 0;
329 }
330 while (ma->recvd.used)
331 delete_memo(ma->recvd.list[0]);
332 reply("MSMSG_DELETED_ALL");
333 return 1;
334 }
335
336 if (!(memo = find_memo(user, cmd, ma, argv[1], &memoid)))
337 return 0;
338 delete_memo(memo);
339 reply("MSMSG_MEMO_DELETED", memoid);
340 return 1;
341 }
342
343 static MODCMD_FUNC(cmd_expire)
344 {
345 unsigned long old_expired = memosExpired;
346 do_expire();
347 reply("MSMSG_MESSAGES_EXPIRED", memosExpired - old_expired);
348 return 1;
349 }
350
351 static MODCMD_FUNC(cmd_expiry)
352 {
353 char interval[INTERVALLEN];
354
355 if (!memoserv_conf.message_expiry) {
356 reply("MSMSG_EXPIRY_OFF");
357 return 1;
358 }
359
360 intervalString(interval, memoserv_conf.message_expiry, user->handle_info);
361 reply("MSMSG_EXPIRY", interval, memoserv_conf.message_expiry);
362 return 1;
363 }
364
365 static MODCMD_FUNC(cmd_set_notify)
366 {
367 struct memo_account *ma;
368 char *choice;
369
370 if (!(ma = memoserv_get_account(user->handle_info)))
371 return 0;
372 if (argc > 1) {
373 choice = argv[1];
374 if (enabled_string(choice)) {
375 ma->flags |= MEMO_NOTIFY_NEW;
376 } else if (disabled_string(choice)) {
377 ma->flags &= ~MEMO_NOTIFY_NEW;
378 } else {
379 reply("MSG_INVALID_BINARY", choice);
380 return 0;
381 }
382 }
383
384 choice = (ma->flags & MEMO_NOTIFY_NEW) ? "on" : "off";
385 reply("MSMSG_SET_NOTIFY", choice);
386 return 1;
387 }
388
389 static MODCMD_FUNC(cmd_set_authnotify)
390 {
391 struct memo_account *ma;
392 char *choice;
393
394 if (!(ma = memoserv_get_account(user->handle_info)))
395 return 0;
396 if (argc > 1) {
397 choice = argv[1];
398 if (enabled_string(choice)) {
399 ma->flags |= MEMO_NOTIFY_LOGIN;
400 } else if (disabled_string(choice)) {
401 ma->flags &= ~MEMO_NOTIFY_LOGIN;
402 } else {
403 reply("MSG_INVALID_BINARY", choice);
404 return 0;
405 }
406 }
407
408 choice = (ma->flags & MEMO_NOTIFY_LOGIN) ? "on" : "off";
409 reply("MSMSG_SET_AUTHNOTIFY", choice);
410 return 1;
411 }
412
413 static MODCMD_FUNC(cmd_set_private)
414 {
415 struct memo_account *ma;
416 char *choice;
417
418 if (!(ma = memoserv_get_account(user->handle_info)))
419 return 0;
420 if (argc > 1) {
421 choice = argv[1];
422 if (enabled_string(choice)) {
423 ma->flags |= MEMO_DENY_NONCHANNEL;
424 } else if (disabled_string(choice)) {
425 ma->flags &= ~MEMO_DENY_NONCHANNEL;
426 } else {
427 reply("MSG_INVALID_BINARY", choice);
428 return 0;
429 }
430 }
431
432 choice = (ma->flags & MEMO_DENY_NONCHANNEL) ? "on" : "off";
433 reply("MSMSG_SET_PRIVATE", choice);
434 return 1;
435 }
436
437 static MODCMD_FUNC(cmd_status)
438 {
439 reply("MSMSG_STATUS_TOTAL", dict_size(memos));
440 reply("MSMSG_STATUS_EXPIRED", memosExpired);
441 reply("MSMSG_STATUS_SENT", memosSent);
442 return 1;
443 }
444
445 static void
446 memoserv_conf_read(void)
447 {
448 dict_t conf_node;
449 const char *str;
450
451 str = "modules/memoserv";
452 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
453 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
454 return;
455 }
456
457 str = database_get_data(conf_node, "message_expiry", RECDB_QSTRING);
458 memoserv_conf.message_expiry = str ? ParseInterval(str) : 60*24*30;
459 }
460
461 static int
462 memoserv_saxdb_read(struct dict *db)
463 {
464 char *str;
465 struct handle_info *sender, *recipient;
466 struct record_data *hir;
467 struct memo *memo;
468 dict_iterator_t it;
469 time_t sent;
470
471 for (it = dict_first(db); it; it = iter_next(it)) {
472 hir = iter_data(it);
473 if (hir->type != RECDB_OBJECT) {
474 log_module(MS_LOG, LOG_WARNING, "Unexpected rectype %d for %s.", hir->type, iter_key(it));
475 continue;
476 }
477
478 if (!(str = database_get_data(hir->d.object, KEY_SENT, RECDB_QSTRING))) {
479 log_module(MS_LOG, LOG_ERROR, "Date sent not present in memo %s; skipping", iter_key(it));
480 continue;
481 }
482 sent = atoi(str);
483
484 if (!(str = database_get_data(hir->d.object, KEY_RECIPIENT, RECDB_QSTRING))) {
485 log_module(MS_LOG, LOG_ERROR, "Recipient not present in memo %s; skipping", iter_key(it));
486 continue;
487 } else if (!(recipient = get_handle_info(str))) {
488 log_module(MS_LOG, LOG_ERROR, "Invalid recipient %s in memo %s; skipping", str, iter_key(it));
489 continue;
490 }
491
492 if (!(str = database_get_data(hir->d.object, KEY_FROM, RECDB_QSTRING))) {
493 log_module(MS_LOG, LOG_ERROR, "Sender not present in memo %s; skipping", iter_key(it));
494 continue;
495 } else if (!(sender = get_handle_info(str))) {
496 log_module(MS_LOG, LOG_ERROR, "Invalid sender %s in memo %s; skipping", str, iter_key(it));
497 continue;
498 }
499
500 if (!(str = database_get_data(hir->d.object, KEY_MESSAGE, RECDB_QSTRING))) {
501 log_module(MS_LOG, LOG_ERROR, "Message not present in memo %s; skipping", iter_key(it));
502 continue;
503 }
504
505 memo = add_memo(sent, memoserv_get_account(recipient), memoserv_get_account(sender), str);
506 if ((str = database_get_data(hir->d.object, KEY_READ, RECDB_QSTRING)))
507 memo->is_read = 1;
508 }
509 return 0;
510 }
511
512 static int
513 memoserv_saxdb_write(struct saxdb_context *ctx)
514 {
515 dict_iterator_t it;
516 struct memo_account *ma;
517 struct memo *memo;
518 char str[7];
519 unsigned int id = 0, ii;
520
521 for (it = dict_first(memos); it; it = iter_next(it)) {
522 ma = iter_data(it);
523 for (ii = 0; ii < ma->recvd.used; ++ii) {
524 memo = ma->recvd.list[ii];
525 saxdb_start_record(ctx, inttobase64(str, id++, sizeof(str)), 0);
526 saxdb_write_int(ctx, KEY_SENT, memo->sent);
527 saxdb_write_string(ctx, KEY_RECIPIENT, memo->recipient->handle->handle);
528 saxdb_write_string(ctx, KEY_FROM, memo->sender->handle->handle);
529 saxdb_write_string(ctx, KEY_MESSAGE, memo->message);
530 if (memo->is_read)
531 saxdb_write_int(ctx, KEY_READ, 1);
532 saxdb_end_record(ctx);
533 }
534 }
535 return 0;
536 }
537
538 static void
539 memoserv_cleanup(void)
540 {
541 dict_delete(memos);
542 }
543
544 static void
545 memoserv_check_messages(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
546 {
547 unsigned int ii, unseen;
548 struct memo_account *ma;
549 struct memo *memo;
550
551 if (!(ma = memoserv_get_account(user->handle_info))
552 || !(ma->flags & MEMO_NOTIFY_LOGIN))
553 return;
554 for (ii = unseen = 0; ii < ma->recvd.used; ++ii) {
555 memo = ma->recvd.list[ii];
556 if (!memo->is_read)
557 unseen++;
558 }
559 if (ma->recvd.used && memoserv_conf.bot)
560 send_message(user, memoserv_conf.bot, "MSMSG_MEMOS_INBOX", unseen, ma->recvd.used - unseen);
561 }
562
563 static void
564 memoserv_rename_account(struct handle_info *hi, const char *old_handle)
565 {
566 struct memo_account *ma;
567 if (!(ma = dict_find(memos, old_handle, NULL)))
568 return;
569 dict_remove2(memos, old_handle, 1);
570 dict_insert(memos, hi->handle, ma);
571 }
572
573 static void
574 memoserv_unreg_account(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
575 {
576 dict_remove(memos, handle->handle);
577 }
578
579 int
580 memoserv_init(void)
581 {
582 MS_LOG = log_register_type("MemoServ", "file:memoserv.log");
583 memos = dict_new();
584 dict_set_free_data(memos, delete_memo_account);
585 reg_auth_func(memoserv_check_messages);
586 reg_handle_rename_func(memoserv_rename_account);
587 reg_unreg_func(memoserv_unreg_account);
588 conf_register_reload(memoserv_conf_read);
589 reg_exit_func(memoserv_cleanup);
590 saxdb_register("MemoServ", memoserv_saxdb_read, memoserv_saxdb_write);
591
592 memoserv_module = module_register("MemoServ", MS_LOG, "mod-memoserv.help", NULL);
593 modcmd_register(memoserv_module, "send", cmd_send, 3, MODCMD_REQUIRE_AUTHED, NULL);
594 modcmd_register(memoserv_module, "list", cmd_list, 1, MODCMD_REQUIRE_AUTHED, NULL);
595 modcmd_register(memoserv_module, "read", cmd_read, 2, MODCMD_REQUIRE_AUTHED, NULL);
596 modcmd_register(memoserv_module, "delete", cmd_delete, 2, MODCMD_REQUIRE_AUTHED, NULL);
597 modcmd_register(memoserv_module, "expire", cmd_expire, 1, MODCMD_REQUIRE_AUTHED, "flags", "+oper", NULL);
598 modcmd_register(memoserv_module, "expiry", cmd_expiry, 1, 0, NULL);
599 modcmd_register(memoserv_module, "status", cmd_status, 1, 0, NULL);
600 modcmd_register(memoserv_module, "set notify", cmd_set_notify, 1, 0, NULL);
601 modcmd_register(memoserv_module, "set authnotify", cmd_set_authnotify, 1, 0, NULL);
602 modcmd_register(memoserv_module, "set private", cmd_set_private, 1, 0, NULL);
603 message_register_table(msgtab);
604
605 if (memoserv_conf.message_expiry)
606 timeq_add(now + memoserv_conf.message_expiry, expire_memos, NULL);
607 return 1;
608 }
609
610 int
611 memoserv_finalize(void) {
612 dict_t conf_node;
613 const char *str;
614
615 str = "modules/memoserv";
616 if (!(conf_node = conf_get_data(str, RECDB_OBJECT))) {
617 log_module(MS_LOG, LOG_ERROR, "config node `%s' is missing or has wrong type.", str);
618 return 0;
619 }
620
621 str = database_get_data(conf_node, "bot", RECDB_QSTRING);
622 if (str)
623 memoserv_conf.bot = GetUserH(str);
624 return 1;
625 }