// 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 EModRet OnCTCPReply(CNick &nick, CString &message) { nicks_[nick.GetNick()] = true; return CONTINUE; } virtual void OnKick(const CNick &nick, const CString &opNick, CChan &chan, const CString &message) { queue_.push_front(nick.GetNick()); } 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"); table.AddRow(); table.SetCell("Command", "Version"); table.SetCell("Description", "Display module version"); 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 if (theCommand.Equals("Version")) PutModule("$Id$"); else PutModule("Unknown command [" + theCommand + "] try 'Help'"); } virtual void OnModCTCP(const CString &message) { CString command(message.Token(0)); if (command.Equals("PING")) PutModNotice("\001PING " + message.Token(1, true) + "\001"); else if (command.Equals("Version")) PutModNotice("\001VERSION $Id$\001"); } 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 OnPart(const CNick &nick, CChan &chan) { queue_.push_front(nick.GetNick()); } virtual EModRet OnPrivAction(CNick &nick, CString &message) { nicks_[nick.GetNick()] = true; return CONTINUE; } virtual EModRet OnPrivCTCP(CNick &nick, CString &message) { nicks_[nick.GetNick()] = true; return CONTINUE; } virtual EModRet OnPrivMsg(CNick &nick, CString &message) { nicks_[nick.GetNick()] = true; return CONTINUE; } virtual EModRet OnPrivNotice(CNick &nick, CString &message) { nicks_[nick.GetNick()] = true; return CONTINUE; } 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, chan, GetUser()->GetChans()) { typedef std::map NickMap; _foreach (const NickMap, nick, (*chan)->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())); 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) && !module->request_.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); } if (module->queue_.empty()) { typedef std::map NickMap; _foreach (NickMap, nick, module->nicks_) module->queue_.push_back(nick->first); } } 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())); CModule *module(NULL); if (modNick.Equals("status") || (module = GetUser()->GetModules().FindModule(modNick)) || (module = CZNC::Get().GetModules().FindModule(modNick))) { response += (module ? module->GetModNick() : prefix + "status") + " "; continue; } } std::map::iterator theNick(nicks_.find(*nick)); if (theNick == nicks_.end()) { nicks_[*nick] = false; queue_.push_front(*nick); } else if (theNick->second) response += theNick->first + " "; } PutUser(response); } return HALT; } return CONTINUE; } MODULEDEFS(SmartISON, "Smart ISON");