m_chanstats.cpp

Go to the documentation of this file.
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                 /* There is no CREATE OR REPLACE PROCEDURE in MySQL */
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                 /* dont prepend any database prefix to events so we can always delete/change old events */
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 &param) 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 &param) 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                 // count smileys
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                 // do not count smileys as words
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