added automatic unregistration of unvisited channel
authorpk910 <philipp@zoelle1.de>
Sat, 18 Feb 2012 12:40:04 +0000 (13:40 +0100)
committerpk910 <philipp@zoelle1.de>
Sat, 18 Feb 2012 12:45:06 +0000 (13:45 +0100)
neonserv.example.conf
src/modules/DummyServ.mod/bot_DummyServ.c
src/modules/NeonHelp.mod/bot_NeonHelp.c
src/modules/NeonServ.mod/bot_NeonServ.c
src/modules/NeonServ.mod/cmd_neonserv.c
src/modules/NeonServ.mod/cmd_neonserv.h
src/modules/NeonServ.mod/cmd_neonserv_unvisited.c
src/modules/NeonSpam.mod/bot_NeonSpam.c
src/modules/botid.h [new file with mode: 0644]
src/modules/global.mod/cmd_global_unregister.c

index ba99ed7cdfe76df97dc6f798f116c1cac2488486..bc09736bab25a7c59961c0fdfe2f6cc5672f790e 100644 (file)
@@ -28,6 +28,8 @@
     "worker_threads" = 5; //threads
     "alertchan" = "";
     "have_halfop" = 0;
+    //unregister NeonSpam channels together with NeonServ?
+    "sync_neonspam_unreg" = 1;
     "CheckAuths" {
         "enabled" = 1;
         "start_time" = 3; //24h format
     "libNeonServ" {
         "enabled" = 1;
         "protected" = 0;
+        // How often to look for channels that have expired?
+        "chan_expire_freq" = "3d";
+        // How long is a channel unvisited (by masters or above) before it can be expired?
+        "chan_expire_delay" = "30d";
     };
     "libNeonSpam" {
         "enabled" = 1;
         "protected" = 0;
     };
     "libstats" {
+        /* statistics module
+          this module doesn't extend the bot functions at all! 
+          It will simply send a small UDP packet to neonserv.krypton-bouncer.de
+          this packet is used to keep the statistics on the website up to date.
+          
+          If you don't want your bot to be listed on this site just disable this plugin
+          (entries will be removed after 6h on the website)
+        */
         "enabled" = 1;
         "protected" = 0;
-        "hide_networkname" = 0;
-        "hide_botnick" = 0;
-        "hide_chancount" = 0;
-        "hide_usercount" = 0;
+        "hide_networkname" = 0; //hide network name
+        "hide_botnick" = 0; //hide bot nick, ident, host
+        "hide_chancount" = 0; //hide joined channel count
+        "hide_usercount" = 0; //hide number of users the bot can see
     };
 };
 
index 01170154bc97b41e798753b8e63d0b644aee5c71..5eeff5a6fcf02b4fc220612147d3e8622f5cab7d 100644 (file)
@@ -15,6 +15,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
  */
 #include "../module.h"
+#include "../botid.h"
 
 #include "bot_DummyServ.h"
 #include "../../modcmd.h"
@@ -37,7 +38,7 @@
 #include "../../EventLogger.h"
 #include "../../bots.h"
 
