00001 #include "module.h"
00002 #include "../extra/sql.h"
00003
00004 class MySQLInterface : public SQL::Interface
00005 {
00006 public:
00007 MySQLInterface(Module *o) : SQL::Interface(o) { }
00008
00009 void OnResult(const SQL::Result &r) anope_override
00010 {
00011 }
00012
00013 void OnError(const SQL::Result &r) anope_override
00014 {
00015 if (!r.GetQuery().query.empty())
00016 Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
00017 else
00018 Log(LOG_DEBUG) << "Chanstats: Error executing query: " << r.GetError();
00019 }
00020 };
00021
00022 class MChanstats : public Module
00023 {
00024 ServiceReference<SQL::Provider> sql;
00025 MySQLInterface sqlinterface;
00026 SQL::Query query;
00027 Anope::string SmileysHappy, SmileysSad, SmileysOther, prefix;
00028 std::vector<Anope::string> TableList, ProcedureList, EventList;
00029
00030 void RunQuery(const SQL::Query &q)
00031 {
00032 if (sql)
00033 sql->Run(&sqlinterface, q);
00034 }
00035
00036 size_t CountWords(const Anope::string &msg)
00037 {
00038 size_t words = 0;
00039 for (size_t pos = 0; pos != Anope::string::npos; pos = msg.find(" ", pos+1))
00040 words++;
00041 return words;
00042 }
00043 size_t CountSmileys(const Anope::string &msg, const Anope::string &smileylist)
00044 {
00045 size_t smileys = 0;
00046 spacesepstream sep(smileylist);
00047 Anope::string buf;
00048
00049 while (sep.GetToken(buf) && !buf.empty())
00050 {
00051 for (size_t pos = msg.find(buf, 0); pos != Anope::string::npos; pos = msg.find(buf, pos+1))
00052 smileys++;
00053 }
00054 return smileys;
00055 }
00056
00057 const Anope::string GetDisplay(User *u)
00058 {
00059 if (u && u->Account() && u->Account()->HasExt("STATS"))
00060 return u->Account()->display;
00061 else
00062 return "";
00063 }
00064
00065 void GetTables()
00066 {
00067 TableList.clear();
00068 ProcedureList.clear();
00069 EventList.clear();
00070 if (!sql)
00071 return;
00072
00073 SQL::Result r = this->sql->RunQuery(this->sql->GetTables(prefix));
00074 for (int i = 0; i < r.Rows(); ++i)
00075 {
00076 const std::map<Anope::string, Anope::string> &map = r.Row(i);
00077 for (std::map<Anope::string, Anope::string>::const_iterator it = map.begin(); it != map.end(); ++it)
00078 TableList.push_back(it->second);
00079 }
00080 query = "SHOW PROCEDURE STATUS WHERE `Db` = Database();";
00081 r = this->sql->RunQuery(query);
00082 for (int i = 0; i < r.Rows(); ++i)
00083 {
00084 ProcedureList.push_back(r.Get(i, "Name"));
00085 }
00086 query = "SHOW EVENTS WHERE `Db` = Database();";
00087 r = this->sql->RunQuery(query);
00088 for (int i = 0; i < r.Rows(); ++i)
00089 {
00090 EventList.push_back(r.Get(i, "Name"));
00091 }
00092 }
00093
00094 bool HasTable(const Anope::string &table)
00095 {
00096 for (std::vector<Anope::string>::const_iterator it = TableList.begin(); it != TableList.end(); ++it)
00097 if (*it == table)
00098 return true;
00099 return false;
00100 }
00101
00102 bool HasProcedure(const Anope::string &table)
00103 {
00104 for (std::vector<Anope::string>::const_iterator it = ProcedureList.begin(); it != ProcedureList.end(); ++it)
00105 if (*it == table)
00106 return true;
00107 return false;
00108 }
00109
00110 bool HasEvent(const Anope::string &table)
00111 {
00112 for (std::vector<Anope::string>::const_iterator it = EventList.begin(); it != EventList.end(); ++it)
00113 if (*it == table)
00114 return true;
00115 return false;
00116 }
00117
00118
00119 void CheckTables()
00120 {
00121 this->GetTables();
00122 if (!this->HasTable(prefix +"chanstats"))
00123 {
00124 query = "CREATE TABLE `" + prefix + "chanstats` ("
00125 "`id` int(11) NOT NULL AUTO_INCREMENT,"
00126 "`chan` varchar(255) NOT NULL DEFAULT '',"
00127 "`nick` varchar(255) NOT NULL DEFAULT '',"
00128 "`type` ENUM('total', 'monthly', 'weekly', 'daily') NOT NULL,"
00129 "`letters` int(10) unsigned NOT NULL DEFAULT '0',"
00130 "`words` int(10) unsigned NOT NULL DEFAULT '0',"
00131 "`line` int(10) unsigned NOT NULL DEFAULT '0',"
00132 "`actions` int(10) unsigned NOT NULL DEFAULT '0',"
00133 "`smileys_happy` int(10) unsigned NOT NULL DEFAULT '0',"
00134 "`smileys_sad` int(10) unsigned NOT NULL DEFAULT '0',"
00135 "`smileys_other` int(10) unsigned NOT NULL DEFAULT '0',"
00136 "`kicks` int(10) unsigned NOT NULL DEFAULT '0',"
00137 "`kicked` int(10) unsigned NOT NULL DEFAULT '0',"
00138 "`modes` int(10) unsigned NOT NULL DEFAULT '0',"
00139 "`topics` int(10) unsigned NOT NULL DEFAULT '0',"
00140 "`time0` int(10) unsigned NOT NULL default '0',"
00141 "`time1` int(10) unsigned NOT NULL default '0',"
00142 "`time2` int(10) unsigned NOT NULL default '0',"
00143 "`time3` int(10) unsigned NOT NULL default '0',"
00144 "`time4` int(10) unsigned NOT NULL default '0',"
00145 "`time5` int(10) unsigned NOT NULL default '0',"
00146 "`time6` int(10) unsigned NOT NULL default '0',"
00147 "`time7` int(10) unsigned NOT NULL default '0',"
00148 "`time8` int(10) unsigned NOT NULL default '0',"
00149 "`time9` int(10) unsigned NOT NULL default '0',"
00150 "`time10` int(10) unsigned NOT NULL default '0',"
00151 "`time11` int(10) unsigned NOT NULL default '0',"
00152 "`time12` int(10) unsigned NOT NULL default '0',"
00153 "`time13` int(10) unsigned NOT NULL default '0',"
00154 "`time14` int(10) unsigned NOT NULL default '0',"
00155 "`time15` int(10) unsigned NOT NULL default '0',"
00156 "`time16` int(10) unsigned NOT NULL default '0',"
00157 "`time17` int(10) unsigned NOT NULL default '0',"
00158 "`time18` int(10) unsigned NOT NULL default '0',"
00159 "`time19` int(10) unsigned NOT NULL default '0',"
00160 "`time20` int(10) unsigned NOT NULL default '0',"
00161 "`time21` int(10) unsigned NOT NULL default '0',"
00162 "`time22` int(10) unsigned NOT NULL default '0',"
00163 "`time23` int(10) unsigned NOT NULL default '0',"
00164 "PRIMARY KEY (`id`),"
00165 "UNIQUE KEY `chan` (`chan`,`nick`,`type`),"
00166 "KEY `nick` (`nick`),"
00167 "KEY `chan_` (`chan`),"
00168 "KEY `type` (`type`)"
00169 ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";
00170 this->RunQuery(query);
00171 }
00172
00173 if (this->HasProcedure(prefix + "chanstats_proc_update"))
00174 {
00175 query = "DROP PROCEDURE " + prefix + "chanstats_proc_update";
00176 this->RunQuery(query);
00177 }
00178 query = "CREATE PROCEDURE `" + prefix + "chanstats_proc_update`"
00179 "(chan_ VARCHAR(255), nick_ VARCHAR(255), line_ INT(10), letters_ INT(10),"
00180 "words_ INT(10), actions_ INT(10), sm_h_ INT(10), sm_s_ INT(10), sm_o_ INT(10),"
00181 "kicks_ INT(10), kicked_ INT(10), modes_ INT(10), topics_ INT(10))"
00182 "BEGIN "
00183 "DECLARE time_ VARCHAR(20);"
00184 "SET time_ = CONCAT('time', hour(now()));"
00185 "INSERT IGNORE INTO `" + prefix + "chanstats` (`nick`,`chan`, `type`) VALUES "
00186 "('', chan_, 'total'), ('', chan_, 'monthly'),"
00187 "('', chan_, 'weekly'), ('', chan_, 'daily');"
00188 "IF nick_ != '' THEN "
00189 "INSERT IGNORE INTO `" + prefix + "chanstats` (`nick`,`chan`, `type`) VALUES "
00190 "(nick_, chan_, 'total'), (nick_, chan_, 'monthly'),"
00191 "(nick_, chan_, 'weekly'),(nick_, chan_, 'daily'),"
00192 "(nick_, '', 'total'), (nick_, '', 'monthly'),"
00193 "(nick_, '', 'weekly'), (nick_, '', 'daily');"
00194 "END IF;"
00195 "SET @update_query = CONCAT('UPDATE `" + prefix + "chanstats` SET line=line+', line_, ',"
00196 "letters=letters+', letters_, ' , words=words+', words_, ', actions=actions+', actions_, ', "
00197 "smileys_happy=smileys_happy+', sm_h_, ', smileys_sad=smileys_sad+', sm_s_, ', "
00198 "smileys_other=smileys_other+', sm_o_, ', kicks=kicks+', kicks_, ', kicked=kicked+', kicked_, ', "
00199 "modes=modes+', modes_, ', topics=topics+', topics_, ', ', time_ , '=', time_, '+', line_ ,' "
00200 "WHERE (nick='''' OR nick=''', nick_, ''') AND (chan='''' OR chan=''', chan_, ''')');"
00201 "PREPARE update_query FROM @update_query;"
00202 "EXECUTE update_query;"
00203 "DEALLOCATE PREPARE update_query;"
00204 "END";
00205 this->RunQuery(query);
00206
00207 if (this->HasProcedure(prefix + "chanstats_proc_chgdisplay"))
00208 {
00209 query = "DROP PROCEDURE " + prefix + "chanstats_proc_chgdisplay;";
00210 this->RunQuery(query);
00211 }
00212 query = "CREATE PROCEDURE `" + prefix + "chanstats_proc_chgdisplay`"
00213 "(old_nick varchar(255), new_nick varchar(255))"
00214 "BEGIN "
00215 "DECLARE res_count int(10) unsigned;"
00216 "SELECT COUNT(nick) INTO res_count FROM `" + prefix + "chanstats` WHERE nick = new_nick;"
00217 "IF res_count = 0 THEN "
00218 "UPDATE `" + prefix + "chanstats` SET `nick` = new_nick WHERE `nick` = old_nick;"
00219 "ELSE "
00220 "my_cursor: BEGIN "
00221 "DECLARE no_more_rows BOOLEAN DEFAULT FALSE;"
00222 "DECLARE chan_ VARCHAR(255);"
00223 "DECLARE type_ ENUM('total', 'monthly', 'weekly', 'daily');"
00224 "DECLARE letters_, words_, line_, actions_, smileys_happy_,"
00225 "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
00226 "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_, time9_,"
00227 "time10_, time11_, time12_, time13_, time14_, time15_, time16_, time17_, time18_,"
00228 "time19_, time20_, time21_, time22_, time23_ INT(10) unsigned;"
00229 "DECLARE stats_cursor CURSOR FOR "
00230 "SELECT chan, type, letters, words, line, actions, smileys_happy,"
00231 "smileys_sad, smileys_other, kicks, kicked, modes, topics, time0, time1,"
00232 "time2, time3, time4, time5, time6, time7, time8, time9, time10, time11,"
00233 "time12, time13, time14, time15, time16, time17, time18, time19, time20,"
00234 "time21, time22, time23 "
00235 "FROM `" + prefix + "chanstats` "
00236 "WHERE `nick` = old_nick;"
00237 "DECLARE CONTINUE HANDLER FOR NOT FOUND "
00238 "SET no_more_rows = TRUE;"
00239 "OPEN stats_cursor;"
00240 "the_loop: LOOP "
00241 "FETCH stats_cursor "
00242 "INTO chan_, type_, letters_, words_, line_, actions_, smileys_happy_,"
00243 "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
00244 "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_,"
00245 "time9_, time10_, time11_, time12_, time13_, time14_, time15_, time16_,"
00246 "time17_, time18_, time19_, time20_, time21_, time22_, time23_;"
00247 "IF no_more_rows THEN "
00248 "CLOSE stats_cursor;"
00249 "LEAVE the_loop;"
00250 "END IF;"
00251 "INSERT INTO `" + prefix + "chanstats` "
00252 "(chan, nick, type, letters, words, line, actions, smileys_happy, "
00253 "smileys_sad, smileys_other, kicks, kicked, modes, topics, time0, time1, "
00254 "time2, time3, time4, time5, time6, time7, time8, time9, time10, time11,"
00255 "time12, time13, time14, time15, time16, time17, time18, time19, time20,"
00256 "time21, time22, time23)"
00257 "VALUES (chan_, new_nick, type_, letters_, words_, line_, actions_, smileys_happy_,"
00258 "smileys_sad_, smileys_other_, kicks_, kicked_, modes_, topics_,"
00259 "time0_, time1_, time2_, time3_, time4_, time5_, time6_, time7_, time8_, "
00260 "time9_, time10_, time11_, time12_, time13_, time14_, time15_, time16_, "
00261 "time17_, time18_, time19_, time20_, time21_, time22_, time23_)"
00262 "ON DUPLICATE KEY UPDATE letters=letters+VALUES(letters), words=words+VALUES(words),"
00263 "line=line+VALUES(line), actions=actions+VALUES(actions),"
00264 "smileys_happy=smileys_happy+VALUES(smileys_happy),"
00265 "smileys_sad=smileys_sad+VALUES(smileys_sad),"
00266 "smileys_other=smileys_other+VALUES(smileys_other),"
00267 "kicks=kicks+VALUES(kicks), kicked=kicked+VALUES(kicked),"
00268 "modes=modes+VALUES(modes), topics=topics+VALUES(topics),"
00269 "time1=time1+VALUES(time1), time2=time2+VALUES(time2), time3=time3+VALUES(time3),"
00270 "time4=time4+VALUES(time4), time5=time5+VALUES(time5), time6=time6+VALUES(time6),"
00271 "time7=time7+VALUES(time7), time8=time8+VALUES(time8), time9=time9+VALUES(time9),"
00272 "time10=time10+VALUES(time10), time11=time11+VALUES(time11), time12=time12+VALUES(time12),"
00273 "time13=time13+VALUES(time13), time14=time14+VALUES(time14), time15=time15+VALUES(time15),"
00274 "time16=time16+VALUES(time16), time17=time17+VALUES(time17), time18=time18+VALUES(time18),"
00275 "time19=time19+VALUES(time19), time20=time20+VALUES(time20), time21=time21+VALUES(time21),"
00276 "time22=time22+VALUES(time22), time23=time23+VALUES(time23);"
00277 "END LOOP;"
00278 "DELETE FROM `" + prefix + "chanstats` WHERE `nick` = old_nick;"
00279 "END my_cursor;"
00280 "END IF;"
00281 "END;";
00282 this->RunQuery(query);
00283
00284
00285 if (this->HasEvent("chanstats_event_cleanup_daily"))
00286 {
00287 query = "DROP EVENT chanstats_event_cleanup_daily";
00288 this->RunQuery(query);
00289 }
00290 query = "CREATE EVENT `chanstats_event_cleanup_daily` "
00291 "ON SCHEDULE EVERY 1 DAY STARTS CURRENT_DATE "
00292 "DO UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
00293 "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
00294 "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
00295 "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0,"
00296 "time20=0, time21=0, time22=0, time23=0 "
00297 "WHERE type='daily';";
00298 this->RunQuery(query);
00299
00300 if (this->HasEvent("chanstats_event_cleanup_weekly"))
00301 {
00302 query = "DROP EVENT `chanstats_event_cleanup_weekly`";
00303 this->RunQuery(query);
00304 }
00305 query = "CREATE EVENT `chanstats_event_cleanup_weekly` "
00306 "ON SCHEDULE EVERY 1 WEEK STARTS (DATE(CURRENT_TIMESTAMP)-WEEKDAY(CURRENT_TIMESTAMP)) "
00307 "DO UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
00308 "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
00309 "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
00310 "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0,"
00311 "time20=0, time21=0, time22=0, time23=0 "
00312 "WHERE type='weekly';";
00313 this->RunQuery(query);
00314
00315 if (this->HasEvent("chanstats_event_cleanup_monthly"))
00316 {
00317 query = "DROP EVENT `chanstats_event_cleanup_monthly`;";
00318 this->RunQuery(query);
00319 }
00320 query = "CREATE EVENT `chanstats_event_cleanup_monthly` "
00321 "ON SCHEDULE EVERY 1 MONTH STARTS LAST_DAY(CURRENT_TIMESTAMP) + INTERVAL 1 DAY "
00322 "DO BEGIN "
00323 "UPDATE `" + prefix + "chanstats` SET letters=0, words=0, line=0, actions=0, smileys_happy=0,"
00324 "smileys_sad=0, smileys_other=0, kicks=0, modes=0, topics=0, time0=0, time1=0, time2=0,"
00325 "time3=0, time4=0, time5=0, time6=0, time7=0, time8=0, time9=0, time10=0, time11=0,"
00326 "time12=0, time13=0, time14=0, time15=0, time16=0, time17=0, time18=0, time19=0, "
00327 "time20=0, time21=0, time22=0, time23=0 "
00328 "WHERE type='monthly';"
00329 "OPTIMIZE TABLE `" + prefix + "chanstats`;"
00330 "END;";
00331 this->RunQuery(query);
00332 }
00333
00334
00335 public:
00336 MChanstats(const Anope::string &modname, const Anope::string &creator) :
00337 Module(modname, creator, CORE), sql("", ""), sqlinterface(this)
00338 {
00339 this->SetAuthor("Anope");
00340
00341 Implementation i[] = { I_OnPrivmsg,
00342 I_OnUserKicked,
00343 I_OnChannelModeSet,
00344 I_OnChannelModeUnset,
00345 I_OnTopicUpdated,
00346 I_OnDelCore,
00347 I_OnChangeCoreDisplay,
00348 I_OnChanDrop,
00349 I_OnReload};
00350 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00351 this->OnReload();
00352 }
00353
00354 void OnReload() anope_override
00355 {
00356 ConfigReader config;
00357 prefix = config.ReadValue("chanstats", "prefix", "anope_", 0);
00358 SmileysHappy = config.ReadValue("chanstats", "SmileysHappy", ":) :-) ;) :D :-D", 0);
00359 SmileysSad = config.ReadValue("chanstats", "SmileysSad", ":( :-( ;( ;-(", 0);
00360 SmileysOther = config.ReadValue("chanstats", "SmileysOther", ":/", 0);
00361
00362 Anope::string engine = config.ReadValue("chanstats", "engine", "", 0);
00363 this->sql = ServiceReference<SQL::Provider>("SQL::Provider", engine);
00364 if (sql)
00365 this->CheckTables();
00366 else
00367 Log(this) << "no database connection to " << engine;
00368 }
00369
00370 void OnTopicUpdated(Channel *c, const Anope::string &user, const Anope::string &topic) anope_override
00371 {
00372 User *u = User::Find(user);
00373 if (!u || !u->Account() || !c->ci || !c->ci->HasExt("STATS"))
00374 return;
00375 query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);";
00376 query.SetValue("channel", c->name);
00377 query.SetValue("nick", GetDisplay(u));
00378 this->RunQuery(query);
00379 }
00380
00381 EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, const Anope::string &, const Anope::string ¶m) anope_override
00382 {
00383 this->OnModeChange(c, setter.GetUser());
00384 return EVENT_CONTINUE;
00385 }
00386
00387 EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, const Anope::string &, const Anope::string ¶m) anope_override
00388 {
00389 this->OnModeChange(c, setter.GetUser());
00390 return EVENT_CONTINUE;
00391 }
00392
00393 private:
00394 void OnModeChange(Channel *c, User *u)
00395 {
00396 if (!u || !u->Account() || !c->ci || !c->ci->HasExt("STATS"))
00397 return;
00398
00399 query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);";
00400 query.SetValue("channel", c->name);
00401 query.SetValue("nick", GetDisplay(u));
00402 this->RunQuery(query);
00403 }
00404 public:
00405 void OnUserKicked(Channel *c, User *target, MessageSource &source, const Anope::string &kickmsg) anope_override
00406 {
00407 if (!c->ci || !c->ci->HasExt("STATS"))
00408 return;
00409
00410 query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0);";
00411 query.SetValue("channel", c->name);
00412 query.SetValue("nick", GetDisplay(target));
00413 this->RunQuery(query);
00414
00415 query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);";
00416 query.SetValue("channel", c->name);
00417 query.SetValue("nick", GetDisplay(source.GetUser()));
00418 this->RunQuery(query);
00419 }
00420 void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
00421 {
00422 if (!c->ci || !c->ci->HasExt("STATS") || (msg[0] == Config->BSFantasyCharacter[0]))
00423 return;
00424
00425 size_t letters = msg.length();
00426 size_t words = this->CountWords(msg);
00427
00428 size_t action = 0;
00429 if (msg.find("\01ACTION")!=Anope::string::npos)
00430 {
00431 action = 1;
00432 letters = letters - 7;
00433 words--;
00434 }
00435
00436
00437 size_t smileys_happy = CountSmileys(msg, SmileysHappy);
00438 size_t smileys_sad = CountSmileys(msg, SmileysSad);
00439 size_t smileys_other = CountSmileys(msg, SmileysOther);
00440
00441
00442 words = words - smileys_happy - smileys_sad - smileys_other;
00443 query = "CALL " + prefix + "chanstats_proc_update(@channel@, @nick@, 1, @letters@, @words@, @action@, "
00444 "@smileys_happy@, @smileys_sad@, @smileys_other@, '0', '0', '0', '0');";
00445 query.SetValue("channel", c->name);
00446 query.SetValue("nick", GetDisplay(u));
00447 query.SetValue("letters", letters);
00448 query.SetValue("words", words);
00449 query.SetValue("action", action);
00450 query.SetValue("smileys_happy", smileys_happy);
00451 query.SetValue("smileys_sad", smileys_sad);
00452 query.SetValue("smileys_other", smileys_other);
00453 this->RunQuery(query);
00454 }
00455 void OnDelCore(NickCore *nc) anope_override
00456 {
00457 query = "DELETE FROM `" + prefix + "chanstats` WHERE `nick` = @nick@;";
00458 query.SetValue("nick", nc->display);
00459 this->RunQuery(query);
00460 }
00461 void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override
00462 {
00463 query = "CALL " + prefix + "chanstats_proc_chgdisplay(@old_display@, @new_display@);";
00464 query.SetValue("old_display", nc->display);
00465 query.SetValue("new_display", newdisplay);
00466 this->RunQuery(query);
00467 }
00468 void OnChanDrop(ChannelInfo *ci) anope_override
00469 {
00470 query = "DELETE FROM `" + prefix + "chanstats` WHERE `chan` = @channel@;";
00471 query.SetValue("channel", ci->name);
00472 this->RunQuery(query);
00473 }
00474 };
00475
00476 MODULE_INIT(MChanstats)
00477