// Smart ISON // // Douglas Thrift // // $Id$ #include #include #include #include #include #include #include #include #include "foreach.hpp" #pragma GCC diagnostic ignored "-Wshadow" struct SmartISONTimer : public CTimer { SmartISONTimer(CModule *module) : CTimer(module, 15, 0, "Smart ISON", "Smart ISON") {} virtual ~SmartISONTimer() {} virtual void RunJob(); }; class SmartISON : public CModule { struct Compare { inline bool operator()(const CString &one, const CString &two) const { return one.AsLower() < two.AsLower(); } }; std::map nicks_; std::deque queue_; std::set request_; public: MODCONSTRUCTOR(SmartISON) {} virtual ~SmartISON() {} virtual bool OnLoad(const CString &args, CString &message) { return AddTimer(new SmartISONTimer(this)); } virtual void OnModCommand(const CString &command) { CString theCommand(command.Token(0)); if (theCommand.Equals("Help")) { CTable table; table.AddColumn("Command"); table.AddColumn("Description"); table.AddRow(); table.SetCell("Command", "Nicks"); table.SetCell("Description", "List nick statuses"); table.AddRow(); table.SetCell("Command", "Queue"); table.SetCell("Description", "List the nick status queue"); PutModule(table); } else if (theCommand.Equals("Nicks")) if (nicks_.empty()) PutModule("Nick status list empty"); else { CTable table; table.AddColumn("Nick"); table.AddColumn("Status"); typedef std::map NickMap; _foreach (const NickMap, nick, nicks_) { table.AddRow(); table.SetCell("Nick", nick->first); table.SetCell("Status", nick->second ? "Online" : "Offline"); } PutModule(table); } else if (theCommand.Equals("Queue")) if (queue_.empty()) PutModule("Nick queue empty"); else { CTable table; table.AddColumn("Nick"); _foreach (const std::deque, nick, queue_) { table.AddRow(); table.SetCell("Nick", *nick); } PutModule(table); } else PutModule("Unknown command [" + theCommand + "] try 'Help'"); } virtual void OnNick(const CNick &nick, const CString &newNick, const std::vector &chans) { const CString &oldNick(nick.GetNick()); nicks_[oldNick] = false; nicks_[newNick] = true; } virtual void OnQuit(const CNick &nick, const CString &message, const std::vector &chans) { const CString &theNick(nick.GetNick()); nicks_[theNick] = false; } virtual EModRet OnRaw(CString &line) { if (line.Token(1) == "303") { typedef std::set NickSet; _foreach (NickSet, nick, request_) nicks_[*nick] = false; std::vector nicks; line.Token(3, true).TrimLeft_n(":").Split(" ", nicks, false); _foreach (std::vector, nick, nicks) nicks_[*nick] = true; return HALT; } return CONTINUE; } virtual EModRet OnUserRaw(CString &line); private: template Type CheckChans() { Type nicks; _foreach (const std::vector, channel, GetUser()->GetChans()) { typedef std::map NickMap; _foreach (const NickMap, nick, (*channel)->GetNicks()) { nicks_[nick->first] = true; nicks.insert(nick->first); } } return nicks; } friend void SmartISONTimer::RunJob(); }; struct SmartISONVoid { inline void insert(const CString &value) {} }; void SmartISONTimer::RunJob() { SmartISON *module(static_cast(GetModule())); if (module->queue_.empty()) { typedef std::map NickMap; _foreach (NickMap, nick, module->nicks_) module->queue_.push_back(nick->first); } module->request_.clear(); std::set online(module->CheckChans >()); size_t size(6); while (module->queue_.size()) { CString nick(module->queue_.front()); if (!online.count(nick)) { if ((size += nick.size() + 1) > 512) break; module->request_.insert(nick); } module->queue_.pop_front(); } if (module->request_.size()) { CString request("ISON"); typedef std::set NickSet; _foreach (NickSet, nick, module->request_) request += " " + *nick; module->PutIRC(request); } } template <> void SmartISON::CheckChans() { CheckChans(); } CModule::EModRet SmartISON::OnUserRaw(CString &line) { if (line.Token(0).Equals("ISON")) { CheckChans(); std::vector nicks; line.Token(1, true).TrimLeft_n(":").Split(" ", nicks, false); if (nicks.empty()) { PutUser(":znc.in 461 " + GetUser()->GetIRCNick().GetNick() + " :Not enough parameters"); } else { CString response(":znc.in 303 " + GetUser()->GetIRCNick().GetNick() + " :"); _foreach (std::vector, nick, nicks) { CString prefix(GetUser()->GetStatusPrefix()); if (nick->Equals(prefix, false, prefix.size())) { CString modNick(nick->substr(prefix.size())); if (modNick.Equals("status") || GetUser()->GetModules().FindModule(modNick) || CZNC::Get().GetModules().FindModule(modNick)) goto online; } if (!nicks_.count(*nick)) queue_.push_front(*nick); if (nicks_[*nick]) online: response += *nick + " "; } PutUser(response); } return HALT; } return CONTINUE; } MODULEDEFS(SmartISON, "Smart ISON");