-#define BOTID 3
+#define BOTID DUMMYSERV_BOTID
 #define BOTALIAS "DummyServ"
 
 static void dummyserv_bot_ready(struct ClientSocket *client) {
index d02b30da9d4d461444e2621c37a5a59234e9b00e..db1321dffac7e491630eda0c7b6db349e096b600 100644 (file)
@@ -15,6 +15,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
  */
 #include "../module.h"
+#include "../botid.h"
 
 #include "bot_NeonHelp.h"
 #include "../../modcmd.h"
@@ -31,7 +32,7 @@
 #include "../../DBHelper.h"
 #include "../../WHOHandler.h"
 
-#define BOTID 4
+#define BOTID NEONHELP_BOTID
 #define BOTALIAS "NeonHelp"
 
 static const struct default_language_entry msgtab[] = {
index 6a14454d7361ef527fa96dfdccf9ab9875216e88..d1b9a5b9c93ab490b7d7a46caf48b53d3cf6e96e 100644 (file)
@@ -15,6 +15,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
  */
 #include "../module.h"
+#include "../botid.h"
  
 #include "bot_NeonServ.h"
 #include "../../modcmd.h"
@@ -37,7 +38,7 @@
 #include "cmd_neonserv.h"
 #include "../../ConfigParser.h"
 
-#define BOTID 1
+#define BOTID NEONSERV_BOTID
 #define BOTALIAS "NeonServ"
 
 static const struct default_language_entry msgtab[] = {
@@ -236,6 +237,7 @@ static const struct default_language_entry msgtab[] = {
     {"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_NODELETE", "$b%s$b is protected from being unregistered (nodelete)."}, /* {ARGS: "#TestChan"} */
     {"NS_UNREGISTER_DONE", "$b%s$b unregistered."}, /* {ARGS: "#TestChan"} */
     {"NS_RECOVER_DONE", "$b%s$b has been recovered."}, /* {ARGS: "#TestChan"} */
     {"NS_RESYNC_DONE", "Synchronized users in $b%s$b with the userlist."}, /* {ARGS: "#TestChan"} */
index 7bb3539a8e57f4fed674e1d02558a826a1ff14e0..6055d35caa30f7c266a9fb70362f67470be3e6aa 100644 (file)
@@ -98,4 +98,6 @@ void register_commands() {
     OPER_COMMAND("unvisited",    neonserv_cmd_unvisited, 0,     400,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
     OPER_COMMAND("noregister",   neonserv_cmd_noregister,0,     300,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG);
     #undef OPER_COMMAND
+    
+    neonserv_cmd_unvisited_init();
 }
index 13fff4df8ad6d8070a7107ff6cd2076e86f57bad..49afad593f65f0bcb4ebab2b5f364aa5dacfa00e 100644 (file)
@@ -42,6 +42,9 @@
 
 void register_commands();
 
+/* cmd_neonserv_unvisited.c */
+void neonserv_cmd_unvisited_init();
+
 CMD_BIND(neonserv_cmd_access);
 CMD_BIND(neonserv_cmd_addban);
 CMD_BIND(neonserv_cmd_addrank);
index 5e639b2836bce8cfb861cbb1330d22610b8c9ef9..455583c26a359d8a22f1d4c21686097597733dcc 100644 (file)
  */
 
 #include "cmd_neonserv.h"
+#include "../botid.h"
 
 struct neonserv_cmd_unvisited_cache {
     struct ClientSocket *client, *textclient;
     struct UserNode *user;
-    int duration;
+    int duration, unregister_matches;
     int who_count, matches;
 };
 
 static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup);
-static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration);
+static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches);
+static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches);
 static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache);
