Anope IRC Services  Version 2.0
m_chanstats.cpp
Go to the documentation of this file.
1 #include "module.h"
2 #include "modules/sql.h"
3 
5 {
6  public:
7  CommandCSSetChanstats(Module *creator) : Command(creator, "chanserv/set/chanstats", 2, 2)
8  {
9  this->SetDesc(_("Turn chanstats statistics on or off"));
10  this->SetSyntax(_("\037channel\037 {ON | OFF}"));
11  }
12 
13  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
14  {
15  ChannelInfo *ci = ChannelInfo::Find(params[0]);
16  if (!ci)
17  {
18  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
19  return;
20  }
21 
22  EventReturn MOD_RESULT;
23  FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
24  if (MOD_RESULT == EVENT_STOP)
25  return;
26 
27  if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
28  {
29  source.Reply(ACCESS_DENIED);
30  return;
31  }
32 
33  if (params[1].equals_ci("ON"))
34  {
35  ci->Extend<bool>("CS_STATS");
36  source.Reply(_("Chanstats statistics are now enabled for this channel."));
37  Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable chanstats";
38  }
39  else if (params[1].equals_ci("OFF"))
40  {
41  Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable chanstats";
42  ci->Shrink<bool>("CS_STATS");
43  source.Reply(_("Chanstats statistics are now disabled for this channel."));
44  }
45  else
46  this->OnSyntaxError(source, "");
47  }
48 
50  {
51  this->SendSyntax(source);
52  source.Reply(" ");
53  source.Reply("Turn Chanstats channel statistics ON or OFF.");
54  return true;
55  }
56 };
57 
59 {
60  public:
61  CommandNSSetChanstats(Module *creator, const Anope::string &sname = "nickserv/set/chanstats", size_t min = 1 ) : Command(creator, sname, min, min + 1)
62  {
63  this->SetDesc(_("Turn chanstats statistics on or off"));
64  this->SetSyntax("{ON | OFF}");
65  }
66  void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, bool saset = false)
67  {
68  NickAlias *na = NickAlias::Find(user);
69  if (!na)
70  {
71  source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
72  return;
73  }
74 
75  EventReturn MOD_RESULT;
76  FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, na->nc, param));
77  if (MOD_RESULT == EVENT_STOP)
78  return;
79 
80  if (param.equals_ci("ON"))
81  {
82  Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable chanstats for " << na->nc->display;
83  na->nc->Extend<bool>("NS_STATS");
84  if (saset)
85  source.Reply(_("Chanstats statistics are now enabled for %s"), na->nc->display.c_str());
86  else
87  source.Reply(_("Chanstats statistics are now enabled for your nick."));
88  }
89  else if (param.equals_ci("OFF"))
90  {
91  Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable chanstats for " << na->nc->display;
92  na->nc->Shrink<bool>("NS_STATS");
93  if (saset)
94  source.Reply(_("Chanstats statistics are now disabled for %s"), na->nc->display.c_str());
95  else
96  source.Reply(_("Chanstats statistics are now disabled for your nick."));
97  }
98  else
99  this->OnSyntaxError(source, "CHANSTATS");
100  }
101 
102  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
103  {
104  this->Run(source, source.nc->display, params[0]);
105  }
106 
108  {
109  this->SendSyntax(source);
110  source.Reply(" ");
111  source.Reply(_("Turns Chanstats statistics ON or OFF."));
112  return true;
113  }
114 };
115 
117 {
118  public:
119  CommandNSSASetChanstats(Module *creator) : CommandNSSetChanstats(creator, "nickserv/saset/chanstats", 2)
120  {
121  this->ClearSyntax();
122  this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
123  }
124 
125  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
126  {
127  this->Run(source, params[0], params[1], true);
128  }
129 
131  {
132  this->SendSyntax(source);
133  source.Reply(" ");
134  source.Reply(_("Turns chanstats channel statistics ON or OFF for this user."));
135  return true;
136  }
137 };
138 
139 class MySQLInterface : public SQL::Interface
140 {
141  public:
143 
145  {
146  }
147 
149  {
150  if (!r.GetQuery().query.empty())
151  Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
152  else
153  Log(LOG_DEBUG) << "Chanstats: Error executing query: " << r.GetError();
154  }
155 };
156 
157 class MChanstats : public Module
158 {
160 
162 
165 
170  std::vector<Anope::string> TableList, ProcedureList, EventList;
172 
173  void RunQuery(const SQL::Query &q)
174  {
175  if (sql)
176  sql->Run(&sqlinterface, q);
177  }
178 
179  size_t CountWords(const Anope::string &msg)
180  {
181  size_t words = 0;
182  for (size_t pos = 0; pos != Anope::string::npos; pos = msg.find(" ", pos+1))
183  words++;
184  return words;
185  }
186  size_t CountSmileys(const Anope::string &msg, const Anope::string &smileylist)
187  {
188  size_t smileys = 0;
189  spacesepstream sep(smileylist);
190  Anope::string buf;
191 
192  while (sep.GetToken(buf) && !buf.empty())
193  {
194  for (size_t pos = msg.find(buf, 0); pos != Anope::string::npos; pos = msg.find(buf, pos+1))
195  smileys++;
196  }
197  return smileys;
198  }
199 
201  {
202  if (u && u->Account() && ns_stats.HasExt(u->Account()))
203  return u->Account()->display;
204  else
205  return "";
206  }
207 
208  void GetTables()
209  {
210  TableList.clear();
211  ProcedureList.clear();
212  EventList.clear();
213  if (!sql)
214  return;
215 
216  SQL::Result r = this->sql->RunQuery(this->sql->GetTables(prefix));
217  for (int i = 0; i < r.Rows(); ++i)
218  {
219  const std::map<Anope::string, Anope::string> &map = r.Row(i);
220  for (std::map<Anope::string, Anope::string>::const_iterator it = map.begin(); it != map.end(); ++it)
221  TableList.push_back(it->second);
222  }
223  query = "SHOW PROCEDURE STATUS WHERE `Db` = Database();";
224  r = this->sql->RunQuery(query);
225  for (int i = 0; i < r.Rows(); ++i)
226  {
227  ProcedureList.push_back(r.Get(i, "Name"));
228  }
229  query = "SHOW EVENTS WHERE `Db` = Database();";
230  r = this->sql->RunQuery(query);
231  for (int i = 0; i < r.Rows(); ++i)
232  {
233  EventList.push_back(r.Get(i, "Name"));
234  }
235  }
236 
237  bool HasTable(const Anope::string &table)
238  {
239  for (std::vector<Anope::string>::const_iterator it = TableList.begin(); it != TableList.end(); ++it)
240  if (*it == table)
241  return true;
242  return false;
243  }
244 
245  bool HasProcedure(const Anope::string &table)
246  {
247  for (std::vector<Anope::string>::const_iterator it = ProcedureList.begin(); it != ProcedureList.end(); ++it)
248  if (*it == table)
249  return true;
250  return false;
251  }
252 
253  bool HasEvent(const Anope::string &table)
254  {
255  for (std::vector<Anope::string>::const_iterator it = EventList.begin(); it != EventList.end(); ++it)
256  if (*it == table)
257  return true;
258  return false;
259  }
260 
261 
262  void CheckTables()
263  {
264  this->GetTables();
265  if (!this->HasTable(prefix +"chanstats"))
266  {
267  query = "CREATE TABLE `" + prefix + "chanstats` ("
268  "`id` int(11) NOT NULL AUTO_INCREMENT,"
269  "`chan` varchar(64) NOT NULL DEFAULT '',"
270  "`nick` varchar(64) NOT NULL DEFAULT '',"
271  "`type` ENUM('total', 'monthly', 'weekly', 'daily') NOT NULL,"
272  "`letters` int(10) unsigned NOT NULL DEFAULT '0',"
273  "`words` int(10) unsigned NOT NULL DEFAULT '0',"
274  "`line` int(10) unsigned NOT NULL DEFAULT '0',"
275  "`actions` int(10) unsigned NOT NULL DEFAULT '0',"
276  "`smileys_happy` int(10) unsigned NOT NULL DEFAULT '0',"
277  "`smileys_sad` int(10) unsigned NOT NULL DEFAULT '0',"
278  "`smileys_other` int(10) unsigned NOT NULL DEFAULT '0',"
279  "`kicks` int(10) unsigned NOT NULL DEFAULT '0',"
280  "`kicked` int(10) unsigned NOT NULL DEFAULT '0',"
281  "`modes` int(10) unsigned NOT NULL DEFAULT '0',"
282  "`topics` int(10) unsigned NOT NULL DEFAULT '0',"
283  "`time0` int(10) unsigned NOT NULL default '0',"
284  "`time1` int(10) unsigned NOT NULL default '0',"
285  "`time2` int(10) unsigned NOT NULL default '0',"
286  "`time3` int(10) unsigned NOT NULL default '0',"
287  "`time4` int(10) unsigned NOT NULL default '0',"
288  "`time5` int(10) unsigned NOT NULL default '0',"
289  "`time6` int(10) unsigned NOT NULL default '0',"
290  "`time7` int(10) unsigned NOT NULL default '0',"
291  "`time8` int(10) unsigned NOT NULL default '0',"
292  "`time9` int(10) unsigned NOT NULL default '0',"
293  "`time10` int(10) unsigned NOT NULL default '0',"
294  "`time11` int(10) unsigned NOT NULL default '0',"
295  "`time12` int(10) unsigned NOT NULL default '0',"
296  "`time13` int(10) unsigned NOT NULL default '0',"
297  "`time14` int(10) unsigned NOT NULL default '0',"
298  "`time15` int(10) unsigned NOT NULL default '0',"
299  "`time16` int(10) unsigned NOT NULL default '0',"
300  "`time17` int(10) unsigned NOT NULL default '0',"
301  "`time18` int(10) unsigned NOT NULL default '0',"
302  "`time19` int(10) unsigned NOT NULL default '0',"
303  "`time20` int(10) unsigned NOT NULL default '0',"
304  "`time21` int(10) unsigned NOT NULL default '0',"
305  "`time22` int(10) unsigned NOT NULL default '0',"
306  "`time23` int(10) unsigned NOT NULL default '0',"
307  "PRIMARY KEY (`id`),"
308  "UNIQUE KEY `chan` (`chan`,`nick`,`type`),"
309  "KEY `nick` (`nick`),"
310  "KEY `chan_` (`chan`),"
311  "KEY `type` (`type`)"
312  ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";
313  this->RunQuery(query);
314  }
315  /* There is no CREATE OR REPLACE PROCEDURE in MySQL */
316  if (this->HasProcedure(prefix + "chanstats_proc_update"))
317  {
318  query = "DROP PROCEDURE " + prefix + "chanstats_proc_update";
319  this->RunQuery(query);
320  }
321  query = "CREATE PROCEDURE `" + prefix + "chanstats_proc_update`"
322  "(chan_ VARCHAR(255), nick_ VARCHAR(255), line_ INT(10), letters_ INT(10),"
323  "words_ INT(10), actions_ INT(10), sm_h_ INT(10), sm_s_ INT(10), sm_o_ INT(10),"
324  "kicks_ INT(10), kicked_ INT(10), modes_ INT(10), topics_ INT(10))"
325  "BEGIN "
326  "DECLARE time_ VARCHAR(20);"
327  "SET time_ = CONCAT('time', hour(now()));"
328  "INSERT IGNORE INTO `" + prefix + "chanstats` (`nick`,`chan`, `type`) VALUES "
329  "('', chan_, 'total'), ('', chan_, 'monthly'),"
330  "('', chan_, 'weekly'), ('', chan_, 'daily');"
331  "IF nick_ != '' THEN "
332  "INSERT IGNORE INTO `" + prefix + "chanstats` (`nick`,`chan`, `type`) VALUES "
333  "(nick_, chan_, 'total'), (nick_, chan_, 'monthly'),"
334  "(nick_, chan_, 'weekly'),(nick_, chan_, 'daily'),"
335  "(nick_, '', 'total'), (nick_, '', 'monthly'),"
336  "(nick_, '', 'weekly'), (nick_, '', 'daily');"
337  "END IF;"
338  "SET @update_query = CONCAT('UPDATE `" + prefix + "chanstats` SET line=line+', line_, ',"
339  "letters=letters+', letters_, ' , words=words+', words_, ', actions=actions+', actions_, ', "
340  "smileys_happy=smileys_happy+', sm_h_, ', smileys_sad=smileys_sad+', sm_s_, ', "
341  "smileys_other=smileys_other+', sm_o_, ', kicks=kicks+', kicks_, ', kicked=kicked+', kicked_, ', "
342  "modes=modes+', modes_, ', topics=topics+', topics_, ', ', time_ , '=', time_, '+', line_ ,' "
343  "WHERE (nick='''' OR nick=''', nick_, ''') AND (chan='''' OR chan=''', chan_, ''')');"
344  "PREPARE update_query FROM @update_query;"
345  "EXECUTE update_query;"
346  "DEALLOCATE PREPARE update_query;"
347  "END";
348  this->RunQuery(query);
349 
350  if (this->HasProcedure(prefix + "chanstats_proc_chgdisplay"))
351  {
352  query = "DROP PROCEDURE " + prefix + "chanstats_proc_chgdisplay;";
353  this->RunQuery(query);
354  }
355  query = "CREATE PROCEDURE `" + prefix + "chanstats_proc_chgdisplay`"
356  "(old_nick varchar(255), new_nick varchar(255))"
357  "BEGIN "
358  "DECLARE res_count int(10) unsigned;"
359  "SELECT COUNT(nick) INTO res_count FROM `" + prefix + "chanstats` WHERE nick = new_nick;"
360  "IF res_count = 0 THEN "
361  "UPDATE `" + prefix + "chanstats` SET `nick` = new_nick WHERE `nick` = old_nick;"
362  "ELSE "
363  "my_cursor: BEGIN "
364  "DECLARE no_more_rows BOOLEAN DEFAULT FALSE;"
365  "DECLARE chan_ VARCHAR(255);"
366  "DECLARE type_ ENUM('total', 'monthly', 'weekly', 'daily');"
367  "DECLARE letters_, words_, line_, actions_, smileys_happy_,"
368  "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
369  "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_, time9_,"
370  "time10_, time11_, time12_, time13_, time14_, time15_, time16_, time17_, time18_,"
371  "time19_, time20_, time21_, time22_, time23_ INT(10) unsigned;"
372  "DECLARE stats_cursor CURSOR FOR "
373  "SELECT chan, type, letters, words, line, actions, smileys_happy,"
374  "smileys_sad, smileys_other, kicks, kicked, modes, topics, time0, time1,"
375  "time2, time3, time4, time5, time6, time7, time8, time9, time10, time11,"
376  "time12, time13, time14, time15, time16, time17, time18, time19, time20,"
377  "time21, time22, time23 "
378  "FROM `" + prefix + "chanstats` "
379  "WHERE `nick` = old_nick;"
380  "DECLARE CONTINUE HANDLER FOR NOT FOUND "
381  "SET no_more_rows = TRUE;"
382  "OPEN stats_cursor;"
383  "the_loop: LOOP "
384  "FETCH stats_cursor "
385  "INTO chan_, type_, letters_, words_, line_, actions_, smileys_happy_,"
386  "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
387  "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_,"
388  "time9_, time10_, time11_, time12_, time13_, time14_, time15_, time16_,"
389  "time17_, time18_, time19_, time20_, time21_, time22_, time23_;"
390  "IF no_more_rows THEN "
391  "CLOSE stats_cursor;"
392  "LEAVE the_loop;"
393  "END IF;"
394  "INSERT INTO `" + prefix + "chanstats` "
395  "(chan, nick, type, letters, words, line, actions, smileys_happy, "
396  "smileys_sad, smileys_other, kicks, kicked, modes, topics, time0, time1, "
397  "time2, time3, time4, time5, time6, time7, time8, time9, time10, time11,"
398  "time12, time13, time14, time15, time16, time17, time18, time19, time20,"
399  "time21, time22, time23)"
400  "VALUES (chan_, new_nick, type_, letters_, words_, line_, actions_, smileys_happy_,"
401  "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
402  "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_, "
403  "time9_, time10_, time11_, time12_, time13_, time14_, time15_, time16_, "
404  "time17_, time18_, time19_, time20_, time21_, time22_, time23_)"
405  "ON DUPLICATE KEY UPDATE letters=letters+VALUES(letters), words=words+VALUES(words),"
406  "line=line+VALUES(line), actions=actions+VALUES(actions),"
407  "smileys_happy=smileys_happy+VALUES(smileys_happy),"
408  "smileys_sad=smileys_sad+VALUES(smileys_sad),"
409  "smileys_other=smileys_other+VALUES(smileys_other),"
410  "kicks=kicks+VALUES(kicks), kicked=kicked+VALUES(kicked),"
411  "modes=modes+VALUES(modes), topics=topics+VALUES(topics),"
412  "time1=time1+VALUES(time1), time2=time2+VALUES(time2), time3=time3+VALUES(time3),"
413  "time4=time4+VALUES(time4), time5=time5+VALUES(time5), time6=time6+VALUES(time6),"
414  "time7=time7+VALUES(time7), time8=time8+VALUES(time8), time9=time9+VALUES(time9),"
415  "time10=time10+VALUES(time10), time11=time11+VALUES(time11), time12=time12+VALUES(time12),"
416  "time13=time13+VALUES(time13), time14=time14+VALUES(time14), time15=time15+VALUES(time15),"
417  "time16=time16+VALUES(time16), time17=time17+VALUES(time17), time18=time18+VALUES(time18),"
418  "time19=time19+VALUES(time19), time20=time20+VALUES(time20), time21=time21+VALUES(time21),"
419  "time22=time22+VALUES(time22), time23=time23+VALUES(time23);"
420  "END LOOP;"
421  "DELETE FROM `" + prefix + "chanstats` WHERE `nick` = old_nick;"
422  "END my_cursor;"
423  "END IF;"
424  "END;";
425  this->RunQuery(query);
426 
427  /* dont prepend any database prefix to events so we can always delete/change old events */
428  if (this->HasEvent("chanstats_event_cleanup_daily"))
429  {
430  query = "DROP EVENT chanstats_event_cleanup_daily";
431  this->RunQuery(query);
432  }
433  query = "CREATE EVENT `chanstats_event_cleanup_daily` "
434  "ON SCHEDULE EVERY 1 DAY STARTS CURRENT_DATE "
435  "DO UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
436  "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
437  "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
438  "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0,"
439  "time20=0, time21=0, time22=0, time23=0 "
440  "WHERE type='daily';";
441  this->RunQuery(query);
442 
443  if (this->HasEvent("chanstats_event_cleanup_weekly"))
444  {
445  query = "DROP EVENT `chanstats_event_cleanup_weekly`";
446  this->RunQuery(query);
447  }
448  query = "CREATE EVENT `chanstats_event_cleanup_weekly` "
449  "ON SCHEDULE EVERY 1 WEEK STARTS ADDDATE(CURDATE(), INTERVAL 1-DAYOFWEEK(CURDATE()) DAY) "
450  "DO UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
451  "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
452  "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
453  "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0,"
454  "time20=0, time21=0, time22=0, time23=0 "
455  "WHERE type='weekly';";
456  this->RunQuery(query);
457 
458  if (this->HasEvent("chanstats_event_cleanup_monthly"))
459  {
460  query = "DROP EVENT `chanstats_event_cleanup_monthly`;";
461  this->RunQuery(query);
462  }
463  query = "CREATE EVENT `chanstats_event_cleanup_monthly` "
464  "ON SCHEDULE EVERY 1 MONTH STARTS LAST_DAY(CURRENT_TIMESTAMP) + INTERVAL 1 DAY "
465  "DO BEGIN "
466  "UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
467  "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
468  "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
469  "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0, "
470  "time20=0, time21=0, time22=0, time23=0 "
471  "WHERE type='monthly';"
472  "OPTIMIZE TABLE `" + prefix + "chanstats`;"
473  "END;";
474  this->RunQuery(query);
475  }
476 
477 
478  public:
479  MChanstats(const Anope::string &modname, const Anope::string &creator) :
480  Module(modname, creator, EXTRA | VENDOR),
481  cs_stats(this, "CS_STATS"), ns_stats(this, "NS_STATS"),
482  commandcssetchanstats(this), commandnssetchanstats(this), commandnssasetchanstats(this),
483  sqlinterface(this)
484  {
485  }
486 
488  {
489  Configuration::Block *block = conf->GetModule(this);
490  prefix = block->Get<const Anope::string>("prefix", "anope_");
491  SmileysHappy = block->Get<const Anope::string>("SmileysHappy");
492  SmileysSad = block->Get<const Anope::string>("SmileysSad");
493  SmileysOther = block->Get<const Anope::string>("SmileysOther");
494  NSDefChanstats = block->Get<bool>("ns_def_chanstats");
495  CSDefChanstats = block->Get<bool>("cs_def_chanstats");
496  Anope::string engine = block->Get<const Anope::string>("engine");
497  this->sql = ServiceReference<SQL::Provider>("SQL::Provider", engine);
498  if (sql)
499  this->CheckTables();
500  else
501  Log(this) << "no database connection to " << engine;
502  }
503 
504  void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
505  {
506  if (!show_all)
507  return;
508  if (cs_stats.HasExt(ci))
509  info.AddOption(_("Chanstats"));
510  }
511 
512  void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
513  {
514  if (!show_hidden)
515  return;
516  if (ns_stats.HasExt(na->nc))
517  info.AddOption(_("Chanstats"));
518  }
519 
521  {
522  User *u = User::Find(user);
523  if (!u || !u->Account() || !c->ci || !cs_stats.HasExt(c->ci))
524  return;
525  query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);";
526  query.SetValue("channel", c->name);
527  query.SetValue("nick", GetDisplay(u));
528  this->RunQuery(query);
529  }
530 
532  {
533  this->OnModeChange(c, setter.GetUser());
534  return EVENT_CONTINUE;
535  }
536 
538  {
539  this->OnModeChange(c, setter.GetUser());
540  return EVENT_CONTINUE;
541  }
542 
543  private:
544  void OnModeChange(Channel *c, User *u)
545  {
546  if (!u || !u->Account() || !c->ci || !cs_stats.HasExt(c->ci))
547  return;
548 
549  query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);";
550  query.SetValue("channel", c->name);
551  query.SetValue("nick", GetDisplay(u));
552  this->RunQuery(query);
553  }
554 
555  public:
557  {
558  if (!cu->chan->ci || !cs_stats.HasExt(cu->chan->ci))
559  return;
560 
561  query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0);";
562  query.SetValue("channel", cu->chan->name);
563  query.SetValue("nick", GetDisplay(cu->user));
564  this->RunQuery(query);
565 
566  query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);";
567  query.SetValue("channel", cu->chan->name);
568  query.SetValue("nick", GetDisplay(source.GetUser()));
569  this->RunQuery(query);
570  }
571 
573  {
574  if (!c->ci || !cs_stats.HasExt(c->ci))
575  return;
576 
577  size_t letters = msg.length();
578  size_t words = this->CountWords(msg);
579 
580  size_t action = 0;
581  if (msg.find("\01ACTION")!=Anope::string::npos)
582  {
583  action = 1;
584  letters = letters - 7;
585  words--;
586  }
587 
588  // count smileys
589  size_t smileys_happy = CountSmileys(msg, SmileysHappy);
590  size_t smileys_sad = CountSmileys(msg, SmileysSad);
591  size_t smileys_other = CountSmileys(msg, SmileysOther);
592 
593  // do not count smileys as words
594  size_t smileys = smileys_happy + smileys_sad + smileys_other;
595  if (smileys > words)
596  words = 0;
597  else
598  words = words - smileys;
599 
600  query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 1, @letters@, @words@, @action@, "
601  "@smileys_happy@, @smileys_sad@, @smileys_other@, '0', '0', '0', '0');";
602  query.SetValue("channel", c->name);
603  query.SetValue("nick", GetDisplay(u));
604  query.SetValue("letters", letters);
605  query.SetValue("words", words);
606  query.SetValue("action", action);
607  query.SetValue("smileys_happy", smileys_happy);
608  query.SetValue("smileys_sad", smileys_sad);
609  query.SetValue("smileys_other", smileys_other);
610  this->RunQuery(query);
611  }
612 
614  {
615  query = "DELETE FROM `" + prefix + "chanstats` WHERE `nick` = @nick@;";
616  query.SetValue("nick", nc->display);
617  this->RunQuery(query);
618  }
619 
621  {
622  query = "CALL " + prefix + "chanstats_proc_chgdisplay(@old_display@, @new_display@);";
623  query.SetValue("old_display", nc->display);
624  query.SetValue("new_display", newdisplay);
625  this->RunQuery(query);
626  }
627 
629  {
630  query = "DELETE FROM `" + prefix + "chanstats` WHERE `chan` = @channel@;";
631  query.SetValue("channel", ci->name);
632  this->RunQuery(query);
633  }
634 
636  {
637  if (CSDefChanstats)
638  ci->Extend<bool>("CS_STATS");
639  }
640 
641  void OnNickRegister(User *user, NickAlias *na, const Anope::string &)
642  {
643  if (NSDefChanstats)
644  na->nc->Extend<bool>("NS_STATS");
645  }
646 };
647 
649 
Serialize::Reference< NickCore > nc
Definition: account.h:47
void CheckTables()
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Anope::string SmileysSad
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: m_chanstats.cpp:13
CommandNSSASetChanstats commandnssasetchanstats
Definition: hashcomp.h:84
MySQLInterface sqlinterface
std::vector< Anope::string > TableList
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
Definition: m_chanstats.cpp:49
const std::map< Anope::string, Anope::string > & Row(size_t index) const
Definition: sql.h:157
#define ACCESS_DENIED
Definition: language.h:73
SQL::Query query
bool HasTable(const Anope::string &table)
bool HasProcedure(const Anope::string &table)
void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
Definition: users.h:34
T * Extend(const Anope::string &name, const T &what)
Definition: extensible.h:224
#define NICK_X_NOT_REGISTERED
Definition: language.h:79
void ClearSyntax()
Definition: command.cpp:135
SerializableExtensibleItem< bool > cs_stats
void OnChanRegistered(ChannelInfo *ci)
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
void RunQuery(const SQL::Query &q)
#define FOREACH_RESULT(ename, ret, args)
Definition: modules.h:95
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, bool saset=false)
Definition: m_chanstats.cpp:66
Serialize::Reference< ChannelInfo > ci
Definition: channels.h:46
void SetValue(const Anope::string &key, const T &value, bool escape=true)
Definition: sql.h:121
Anope::string name
Definition: channels.h:44
void OnModeChange(Channel *c, User *u)
void OnError(const SQL::Result &r) anope_override
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
Interface(Module *m)
Definition: sql.h:188
Anope::string SmileysOther
CommandNSSetChanstats commandnssetchanstats
Definition: sql.h:96
void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
void Shrink(const Anope::string &name)
Definition: extensible.h:253
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
void OnResult(const SQL::Result &r) anope_override
static const size_type npos
Definition: anope.h:44
void Reply(const char *message,...)
Definition: command.cpp:96
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
void OnDelChan(ChannelInfo *ci) anope_override
void OnDelCore(NickCore *nc) anope_override
EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *, const Anope::string &param) anope_override
SerializableExtensibleItem< bool > ns_stats
size_t CountWords(const Anope::string &msg)
Anope::string display
Definition: account.h:113
CommandCSSetChanstats commandcssetchanstats
void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
MChanstats(const Anope::string &modname, const Anope::string &creator)
void GetTables()
Anope::string SmileysHappy
EventReturn
Definition: modules.h:129
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
#define MODULE_INIT(x)
Definition: modules.h:45
void OnPreUserKicked(const MessageSource &source, ChanUserContainer *cu, const Anope::string &kickmsg) anope_override
Definition: sql.h:8
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override
#define CHAN_X_NOT_REGISTERED
Definition: language.h:84
Anope::string prefix
static User * Find(const Anope::string &name, bool nick_only=false)
Definition: users.cpp:815
void OnTopicUpdated(Channel *c, const Anope::string &user, const Anope::string &topic) anope_override
std::vector< Anope::string > EventList
NickCore * Account() const
Definition: users.cpp:422
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
bool CSDefChanstats
bool HasEvent(const Anope::string &table)
void OnReload(Configuration::Conf *conf) anope_override
void SendSyntax(CommandSource &)
Definition: command.cpp:145
CommandNSSASetChanstats(Module *creator)
NickCore * GetAccount()
Definition: command.cpp:36
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
T Get(const Anope::string &tag)
Definition: config.h:44
std::vector< Anope::string > ProcedureList
void OnNickRegister(User *user, NickAlias *na, const Anope::string &)
const Anope::string GetDisplay(User *u)
int Rows() const
Definition: sql.h:155
ServiceReference< SQL::Provider > sql
bool HasExt(const Extensible *obj) const
Definition: extensible.h:111
MySQLInterface(Module *o)
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
size_t CountSmileys(const Anope::string &msg, const Anope::string &smileylist)
#define _(x)
Definition: services.h:50
Definition: modules.h:163
const Anope::string Get(size_t index, const Anope::string &col) const
Definition: sql.h:169
CommandCSSetChanstats(Module *creator)
Definition: m_chanstats.cpp:7
CommandNSSetChanstats(Module *creator, const Anope::string &sname="nickserv/set/chanstats", size_t min=1)
Definition: m_chanstats.cpp:61
bool NSDefChanstats