diff options
-rw-r--r-- | data/botserv.example.conf | 52 | ||||
-rw-r--r-- | data/chanserv.example.conf | 2 | ||||
-rw-r--r-- | include/config.h | 4 | ||||
-rw-r--r-- | include/modules.h | 14 | ||||
-rw-r--r-- | modules/pseudoclients/botserv.cpp | 106 | ||||
-rw-r--r-- | src/command.cpp | 1 | ||||
-rw-r--r-- | src/config.cpp | 31 |
7 files changed, 162 insertions, 48 deletions
diff --git a/data/botserv.example.conf b/data/botserv.example.conf index e4605f1ab..fd3d2dc1e 100644 --- a/data/botserv.example.conf +++ b/data/botserv.example.conf @@ -308,3 +308,55 @@ command { service = "BotServ"; name = "SET NOBOT"; command = "botserv/set/nobot" module { name = "bs_set_private" } command { service = "BotServ"; name = "SET PRIVATE"; command = "botserv/set/private"; } +/* Fantasy commands + * + * Fantasy commands can be executed in channels that have a BotServ bot by prefixing the + * command with one of the fantasy characters configured in botserv:fantasycharacter. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +fantasy { name = "ACCESS"; command = "chanserv/access"; } +fantasy { name = "AKICK"; command = "chanserv/akick"; } +fantasy { name = "AOP"; command = "chanserv/aop"; } +fantasy { name = "APPENDTOPIC"; command = "chanserv/appendtopic"; } +fantasy { name = "BAN"; command = "chanserv/ban"; } +fantasy { name = "CLEARUSERS"; command = "chanserv/clearusers"; } +fantasy { name = "CLONE"; command = "chanserv/clone"; } +fantasy { name = "DEHALFOP"; command = "chanserv/dehalfop"; } +fantasy { name = "DEOP"; command = "chanserv/deop"; } +fantasy { name = "DEOWNER"; command = "chanserv/deowner"; } +fantasy { name = "DEPROTECT"; command = "chanserv/deprotect"; } +fantasy { name = "DEVOICE"; command = "chanserv/devoice"; } +fantasy { name = "DOWN"; command = "chanserv/down"; } +fantasy { name = "ENFORCE"; command = "chanserv/enforce"; } +fantasy { name = "ENTRYMSG"; command = "chanserv/entrymsg"; } +fantasy { name = "FLAGS"; command = "chanserv/flags"; } +fantasy { name = "HALFOP"; command = "chanserv/halfop"; } +fantasy { name = "HELP"; command = "chanserv/help"; } +fantasy { name = "HOP"; command = "chanserv/hop"; } +fantasy { name = "INFO"; command = "chanserv/info"; } +fantasy { name = "INVITE"; command = "chanserv/invite"; } +fantasy { name = "K"; command = "chanserv/kick"; } +fantasy { name = "KB"; command = "chanserv/ban"; } +fantasy { name = "KICK"; command = "chanserv/kick"; } +fantasy { name = "LEVELS"; command = "chanserv/levels"; } +fantasy { name = "LIST"; command = "chanserv/list"; } +fantasy { name = "LOG"; command = "chanserv/log"; } +fantasy { name = "MODE"; command = "chanserv/mode"; } +fantasy { name = "OP"; command = "chanserv/op"; } +fantasy { name = "OWNER"; command = "chanserv/owner"; } +fantasy { name = "PROTECT"; command = "chanserv/protect"; } +fantasy { name = "QOP"; command = "chanserv/qop"; } +fantasy { name = "SEEN"; command = "chanserv/seen"; } +fantasy { name = "SOP"; command = "chanserv/sop"; } +fantasy { name = "SUSPEND"; command = "chanserv/suspend"; } +fantasy { name = "SYNC"; command = "chanserv/sync"; } +fantasy { name = "TOPIC"; command = "chanserv/topic"; } +fantasy { name = "UNBAN"; command = "chanserv/unban"; } +fantasy { name = "UNBAN"; command = "chanserv/unban"; } +fantasy { name = "UNSUSPEND"; command = "chanserv/unsuspend"; } +fantasy { name = "UP"; command = "chanserv/up"; } +fantasy { name = "VOICE"; command = "chanserv/voice"; } +fantasy { name = "VOP"; command = "chanserv/vop"; } + diff --git a/data/chanserv.example.conf b/data/chanserv.example.conf index f507c5cab..e6e21dba3 100644 --- a/data/chanserv.example.conf +++ b/data/chanserv.example.conf @@ -882,7 +882,6 @@ command { service = "ChanServ"; name = "APPENDTOPIC"; command = "chanserv/append */ module { name = "cs_ban" } command { service = "ChanServ"; name = "BAN"; command = "chanserv/ban"; } -command { service = "ChanServ"; name = "KB"; command = "chanserv/ban"; } /* * cs_tban @@ -999,7 +998,6 @@ command { service = "ChanServ"; name = "INVITE"; command = "chanserv/invite"; } */ module { name = "cs_kick" } command { service = "ChanServ"; name = "KICK"; command = "chanserv/kick"; } -command { service = "ChanServ"; name = "K"; command = "chanserv/kick"; } /* * cs_list diff --git a/include/config.h b/include/config.h index de68d3105..f4f164b37 100644 --- a/include/config.h +++ b/include/config.h @@ -688,6 +688,10 @@ class CoreExport ServerConfig std::list<OperType *> MyOperTypes; /* List of pairs of opers and their opertype from the config */ std::vector<Oper *> Opers; + + /* Map of fantasy commands */ + typedef Anope::insensitive_map<CommandInfo> fantasy_map; + fantasy_map Fantasy; }; /** This class can be used on its own to represent an exception, or derived to represent a module-specific exception. diff --git a/include/modules.h b/include/modules.h index 14fdff066..627bbab59 100644 --- a/include/modules.h +++ b/include/modules.h @@ -348,20 +348,22 @@ class CoreExport Module : public Extensible virtual EventReturn OnDecrypt(const Anope::string &hashm, const Anope::string &src, Anope::string &dest) { return EVENT_CONTINUE; } /** Called on fantasy command - * @param command The command - * @param u The user using the command + * @param source The source of the command + * @param c The command * @param ci The channel it's being used in * @param params The params + * @return EVENT_STOP to halt processing and not run the command, EVENT_ALLOW to allow the command to be executed */ - virtual void OnBotFantasy(const Anope::string &command, User *u, ChannelInfo *ci, const Anope::string ¶ms) { } + virtual EventReturn OnBotFantasy(CommandSource &source, Command *c, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) { return EVENT_CONTINUE; } /** Called on fantasy command without access - * @param command The command - * @param u The user using the command + * @param source The source of the command + * @param c The command * @param ci The channel it's being used in * @param params The params + * @return EVENT_STOP to halt processing and not run the command, EVENT_ALLOW to allow the command to be executed */ - virtual void OnBotNoFantasyAccess(const Anope::string &command, User *u, ChannelInfo *ci, const Anope::string ¶ms) { } + virtual EventReturn OnBotNoFantasyAccess(CommandSource &source, Command *c, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) { return EVENT_CONTINUE; } /** Called after a bot joins a channel * @param c The channel diff --git a/modules/pseudoclients/botserv.cpp b/modules/pseudoclients/botserv.cpp index d792f74da..ec4bac11d 100644 --- a/modules/pseudoclients/botserv.cpp +++ b/modules/pseudoclients/botserv.cpp @@ -15,9 +15,8 @@ class BotServCore : public Module { - Channel *fantasy_channel; public: - BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), fantasy_channel(NULL) + BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) { this->SetAuthor("Anope"); @@ -25,7 +24,7 @@ class BotServCore : public Module if (BotServ == NULL) throw ModuleException("No bot named " + Config->BotServ); - Implementation i[] = { I_OnPrivmsg, I_OnPreCommand, I_OnJoinChannel, I_OnLeaveChannel, + Implementation i[] = { I_OnPrivmsg, I_OnJoinChannel, I_OnLeaveChannel, I_OnPreHelp, I_OnPostHelp, I_OnChannelModeSet }; ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); @@ -45,7 +44,6 @@ class BotServCore : public Module ircdproto->SendCTCP(c->ci->bi, u->nick, "%s", ctcp.c_str()); } - Anope::string realbuf = msg; bool was_action = false; @@ -66,58 +64,88 @@ class BotServCore : public Module if (!Config->BSFantasyCharacter.empty()) realbuf.erase(realbuf.begin()); - size_t space = realbuf.find(' '); - Anope::string command, rest; - if (space == Anope::string::npos) - command = realbuf; - else + std::vector<Anope::string> params = BuildStringVector(realbuf); + + ServerConfig::fantasy_map::const_iterator it = Config->Fantasy.end(); + unsigned count = 0; + for (unsigned max = params.size(); it == Config->Fantasy.end() && max > 0; --max) { - command = realbuf.substr(0, space); - rest = realbuf.substr(space + 1); + Anope::string full_command; + for (unsigned i = 0; i < max; ++i) + full_command += " " + params[i]; + full_command.erase(full_command.begin()); + + ++count; + it = Config->Fantasy.find(full_command); } - if (c->ci->AccessFor(u).HasPriv("FANTASIA")) + if (it == Config->Fantasy.end()) + return; + + const CommandInfo &info = it->second; + service_reference<Command> cmd("Command", info.name); + if (!cmd) { - FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(command, u, c->ci, rest)); + Log(LOG_DEBUG) << "Fantasy command " << it->first << " exists for nonexistant service " << info.name << "!"; + return; } - else + + for (unsigned i = 0, j = params.size() - (count - 1); i < j; ++i) + params.erase(params.begin()); + + while (cmd->MaxParams > 0 && params.size() > cmd->MaxParams) { - FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(command, u, c->ci, rest)); + params[cmd->MaxParams - 1] += " " + params[cmd->MaxParams]; + params.erase(params.begin() + cmd->MaxParams); } - BotInfo *bi = findbot(Config->ChanServ); - if (bi == NULL) - bi = findbot(Config->BotServ); - if (bi == NULL || !bi->commands.count(command)) + /* All ChanServ commands take the channel as a first parameter */ + if (cmd->name.find("chanserv/") == 0) + params.insert(params.begin(), c->ci->name); + + // Command requires registered users only + if (!cmd->HasFlag(CFLAG_ALLOW_UNREGISTERED) && !u->Account()) return; + if (params.size() < cmd->MinParams) + return; + + CommandSource source(u->nick, u, u->Account(), u); + source.c = c; + source.owner = c->ci->bi; + source.service = c->ci->bi; + source.command = it->first; + source.permission = info.permission; + + EventReturn MOD_RESULT; if (c->ci->AccessFor(u).HasPriv("FANTASIA")) { - this->fantasy_channel = c; - bi->OnMessage(u, realbuf); - this->fantasy_channel = NULL; + FOREACH_RESULT(I_OnBotFantasy, OnBotFantasy(source, cmd, c->ci, params)); + } + else + { + FOREACH_RESULT(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(source, cmd, c->ci, params)); } - } - } - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) anope_override - { - if (this->fantasy_channel != NULL) - { - if (!command->HasFlag(CFLAG_STRIP_CHANNEL)) + if (MOD_RESULT == EVENT_STOP || !c->ci->AccessFor(u).HasPriv("FANTASIA")) + return; + + if (MOD_RESULT != EVENT_ALLOW && !info.permission.empty() && !source.HasCommand(info.permission)) + return; + + FOREACH_RESULT(I_OnPreCommand, OnPreCommand(source, cmd, params)); + if (MOD_RESULT == EVENT_STOP) + return; + + dynamic_reference<User> user_reference(u); + dynamic_reference<NickCore> nc_reference(u->Account()); + cmd->Execute(source, params); + + if (user_reference && nc_reference) { - params.insert(params.begin(), this->fantasy_channel->name); - if (command->MaxParams && params.size() > command->MaxParams) - { - params[params.size() - 2] += " " + params[params.size() - 1]; - params.erase(params.end() - 1); - } + FOREACH_MOD(I_OnPostCommand, OnPostCommand(source, cmd, params)); } - source.c = this->fantasy_channel; - source.service = this->fantasy_channel->ci->WhoSends(); } - - return EVENT_CONTINUE; } void OnJoinChannel(User *user, Channel *c) anope_override diff --git a/src/command.cpp b/src/command.cpp index 9f1b26fa4..976f89de7 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -239,7 +239,6 @@ void RunCommand(CommandSource &source, const Anope::string &message) if (MOD_RESULT == EVENT_STOP) return; - if (params.size() < c->MinParams) { c->OnSyntaxError(source, !params.empty() ? params[params.size() - 1] : ""); diff --git a/src/config.cpp b/src/config.cpp index e0cd21b68..b90c8598a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -890,6 +890,8 @@ static bool DoneCommands(ServerConfig *config, const Anope::string &) return true; } +/*************************************************************************/ + static bool InitPrivileges(ServerConfig *config, const Anope::string &) { PrivilegeManager::ClearPrivileges(); @@ -1040,6 +1042,30 @@ static bool DoneServices(ServerConfig *config, const Anope::string &) /*************************************************************************/ +static bool InitFantasy(ServerConfig *config, const Anope::string &) +{ + config->Fantasy.clear(); + return true; +} + +static bool DoFantasy(ServerConfig *config, const Anope::string &, const Anope::string *, ValueList &values, int *) +{ + Anope::string name = values[0].GetValue(); + Anope::string service = values[1].GetValue(); + Anope::string permission = values[2].GetValue(); + + config->Fantasy[name].name = service; + config->Fantasy[name].permission = permission; + return true; +} + +static bool DoneFantasy(ServerConfig *config, const Anope::string &) +{ + return true; +} + +/*************************************************************************/ + ConfigurationFile::ConfigurationFile(const Anope::string &n, bool e) : name(n), executable(e), fp(NULL) { } @@ -1359,6 +1385,11 @@ ConfigItems::ConfigItems(ServerConfig *conf) {"", "", "", ""}, {DT_STRING, DT_STRING, DT_INTEGER, DT_STRING}, InitPrivileges, DoPrivileges, DonePrivileges}, + {"fantasy", + {"name", "command", "permission", ""}, + {"", "", "", ""}, + {DT_STRING, DT_STRING, DT_STRING}, + InitFantasy, DoFantasy, DoneFantasy}, {"", {""}, {""}, |