1 /* Fantasy functionality
2  *
3  * (C) 2003-2020 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  */
11 
12 #include "module.h"
13 
14 class CommandBSSetFantasy : public Command
15 {
16  public:
CommandBSSetFantasy(Module * creator,const Anope::string & sname="botserv/set/fantasy")17 	CommandBSSetFantasy(Module *creator, const Anope::string &sname = "botserv/set/fantasy") : Command(creator, sname, 2, 2)
18 	{
19 		this->SetDesc(_("Enable fantaisist commands"));
20 		this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
21 	}
22 
Execute(CommandSource & source,const std::vector<Anope::string> & params)23 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
24 	{
25 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
26 		const Anope::string &value = params[1];
27 
28 		if (ci == NULL)
29 		{
30 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
31 			return;
32 		}
33 
34 		if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
35 		{
36 			source.Reply(ACCESS_DENIED);
37 			return;
38 		}
39 
40 		if (Anope::ReadOnly)
41 		{
42 			source.Reply(_("Sorry, bot option setting is temporarily disabled."));
43 			return;
44 		}
45 
46 		if (value.equals_ci("ON"))
47 		{
48 			bool override = !source.AccessFor(ci).HasPriv("SET");
49 			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy";
50 
51 			ci->Extend<bool>("BS_FANTASY");
52 			source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str());
53 		}
54 		else if (value.equals_ci("OFF"))
55 		{
56 			bool override = !source.AccessFor(ci).HasPriv("SET");
57 			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable fantasy";
58 
59 			ci->Shrink<bool>("BS_FANTASY");
60 			source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str());
61 		}
62 		else
63 			this->OnSyntaxError(source, source.command);
64 	}
65 
OnHelp(CommandSource & source,const Anope::string &)66 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
67 	{
68 		this->SendSyntax(source);
69 		source.Reply(_(" \n"
70 				"Enables or disables \002fantasy\002 mode on a channel.\n"
71 				"When it is enabled, users will be able to use\n"
72 				"fantasy commands on a channel when prefixed\n"
73 				"with one of the following fantasy characters: \002%s\002\n"
74 				" \n"
75 				"Note that users wanting to use fantaisist\n"
76 				"commands MUST have enough access for both\n"
77 				"the FANTASIA and the command they are executing."),
78 				Config->GetModule(this->owner)->Get<const Anope::string>("fantasycharacter", "!").c_str());
79 		return true;
80 	}
81 };
82 
83 class Fantasy : public Module
84 {
85 	SerializableExtensibleItem<bool> fantasy;
86 
87 	CommandBSSetFantasy commandbssetfantasy;
88 
89  public:
Fantasy(const Anope::string & modname,const Anope::string & creator)90 	Fantasy(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
91 		fantasy(this, "BS_FANTASY"), commandbssetfantasy(this)
92 	{
93 	}
94 
OnPrivmsg(User * u,Channel * c,Anope::string & msg)95 	void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
96 	{
97 		if (!u || !c || !c->ci || !c->ci->bi || msg.empty() || msg[0] == '\1')
98 			return;
99 
100 		if (Config->GetClient("BotServ") && !fantasy.HasExt(c->ci))
101 			return;
102 
103 		std::vector<Anope::string> params;
104 		spacesepstream(msg).GetTokens(params);
105 
106 		if (params.empty())
107 			return;
108 
109 		Anope::string normalized_param0 = Anope::NormalizeBuffer(params[0]);
110 		Anope::string fantasy_chars = Config->GetModule(this)->Get<Anope::string>("fantasycharacter", "!");
111 
112 		if (!normalized_param0.find(c->ci->bi->nick))
113 		{
114 			params.erase(params.begin());
115 		}
116 		else if (!normalized_param0.find_first_of(fantasy_chars))
117 		{
118 			size_t sz = params[0].find_first_of(fantasy_chars);
119 			if (sz == Anope::string::npos)
120 				return; /* normalized_param0 is a subset of params[0] so this can't happen */
121 
122 			params[0].erase(0, sz + 1);
123 		}
124 		else
125 		{
126 			return;
127 		}
128 
129 		if (params.empty())
130 			return;
131 
132 		CommandInfo::map::const_iterator it = Config->Fantasy.end();
133 		unsigned count = 0;
134 		for (unsigned max = params.size(); it == Config->Fantasy.end() && max > 0; --max)
135 		{
136 			Anope::string full_command;
137 			for (unsigned i = 0; i < max; ++i)
138 				full_command += " " + params[i];
139 			full_command.erase(full_command.begin());
140 
141 			++count;
142 			it = Config->Fantasy.find(Anope::NormalizeBuffer(full_command));
143 		}
144 
145 		if (it == Config->Fantasy.end())
146 			return;
147 
148 		const CommandInfo &info = it->second;
149 		ServiceReference<Command> cmd("Command", info.name);
150 		if (!cmd)
151 		{
152 			Log(LOG_DEBUG) << "Fantasy command " << it->first << " exists for nonexistent service " << info.name << "!";
153 			return;
154 		}
155 
156 		for (unsigned i = 0, j = params.size() - (count - 1); i < j; ++i)
157 			params.erase(params.begin());
158 
159 		/* Some commands take the channel as a first parameter */
160 		if (info.prepend_channel)
161 			params.insert(params.begin(), c->name);
162 
163 		while (cmd->max_params > 0 && params.size() > cmd->max_params)
164 		{
165 			params[cmd->max_params - 1] += " " + params[cmd->max_params];
166 			params.erase(params.begin() + cmd->max_params);
167 		}
168 
169 		// Command requires registered users only
170 		if (!cmd->AllowUnregistered() && !u->Account())
171 			return;
172 
173 		if (params.size() < cmd->min_params)
174 			return;
175 
176 		CommandSource source(u->nick, u, u->Account(), u, c->ci->bi);
177 		source.c = c;
178 		source.command = it->first;
179 		source.permission = info.permission;
180 
181 		AccessGroup ag = c->ci->AccessFor(u);
182 		bool has_fantasia = ag.HasPriv("FANTASIA") || source.HasPriv("botserv/fantasy");
183 
184 		EventReturn MOD_RESULT;
185 		if (has_fantasia)
186 		{
187 			FOREACH_RESULT(OnBotFantasy, MOD_RESULT, (source, cmd, c->ci, params));
188 		}
189 		else
190 		{
191 			FOREACH_RESULT(OnBotNoFantasyAccess, MOD_RESULT, (source, cmd, c->ci, params));
192 		}
193 
194 		if (MOD_RESULT == EVENT_STOP || !has_fantasia)
195 			return;
196 
197 		if (MOD_RESULT != EVENT_ALLOW && !info.permission.empty() && !source.HasCommand(info.permission))
198 			return;
199 
200 		FOREACH_RESULT(OnPreCommand, MOD_RESULT, (source, cmd, params));
201 		if (MOD_RESULT == EVENT_STOP)
202 			return;
203 
204 		Reference<NickCore> nc_reference(u->Account());
205 		cmd->Execute(source, params);
206 		if (!nc_reference)
207 			source.nc = NULL;
208 		FOREACH_MOD(OnPostCommand, (source, cmd, params));
209 	}
210 
OnBotInfo(CommandSource & source,BotInfo * bi,ChannelInfo * ci,InfoFormatter & info)211 	void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info)
212 	{
213 		if (fantasy.HasExt(ci))
214 			info.AddOption(_("Fantasy"));
215 	}
216 };
217 
218 MODULE_INIT(Fantasy)
219