1 |
// Smart ISON |
2 |
// |
3 |
// Douglas Thrift |
4 |
// |
5 |
// $Id$ |
6 |
|
7 |
#include <main.h> |
8 |
#include <Modules.h> |
9 |
#include <Chan.h> |
10 |
#include <User.h> |
11 |
#include <znc.h> |
12 |
|
13 |
#include <deque> |
14 |
#include <map> |
15 |
#include <set> |
16 |
|
17 |
#include "foreach.hpp" |
18 |
|
19 |
#pragma GCC diagnostic ignored "-Wshadow" |
20 |
|
21 |
struct SmartISONTimer : public CTimer |
22 |
{ |
23 |
SmartISONTimer(CModule *module) : CTimer(module, 15, 0, "Smart ISON", "Smart ISON") {} |
24 |
virtual ~SmartISONTimer() {} |
25 |
|
26 |
virtual void RunJob(); |
27 |
}; |
28 |
|
29 |
class SmartISON : public CModule |
30 |
{ |
31 |
struct Compare |
32 |
{ |
33 |
inline bool operator()(const CString &one, const CString &two) const |
34 |
{ |
35 |
return one.AsLower() < two.AsLower(); |
36 |
} |
37 |
}; |
38 |
|
39 |
std::map<CString, bool, Compare> nicks_; |
40 |
std::deque<CString> queue_; |
41 |
std::set<CString, Compare> request_; |
42 |
|
43 |
public: |
44 |
MODCONSTRUCTOR(SmartISON) {} |
45 |
virtual ~SmartISON() {} |
46 |
|
47 |
virtual EModRet OnCTCPReply(CNick &nick, CString &message) |
48 |
{ |
49 |
nicks_[nick.GetNick()] = true; |
50 |
|
51 |
return CONTINUE; |
52 |
} |
53 |
|
54 |
virtual void OnKick(const CNick &nick, const CString &opNick, CChan &chan, const CString &message) |
55 |
{ |
56 |
queue_.push_front(nick.GetNick()); |
57 |
} |
58 |
|
59 |
virtual bool OnLoad(const CString &args, CString &message) |
60 |
{ |
61 |
return AddTimer(new SmartISONTimer(this)); |
62 |
} |
63 |
|
64 |
virtual void OnModCommand(const CString &command) |
65 |
{ |
66 |
CString theCommand(command.Token(0)); |
67 |
|
68 |
if (theCommand.Equals("Help")) |
69 |
{ |
70 |
CTable table; |
71 |
|
72 |
table.AddColumn("Command"); |
73 |
table.AddColumn("Description"); |
74 |
table.AddRow(); |
75 |
table.SetCell("Command", "Nicks"); |
76 |
table.SetCell("Description", "List nick statuses"); |
77 |
table.AddRow(); |
78 |
table.SetCell("Command", "Queue"); |
79 |
table.SetCell("Description", "List the nick status queue"); |
80 |
table.AddRow(); |
81 |
table.SetCell("Command", "Version"); |
82 |
table.SetCell("Description", "Display module version"); |
83 |
|
84 |
PutModule(table); |
85 |
} |
86 |
else if (theCommand.Equals("Nicks")) |
87 |
if (nicks_.empty()) |
88 |
PutModule("Nick status list empty"); |
89 |
else |
90 |
{ |
91 |
CTable table; |
92 |
|
93 |
table.AddColumn("Nick"); |
94 |
table.AddColumn("Status"); |
95 |
|
96 |
typedef std::map<CString, bool, Compare> NickMap; |
97 |
|
98 |
_foreach (const NickMap, nick, nicks_) |
99 |
{ |
100 |
table.AddRow(); |
101 |
table.SetCell("Nick", nick->first); |
102 |
table.SetCell("Status", nick->second ? "Online" : "Offline"); |
103 |
} |
104 |
|
105 |
PutModule(table); |
106 |
} |
107 |
else if (theCommand.Equals("Queue")) |
108 |
if (queue_.empty()) |
109 |
PutModule("Nick queue empty"); |
110 |
else |
111 |
{ |
112 |
CTable table; |
113 |
|
114 |
table.AddColumn("Nick"); |
115 |
|
116 |
_foreach (const std::deque<CString>, nick, queue_) |
117 |
{ |
118 |
table.AddRow(); |
119 |
table.SetCell("Nick", *nick); |
120 |
} |
121 |
|
122 |
PutModule(table); |
123 |
} |
124 |
else if (theCommand.Equals("Version")) |
125 |
PutModule("$Id$"); |
126 |
else |
127 |
PutModule("Unknown command [" + theCommand + "] try 'Help'"); |
128 |
} |
129 |
|
130 |
virtual void OnModCTCP(const CString &message) |
131 |
{ |
132 |
CString command(message.Token(0)); |
133 |
|
134 |
if (command.Equals("PING")) |
135 |
PutModNotice("\001PING " + message.Token(1, true) + "\001"); |
136 |
else if (command.Equals("Version")) |
137 |
PutModNotice("\001VERSION $Id$\001"); |
138 |
} |
139 |
|
140 |
virtual void OnNick(const CNick &nick, const CString &newNick, const std::vector<CChan *> &chans) |
141 |
{ |
142 |
const CString &oldNick(nick.GetNick()); |
143 |
|
144 |
nicks_[oldNick] = false; |
145 |
nicks_[newNick] = true; |
146 |
} |
147 |
|
148 |
virtual void OnPart(const CNick &nick, CChan &chan) |
149 |
{ |
150 |
queue_.push_front(nick.GetNick()); |
151 |
} |
152 |
|
153 |
virtual EModRet OnPrivAction(CNick &nick, CString &message) |
154 |
{ |
155 |
nicks_[nick.GetNick()] = true; |
156 |
|
157 |
return CONTINUE; |
158 |
} |
159 |
|
160 |
virtual EModRet OnPrivCTCP(CNick &nick, CString &message) |
161 |
{ |
162 |
nicks_[nick.GetNick()] = true; |
163 |
|
164 |
return CONTINUE; |
165 |
} |
166 |
|
167 |
virtual EModRet OnPrivMsg(CNick &nick, CString &message) |
168 |
{ |
169 |
nicks_[nick.GetNick()] = true; |
170 |
|
171 |
return CONTINUE; |
172 |
} |
173 |
|
174 |
virtual EModRet OnPrivNotice(CNick &nick, CString &message) |
175 |
{ |
176 |
nicks_[nick.GetNick()] = true; |
177 |
|
178 |
return CONTINUE; |
179 |
} |
180 |
|
181 |
virtual void OnQuit(const CNick &nick, const CString &message, const std::vector<CChan *> &chans) |
182 |
{ |
183 |
const CString &theNick(nick.GetNick()); |
184 |
|
185 |
nicks_[theNick] = false; |
186 |
} |
187 |
|
188 |
virtual EModRet OnRaw(CString &line) |
189 |
{ |
190 |
if (line.Token(1) == "303") |
191 |
{ |
192 |
typedef std::set<CString, Compare> NickSet; |
193 |
|
194 |
_foreach (NickSet, nick, request_) |
195 |
nicks_[*nick] = false; |
196 |
|
197 |
std::vector<CString> nicks; |
198 |
|
199 |
line.Token(3, true).TrimLeft_n(":").Split(" ", nicks, false); |
200 |
|
201 |
_foreach (std::vector<CString>, nick, nicks) |
202 |
nicks_[*nick] = true; |
203 |
|
204 |
return HALT; |
205 |
} |
206 |
|
207 |
return CONTINUE; |
208 |
} |
209 |
|
210 |
virtual EModRet OnUserRaw(CString &line); |
211 |
|
212 |
private: |
213 |
template <typename Type> |
214 |
Type CheckChans() |
215 |
{ |
216 |
Type nicks; |
217 |
|
218 |
_foreach (const std::vector<CChan *>, chan, GetUser()->GetChans()) |
219 |
{ |
220 |
typedef std::map<CString, CNick *> NickMap; |
221 |
|
222 |
_foreach (const NickMap, nick, (*chan)->GetNicks()) |
223 |
{ |
224 |
nicks_[nick->first] = true; |
225 |
nicks.insert(nick->first); |
226 |
} |
227 |
} |
228 |
|
229 |
return nicks; |
230 |
} |
231 |
|
232 |
friend void SmartISONTimer::RunJob(); |
233 |
}; |
234 |
|
235 |
struct SmartISONVoid |
236 |
{ |
237 |
inline void insert(const CString &value) {} |
238 |
}; |
239 |
|
240 |
void SmartISONTimer::RunJob() |
241 |
{ |
242 |
SmartISON *module(static_cast<SmartISON *>(GetModule())); |
243 |
|
244 |
module->request_.clear(); |
245 |
|
246 |
std::set<CString, SmartISON::Compare> online(module->CheckChans<std::set<CString, SmartISON::Compare> >()); |
247 |
size_t size(6); |
248 |
|
249 |
while (module->queue_.size()) |
250 |
{ |
251 |
CString nick(module->queue_.front()); |
252 |
|
253 |
if (!online.count(nick) && !module->request_.count(nick)) |
254 |
{ |
255 |
if ((size += nick.size() + 1) > 512) |
256 |
break; |
257 |
|
258 |
module->request_.insert(nick); |
259 |
} |
260 |
|
261 |
module->queue_.pop_front(); |
262 |
} |
263 |
|
264 |
if (module->request_.size()) |
265 |
{ |
266 |
CString request("ISON"); |
267 |
|
268 |
typedef std::set<CString, SmartISON::Compare> NickSet; |
269 |
|
270 |
_foreach (NickSet, nick, module->request_) |
271 |
request += " " + *nick; |
272 |
|
273 |
module->PutIRC(request); |
274 |
} |
275 |
|
276 |
if (module->queue_.empty()) |
277 |
{ |
278 |
typedef std::map<CString, bool, SmartISON::Compare> NickMap; |
279 |
|
280 |
_foreach (NickMap, nick, module->nicks_) |
281 |
module->queue_.push_back(nick->first); |
282 |
} |
283 |
} |
284 |
|
285 |
template <> |
286 |
void SmartISON::CheckChans() |
287 |
{ |
288 |
CheckChans<SmartISONVoid>(); |
289 |
} |
290 |
|
291 |
CModule::EModRet SmartISON::OnUserRaw(CString &line) |
292 |
{ |
293 |
if (line.Token(0).Equals("ISON")) |
294 |
{ |
295 |
CheckChans<void>(); |
296 |
|
297 |
std::vector<CString> nicks; |
298 |
|
299 |
line.Token(1, true).TrimLeft_n(":").Split(" ", nicks, false); |
300 |
|
301 |
if (nicks.empty()) |
302 |
{ |
303 |
PutUser(":znc.in 461 " + GetUser()->GetIRCNick().GetNick() + " :Not enough parameters"); |
304 |
} |
305 |
else |
306 |
{ |
307 |
CString response(":znc.in 303 " + GetUser()->GetIRCNick().GetNick() + " :"); |
308 |
|
309 |
_foreach (std::vector<CString>, nick, nicks) |
310 |
{ |
311 |
CString prefix(GetUser()->GetStatusPrefix()); |
312 |
|
313 |
if (nick->Equals(prefix, false, prefix.size())) |
314 |
{ |
315 |
CString modNick(nick->substr(prefix.size())); |
316 |
CModule *module(NULL); |
317 |
|
318 |
if (modNick.Equals("status") || (module = GetUser()->GetModules().FindModule(modNick)) || (module = CZNC::Get().GetModules().FindModule(modNick))) |
319 |
{ |
320 |
response += (module ? module->GetModNick() : prefix + "status") + " "; |
321 |
continue; |
322 |
} |
323 |
} |
324 |
|
325 |
std::map<CString, bool, Compare>::iterator theNick(nicks_.find(*nick)); |
326 |
|
327 |
if (theNick == nicks_.end()) |
328 |
{ |
329 |
nicks_[*nick] = false; |
330 |
|
331 |
queue_.push_front(*nick); |
332 |
} |
333 |
else if (theNick->second) |
334 |
response += theNick->first + " "; |
335 |
} |
336 |
|
337 |
PutUser(response); |
338 |
} |
339 |
|
340 |
return HALT; |
341 |
} |
342 |
|
343 |
return CONTINUE; |
344 |
} |
345 |
|
346 |
MODULEDEFS(SmartISON, "Smart ISON"); |