+static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel);
+static TIMEQ_CALLBACK(neonserv_check_unvisited_timer);
 
 CMD_BIND(neonserv_cmd_unvisited) {
     int duration = (argc ? strToTime(user, argv[0]) : 60*60*24*7*3);
     reply(getTextBot(), user, "NS_SEARCH_HEADER");
+    int unreg = 0;
+    if(argc > 1 && !stricmp(argv[1], "unregister")) 
+        unreg = 1;
+    neonserv_check_unvisited(client, getTextBot(), user, duration, unreg);
+}
+
+void neonserv_cmd_unvisited_init() {
+    if(!timeq_name_exists("neonserv_unvisited"))
+        timeq_add_name("neonserv_unvisited", 1200, module_id, neonserv_check_unvisited_timer, NULL);
+}
+
+static TIMEQ_CALLBACK(neonserv_check_unvisited_timer) {
+    char tmp[200];
+    char *modname = get_module_name(module_id);
+    sprintf(tmp, "modules/%s/chan_expire_freq", modname);
+    char *check_freq_str = get_string_field(tmp);
+    int check_freq;
+    if(!check_freq_str || (check_freq = strToTime(NULL, check_freq_str)) < (60*60)) {
+        timeq_add_name("neonserv_unvisited", 1800, module_id, neonserv_check_unvisited_timer, NULL);
+        return;
+    }
+    sprintf(tmp, "modules/%s/chan_expire_delay", modname);
+    char *check_expire_str = get_string_field(tmp);
+    int duration;
+    if(!check_expire_str || (duration = strToTime(NULL, check_expire_str)) < 60*60*24*7) return;
+    neonserv_check_unvisited(NULL, NULL, NULL, duration, 1);
+}
+
+static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches) {
     MYSQL_RES *res, *res2;
     MYSQL_ROW row, row2;
     struct ChanNode *channel;
@@ -40,12 +73,18 @@ CMD_BIND(neonserv_cmd_unvisited) {
         return;
     }
     cache->client = client;
-    cache->textclient = getTextBot();
+    cache->textclient = textclient;
     cache->user = user;
     cache->duration = duration;
+    cache->unregister_matches = unregister_matches;
     cache->who_count = 1; /* small fake to prevent the cache to be freed too early */
     cache->matches = 0;
-    printf_mysql_query("SELECT `channel_id`, `channel_name`, `channel_nodelete` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `botid` = '%d'", client->botid);
+    int botid;
+    if(client)
+        botid = client->botid;
+    else
+        botid = NEONSERV_BOTID;
+    printf_mysql_query("SELECT `channel_id`, `channel_name`, `channel_nodelete` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `botid` = '%d'", botid);
     res = mysql_use();
     while ((row = mysql_fetch_row(res)) != NULL) {
         if(!strcmp(row[2], "1")) continue;
@@ -59,11 +98,14 @@ CMD_BIND(neonserv_cmd_unvisited) {
             channel->channel_id = atoi(row[0]);
             get_userlist_with_invisible(channel, module_id, neonserv_cmd_unvisited_userlist_lookup, cache);
         } else {
-            reply(getTextBot(), user, "%s", row[1]);
+            if(textclient)
+                reply(textclient, user, "%s", row[1]);
+            if(unregister_matches)
+                neonserv_cmd_unvisited_unreg(client, row[1]);
             cache->matches++;
         }   
     }
-    cache->who_count--; //see fix on line 46
+    cache->who_count--; //see fix on line 78
     if(cache->who_count == 0) {
         neonserv_cmd_unvisited_async2(cache);
     }
@@ -71,7 +113,7 @@ CMD_BIND(neonserv_cmd_unvisited) {
 
 static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup) {
     struct neonserv_cmd_unvisited_cache *cache = data;
-    if(neonserv_cmd_unvisited_async1(cache->client, cache->textclient, cache->user, chan, cache->duration))
+    if(neonserv_cmd_unvisited_async1(cache->client, cache->textclient, cache->user, chan, cache->duration, cache->unregister_matches))
         cache->matches++;
     cache->who_count--;
     if(cache->who_count == 0) {
@@ -79,7 +121,7 @@ static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup) {
     }
 }
 
-static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration) {
+static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches) {
     struct ChanUser *chanuser;
     MYSQL_RES *res2;
     MYSQL_ROW row2;
@@ -97,12 +139,68 @@ static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct Cli
         }
     }
     if(!active) {
-        reply(textclient, user, "%s", chan->name);
+        if(textclient)
+            reply(textclient, user, "%s", chan->name);
+        if(unregister_matches)
+            neonserv_cmd_unvisited_unreg(client, chan->name);
     }
     return !active;
 }
 
 static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache) {
-    reply(cache->textclient, cache->user, "NS_TABLE_COUNT", cache->matches);
+    if(cache->textclient)
+        reply(cache->textclient, cache->user, "NS_TABLE_COUNT", cache->matches);
     free(cache);
 }
