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