diff --git a/Modules.cpp b/Modules.cpp index 593ff0d3..65538e7d 100644 --- a/Modules.cpp +++ b/Modules.cpp @@ -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::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::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()); +} diff --git a/Modules.h b/Modules.h index 70d4fe3c..ec2abc73 100644 --- a/Modules.h +++ b/Modules.h @@ -200,6 +200,58 @@ protected: 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 @@ public: 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 @@ protected: private: MCString m_mssRegistry; //!< way to save name/value pairs. Note there is no encryption involved in this VWebSubPages m_vSubPages; + map m_mCommands; }; class CModules : public vector {