+
+static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel) {
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    int sync_neonspam_unreg = get_int_field("General/sync_neonspam_unreg");
+    int botid;
+    if(client)
+        botid = client->botid;
+    else
+        botid = NEONSERV_BOTID;
+    printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended`, `channels`.`channel_nodelete` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid);
+    res = mysql_use();
+    if ((row = mysql_fetch_row(res)) == NULL) {
+        return;
+    }
+    int clientid = atoi(row[0]);
+    struct ClientSocket *bot;
+    for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+        if(bot->clientid == clientid)
+            break;
+    }
+    if(!strcmp(row[3], "1")) return;
+    printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]);
+    if(bot && strcmp(row[2], "1")) {
+        putsock(bot, "PART %s :Channel unregistered.", channel);
+    }
+    if(botid == NEONSERV_BOTID && sync_neonspam_unreg) {
+        botid = NEONSPAM_BOTID;
+        printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid);
+        res = mysql_use();
+        if ((row = mysql_fetch_row(res)) == NULL) {
+            return;
+        }
+        clientid = atoi(row[0]);
+        for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+            if(bot->clientid == clientid)
+                break;
+        }
+        printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]);
+        if(bot && strcmp(row[2], "1")) {
+            putsock(bot, "PART %s :Channel unregistered.", channel);
+        }
+    }
+    char *alertchan = get_string_field("General.alertchan");
+    if(alertchan) {
+        struct ChanNode *alertchan_chan = getChanByName(alertchan);
+        struct ClientSocket *alertclient;
+        if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) {
+            putsock(alertclient, "PRIVMSG %s :Unregistered %s (unvisited)", alertchan_chan->name, channel);
+        }
+    }
+}
index 89aeb92be68236a07f30438667c517c56e4855ba..5ccfbc2d82138e4028cd5beb6b6da11b12737745 100644 (file)
@@ -15,6 +15,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
  */
 #include "../module.h"
+#include "../botid.h"
 
 #include "bot_NeonSpam.h"
 #include "../../modcmd.h"
@@ -38,7 +39,7 @@
 #include "../../bots.h"
 #include "cmd_neonspam.h"
 
-#define BOTID 2
+#define BOTID NEONSPAM_BOTID
 #define BOTALIAS "NeonSpam"
 
 static const struct default_language_entry msgtab[] = {
diff --git a/src/modules/botid.h b/src/modules/botid.h
new file mode 100644 (file)
index 0000000..0abb44a
--- /dev/null
@@ -0,0 +1,25 @@
+/* botid.h - NeonServ v5.4
+ * 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/>. 
+ */
+#ifndef _botid_h
+#define _botid_h
+
+#define NEONSERV_BOTID 1
+#define NEONSPAM_BOTID 2
+#define DUMMYSERV_BOTID 3
+#define NEONHELP_BOTID 4
+
+#endif
\ No newline at end of file
index e5525faca946391d0416bd6d52875f5a84eb6704..97ef4f9a8fc8ee60191b98a4aa62d33d90d4d830 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "cmd_global.h"
+#include "../botid.h"
 
 /*
 * argv[0] - channel
@@ -29,25 +30,30 @@ CMD_BIND(global_cmd_unregister) {
     else
         channel = (chan ? chan->name : "");
     if(!is_valid_chan(channel)) {
-        reply(getTextBot(), user, "NS_INVALID_CHANNEL_NAME", argv[0]);
+        reply(getTextBot(), user, "NS_INVALID_CHANNEL_NAME", channel);
         return;
     }
     int chanid;
-    printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel));
+    printf_mysql_query("SELECT `channel_id`, `channel_nodelete` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel));
     res = mysql_use();
     if ((row = mysql_fetch_row(res)) != NULL) {
         chanid = atoi(row[0]);
     } else {
-        reply(getTextBot(), user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick);
+        reply(getTextBot(), user, "NS_UNREGISTER_NOT_REGISTERED", channel, client->user->nick);
         return;
     }
+    if(client->botid == NEONSERV_BOTID && !strcmp(row[1], "1")) {
+        reply(getTextBot(), user, "NS_UNREGISTER_NODELETE", channel);
+        return;
+    }
+    int sync_neonspam_unreg = get_int_field("General/sync_neonspam_unreg");
     if(client->botid == 0)
         printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '0' AND `botid` = '%d'", chanid, client->clientid);
     else
         printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid);
     res = mysql_use();
     if ((row = mysql_fetch_row(res)) == NULL) {
-        reply(getTextBot(), user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick);
+        reply(getTextBot(), user, "NS_UNREGISTER_NOT_REGISTERED", channel, client->user->nick);
         return;
     }
     int botid = atoi(row[0]);
@@ -61,5 +67,21 @@ CMD_BIND(global_cmd_unregister) {
     if(bot && strcmp(row[2], "1")) {
         putsock(bot, "PART %s :Channel unregistered.", channel);
     }
+    if(client->botid == NEONSERV_BOTID && sync_neonspam_unreg) {
+        printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), NEONSPAM_BOTID);
+        res = mysql_use();
+        if ((row = mysql_fetch_row(res)) == NULL) {
+            return;
+        }
+        botid = atoi(row[0]);
+        for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
+            if(bot->clientid == botid)
+                break;
+        }
+        printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]);
+        if(bot && strcmp(row[2], "1")) {
+            putsock(bot, "PART %s :Channel unregistered.", channel);
+        }
+    }
     logEvent(event);
 }