eb8eabfb148df003e9073147e8a3c0dd11e23f4c
[NeonServV5.git] / src / modules / NeonServ.mod / cmd_neonserv_set.c
1 /* cmd_neonserv_set.c - NeonServ v5.4
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "cmd_neonserv.h"
19
20 typedef char* neonserv_cmd_set_function(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
21 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *argument);
22 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
23 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
24 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
25 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
26
27 #define NS_VALID_FUNCTION 0x01
28 #define NS_VALID_STRING   0x02
29 #define NS_VALID_ACCESS   0x04
30 #define NS_VALID_NO501    0x08
31 #define NS_VALID_OPTIONS  0x10
32 #define NS_VALID_NUMERIC  0x20
33 #define NS_VALID_BOOLEAN  0x40
34 #define NS_VALID_IF_HALFOP 0x80
35
36 #define NS_HAS_OPT  0x100 /* options (SET_OPTION_{NAME}_{VALUE}) */
37 #define NS_HAS_HELP 0x200 /* help    (SET_HELP_{NAME}) - only shown if help is requested */
38
39 static const struct {
40     const char *setting;
41     const char *chanfield;
42     unsigned int valid;
43     void *parameter;
44 } channel_settings[] = {
45     {"TRIGGER",         NULL,                   NS_VALID_FUNCTION,                  neonserv_cmd_set_trigger},
46     {"DEFAULTTOPIC",    "channel_defaulttopic", NS_VALID_STRING,                    NULL},
47     {"TOPICMASK",       "channel_topicmask",    NS_VALID_STRING,                    NULL},
48     {"ADVANCEDTOPIC",   "channel_exttopic",     NS_VALID_BOOLEAN | NS_HAS_OPT,      NULL},
49     {"GREETING",        "channel_greeting",     NS_VALID_STRING,                    NULL},
50     {"USERGREETING",    "channel_usergreeting", NS_VALID_STRING,                    NULL},
51     {"USERINFO",        "channel_userinfo",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
52     {"WIPEINFO",        "channel_wipeinfo",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
53     {"MODES",           "channel_modes",        NS_VALID_FUNCTION,                  neonserv_cmd_set_modes},
54     {"INVITEME",        "channel_getinvite",    NS_VALID_ACCESS,                    NULL},
55     {"GIVEOPS",         "channel_getop",        NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
56     {"GIVEHALFOPS",     "channel_gethalfop",    NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL},
57     {"GIVEVOICE",       "channel_getvoice",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
58     {"ENFOPS",          "channel_canop",        NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
59     {"ENFHALFOPS",      "channel_canhalfop",    NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL},
60     {"ENFVOICE",        "channel_canvoice",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
61     {"KICK",            "channel_cankick",      NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
62     {"BAN",             "channel_canban",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
63     {"STATICBAN",       "channel_staticban",    NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
64     {"PUBCMD",          "channel_pubcmd",       NS_VALID_ACCESS,                    NULL},
65     {"ENFMODES",        "channel_enfmodes",     NS_VALID_ACCESS,                    NULL},
66     {"ENFTOPIC",        "channel_enftopic",     NS_VALID_ACCESS,                    NULL},
67     {"TOPICSNARF",      "channel_topicsnarf",   NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
68     {"CHANGETOPIC",     "channel_changetopic",  NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
69     {"SETTERS",         "channel_setters",      NS_VALID_ACCESS | NS_VALID_NO501 | NS_HAS_HELP, NULL},
70     {"ADDUSER",         "channel_canadd",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
71     {"DELUSER",         "channel_candel",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
72     {"CLVL",            "channel_canclvl",      NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
73     {"RESYNC",          "channel_canresync",    NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
74     {"SUSPEND",         "channel_cansuspend",   NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
75     {"NOTICEUSERS",     "channel_notice",       NS_VALID_ACCESS,                    NULL},
76     {"NOTICEREACTION",  "channel_noticereaction", NS_VALID_OPTIONS | NS_HAS_OPT,    "4"},
77     {"CTCPUSERS",       "channel_ctcp",         NS_VALID_ACCESS,                    NULL},
78     {"CTCPREACTION",    "channel_ctcpreaction", NS_VALID_OPTIONS | NS_HAS_OPT,      "4"},
79     {"PROTECT",         "channel_protect",      NS_VALID_OPTIONS | NS_HAS_OPT,      "4"},
80     {"TOYS",            "channel_toys",         NS_VALID_OPTIONS | NS_HAS_OPT,      "3"},
81     {"DYNLIMIT",        "channel_dynlimit",     NS_VALID_NUMERIC | NS_VALID_FUNCTION | NS_HAS_OPT, neonserv_cmd_set_dynlimit},
82     {"NODELETE",        "channel_nodelete",     NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_nodelete},
83     {NULL, NULL, 0, NULL}
84 };
85
86 #define MAX_QUERY_LEN 1024
87 CMD_BIND(neonserv_cmd_set) {
88     int i, j;
89     if(argc && !strcmp(argv[0], "defaults")) {
90         //reset channel settings
91         int uaccess = getChannelAccess(user, chan);
92         if(uaccess < 500) {
93             if(isGodMode(user)) {
94                 event->flags |= CMDFLAG_OPLOG;
95             } else {
96                 reply(getTextBot(), user, "NS_SET_DEFAULTS_OWNER", chan->name);
97                 return;
98             }
99         }
100         int seed = 0;
101         char *tmp;
102         static char defaultskey[16];
103         for(tmp = user->auth; *tmp; tmp++)
104             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
105         for(tmp = chan->name; *tmp; tmp++)
106             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
107         sprintf(defaultskey, "%08x", seed);
108         if(argc > 1 && !strcmp(argv[1], defaultskey)) {
109             char query[MAX_QUERY_LEN];
110             int querypos = 0;
111             i = 0;
112             while(channel_settings[i].setting) {
113                 if(channel_settings[i].chanfield)
114                     querypos += sprintf(query + querypos, "`%s` = NULL, ", channel_settings[i].chanfield);
115                 i++;
116             }
117             if(querypos) {
118                 query[querypos-2] = '\0';
119             }
120             printf_mysql_query("UPDATE `channels` SET %s WHERE `channel_id` = '%d'", query, chan->channel_id);
121             reply(getTextBot(), user, "NS_SET_DEFAULTS_DONE", chan->name);
122             logEvent(event);
123         } else {
124             reply(getTextBot(), user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey);
125         }
126     } else if(argc && strcmp(argv[0], "help")) {
127         //find the correct command
128         i = 0;
129         j = 0;
130         char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL);
131         int with_halfops = get_int_field("General.have_halfop");
132         while(channel_settings[i].setting) {
133             if(!stricmp(channel_settings[i].setting, argv[0]) && (!(channel_settings[i].valid & NS_VALID_IF_HALFOP) || with_halfops)) {
134                 //setting found
135                 if(channel_settings[i].valid & NS_VALID_FUNCTION) {
136                     neonserv_cmd_set_function *func = channel_settings[i].parameter;
137                     func(client, user, chan, event, channel_settings[i].setting, args);
138                 } else {
139                     neonserv_cmd_set_setting(client, user, chan, event, i, args);
140                 }
141                 j = 1;
142                 break;
143             }
144             i++;
145         }
146         if(j == 0) {
147             //unknown setting
148             reply(getTextBot(), user, "NS_SET_UNKNOWN_SETTING", argv[0]);
149         }
150     } else {
151         char query[MAX_QUERY_LEN], *value, *org_value, *tmp, nameBuf[64];
152         int querypos = 0;
153         MYSQL_RES *res, *defaults_res;
154         MYSQL_ROW row, defaults;
155         struct Table *table;
156         char *content[2];
157         int with_halfops = get_int_field("General.have_halfop");
158         i = 0;
159         j = 0;
160         while(channel_settings[i].setting) {
161             if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) {
162                 j++;
163                 i++;
164                 continue;
165             }
166             if(channel_settings[i].chanfield)
167                 querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield);
168             i++;
169         }
170         table = table_init(2, i-j, 0);
171         table_set_bold(table, 0, 1);
172         printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query);
173         defaults_res = mysql_use();
174         defaults = mysql_fetch_row(defaults_res);
175         printf_mysql_query("SELECT `channel_name` %s FROM `channels` WHERE `channel_id` = '%d'", query, chan->channel_id);
176         res = mysql_use();
177         row = mysql_fetch_row(res);
178         i = 0;
179         j = 0;
180         reply(getTextBot(), user, "NS_SET_HEADER", chan->name);
181         while(channel_settings[i].setting) {
182             if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) {
183                 i++;
184                 continue;
185             }
186             if(channel_settings[i].chanfield) {
187                 j++;
188                 org_value = (row[j] ? row[j] : defaults[j]);
189             } else if(channel_settings[i].valid & NS_VALID_FUNCTION) {
190                 neonserv_cmd_set_function *func = channel_settings[i].parameter;
191                 org_value = func(client, user, chan, event, NULL, NULL);
192             } else
193                 org_value = "0";
194             value = org_value;
195             if(channel_settings[i].valid & NS_VALID_BOOLEAN) {
196                 if(!strcmp(value, "0"))
197                     value = get_language_string(user, "NS_SET_OFF");
198                 else
199                     value = get_language_string(user, "NS_SET_ON");
200             }
201             strcpy(query, value);
202             querypos = strlen(query);
203             if(channel_settings[i].valid & NS_HAS_OPT) {
204                 sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[i].setting, org_value);
205                 tmp = get_language_string(user, nameBuf);
206                 if(tmp) {
207                     querypos += sprintf(query+querypos, " - %s", tmp);
208                 }
209             }
210             if(argc && channel_settings[i].valid & NS_HAS_HELP) {
211                 sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[i].setting);
212                 tmp = get_language_string(user, nameBuf);
213                 if(tmp) {
214                     querypos += sprintf(query+querypos, " - %s", tmp);
215                 }
216             }
217             content[0] = (char*)channel_settings[i].setting;
218             content[1] = query;
219             table_add(table, content);
220             i++;
221         }
222         char **table_lines = table_end(table);
223         for(i = 0; i < table->entrys; i++) {
224             reply(getTextBot(), user, table_lines[i]);
225         }
226         table_free(table);
227     }
228 }
229
230 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *args) {
231     char *value;
232     char nameBuf[64];
233     //get current value
234     MYSQL_RES *res;
235     MYSQL_ROW row;
236     printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, chan->channel_id);
237     res = mysql_use();
238     row = mysql_fetch_row(res);
239     if(row[0] == NULL) {
240         printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_settings[setting].chanfield);
241         res = mysql_use();
242         row = mysql_fetch_row(res);
243     }
244     value = row[0];
245     if(args) {
246         //change the channel setting
247         //check the new argument
248         int valid = channel_settings[setting].valid;
249         if(valid & NS_VALID_STRING) {
250             if(!strcmp(args, "*")) {
251                 args = "";
252             }
253         }
254         if(valid & NS_VALID_ACCESS) {
255             int caccess = atoi(args);
256             int max = ((valid & NS_VALID_NO501) ? 500 : 501);
257             if(caccess < 0 || caccess > max) {
258                 reply(getTextBot(), user, "NS_INVALID_ACCESS", caccess);
259                 return;
260             }
261             int uaccess = getChannelAccess(user, chan);
262             if(uaccess == 500) uaccess++;
263             if(atoi(value) > uaccess) {
264                 if(isGodMode(user)) {
265                     event->flags |= CMDFLAG_OPLOG;
266                 } else {
267                     reply(getTextBot(), user, "NS_SET_CANNOT_SET");
268                     return;
269                 }
270             }
271             if(caccess > uaccess) {
272                 if(isGodMode(user)) {
273                     event->flags |= CMDFLAG_OPLOG;
274                 } else {
275                     reply(getTextBot(), user, "NS_SET_BADLEVEL");
276                     return;
277                 }
278             }
279             sprintf(nameBuf, "%d", caccess);
280             args = nameBuf;
281         }
282         if(valid & NS_VALID_OPTIONS) {
283             int options = atoi((char *) channel_settings[setting].parameter);
284             int coption = atoi(args);
285             if(coption < 0 || coption >= options) {
286                 reply(getTextBot(), user, "NS_SET_INVALID_OPTION", coption);
287                 int i;
288                 int nameBufPos = 0;
289                 if(valid & NS_HAS_OPT) {
290                     for(i = 0; i < options; i++) {
291                         sprintf(nameBuf, "NS_SET_OPTION_%s_%d", channel_settings[setting].setting, i);
292                         reply(getTextBot(), user, "\002%d\002 - %s", i, get_language_string(user, nameBuf));
293                     }
294                 } else {
295                     for(i = 0; i < options; i++) {
296                         nameBufPos += sprintf(nameBuf + nameBufPos, "\002%d\002, ", i);
297                     }
298                     if(nameBufPos) {
299                         nameBuf[nameBufPos-2] = '\0';
300                         reply(getTextBot(), user, nameBuf);
301                     }
302                 }
303                 return;
304             }
305         }
306         if(valid & NS_VALID_NUMERIC) {
307             sprintf(nameBuf, "%d", atoi(args));
308             args = nameBuf;
309         }
310         if(valid & NS_VALID_BOOLEAN) {
311             if(!strcmp(args, "0") || !stricmp(args, "off") || !stricmp(args, get_language_string(user, "NS_SET_OFF"))) {
312                 args = "0";
313             } else if(!strcmp(args, "1") || !stricmp(args, "on") || !stricmp(args, get_language_string(user, "NS_SET_ON"))) {
314                 args = "1";
315             } else {
316                 reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", args);
317                 return;
318             }
319         }
320         //valid - set it
321         value = args;
322         printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, escape_string(value), chan->channel_id);
323         logEvent(event);
324     }
325     if(channel_settings[setting].valid & NS_HAS_OPT) {
326         sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[setting].setting, value);
327         char *tmp = get_language_string(user, nameBuf);
328         if(tmp)
329             reply(getTextBot(), user, "\002%s\002 %s - %s", channel_settings[setting].setting, value, tmp);
330         else
331             reply(getTextBot(), user, "\002%s\002 %s", channel_settings[setting].setting, value);
332     } else
333         reply(getTextBot(), user, "\002%s\002 %s", channel_settings[setting].setting, value);
334     if(channel_settings[setting].valid & NS_HAS_HELP) {
335          sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[setting].setting);
336          reply(getTextBot(), user, "  %s", get_language_string(user, nameBuf));
337     }
338 }
339
340 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
341     char *trigger;
342     //get current trigger
343     MYSQL_RES *res;
344     MYSQL_ROW row;
345     printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid);
346     res = mysql_use();
347     row = mysql_fetch_row(res);
348     trigger = (row[0] ? row[0] : row[1]);
349     if(argument) {
350         int uaccess = getChannelAccess(user, chan);
351         if(uaccess < 500) {
352             if(isGodMode(user)) {
353                 event->flags |= CMDFLAG_OPLOG;
354             } else {
355                 reply(getTextBot(), user, "NS_SET_TRIGGER_OWNER", chan->name);
356                 return NULL;
357             }
358         }
359         if(strlen(argument) > 15)
360             argument[15] = '\0';
361         printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `chanid` = '%d' AND `botid` = '%d'", escape_string(argument), chan->channel_id, client->clientid);
362         trigger = argument;
363         changeChannelTrigger(client->botid, chan, trigger);
364         logEvent(event);
365     }
366     if(setting) {
367         reply(getTextBot(), user, "\002%s\002 %s", setting, trigger);
368     }
369     return trigger;
370 }
371
372 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
373     char *value;
374     char valueBuf[MAXLEN];
375     //get current value
376     MYSQL_RES *res;
377     MYSQL_ROW row;
378     printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
379     res = mysql_use();
380     row = mysql_fetch_row(res);
381     if(row[0] == NULL) {
382         printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
383         res = mysql_use();
384         row = mysql_fetch_row(res);
385     }
386     value = row[0];
387     if(argument) {
388         //change the channel setting
389         struct ModeNode *modenode = createModeNode(NULL);
390         parseModeString(modenode, argument);
391         getFullModeString(modenode, valueBuf);
392         value = valueBuf;
393         printf_mysql_query("UPDATE `channels` SET `channel_modes` = '%s' WHERE `channel_id` = '%d'", escape_string(value), chan->channel_id);
394         //TODO: set modelock
395         freeModeNode(modenode);
396     }
397     if(setting) {
398         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
399     }
400     return value;
401 }
402
403 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
404     char *value;
405     char tmp[64];
406     //get current value
407     MYSQL_RES *res;
408     MYSQL_ROW row;
409     printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
410     res = mysql_use();
411     row = mysql_fetch_row(res);
412     if(row[0] == NULL) {
413         printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_name` = 'defaults'");
414         res = mysql_use();
415         row = mysql_fetch_row(res);
416     }
417     value = row[0];
418     if(argument) {
419         //change the channel setting
420         sprintf(tmp, "%d", atoi(argument));
421         argument = tmp;
422         printf_mysql_query("UPDATE `channels` SET `channel_dynlimit` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
423         if(strcmp(argument, "0"))
424             putsock(client, "MODE %s +l %d", chan->name, (chan->usercount + atoi(argument)));
425         else if(isModeSet(chan->modes, 'l'))
426             putsock(client, "MODE %s -l", chan->name);
427         value = argument;
428         logEvent(event);
429     }
430     if(setting) {
431         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
432     }
433     return value;
434 }
435
436 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
437     char *value;
438     //get current value
439     MYSQL_RES *res;
440     MYSQL_ROW row;
441     printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
442     res = mysql_use();
443     row = mysql_fetch_row(res);
444     if(row[0] == NULL) {
445         printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_name` = 'defaults'");
446         res = mysql_use();
447         row = mysql_fetch_row(res);
448     }
449     value = row[0];
450     if(argument && isGodMode(user)) {
451         //change the channel setting
452         if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
453             argument = "0";
454         } else if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
455             argument = "1";
456         } else {
457             reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", argument);
458             return NULL;
459         }
460         printf_mysql_query("UPDATE `channels` SET `channel_nodelete` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
461         event->flags |= CMDFLAG_OPLOG;
462         value = argument;
463         logEvent(event);
464     }
465     if(setting) {
466         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
467     }
468     return value;
469 }
470
471 #undef MAX_QUERY_LEN