2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
5 * Copyright (C) 2013, 2017-2018, 2020 Sadie Powell <sadie@witchery.services>
6 * Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
7 * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
8 * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
9 * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
10 * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
11 * Copyright (C) 2006-2007 Craig Edwards <brain@inspircd.org>
13 * This file is part of InspIRCd. InspIRCd is free software: you can
14 * redistribute it and/or modify it under the terms of the GNU General Public
15 * License as published by the Free Software Foundation, version 2.
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "modules/whois.h"
33 RPL_WHOISHELPOP = 310,
36 ERR_HELPNOTFOUND = 524,
42 typedef std::vector<std::string> HelpMessage;
46 // The body of the help topic.
47 const HelpMessage body;
49 // The title of the help topic.
50 const std::string title;
52 HelpTopic(const HelpMessage& Body, const std::string& Title)
59 typedef std::map<std::string, HelpTopic, irc::insensitive_swo> HelpMap;
61 class CommandHelpop : public Command
64 const std::string startkey;
70 CommandHelpop(Module* Creator)
71 : Command(Creator, "HELPOP", 0)
74 syntax = "<any-text>";
77 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
79 const std::string& topic = parameters.empty() ? startkey : parameters[0];
80 HelpMap::const_iterator titer = help.find(topic);
81 if (titer == help.end())
83 user->WriteNumeric(ERR_HELPNOTFOUND, topic, nohelp);
87 const HelpTopic& entry = titer->second;
88 user->WriteNumeric(RPL_HELPSTART, topic, entry.title);
89 for (HelpMessage::const_iterator liter = entry.body.begin(); liter != entry.body.end(); ++liter)
90 user->WriteNumeric(RPL_HELPTXT, topic, *liter);
91 user->WriteNumeric(RPL_ENDOFHELP, topic, "End of /HELPOP.");
98 , public Whois::EventListener
102 SimpleUserModeHandler ho;
106 : Whois::EventListener(this)
108 , ho(this, "helpop", 'h', true)
112 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
114 size_t longestkey = 0;
117 ConfigTagList tags = ServerInstance->Config->ConfTags("helpop");
118 if (tags.first == tags.second)
119 throw ModuleException("You have loaded the helpop module but not configured any help topics!");
121 for (ConfigIter i = tags.first; i != tags.second; ++i)
123 ConfigTag* tag = i->second;
125 // Attempt to read the help key.
126 const std::string key = tag->getString("key");
128 throw ModuleException(InspIRCd::Format("<helpop:key> is empty at %s", tag->getTagLocation().c_str()));
129 else if (irc::equals(key, "index"))
130 throw ModuleException(InspIRCd::Format("<helpop:key> is set to \"index\" which is reserved at %s", tag->getTagLocation().c_str()));
131 else if (key.length() > longestkey)
132 longestkey = key.length();
134 // Attempt to read the help value.
136 if (!tag->readString("value", value, true) || value.empty())
137 throw ModuleException(InspIRCd::Format("<helpop:value> is empty at %s", tag->getTagLocation().c_str()));
139 // Parse the help body. Empty lines are replaced with a single
140 // space because some clients are unable to show blank lines.
142 irc::sepstream linestream(value, '\n', true);
143 for (std::string line; linestream.GetToken(line); )
144 helpmsg.push_back(line.empty() ? " " : line);
146 // Read the help title and store the topic.
147 const std::string title = tag->getString("title", InspIRCd::Format("*** Help for %s", key.c_str()), 1);
148 if (!newhelp.insert(std::make_pair(key, HelpTopic(helpmsg, title))).second)
150 throw ModuleException(InspIRCd::Format("<helpop> tag with duplicate key '%s' at %s",
151 key.c_str(), tag->getTagLocation().c_str()));
155 // The number of items we can fit on a page.
156 HelpMessage indexmsg;
157 size_t maxcolumns = 80 / (longestkey + 2);
158 for (HelpMap::iterator iter = newhelp.begin(); iter != newhelp.end(); )
160 std::string indexline;
161 for (size_t column = 0; column != maxcolumns; )
163 if (iter == newhelp.end())
166 indexline.append(iter->first);
167 if (++column != maxcolumns)
168 indexline.append(longestkey - iter->first.length() + 2, ' ');
171 indexmsg.push_back(indexline);
173 newhelp.insert(std::make_pair("index", HelpTopic(indexmsg, "List of help topics")));
174 cmd.help.swap(newhelp);
176 ConfigTag* tag = ServerInstance->Config->ConfValue("helpmsg");
177 cmd.nohelp = tag->getString("nohelp", "There is no help for the topic you searched for. Please try again.", 1);
180 void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
182 if (whois.GetTarget()->IsModeSet(ho))
183 whois.SendLine(RPL_WHOISHELPOP, "is available for help.");
186 Version GetVersion() CXX11_OVERRIDE
188 return Version("Provides help to users via the HELPOP command", VF_VENDOR);
192 MODULE_INIT(ModuleHelpop)