From e192ffc0fed753aa5a5c5081674511f90c5a953b Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 18 Feb 2012 13:40:04 +0100 Subject: [PATCH] added automatic unregistration of unvisited channel --- neonserv.example.conf | 22 +++- src/modules/DummyServ.mod/bot_DummyServ.c | 3 +- src/modules/NeonHelp.mod/bot_NeonHelp.c | 3 +- src/modules/NeonServ.mod/bot_NeonServ.c | 4 +- src/modules/NeonServ.mod/cmd_neonserv.c | 2 + src/modules/NeonServ.mod/cmd_neonserv.h | 3 + .../NeonServ.mod/cmd_neonserv_unvisited.c | 118 ++++++++++++++++-- src/modules/NeonSpam.mod/bot_NeonSpam.c | 3 +- src/modules/botid.h | 25 ++++ .../global.mod/cmd_global_unregister.c | 30 ++++- 10 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 src/modules/botid.h diff --git a/neonserv.example.conf b/neonserv.example.conf index ba99ed7..bc09736 100644 --- a/neonserv.example.conf +++ b/neonserv.example.conf @@ -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 @@ -72,18 +74,30 @@ "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 }; }; diff --git a/src/modules/DummyServ.mod/bot_DummyServ.c b/src/modules/DummyServ.mod/bot_DummyServ.c index 0117015..5eeff5a 100644 --- a/src/modules/DummyServ.mod/bot_DummyServ.c +++ b/src/modules/DummyServ.mod/bot_DummyServ.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #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) { diff --git a/src/modules/NeonHelp.mod/bot_NeonHelp.c b/src/modules/NeonHelp.mod/bot_NeonHelp.c index d02b30d..db1321d 100644 --- a/src/modules/NeonHelp.mod/bot_NeonHelp.c +++ b/src/modules/NeonHelp.mod/bot_NeonHelp.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #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[] = { diff --git a/src/modules/NeonServ.mod/bot_NeonServ.c b/src/modules/NeonServ.mod/bot_NeonServ.c index 6a14454..d1b9a5b 100644 --- a/src/modules/NeonServ.mod/bot_NeonServ.c +++ b/src/modules/NeonServ.mod/bot_NeonServ.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #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"} */ diff --git a/src/modules/NeonServ.mod/cmd_neonserv.c b/src/modules/NeonServ.mod/cmd_neonserv.c index 7bb3539..6055d35 100644 --- a/src/modules/NeonServ.mod/cmd_neonserv.c +++ b/src/modules/NeonServ.mod/cmd_neonserv.c @@ -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(); } diff --git a/src/modules/NeonServ.mod/cmd_neonserv.h b/src/modules/NeonServ.mod/cmd_neonserv.h index 13fff4d..49afad5 100644 --- a/src/modules/NeonServ.mod/cmd_neonserv.h +++ b/src/modules/NeonServ.mod/cmd_neonserv.h @@ -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); diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c b/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c index 5e639b2..455583c 100644 --- a/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c +++ b/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c @@ -16,21 +16,54 @@ */ #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); + } + } +} diff --git a/src/modules/NeonSpam.mod/bot_NeonSpam.c b/src/modules/NeonSpam.mod/bot_NeonSpam.c index 89aeb92..5ccfbc2 100644 --- a/src/modules/NeonSpam.mod/bot_NeonSpam.c +++ b/src/modules/NeonSpam.mod/bot_NeonSpam.c @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #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 index 0000000..0abb44a --- /dev/null +++ b/src/modules/botid.h @@ -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 . + */ +#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 diff --git a/src/modules/global.mod/cmd_global_unregister.c b/src/modules/global.mod/cmd_global_unregister.c index e5525fa..97ef4f9 100644 --- a/src/modules/global.mod/cmd_global_unregister.c +++ b/src/modules/global.mod/cmd_global_unregister.c @@ -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); } -- 2.20.1