added full half-op support
[NeonServV5.git] / src / bot_NeonServ.c
index 71f5942d20fd5376fbf9a5db4f8e5604b5b9859a..00b87c2c6ed1da3acec1e1ca947435b18797e0ef 100644 (file)
@@ -1,3 +1,19 @@
+/* bot_NeonServ.c - NeonServ v5.3
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
 
 #include "bot_NeonServ.h"
 #include "modcmd.h"
 #include "DBHelper.h"
 #include "tools.h"
 #include "timeq.h"
-#include "version.h"
 #include "EventLogger.h"
 #include "cmd_neonserv.h"
+#include "ConfigParser.h"
 
 #define BOTID 1
+#define BOTALIAS "NeonServ"
 
 static const struct default_language_entry msgtab[] = {
     {"NS_USER_UNKNOWN", "User with nick $b%s$b does not exist."}, /* {ARGS: "TestUser"} */
@@ -41,6 +58,7 @@ static const struct default_language_entry msgtab[] = {
     {"NS_NO_ACCESS", "You lack sufficient access to use this command."},
     {"NS_USER_PROTECTED", "Sorry, $b%s$b is protected."}, /* {ARGS: "TestUser"} */
     {"NS_SERVICE_IMMUNE", "$b%s$b may not be kicked, killed, banned, or deopped."}, /* {ARGS: "TestUser"} */
+       {"NS_YOU_PROTECTED", "You may not kick or ban yourself."},
     {"NS_TABLE_NONE", "   None"},
     {"NS_TABLE_COUNT", "Found $b%d$b matches."}, /* {ARGS: 5} */
     {"NS_BAN_ALREADY_ADDED", "$b%s$b is already banned in %s."}, /* {ARGS: "*!*@moeeep.*", "#TestChan"} */
@@ -73,8 +91,10 @@ static const struct default_language_entry msgtab[] = {
     {"NS_SUSPEND_NOT", "$b%s$b is not suspended." }, /* {ARGS: "TestUser"} */
     {"NS_SUSPEND_DONE", "$b%s$b's access to $b%s$b has been suspended." }, /* {ARGS: "TestUser", "#TestChan"} */
     {"NS_SUSPEND_RESTORED", "$b%s$b's access to $b%s$b has been restored." }, /* {ARGS: "TestUser", "#TestChan"} */
+    {"NS_DELME_OWNER", "You cannot delete your owner access in $b%s$b."}, /* {ARGS: "#TestChan"} */
     {"NS_DELME_KEY", "To really remove yourself, you must use 'deleteme %s'."}, /* {ARGS: "abc123"} */
     {"NS_DELME_DONE", "Your $b%d$b access has been deleted from $b%s$b."}, /* {ARGS: 123, "#TestChan"} */
+    {"NS_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $bmyaccess$b with no arguments)."},
     {"NS_MYACCESS_HEADER", "Showing all channel entries for account $b%s$b:"}, /* {ARGS: "TestAuth"} */
     {"NS_MYACCESS_HEADER_MATCH", "Showing all channel entries for account $b%s$b matching %s:"}, /* {ARGS: "TestAuth", "#Test*"} */
     {"NS_MYACCESS_HEADER_NAME", "Name"},
@@ -89,22 +109,29 @@ static const struct default_language_entry msgtab[] = {
     {"NS_MDELUSER_DONE", "Deleted $b%d$b account(s) matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list."}, /* {ARGS: 10, "Test*", 1, 200, "#TestChan"} */
     {"NS_TRIM_DURATION_TOO_SHORT", "You must include a minimum inactivity duration of at least %d seconds to trim."},
     {"NS_TRIM_DONE", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s."}, /* {ARGS: 10, 1, 100, "#TestChan", "10 days"} */
+    {"NS_TRIM_BAN_DONE", "Trimmed $b%d bans$b from the %s ban list who were banned for at least %s."}, /* {ARGS: 5, "#TestChan", "1 day"} */
     {"NS_GIVEOWNER_SELF", "You cannot give ownership to your own account."},
     {"NS_GIVEOWNER_TIMEOUT", "You must wait %s before you can give ownership of $b%s$b to someone else."}, /* {ARGS: "5 hours", "#TestChan"} */
     {"NS_GIVEOWNER_CONFIRM", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'."}, /* {ARGS: "TestUser", "abc123"} */
     {"NS_GIVEOWNER_DONE", "Ownership of $b%s$b has been transferred to account $b%s$b."}, /* {ARGS: "#TestChan", "TestUser"} */
     {"NS_OP_FAIL", "$b%s$b could not op some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
     {"NS_OP_DONE", "Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */
+    {"NS_HALFOP_FAIL", "$b%s$b could not halfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
+    {"NS_HALFOP_DONE", "Half-Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */
     {"NS_VOICE_FAIL", "$b%s$b could not voice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
     {"NS_VOICE_DONE", "Voiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */
     {"NS_DEOP_FAIL", "$b%s$b could not deop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
     {"NS_DEOP_DONE", "Deopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */
+    {"NS_DEHALFOP_FAIL", "$b%s$b could not dehalfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
+    {"NS_DEHALFOP_DONE", "Dehalfopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */
     {"NS_DEVOICE_FAIL", "$b%s$b could not devoice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
     {"NS_DEVOICE_DONE", "Devoiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */
     {"NS_OPALL_SECURITY", "$bWARNING$b: Opping all users on a channel is very insecure! If you still want do op all users on %s use: '$bopall FORCE$b [nick mask]'"},
     {"NS_OPALL_DONE", "Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
+    {"NS_HALFOPALL_DONE", "Half-Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
     {"NS_VOICEALL_DONE", "Voiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
     {"NS_DEOPALL_DONE", "Deopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
+    {"NS_DEHALFOPALL_DONE", "Dehalfopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
     {"NS_DEVOICEALL_DONE", "Devoiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */
     {"NS_KICK_DONE", "Kicked $b%d$b users from %s"}, /* {ARGS: 20, "#TestChan"} */
     {"NS_KICK_FAIL", "$b%s$b could not kick some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
@@ -113,6 +140,7 @@ static const struct default_language_entry msgtab[] = {
     {"NS_BAN_DONE", "$b%d$b masks added to the %s ban list. (matching %d users)"}, /* {ARGS: 5, "#TestChan", 15} */
     {"NS_BAN_FAIL", "$b%s$b could not ban some of the nicks you provided."}, /* {ARGS: "NeonServ"} */
     {"NS_LAME_MASK", "$b%s$b is a little too general. Try making it more specific."}, /* {ARGS: "*!*@*"} */
+    {"NS_LAME_MASK_WARNING", "$k4WARNING$k: $b%s$b is very general. (matches %d users)"},
     {"NS_SET_HEADER", "Channel Settings for %s:"}, /* {ARGS: "#TestChan"} */
     {"NS_SET_ON", "on"},
     {"NS_SET_OFF", "off"},
@@ -204,6 +232,7 @@ static const struct default_language_entry msgtab[] = {
     {"NS_REGISTER_FULL", "the bot can not join more channels."},
     {"NS_REGISTER_DISCONNECTED", "%s has been registered with a Bot, that is currently NOT connected. The Bot should join the channel, when it reconnects to the IRC-Network."}, /* {ARGS: "#TestChan"} */
     {"NS_REGISTER_DONE", "$b%s$b is now registered to $b%s$b."}, /* {ARGS: "#TestChan", "TestUser"} */
+    {"NS_REGISTER_DONE_NOAUTH", "$b%s$b is now registered."}, /* {ARGS: "#TestChan"} */
     {"NS_UNREGISTER_NOT_REGISTERED", "$b%s$b is not registered with %s."}, /* {ARGS: "#TestChan", "NeonServ"} */
     {"NS_UNREGISTER_DONE", "$b%s$b unregistered."}, /* {ARGS: "#TestChan"} */
     {"NS_RECOVER_DONE", "$b%s$b has been recovered."}, /* {ARGS: "#TestChan"} */
@@ -222,6 +251,7 @@ static const struct default_language_entry msgtab[] = {
     {"NS_PEEK_TOPIC", "Topic:       %s"}, /* {ARGS: "TOPIC"} */
     {"NS_PEEK_MODES", "Modes:       %s"}, /* {ARGS: "+xyz"} */
     {"NS_PEEK_USERS", "Total Users: %d (%d ops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 20, 4, 6, 8, 2} */
+    {"NS_PEEK_USERS_HALFOP", "Total Users: %d (%d ops, %d halfops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 25, 5, 4, 6, 8, 2} */
     {"NS_PEEK_OPS", "Ops:"},
     {"NS_USET_GLOBAL", "$b--- Global ---$b"},
     {"NS_USET_CHANNEL", "$b--- User options (channel) ---$b"},
@@ -265,15 +295,90 @@ static const struct default_language_entry msgtab[] = {
     {"NS_BOTWAR_REPORTED", "A supporter has been informed to help you preventing botwars in the future."},
     {"NS_BOTWAR_ALERT", "$b$k4BOTWAR ALERT:$k$b Botwar in $b%s$b detected. (opponent: $b%s$b) Please join and help them preventing Botwars."}, /* {ARGS: "#TestChan", "OtherBot"} */
     {"NS_INVITE_FAIL", "$b%s$b is not registered with %s or suspended."}, /* {ARGS: "#TestChan", "NeonServ"} */
-    {"NS_SETACCESS_DONE", "$b%s$b has now %d global access."},
+    {"NS_SETACCESS_DONE", "$b%s$b has now %d global access."}, /* {ARGS: "TestUser", 1000} */
+    {"NS_ADDRANK_EXISTS", "Another support ranking called '$b%s$b' already exists."},  /* {ARGS: "Supporter"} */
+    {"NS_ADDRANK_DONE", "Support ranking '$b%s$b' created."},  /* {ARGS: "Supporter"} */
+    {"NS_DELRANK_NOT_FOUND", "There is no support ranking called '$b%s$b'."},  /* {ARGS: "Supporter"} */
+    {"NS_DELRANK_DELETED", "Support ranking called '$b%s$b' removed."},  /* {ARGS: "Supporter"} */
+    {"NS_LISTRANK_ID", "Id"},
+    {"NS_LISTRANK_NAME", "Name"},
+    {"NS_LISTRANK_ASSIGNED", "Assigned to"},
+    {"NS_LISTRANK_UNRANKED", "There are also %d unranked users with global access."},  /* {ARGS: 10} */
+    {"NS_SETRANK_NOT_FOUND", "There is no support ranking with ID '$b%s$b'."},  /* {ARGS: 42} */
+    {"NS_SETRANK_HEAD", "Support ranking settings for Id %s:"},  /* {ARGS: 42} */
+    {"NS_SETRANK_UNKNOWN_SETTING", "$b%s$b is an unknown support rank setting."},  /* {ARGS: "moep"} */
+    {"NS_SETRANK_ORDER_INVALID", "%d is an invalid numeric value. (valid: 1-99)"},  /* {ARGS: 100} */
+    {"NS_ASSIGNRANK_DONE", "$b%s$b is now ranked as '$b%s$b'."},  /* {ARGS: "TestUser", "Supporter"} */
+    {"NS_INFO_HEADER", "$b%s$b Information:"}, /* {ARGS: "#TestChan"} */
+    {"NS_INFO_DEFAULTTOPIC", "Default Topic:"},
+    {"NS_INFO_MODELOCK", "Mode Lock:"},
+    {"NS_INFO_RECORD", "Record Visitors:"},
+    {"NS_INFO_OWNER", "Owner:"},
+    {"NS_INFO_USERS", "Total User Count:"},
+    {"NS_INFO_BANS", "Ban Count:"},
+    {"NS_INFO_VISITED", "Visited:"},
+    {"NS_INFO_REGISTERED", "Registered:"},
+    {"NS_INFO_REGISTRAR", "Registered by:"},
+    {"NS_INFO_OWNERLOG", "Ownership transfer history for $b%s$b:"}, /* {ARGS: "#TestChan"} */
+    {"NS_INFO_OWNERCHANGE", " from %s to %s on %s"},
+    {"NS_RENAME_DONE", "Renamed $b%s$b to $b%s$b."}, /* {ARGS: "TestUser", "TestUser2"} */
+    {"NS_RENAME_FAIL", "Failed renaming $b%s$b."}, /* {ARGS: "TestUser"} */
+    {"NS_FUN_DISABLED", "Fun commands are disabled in %s."}, /* {ARGS: "#TestChan"} */
+    {"NS_UNBIND_REQUIRED", "%1$s is a required function and there is no other command bound to %1$s. Bind another command to %1$s first."}, /* {ARGS: "bind"} */
+    {"NS_COMMANDS_NAME", "Name"},
+    {"NS_COMMANDS_ACCESS", "Access"},
+    {"NS_COMMANDS_GACCESS", "GodAccess"},
+    {"NS_COMMANDS_TRIGGERED", "Triggered"},
+    {"NS_COMMANDS_FUNCTION", "Function"},
+    {"NS_DNR_SET", "$b%s$b is do-not-register (by $b%s$b): %s" }, /* {ARGS: "#TestChan", "TestUser", "because of it is like it is"} */
+    {"NS_DNR_SET_EXPIRES", "$b%s$b is do-not-register (by $b%s$b; expires %s): %s" }, /* {ARGS: "#TestChan", "TestUser", "1 day", "because of it is like it is"} */
+    {"NS_DNR_SET_ANONYM", "$b%s$b is do-not-register. Please contact the support to get more information."}, /* {ARGS: "TestUser"} */
+    {"NS_NOREGISTER_INVALID_DURATION", "$b%s$b is not a valid duration."}, /* {ARGS: "möp"} */
+    {"NS_NOREGISTER_REGISTERED", "$b%s$b is currently registered and can't be added to the do-not-register list."}, /* {ARGS: "#TestChan"} */
+    {"NS_NOREGISTER_DONE", "added $b%s$b to the do-not-register list."}, /* {ARGS: "#TestChan"} */
+    {"NS_NOREGISTER_HEAD", "The following do-not-registers were found:"},
+    {"NS_DNR_TARGET", "Target"},
+    {"NS_DNR_USER", "Issuer"},
+    {"NS_DNR_EXPIRES", "Expires"},
+    {"NS_DNR_REASON", "Reason"},
+    {"NS_STAFF_LOGGEDIN", "Logged in as"},
+    {"NS_BOTS_ID", "Id"},
+    {"NS_BOTS_NICK", "Nick"},
+    {"NS_BOTS_SERVER", "Server:Port(:Pass)"},
+    {"NS_BOTS_CLASS", "Bot Class"},
+    {"NS_BOTS_FLAGS", "Flags"},
+    {"NS_BOTS_CHANNELS", "Channels"},
+    {"NS_BOTS_TRIGGER", "Trigger"},
+    {"NS_NICKLIST_NICK", "Nick"},
+    {"NS_NICKLIST_STATE", "State"},
+    {"NS_NICKLIST_ACCESS", "Access"},
+    {"NS_NICKLIST_SYNC", "use `nicklist sync` to fix all red and orange entrys in the list above (add opped users with %d and voiced with %d access)"},
+    {"NS_SETBOT_UNKNOWN", "`%d` is an unknown botid."}, /* {ARGS: 50} */
+    {"NS_SETBOT_HEADER", "$bSettings for botid `%d`:$b"}, /* {ARGS: 50} */
+    {"NS_SETBOT_SETTING", "$b%s$b is an unknown bot setting."}, /* {ARGS: "strangeSetting"} */
+    {"NS_SETBOT_NICK_INVALID", "`%s` is an invalid botnick."}, /* {ARGS: "-SuperMagicBananaBotWithManyFunctions"} */
+    {"NS_SETBOT_NEED_RESTART", "You need to reconnect the bot to apply this setting."},
+    {"NS_SETBOT_PORT_INVALID", "`%s` is an invalid port number."}, /* {ARGS: "-1"} */
+    {"NS_SETBOT_INVALID_CLASS", "`%s` is an invalid botclass."}, /* {ARGS: "MistColaLeer"} */
+    {"NS_SETBOT_MAXCHAN_INVALID", "`%s` is an invalid maxchan value."}, /* {ARGS: "-1"} */
+    {"NS_SETBOT_PRIORITY_INVALID", "`%s` is an invalid priority value."}, /* {ARGS: "-1"} */
+    {"NS_SETBOT_TRIGGER_INVALID", "`%s` is an invalid bot trigger."}, /* {ARGS: "tooLongTrigger"} */
+    {"NS_SETBOT_TRIGGER_NOTE", "Please note: This Setting will only affect new channels."},
+    {"NS_ADDBOT_EXISTING", "A bot with nick %s does already exist."}, /* {ARGS: "NeonServ"} */
+    {"NS_ADDBOT_DONE", "Added %s with BotID $b%d$b."}, /* {ARGS: "NeonServ", 2} */
+    {"NS_DELBOT_NOT_FOUND", "Bot with BotID / nick $b%s$b not found."}, /* {ARGS: "NeonServ"} */
+    {"NS_DELBOT_DONE", "Bot deleted."},
+    {"NS_RECONNECT_DONE", "Reconnected bot."},
+    {"NS_MODCMD_SETTING", "$b%s$b is an unknown modcmd setting."}, /* {ARGS: "strangeSetting"} */
+    {"NS_MODCMD_HEADER", "$bSettings for command %s:$b"}, /* {ARGS: "access"} */
+    {"NS_MODCMD_OUTRANKED", "$b%s$b outranks you. (required access: %d)"}, /* {ARGS: "die", 1000} */
+    {"NS_MODCMD_STATIC_FLAG", "This Flag is added statically. It can't be modified manually."},
     {NULL, NULL}
 };
 
-/* TODO: 
-trim bans
+/* TODO:
 cmd_neonserv_open.c
-cmd_neonserv_info.c
-parse, check and set modelock
+set modelock
 cmd_neonserv_modcmd.c
 cmd_neonserv_allowregister.c
 cmd_neonserv_noregister.c
@@ -281,7 +386,6 @@ cmd_neonserv_expire.c
 cmd_neonserv_unvisited.c
 cmd_neonserv_merge.c
 cmd_neonserv_dnrsearch.c
-cmd_neonserv_rename.c
 cmd_neonserv_iplocate.c
 cmd_neonserv_calc.c
 */
@@ -289,31 +393,16 @@ cmd_neonserv_calc.c
 #include "event_neonserv_join.c"
 #include "event_neonserv_part.c"
 #include "event_neonserv_quit.c"
-//#include "event_neonserv_kick.c"
-//#include "event_neonserv_mode.c"
+#include "event_neonserv_kick.c"
+#include "event_neonserv_mode.c"
 #include "event_neonserv_ctcp.c"
 #include "event_neonserv_notice.c"
 #include "event_neonserv_invite.c"
 #include "event_neonserv_topic.c"
 
+
 struct ClientSocket *getBotForChannel(struct ChanNode *chan) {
-    struct ClientSocket *bot, *use_bot = NULL, *second_bot = NULL, *third_bot = NULL;
-    struct ChanUser *chanuser;
-    for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
-        if(bot->botid != BOTID) continue;
-        if((chanuser = getChanUser(bot->user, chan)) != NULL) {
-            if((chanuser->flags & CHANUSERFLAG_OPPED)) {
-                use_bot = bot;
-                if(bot->flags & SOCKET_FLAG_PREFERRED) break;
-            } else if(bot->flags & SOCKET_FLAG_PREFERRED)
-                second_bot = bot;
-            else
-                third_bot = bot;
-        }
-    }
-    if(!use_bot) use_bot = second_bot;
-    if(!use_bot) use_bot = third_bot;
-    return use_bot;
+    return getChannelBot(chan, BOTID);
 }
 
 static void neonserv_bot_ready(struct ClientSocket *client) {
@@ -334,156 +423,67 @@ static void neonserv_bot_ready(struct ClientSocket *client) {
     }
 }
 
-static void neonserv_trigger_callback(struct ChanNode *chan, char *trigger) {
-    strcpy(trigger, "+");
+static void neonserv_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) {
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    loadChannelSettings(chan);
+    if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) {
+        strcpy(trigger, "+");
+        return;
+    }
+    printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID);
+    res = mysql_use();
+    if(!(row = mysql_fetch_row(res))) {
+        strcpy(trigger, "+");
+        return;
+    }
+    if(row[0] && *row[0])
+        strcpy(trigger, row[0]);
+    else
+        strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "~"));
 }
 
 static void start_bots() {
-    struct UserNode *user;
     struct ClientSocket *client;
     MYSQL_RES *res, *res2;
     MYSQL_ROW row;
     
-    printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID);
+    printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID);
     res = mysql_use();
     
     while ((row = mysql_fetch_row(res)) != NULL) {
-        
-        user = addUser(row[0]);
-        strcpy(user->ident, row[1]);
-        strcpy(user->realname, row[2]);
-        user->flags |= USERFLAG_ISBOT;
-        client = create_socket(row[3], atoi(row[4]), row[5], user);
+        client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]);
         client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0);
+        client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0);
+        client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0);
         client->botid = BOTID;
         client->clientid = atoi(row[7]);
         connect_socket(client);
-        printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access` FROM `bot_binds` WHERE `botclass` = '%d'", client->botid);
-        res2 = mysql_use();
-        while ((row = mysql_fetch_row(res2)) != NULL) {
-            if(bind_cmd_to_command(BOTID, row[0], row[1])) {
-                if(row[2] && strcmp(row[2], "")) {
-                    bind_set_parameters(BOTID, row[0], row[2]);
-                }
-                if(row[3]) {
-                    bind_set_global_access(BOTID, row[0], atoi(row[3]));
-                }
-                if(row[4]) {
-                    bind_set_channel_access(BOTID, row[0], row[4]);
-                }
-            }
-        }
     }
     
-    //load all timed bans
-    printf_mysql_query("SELECT `ban_id`, `ban_timeout` FROM `bans` WHERE `ban_timeout` > 0");
-    res = mysql_use();
-    char nameBuf[20];
-    while ((row = mysql_fetch_row(res)) != NULL) {
-        if(atol(row[1]) - time(0) > 0) {
-            sprintf(nameBuf, "ban_%s", row[0]);
-            timeq_add_name(nameBuf, atol(row[1]) - time(0), channel_ban_timeout, strdup(row[0]));
-        } else {
-            //timed out
-            printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[0]);
-        }
-    }
-}
-
-TIMEQ_CALLBACK(channel_ban_timeout) {
-    char *str_banid = data;
-    MYSQL_RES *res;
-    MYSQL_ROW row;
-    printf_mysql_query("SELECT `ban_mask`, `channel_name` FROM `bans` LEFT JOIN `channels` ON `ban_channel` = `channel_id` WHERE `ban_id` = '%s'", str_banid);
-    res = mysql_use();
-    struct ChanNode *chan;
-    if((row = mysql_fetch_row(res)) != NULL && (chan = getChanByName(row[1])) != NULL) {
-        struct ClientSocket *use_bot = getBotForChannel(chan);
-        if(use_bot) {
-            putsock(use_bot, "MODE %s -b %s", chan->name, row[0]);
+    printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID);
+    res2 = mysql_use();
+    while ((row = mysql_fetch_row(res2)) != NULL) {
+        if(bind_cmd_to_command(BOTID, row[0], row[1])) {
+            if(row[2] && strcmp(row[2], "")) {
+                bind_set_parameters(BOTID, row[0], row[2]);
+            }
+            if(row[3]) {
+                bind_set_global_access(BOTID, row[0], atoi(row[3]));
+            }
+            if(row[4]) {
+                bind_set_channel_access(BOTID, row[0], row[4]);
+            }
+            if(strcmp(row[5], "0"))
+                bind_set_bind_flags(BOTID, row[0], atoi(row[5]));
         }
-        printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", str_banid);
     }
-    free(str_banid);
+    bind_unbound_required_functions(BOTID);
 }
 
 void init_NeonServ() {
     
-    #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(BOTID, NAME, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS)
-    //               NAME              FUNCTION        PARAMS     PRIVS                FLAGS
-    USER_COMMAND("adduser",      neonserv_cmd_adduser,   2, "#channel_canadd",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("deluser",      neonserv_cmd_deluser,   1, "#channel_candel",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("clvl",         neonserv_cmd_clvl,      2, "#channel_canclvl",     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("access",       neonserv_cmd_access,    0, NULL,                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_LOG);
-    USER_COMMAND("users",        neonserv_cmd_users,     0, NULL,                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
-    USER_COMMAND("suspend",      neonserv_cmd_suspend,   1, "#channel_cansuspend",  CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("unsuspend",    neonserv_cmd_unsuspend, 1, "#channel_cansuspend",  CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("delme",        neonserv_cmd_delme,     0,  "1",                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("myaccess",     neonserv_cmd_myaccess,  0, NULL,                   CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    USER_COMMAND("up",           neonserv_cmd_up,        0, "#channel_getop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("down",         neonserv_cmd_down,      0, NULL,                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_LOG);
-    USER_COMMAND("upall",        neonserv_cmd_upall,     0, NULL,                   CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("downall",      neonserv_cmd_downall,   0, NULL,                   CMDFLAG_LOG);
-    USER_COMMAND("mdeluser",     neonserv_cmd_mdeluser,  2, "#channel_candel",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("trim",         neonserv_cmd_trim,      2, NULL,                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("giveowner",    neonserv_cmd_giveowner, 1, "500",                  CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("op",           neonserv_cmd_op,        1, "#channel_canop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("deop",         neonserv_cmd_deop,      1, "#channel_canop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("voice",        neonserv_cmd_voice,     1, "#channel_canvoice",    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("devoice",      neonserv_cmd_devoice,   1, "#channel_canvoice",    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("opall",        neonserv_cmd_opall,     0, "#channel_canop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("deopall",      neonserv_cmd_deopall,   0, "#channel_canop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("voiceall",     neonserv_cmd_voiceall,  0, "#channel_canvoice",    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("devoiceall",   neonserv_cmd_devoiceall,0, "#channel_canvoice",    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("set",          neonserv_cmd_set,       0, "#channel_setters",     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("kick",         neonserv_cmd_kick,      1, "#channel_cankick",     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("kickban",      neonserv_cmd_kickban,   1, "#channel_cankick,#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("ban",          neonserv_cmd_ban,       1, "#channel_canban",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("wipeinfo",     neonserv_cmd_wipeinfo,  1, "#channel_wipeinfo",    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("addban",       neonserv_cmd_addban,    1, "#channel_staticban",   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("bans",         neonserv_cmd_bans,      0, "1",                    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    USER_COMMAND("delban",       neonserv_cmd_delban,    1, "#channel_staticban",   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("netinfo",      neonserv_cmd_netinfo,   0, NULL,                   0);
-    USER_COMMAND("topic",        neonserv_cmd_topic,     0, "#channel_changetopic", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("chanservsync", neonserv_cmd_chanservsync, 0,"500",                CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("resync",       neonserv_cmd_resync,    0, "#channel_canresync",   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("addtimeban",   neonserv_cmd_addtimeban,2, "#channel_staticban",   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("mode",         neonserv_cmd_mode,      1, "#channel_getop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("version",      neonserv_cmd_version,   0, NULL,                   0);
-    USER_COMMAND("peek",         neonserv_cmd_peek,      0, NULL,                   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
-    USER_COMMAND("uset",         neonserv_cmd_uset,      0, NULL,                   CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    USER_COMMAND("unban",        neonserv_cmd_unban,     1, "#channel_canban",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("unbanall",     neonserv_cmd_unbanall,  0, "#channel_canban",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("unbanme",      neonserv_cmd_unbanme,   0, "#channel_canban",      CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("invite",       neonserv_cmd_invite,    1, "#channel_canop",       CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("inviteme",     neonserv_cmd_inviteme,  0, "#channel_getinvite",   CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG);
-    USER_COMMAND("help",         neonserv_cmd_help,      0, NULL,                   0);
-    USER_COMMAND("events",       neonserv_cmd_events,    0, "1",                    CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    USER_COMMAND("command",      neonserv_cmd_command,   1, NULL,                   0);
-    #undef USER_COMMAND
-    
-    #define OPER_COMMAND(NAME,FUNCTION,PARAMCOUNT,GACCESS,FLAGS) register_command(BOTID, NAME, FUNCTION, PARAMCOUNT, NULL, GACCESS, FLAGS)
-    //            NAME            FUNCTION              PARAMS  ACCS  FLAGS
-    OPER_COMMAND("trace",        neonserv_cmd_trace,     1,     400,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    OPER_COMMAND("register",     neonserv_cmd_register,  2,     200,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("unregister",   neonserv_cmd_unregister,1,     200,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("recover",      neonserv_cmd_recover,   1,     200,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("say",          neonserv_cmd_say,       2,     600,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("emote",        neonserv_cmd_emote,     2,     600,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("notice",       neonserv_cmd_notice,    2,     600,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("raw",          neonserv_cmd_raw,       1,     800,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("god",          neonserv_cmd_god,       0,     1,    CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("reloadlang",   neonserv_cmd_reloadlang,1,     500,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("csuspend",     neonserv_cmd_csuspend,  1,     100,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("cunsuspend",   neonserv_cmd_cunsuspend,1,     100,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("move",         neonserv_cmd_move,      2,     300,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
-    OPER_COMMAND("bind",         neonserv_cmd_bind,      2,     900,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("unbind",       neonserv_cmd_unbind,    1,     900,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("oplog",        neonserv_cmd_oplog,     0,     1,    CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    OPER_COMMAND("search",       neonserv_cmd_search,    1,     400,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
-    OPER_COMMAND("setaccess",    neonserv_cmd_setaccess, 2,     1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
-    #undef OPER_COMMAND
-    
+    set_bot_alias(BOTID, BOTALIAS);
     start_bots();
     
     //register events
@@ -492,10 +492,12 @@ void init_NeonServ() {
     bind_part(neonserv_event_part);
     bind_quit(neonserv_event_quit);
     bind_chanctcp(neonserv_event_chanctcp);
-    bind_privctcp(neonserv_event_privctcp);
+    bind_privctcp(general_event_privctcp);
     bind_channotice(neonserv_event_channotice);
     bind_topic(neonserv_event_topic);
     bind_invite(neonserv_event_invite);
+    bind_mode(neonserv_event_mode);
+    bind_kick(neonserv_event_kick);
     
     set_trigger_callback(BOTID, neonserv_trigger_callback);
     
@@ -511,3 +513,4 @@ void free_NeonServ() {
 }
 
 #undef BOTID
+#undef BOTALIAS