Permalink
Browse files

Add a generic command handling to CModule

With this, modules can use AddCommand() to register commands. CModule will
dispatch all calls to OnModCommand() to the correct command handler.

If a module calls AddHelpCommand(), it will also generate help output.

Signed-off-by: Uli Schlachter <psychon@znc.in>
  • Loading branch information...
1 parent cb2e50a commit ebd7e53d7f4153be6862e6c4c078e1d9de5a5a66 @psychon psychon committed Feb 21, 2011
Showing with 178 additions and 1 deletion.
  1. +100 −1 Modules.cpp
  2. +78 −0 Modules.h
View
@@ -382,6 +382,68 @@ void CModule::ListSockets() {
PutModule(Table);
}
+bool CModule::AddCommand(const CModCommand& Command)
+{
+ if (Command.GetFunction() == NULL)
+ return false;
+ if (Command.GetCommand().find(' ') != CString::npos)
+ return false;
+ if (FindCommand(Command.GetCommand()) != NULL)
+ return false;
+
+ m_mCommands[Command.GetCommand()] = Command;
+ return true;
+}
+
+bool CModule::AddCommand(const CString& sCmd, CModCommand::ModCmdFunc func, const CString& sArgs, const CString& sDesc)
+{
+ CModCommand cmd(sCmd, func, sArgs, sDesc);
+ return AddCommand(cmd);
+}
+
+void CModule::AddHelpCommand()
+{
+ AddCommand("Help", &CModule::HandleHelpCommand, "", "Generate this output");
+}
+
+bool CModule::RemCommand(const CString& sCmd)
+{
+ return m_mCommands.erase(sCmd) > 0;
+}
+
+const CModCommand* CModule::FindCommand(const CString& sCmd) const
+{
+ map<CString, CModCommand>::const_iterator it;
+ for (it = m_mCommands.begin(); it != m_mCommands.end(); ++it) {
+ if (!it->first.Equals(sCmd))
+ continue;
+ return &it->second;
+ }
+ return NULL;
+}
+
+bool CModule::HandleCommand(const CString& sLine) {
+ const CString& sCmd = sLine.Token(0);
+ const CModCommand* pCmd = FindCommand(sCmd);
+
+ if (pCmd) {
+ pCmd->Call(this, sLine);
+ return true;
+ }
+
+ return false;
+}
+
+void CModule::HandleHelpCommand(const CString& sLine) {
+ CTable Table;
+ map<CString, CModCommand>::const_iterator it;
+
+ CModCommand::InitHelp(Table);
+ for (it = m_mCommands.begin(); it != m_mCommands.end(); ++it)
+ it->second.AddHelp(Table);
+ PutModule(Table);
+}
+
CString CModule::GetModNick() const { return ((m_pUser) ? m_pUser->GetStatusPrefix() : "*") + m_sModName; }
// Webmods
@@ -416,7 +478,7 @@ void CModule::OnMode(const CNick& OpNick, CChan& Channel, char uMode, const CStr
CModule::EModRet CModule::OnRaw(CString& sLine) { return CONTINUE; }
CModule::EModRet CModule::OnStatusCommand(CString& sCommand) { return CONTINUE; }
-void CModule::OnModCommand(const CString& sCommand) {}
+void CModule::OnModCommand(const CString& sCommand) { HandleCommand(sCommand); }
void CModule::OnModNotice(const CString& sMessage) {}
void CModule::OnModCTCP(const CString& sMessage) {}
@@ -1089,3 +1151,40 @@ ModHandle CModules::OpenModule(const CString& sModule, const CString& sModPath,
return p;
}
+
+CModCommand::CModCommand()
+ : m_sCmd(), m_pFunc(NULL), m_sArgs(), m_sDesc()
+{
+}
+
+CModCommand::CModCommand(const CString& sCmd, ModCmdFunc func, const CString& sArgs, const CString& sDesc)
+ : m_sCmd(sCmd), m_pFunc(func), m_sArgs(sArgs), m_sDesc(sDesc)
+{
+}
+
+CModCommand::CModCommand(const CModCommand& other)
+ : m_sCmd(other.m_sCmd), m_pFunc(other.m_pFunc), m_sArgs(other.m_sArgs), m_sDesc(other.m_sDesc)
+{
+}
+
+CModCommand& CModCommand::operator=(const CModCommand& other)
+{
+ m_sCmd = other.m_sCmd;
+ m_pFunc = other.m_pFunc;
+ m_sArgs = other.m_sArgs;
+ m_sDesc = other.m_sDesc;
+ return *this;
+}
+
+void CModCommand::InitHelp(CTable& Table) {
+ Table.AddColumn("Command");
+ Table.AddColumn("Arguments");
+ Table.AddColumn("Description");
+}
+
+void CModCommand::AddHelp(CTable& Table) const {
+ Table.AddRow();
+ Table.SetCell("Command", GetCommand());
+ Table.SetCell("Arguments", GetArgs());
+ Table.SetCell("Description", GetDescription());
+}
View
@@ -200,6 +200,58 @@ class CModInfo {
CString m_sDescription;
};
+/** A helper class for handling commands in modules. */
+class CModCommand {
+public:
+ /// Type for the callback function that handles the actual command.
+ typedef void (CModule::*ModCmdFunc)(const CString& sLine);
+
+ /// Default constructor, needed so that this can be saved in a std::map.
+ CModCommand();
+
+ /** Construct a new CModCommand.
+ * @param sCmd The name of the command.
+ * @param func The command's callback function.
+ * @param sArgs Help text describing the arguments to this command.
+ * @param sDesc Help text describing what this command does.
+ */
+ CModCommand(const CString& sCmd, ModCmdFunc func, const CString& sArgs, const CString& sDesc);
+
+ /** Copy constructor, needed so that this can be saved in a std::map.
+ * @param other Object to copy from.
+ */
+ CModCommand(const CModCommand& other);
+
+ /** Assignment operator, needed so that this can be saved in a std::map.
+ * @param other Object to copy from.
+ */
+ CModCommand& operator=(const CModCommand& other);
+
+ /** Initialize a CTable so that it can be used with AddHelp().
+ * @param Table The instance of CTable to initialize.
+ */
+ static void InitHelp(CTable& Table);
+
+ /** Add this command to the CTable instance.
+ * @param Table Instance of CTable to which this should be added.
+ * @warning The Table should be initialized via InitHelp().
+ */
+ void AddHelp(CTable& Table) const;
+
+ const CString& GetCommand() const { return m_sCmd; }
+ ModCmdFunc GetFunction() const { return m_pFunc; }
+ const CString& GetArgs() const { return m_sArgs; }
+ const CString& GetDescription() const { return m_sDesc; }
+
+ void Call(CModule *pMod, const CString& sLine) const { (pMod->*m_pFunc)(sLine); }
+
+private:
+ CString m_sCmd;
+ ModCmdFunc m_pFunc;
+ CString m_sArgs;
+ CString m_sDesc;
+};
+
/** The base class for your own ZNC modules.
*
* If you want to write a module for znc, you will have to implement a class
@@ -758,6 +810,31 @@ class CModule {
virtual void ListSockets();
// !Socket stuff
+ // Command stuff
+ /// Register the "Help" command.
+ void AddHelpCommand();
+ /// @return True if the command was successfully added.
+ bool AddCommand(const CModCommand& Command);
+ /// @return True if the command was successfully added.
+ bool AddCommand(const CString& sCmd, CModCommand::ModCmdFunc func, const CString& sArgs = "", const CString& sDesc = "");
+ /// @return True if the command was successfully removed.
+ bool RemCommand(const CString& sCmd);
+ /// @return The CModCommand instance or NULL if none was found.
+ const CModCommand* FindCommand(const CString& sCmd) const;
+ /** This function tries to dispatch the given command via the correct
+ * instance of CModCommand. Before this can be called, commands have to
+ * be added via AddCommand(). If no "help" command is added, this
+ * function will call HandleHelpCommand.
+ * @param sLine The command line to handle.
+ * @return True if something was done, else false.
+ */
+ bool HandleCommand(const CString& sLine);
+ /** Send a description of all registered commands via PutModule().
+ * @param sLine The help command that is being asked for.
+ */
+ void HandleHelpCommand(const CString& sLine = "");
+ // !Command stuff
+
bool LoadRegistry();
bool SaveRegistry() const;
bool SetNV(const CString & sName, const CString & sValue, bool bWriteToDisk = true);
@@ -814,6 +891,7 @@ class CModule {
private:
MCString m_mssRegistry; //!< way to save name/value pairs. Note there is no encryption involved in this
VWebSubPages m_vSubPages;
+ map<CString, CModCommand> m_mCommands;
};
class CModules : public vector<CModule*> {

0 comments on commit ebd7e53

Please sign in to comment.