1
0
mirror of https://github.com/anope/anope.git synced 2026-07-02 21:23:12 +02:00

Added Chanstats. It uses a new, improved database format and is not compatible with current phpdenora or magirc installations.

This commit is contained in:
DukePyrolator
2012-04-08 12:43:34 +02:00
parent 9e1fda2a44
commit deb5196101
25 changed files with 983 additions and 43 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Only install example.chk and example.conf from this directory
# NOTE: I would've had this just find all files in the directory, but that would include files not needed (like this file)
set(DATA example.chk botserv.example.conf example.conf hostserv.example.conf modules.example.conf operserv.example.conf chanserv.example.conf global.example.conf memoserv.example.conf nickserv.example.conf)
set(DATA example.chk botserv.example.conf example.conf hostserv.example.conf modules.example.conf operserv.example.conf chanserv.example.conf global.example.conf memoserv.example.conf nickserv.example.conf chanstats.example.conf)
install(FILES ${DATA}
DESTINATION data
)
+63
View File
@@ -0,0 +1,63 @@
/*
* Example configuration file for chanstats
* Make sure BotServ, ChanServ and NickServ are running.
*/
module { name = "m_chanstats" }
chanstats
{
/*
* WARNING: DO NOT USE THE SAME DATABASE AS db_mysql! db_mysql drops ALL tables in the db!
* For this, we add a second mysql{ } block after the chanstats{ } configuration block
* and give it another engine name, so m_mysql opens a second connection to the mysql server.
* /
/*
* The name of this engine.
* This must match with the name in the mysql{ } block
*/
engine = "mysql/chanstats"
smileyshappy = ":) :-) ;) :D :-D"
smileyssad = ":( :-( ;( ;-("
smileysother = ":/"
/*
* Enable Chanstats for new registered nicks / channels
* set it to 0 to disable it.
*/
NSDefChanstats = 1
CSDefChanstats = 1
}
mysql
{
name = "mysql/chanstats"
database = "anope"
server = "127.0.0.1"
username = "anope"
password = "anope"
port = 3306
}
module { name = "cs_set_chanstats" }
command { service = "ChanServ"; name = "SET CHANSTATS"; command = "chanserv/set/chanstats"; }
module { name = "ns_set_chanstats" }
command { service = "NickServ"; name = "SET CHANSTATS"; command = "nickserv/set/chanstats"; }
module { name = "cs_fantasy_stats" }
command { service = "ChanServ"; name = "STATS"; command = "chanserv/stats"; }
command { service = "ChanServ"; name = "GSTATS"; command = "chanserv/gstats"; }
module { name = "cs_fantasy_top" }
command { service = "ChanServ"; name = "TOP"; command = "chanserv/top"; }
command { service = "ChanServ"; name = "TOP10"; command = "chanserv/top10"; }
command { service = "ChanServ"; name = "GTOP"; command = "chanserv/gtop"; }
command { service = "ChanServ"; name = "GTOP10"; command = "chanserv/gtop10"; }
+12 -2
View File
@@ -1172,7 +1172,17 @@ module { name = "enc_md5" }
/* Extra (optional) modules */
include
{
type = "file"
name = "modules.example.conf"
type = "file"
name = "modules.example.conf"
}
/*
* Chanstats Modules
* Requires a MySQL Database
*/
#include
#{
# type = "file"
# name = "chanstats.example.conf"
#}
+3 -1
View File
@@ -97,6 +97,8 @@ enum NickCoreFlag
/* If set means the nick core does not have their email addrses confirmed.
*/
NI_UNCONFIRMED,
/* Chanstats are enabled for this user */
NI_STATS,
NI_END
};
@@ -104,7 +106,7 @@ enum NickCoreFlag
const Anope::string NickCoreFlagStrings[] = {
"BEGIN", "KILLPROTECT", "SECURE", "MSG", "MEMO_HARDMAX", "MEMO_SIGNON", "MEMO_RECEIVE",
"PRIVATE", "HIDE_EMAIL", "HIDE_MASK", "HIDE_QUIT", "KILL_QUICK", "KILL_IMMED",
"MEMO_MAIL", "HIDE_STATUS", "SUSPENDED", "AUTOOP", "FORBIDDEN", "UNCONFIRMED", ""
"MEMO_MAIL", "HIDE_STATUS", "SUSPENDED", "AUTOOP", "FORBIDDEN", "UNCONFIRMED", "STATS", ""
};
class CoreExport NickAlias : public Base, public Extensible, public Flags<NickNameFlag, NS_END>, public Serializable
+4 -2
View File
@@ -135,18 +135,20 @@ class CoreExport Channel : public Base, public Extensible, public Flags<ChannelF
std::pair<ModeList::iterator, ModeList::iterator> GetModeList(ChannelModeName Name);
/** Set a mode internally on a channel, this is not sent out to the IRCd
* @param setter The setter
* @param cm The mode
* @param param The param
* @param EnforceMLock true if mlocks should be enforced, false to override mlock
*/
void SetModeInternal(ChannelMode *cm, const Anope::string &param = "", bool EnforceMLock = true);
void SetModeInternal(User *setter, ChannelMode *cm, const Anope::string &param = "", bool EnforceMLock = true);
/** Remove a mode internally on a channel, this is not sent out to the IRCd
* @param setter The Setter
* @param cm The mode
* @param param The param
* @param EnforceMLock true if mlocks should be enforced, false to override mlock
*/
void RemoveModeInternal(ChannelMode *cm, const Anope::string &param = "", bool EnforceMLock = true);
void RemoveModeInternal(User *setter, ChannelMode *cm, const Anope::string &param = "", bool EnforceMLock = true);
/** Set a mode on a channel
* @param bi The client setting the modes
+6 -3
View File
@@ -439,9 +439,10 @@ class CoreExport Module : public Extensible
/** Called when a new topic is set
* @param c The channel
* @param setter The user who set the new topic
* @param topic The new topic
*/
virtual void OnTopicUpdated(Channel *c, const Anope::string &topic) { }
virtual void OnTopicUpdated(Channel *c, User *setter, const Anope::string &topic) { }
/** Called before a channel expires
* @param ci The channel
@@ -847,19 +848,21 @@ class CoreExport Module : public Extensible
/** Called when a mode is set on a channel
* @param c The channel
* @param setter The user who is setting the mode
* @param Name The mode name
* @param param The mode param, if there is one
* @return EVENT_STOP to make mlock/secureops etc checks not happen
*/
virtual EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string &param) { return EVENT_CONTINUE; }
virtual EventReturn OnChannelModeSet(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) { return EVENT_CONTINUE; }
/** Called when a mode is unset on a channel
* @param c The channel
* @param setter the user who is unsetting the mode
* @param Name The mode name
* @param param The mode param, if there is one
* @return EVENT_STOP to make mlock/secureops etc checks not happen
*/
virtual EventReturn OnChannelModeUnset(Channel *c, ChannelModeName Name, const Anope::string &param) { return EVENT_CONTINUE; }
virtual EventReturn OnChannelModeUnset(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) { return EVENT_CONTINUE; }
/** Called when a mode is set on a user
* @param u The user
+3 -1
View File
@@ -57,6 +57,8 @@ enum ChannelInfoFlag
* is set or not
*/
CI_PERSIST,
/* Chanstats are enabled */
CI_STATS,
CI_END
};
@@ -64,7 +66,7 @@ enum ChannelInfoFlag
const Anope::string ChannelInfoFlagStrings[] = {
"BEGIN", "KEEPTOPIC", "SECUREOPS", "PRIVATE", "TOPICLOCK", "RESTRICTED",
"PEACE", "SECURE", "NO_EXPIRE", "MEMO_HARDMAX", "SECUREFOUNDER",
"SIGNKICK", "SIGNKICK_LEVEL", "SUSPENDED", "PERSIST", ""
"SIGNKICK", "SIGNKICK_LEVEL", "SUSPENDED", "PERSIST", "STATS", ""
};
/** Flags for badwords
+168
View File
@@ -0,0 +1,168 @@
/* Chanstats core functions
*
* (C) 2003-2012 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
/*************************************************************************/
#include "module.h"
#include "../extra/sql.h"
class MySQLInterface : public SQLInterface
{
public:
MySQLInterface(Module *o) : SQLInterface(o) { }
void OnResult(const SQLResult &r) anope_override
{
}
void OnError(const SQLResult &r) anope_override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
else
Log(LOG_DEBUG) << "Chanstats: Error executing query: " << r.GetError();
}
};
class CommandCSStats : public Command
{
public:
CommandCSStats(Module *creator) : Command (creator, "chanserv/stats", 0, 2)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays your Channel Stats"));
this->SetSyntax(_("\037nick\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CommandCSGStats : public Command
{
public:
CommandCSGStats(Module *creator) : Command (creator, "chanserv/gstats", 0, 2)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays your Global Stats"));
this->SetSyntax(_("\037nick\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CSStats;
static CSStats *me;
class CSStats : public Module
{
CommandCSStats commandcsstats;
CommandCSGStats commandcsgstats;
service_reference<SQLProvider> sql;
MySQLInterface sqlinterface;
public:
CSStats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
commandcsstats(this), commandcsgstats(this), sql("", ""), sqlinterface(this)
{
me = this;
this->SetAuthor("Anope");
Implementation i[] = { I_OnReload };
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
this->OnReload();
}
void OnReload() anope_override
{
ConfigReader config;
Anope::string engine = config.ReadValue("chanstats", "engine", "", 0);
this->sql = service_reference<SQLProvider>("SQLProvider", engine);
}
SQLResult RunQuery(const SQLQuery &query)
{
if (!this->sql)
throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?");
SQLResult res = this->sql->RunQuery(query);
if (!res.GetError().empty())
throw SQLException(res.GetError());
return res;
}
void DoStats(CommandSource &source, const bool is_global, const std::vector<Anope::string> &params)
{
if (!source.u || !source.c)
return;
Anope::string display;
if (params.empty())
display = source.u->Account()->display;
else if (NickAlias *na = findnick(params[0]))
display = na->nc->display;
else
{
source.Reply(_("%s not found."), params[0].c_str());
return;
}
try
{
SQLQuery query;
if (is_global)
query = "SELECT sum(letters) as letters, sum(words) as words, sum(line) as line,"
" sum(smileys) as smileys, sum(actions) as actions"
" FROM `anope_bs_chanstats_view_sum_all`"
" WHERE `nickserv_display` = @display@";
else
{
query = "SELECT letters, words, line, smileys, actions "
" FROM `anope_bs_chanstats_view_sum_all` "
" WHERE `nickserv_display` = @display@ AND `chanserv_name` = @channel@;";
query.setValue("channel", source.c->ci->name);
}
query.setValue("display", display);
SQLResult res = this->RunQuery(query);
if (res.Rows() > 0)
{
if (is_global)
source.Reply(_("Network stats for %s:"), display.c_str());
else
source.Reply(_("Channel stats for %s on %s:"), display.c_str(), source.c->name.c_str());
source.Reply(_("letters: %s, words: %s, lines: %s, smileys %s, actions: %s"),
res.Get(0, "letters").c_str(), res.Get(0, "words").c_str(),
res.Get(0, "line").c_str(), res.Get(0, "smileys").c_str(),
res.Get(0, "actions").c_str());
}
else
source.Reply(_("No stats for %s"), display.c_str());
}
catch (const SQLException &ex)
{
Log(LOG_DEBUG) << ex.GetReason();
}
}
};
void CommandCSStats::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoStats(source, false, params);
}
void CommandCSGStats::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoStats(source, true, params);
}
MODULE_INIT(CSStats)
+213
View File
@@ -0,0 +1,213 @@
/* Chanstats core functions
*
* (C) 2003-2012 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
/*************************************************************************/
#include "module.h"
#include "../extra/sql.h"
class MySQLInterface : public SQLInterface
{
public:
MySQLInterface(Module *o) : SQLInterface(o) { }
void OnResult(const SQLResult &r) anope_override
{
}
void OnError(const SQLResult &r) anope_override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
else
Log(LOG_DEBUG) << "Chanstats: Error executing query: " << r.GetError();
}
};
class CommandCSTop : public Command
{
public:
CommandCSTop(Module *creator) : Command (creator, "chanserv/top", 0, 2)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays the top 3 users of a channel"));
this->SetSyntax(_("\037channel\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CommandCSTop10 : public Command
{
public:
CommandCSTop10(Module *creator) : Command (creator, "chanserv/top10", 0, 2)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays the top 10 users of a channel"));
this->SetSyntax(_("\037channel\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CommandCSGTop : public Command
{
public:
CommandCSGTop(Module *creator) : Command (creator, "chanserv/gtop", 0, 1)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays the top 3 users of the network"));
this->SetSyntax("");
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CommandCSGTop10 : public Command
{
public:
CommandCSGTop10(Module *creator) : Command (creator, "chanserv/gtop10", 0, 1)
{
this->SetFlag(CFLAG_STRIP_CHANNEL);
this->SetDesc(_("Displays the top 10 users of the network"));
this->SetSyntax("");
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params);
};
class CSTop;
static CSTop *me;
class CSTop : public Module
{
CommandCSTop commandcstop;
CommandCSGTop commandcsgtop;
CommandCSTop10 commandcstop10;
CommandCSGTop10 commandcsgtop10;
service_reference<SQLProvider> sql;
MySQLInterface sqlinterface;
public:
CSTop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
commandcstop(this), commandcsgtop(this), commandcstop10(this), commandcsgtop10(this), sql("", ""),
sqlinterface(this)
{
me = this;
this->SetAuthor("Anope");
Implementation i[] = { I_OnReload };
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
this->OnReload();
}
void OnReload() anope_override
{
ConfigReader config;
Anope::string engine = config.ReadValue("chanstats", "engine", "", 0);
this->sql = service_reference<SQLProvider>("SQLProvider", engine);
}
SQLResult RunQuery(const SQLQuery &query)
{
if (!this->sql)
throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?");
SQLResult res = sql->RunQuery(query);
if (!res.GetError().empty())
throw SQLException(res.GetError());
return res;
}
void DoTop(CommandSource &source, const std::vector<Anope::string> &params, bool is_global, int limit = 1)
{
if (!source.u || !source.c || !source.c->ci)
return;
Anope::string channel;
if (is_global || params.empty())
channel = source.c->ci->name;
else if (!params.empty())
channel = params[0];
else
{
source.Reply(_("%s not found."), params[0].c_str());
return;
}
try
{
SQLQuery query;
if (is_global)
{
query = Anope::printf("SELECT nickserv_display, sum(letters) as letters, sum(words) as words,"
" sum(line) as line, sum(smileys) as smileys, sum(actions) as actions"
" FROM `anope_bs_chanstats_view_sum_all`"
" WHERE nickserv_display IS NOT NULL"
" GROUP BY nickserv_display ORDER BY letters DESC LIMIT %i;", limit);
}
else
{
query = Anope::printf("SELECT nickserv_display, sum(letters) as letters, sum(words) as words,"
" sum(line) as line, sum(smileys) as smileys, sum(actions) as actions"
" FROM `anope_bs_chanstats_view_sum_all`"
" WHERE nickserv_display IS NOT NULL AND `chanserv_name` = @channel@"
" GROUP BY nickserv_display ORDER BY letters DESC LIMIT %i;", limit);
query.setValue("channel", channel.c_str());
}
SQLResult res = this->RunQuery(query);
if (res.Rows() > 0)
{
source.Reply(_("Top %i of %s"), limit, (is_global ? "Network" : channel.c_str()));
for (int i = 0; i < res.Rows(); ++i)
{
source.Reply(_("%2lu \002%-16s\002 letters: %s, words: %s, lines: %s, smileys %s, actions: %s"),
i+1, res.Get(i, "nickserv_display").c_str(), res.Get(i, "letters").c_str(),
res.Get(i, "words").c_str(), res.Get(i, "line").c_str(),
res.Get(0, "smileys").c_str(), res.Get(0, "actions").c_str());
}
}
else
source.Reply(_("No stats for %s"), is_global ? "Network" : channel.c_str());
}
catch (const SQLException &ex)
{
Log(LOG_DEBUG) << ex.GetReason();
}
}
};
void CommandCSTop::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoTop(source, params, false, 3);
}
void CommandCSTop10::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoTop(source, params, false, 10);
}
void CommandCSGTop::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoTop(source, params, true, 3);
}
void CommandCSGTop10::Execute(CommandSource &source, const std::vector<Anope::string> &params)
{
me->DoTop(source, params, true, 10);
}
MODULE_INIT(CSTop)
+1
View File
@@ -94,6 +94,7 @@ class CommandCSInfo : public Command
CheckOptStr(optbuf, CI_TOPICLOCK, _("Topic Lock"), ci, u->Account());
CheckOptStr(optbuf, CI_PERSIST, _("Persistant"), ci, u->Account());
CheckOptStr(optbuf, CI_NO_EXPIRE, _("No expire"), ci, u->Account());
CheckOptStr(optbuf, CI_STATS, _("Chanstats"), ci, u->Account());
info["Options"] = optbuf.empty() ? _("None") : optbuf;
info["Mode lock"] = ci->GetMLockAsString(true);
+90
View File
@@ -0,0 +1,90 @@
/* NickServ core functions
*
* (C) 2003-2012 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
/*************************************************************************/
#include "module.h"
class CommandCSSetChanstats : public Command
{
public:
CommandCSSetChanstats(Module *creator) : Command(creator, "chanserv/set/chanstats", 2, 2)
{
this->SetDesc(_("Turn chanstat statistics on or off"));
this->SetSyntax(_("\037channel\037 {ON | OFF}"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
User *u = source.u;
ChannelInfo *ci = cs_findchan(params[0]);
if (!ci)
{
source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
return;
}
if (source.permission.empty() && !ci->AccessFor(u).HasPriv("SET"))
{
source.Reply(ACCESS_DENIED);
return;
}
if (params[1].equals_ci("ON"))
{
ci->SetFlag(CI_STATS);
source.Reply(_("Chanstats statistics are now enabled for this channel"));
}
else if (params[1].equals_ci("OFF"))
{
ci->UnsetFlag(CI_STATS);
source.Reply(_("Chanstats statistics are now disabled for this channel"));
}
else
this->OnSyntaxError(source, "");
return;
}
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply("Turn Chanstats channel statistics ON or OFF");
return true;
}
};
class CSSetChanstats : public Module
{
CommandCSSetChanstats commandcssetchanstats;
bool CSDefChanstats;
public:
CSSetChanstats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
commandcssetchanstats(this)
{
this->SetAuthor("Anope");
Implementation i[] = { I_OnReload, I_OnChanRegistered };
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
this->OnReload();
}
void OnReload() anope_override
{
ConfigReader config;
CSDefChanstats = config.ReadFlag("chanstats", "CSDefChanstats", "0", 0);
}
void OnChanRegistered(ChannelInfo *ci) anope_override
{
if (CSDefChanstats)
ci->SetFlag(CI_STATS);
}
};
MODULE_INIT(CSSetChanstats)
+1
View File
@@ -120,6 +120,7 @@ class CommandNSInfo : public Command
CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_MSG, _("Message mode"), na->nc);
CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_AUTOOP, _("Auto-op"), na->nc);
CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_SUSPENDED, _("Suspended"), na->nc);
CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_STATS, _("Chanstats"), na->nc);
CheckOptStr<NickNameFlag, NS_END>(u, optbuf, NS_NO_EXPIRE, _("No expire"), na);
info[_("Options")] = optbuf.empty() ? _("None") : optbuf;
+115
View File
@@ -0,0 +1,115 @@
/* NickServ core functions
*
* (C) 2003-2012 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
/*************************************************************************/
#include "module.h"
class CommandNSSetChanstats : public Command
{
public:
CommandNSSetChanstats(Module *creator, const Anope::string &sname = "nickserv/set/chanstats", size_t min = 1 ) : Command(creator, sname, min, min + 1)
{
this->SetDesc(_("Turn chanstat statistic on or off"));
this->SetSyntax(_("{ON | OFF}"));
}
void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
{
NickAlias *na = findnick(user);
if (!na)
{
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
return;
}
if (param.equals_ci("ON"))
{
na->nc->SetFlag(NI_STATS);
source.Reply(_("Chanstat statistics are now enabled for your nick"));
}
else if (param.equals_ci("OFF"))
{
na->nc->UnsetFlag(NI_STATS);
source.Reply(_("Chanstat statistics are now disabled for your nick"));
}
else
this->OnSyntaxError(source, "CHANSTATS");
return;
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
this->Run(source, source.u->Account()->display, params[0]);
}
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_("Turns Chanstats statistics ON or OFF"));
return true;
}
};
class CommandNSSASetChanstats : public CommandNSSetChanstats
{
public:
CommandNSSASetChanstats(Module *creator) : CommandNSSetChanstats(creator, "nickserv/saset/chanstats", 2)
{
this->ClearSyntax();
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
this->Run(source, params[0], params[1]);
}
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_("Turns chanstats channel statistics ON or OFF for this user"));
return true;
}
};
class NSSetChanstats : public Module
{
CommandNSSetChanstats commandnssetchanstats;
CommandNSSASetChanstats commandnssasetchanstats;
bool NSDefChanstats;
public:
NSSetChanstats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
commandnssetchanstats(this), commandnssasetchanstats(this)
{
this->SetAuthor("Anope");
Implementation i[] = { I_OnReload, I_OnNickRegister };
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
this->OnReload();
}
void OnReload() anope_override
{
ConfigReader config;
NSDefChanstats = config.ReadFlag("chanstats", "NSDefChanstats", "0", 0);
}
void OnNickRegister(NickAlias *na) anope_override
{
if (NSDefChanstats)
na->nc->SetFlag(NI_STATS);
}
};
MODULE_INIT(NSSetChanstats)
+2 -2
View File
@@ -438,7 +438,7 @@ class OSDefcon : public Module
return EVENT_CONTINUE;
}
EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string &param) anope_override
EventReturn OnChannelModeSet(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) anope_override
{
ChannelMode *cm = ModeManager::FindChannelModeByName(Name);
@@ -452,7 +452,7 @@ class OSDefcon : public Module
return EVENT_CONTINUE;
}
EventReturn OnChannelModeUnset(Channel *c, ChannelModeName Name, const Anope::string &) anope_override
EventReturn OnChannelModeUnset(Channel *c, User *setter, ChannelModeName Name, const Anope::string &) anope_override
{
ChannelMode *cm = ModeManager::FindChannelModeByName(Name);
+268
View File
@@ -0,0 +1,268 @@
#include "module.h"
#include "../extra/sql.h"
class MySQLInterface : public SQLInterface
{
public:
MySQLInterface(Module *o) : SQLInterface(o) { }
void OnResult(const SQLResult &r) anope_override
{
}
void OnError(const SQLResult &r) anope_override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
else
Log(LOG_DEBUG) << "Chanstats: Error executing query: " << r.GetError();
}
};
class MChanstats : public Module
{
service_reference<SQLProvider> sql;
MySQLInterface sqlinterface;
SQLQuery query;
Anope::string SmileysHappy, SmileysSad, SmileysOther;
std::vector<Anope::string> TableList;
void RunQuery(const SQLQuery &q)
{
if (sql)
sql->Run(&sqlinterface, q);
}
void SetQuery()
{
query = "INSERT DELAYED INTO `anope_bs_chanstats` (chanserv_name, nickserv_display, day, hour, @what@) "
"VALUES (@chanserv_name@, @nickserv_display@, CURRENT_DATE, HOUR(NOW()), '1') "
"ON DUPLICATE KEY UPDATE @what@=@what@+1;";
}
size_t CountWords(const Anope::string &msg)
{
size_t words = 0;
for (size_t pos = 0; pos != Anope::string::npos; pos = msg.find(" ", pos+1))
words++;
return words;
}
size_t CountSmileys(const Anope::string &msg, const Anope::string &smileylist)
{
size_t smileys = 0;
spacesepstream sep(smileylist);
Anope::string buf;
while (sep.GetToken(buf) && !buf.empty())
{
for (size_t pos = msg.find(buf, 0); pos != Anope::string::npos; pos = msg.find(buf, pos+1))
smileys++;
}
return smileys;
}
void GetTables()
{
TableList.clear();
SQLResult r = this->sql->RunQuery(this->sql->GetTables());
for (int i = 0; i < r.Rows(); ++i)
{
const std::map<Anope::string, Anope::string> &map = r.Row(i);
for (std::map<Anope::string, Anope::string>::const_iterator it = map.begin(); it != map.end(); ++it)
TableList.push_back(it->second);
}
}
bool HasTable(const Anope::string &table)
{
for (std::vector<Anope::string>::const_iterator it = TableList.begin(); it != TableList.end(); ++it)
if (*it == table)
return true;
return false;
}
void CheckTables()
{
this->GetTables();
if (!this->HasTable("anope_bs_chanstats"))
{
query = "CREATE TABLE `anope_bs_chanstats` ("
"`id` int(11) NOT NULL AUTO_INCREMENT,"
"`chanserv_name` varchar(255) NOT NULL DEFAULT '',"
"`nickserv_display` varchar(255) NOT NULL DEFAULT '',"
"`day` date NOT NULL,"
"`hour` tinyint(2) NOT NULL,"
"`letters` int(10) unsigned NOT NULL DEFAULT '0',"
"`words` int(10) unsigned NOT NULL DEFAULT '0',"
"`line` int(10) unsigned NOT NULL DEFAULT '0',"
"`actions` int(10) unsigned NOT NULL DEFAULT '0',"
"`smileys_happy` int(10) unsigned NOT NULL DEFAULT '0',"
"`smileys_sad` int(10) unsigned NOT NULL DEFAULT '0',"
"`smileys_other` int(10) unsigned NOT NULL DEFAULT '0',"
"`kicks` int(10) unsigned NOT NULL DEFAULT '0',"
"`kicked` int(10) unsigned NOT NULL DEFAULT '0',"
"`modes` int(10) unsigned NOT NULL DEFAULT '0',"
"`topics` int(10) unsigned NOT NULL DEFAULT '0',"
"PRIMARY KEY (`id`),"
"UNIQUE KEY `chanserv_name` (`chanserv_name`,`nickserv_display`,`day`,`hour`),"
"KEY `nickserv_display` (`nickserv_display`), "
"KEY `day` (`day`),"
"KEY `hour` (`hour`)"
") ENGINE=InnoDB DEFAULT CHARSET=utf8;";
this->RunQuery(query);
}
if (!this->HasTable("anope_bs_chanstats_view_sum_all"))
{
query = "CREATE OR REPLACE VIEW `anope_bs_chanstats_view_sum_all` AS "
"SELECT `anope_bs_chanstats`.`chanserv_name` AS `chanserv_name`,"
"`anope_bs_chanstats`.`nickserv_display` AS `nickserv_display`,"
"sum(`anope_bs_chanstats`.`letters`) AS `letters`,"
"sum(`anope_bs_chanstats`.`words`) AS `words`,"
"sum(`anope_bs_chanstats`.`line`) AS `line`,"
"sum(`anope_bs_chanstats`.`actions`) AS `actions`,"
"((sum(`anope_bs_chanstats`.`smileys_happy`) "
"+ sum(`anope_bs_chanstats`.`smileys_sad`)) "
"+ sum(`anope_bs_chanstats`.`smileys_other`)) AS `smileys`,"
"sum(`anope_bs_chanstats`.`smileys_happy`) AS `smileys_happy`,"
"sum(`anope_bs_chanstats`.`smileys_sad`) AS `smileys_sad`,"
"sum(`anope_bs_chanstats`.`smileys_other`) AS `smileys_other`,"
"sum(`anope_bs_chanstats`.`kicks`) AS `kicks`,"
"sum(`anope_bs_chanstats`.`kicked`) AS `kicked`,"
"sum(`anope_bs_chanstats`.`modes`) AS `modes`,"
"sum(`anope_bs_chanstats`.`topics`) AS `topics` "
"FROM `anope_bs_chanstats` "
"GROUP BY `anope_bs_chanstats`.`chanserv_name`,`anope_bs_chanstats`.`nickserv_display`;";
this->RunQuery(query);
}
}
public:
MChanstats(const Anope::string &modname, const Anope::string &creator) :
Module(modname, creator, CORE), sql("", ""), sqlinterface(this)
{
this->SetAuthor("Anope");
Implementation i[] = { I_OnPrivmsg,
I_OnUserKicked,
I_OnChannelModeSet,
I_OnChannelModeUnset,
I_OnTopicUpdated,
I_OnReload};
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
this->OnReload();
}
void OnReload() anope_override
{
ConfigReader config;
SmileysHappy = config.ReadValue("chanstats", "SmileysHappy", ":) :-) ;) :D :-D", 0);
SmileysSad = config.ReadValue("chanstats", "SmileysSad", ":( :-( ;( ;-(", 0);
SmileysOther = config.ReadValue("chanstats", "SmileysOther", ":/", 0);
Anope::string engine = config.ReadValue("chanstats", "engine", "", 0);
this->sql = service_reference<SQLProvider>("SQLProvider", engine);
this->CheckTables();
}
void OnTopicUpdated(Channel *c, User *u, const Anope::string &topic) anope_override
{
if (!u || !u->Account() || !c->ci || !c->ci->HasFlag(CI_STATS))
return;
bool has_display = u->Account()->HasFlag(NI_STATS);
this->SetQuery();
query.setValue("chanserv_name", c->name);
query.setValue("nickserv_display", has_display ? u->Account()->display : "");
query.setValue("what", "topics", false);
this->RunQuery(query);
}
EventReturn OnChannelModeSet(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) anope_override
{
this->OnModeChange(c, setter);
return EVENT_CONTINUE;
}
EventReturn OnChannelModeUnset(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) anope_override
{
this->OnModeChange(c, setter);
return EVENT_CONTINUE;
}
void OnModeChange(Channel *c, User *u)
{
if (!u || !u->Account() || !c->ci || !c->ci->HasFlag(CI_STATS))
return;
bool has_display = u->Account()->HasFlag(NI_STATS);
this->SetQuery();
query.setValue("chanserv_name", c->name);
query.setValue("nickserv_display", has_display ? u->Account()->display : "");
query.setValue("what", "modes", false);
this->RunQuery(query);
}
void OnUserKicked(Channel *c, User *target, const Anope::string &source, const Anope::string &kickmsg) anope_override
{
if (!c->ci || !c->ci->HasFlag(CI_STATS))
return;
bool has_display = target && target->Account() && target->Account()->HasFlag(NI_STATS);
this->SetQuery();
query.setValue("chanserv_name", c->name);
query.setValue("nickserv_display", has_display ? target->Account()->display : "");
query.setValue("what", "kicked", false);
this->RunQuery(query);
User *kicker = finduser(source);
has_display = kicker && kicker->Account() && kicker->Account()->HasFlag(NI_STATS);
this->SetQuery();
query.setValue("chanserv_name", c->name);
query.setValue("nickserv_display", has_display ? kicker->Account()->display : "");
query.setValue("what", "kicks", false);
this->RunQuery(query);
}
void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
{
if (!c->ci || !c->ci->HasFlag(CI_STATS) || (msg[0] == Config->BSFantasyCharacter[0]))
return;
size_t letters = msg.length();
size_t words = this->CountWords(msg);
size_t action = 0;
if (msg.find("\01ACTION")!=Anope::string::npos)
{
action = 1;
letters = letters - 7;
words--;
}
// count smileys
size_t smileys_happy = CountSmileys(msg, SmileysHappy);
size_t smileys_sad = CountSmileys(msg, SmileysSad);
size_t smileys_other = CountSmileys(msg, SmileysOther);
// do not count smileys as words
words = words - smileys_happy - smileys_sad - smileys_other;
bool has_display = u && u->Account() && u->Account()->HasFlag(NI_STATS);
query = "INSERT DELAYED INTO `anope_bs_chanstats` (chanserv_name, nickserv_display, day, hour, letters, words, line, actions, smileys_happy, smileys_sad, smileys_other) "
"VALUES (@chanserv_name@, @nickserv_display@, CURRENT_DATE, HOUR(NOW()), @letters@, @words@, 1, @actions@, @smileys_happy@, @smileys_sad@, @smileys_other@) "
"ON DUPLICATE KEY UPDATE letters=letters+VALUES(letters), words=words+VALUES(words), line=line+1, actions=actions+VALUES(actions), "
"smileys_happy=smileys_happy+VALUES(smileys_happy), smileys_sad=smileys_sad+VALUES(smileys_sad), smileys_other=smileys_other+VALUES(smileys_other);";
query.setValue("nickserv_display", has_display ? u->Account()->display : "");
query.setValue("chanserv_name", c->name);
query.setValue("letters", letters);
query.setValue("words", words);
query.setValue("actions", action);
query.setValue("smileys_happy", smileys_happy);
query.setValue("smileys_sad", smileys_sad);
query.setValue("smileys_other", smileys_other);
this->RunQuery(query);
}
};
MODULE_INIT(MChanstats)
+1 -1
View File
@@ -22,7 +22,7 @@ class HelpChannel : public Module
OnReload();
}
EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string &param) anope_override
EventReturn OnChannelModeSet(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) anope_override
{
if (Name == CMODE_OP && c && c->ci && c->name.equals_ci(this->HelpChan))
{
+1 -1
View File
@@ -475,7 +475,7 @@ class BahamutIRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
+1 -1
View File
@@ -468,7 +468,7 @@ class InspircdIRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
+1 -1
View File
@@ -718,7 +718,7 @@ class InspircdIRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
+4 -4
View File
@@ -408,7 +408,7 @@ class PlexusIRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
@@ -514,11 +514,11 @@ bool event_bmask(const Anope::string &source, const std::vector<Anope::string> &
{
Anope::string b = myStrGetToken(bans, ' ', i);
if (ban && params[2].equals_cs("b"))
c->SetModeInternal(ban, b);
c->SetModeInternal(NULL, ban, b);
else if (except && params[2].equals_cs("e"))
c->SetModeInternal(except, b);
c->SetModeInternal(NULL, except, b);
if (invex && params[2].equals_cs("I"))
c->SetModeInternal(invex, b);
c->SetModeInternal(NULL, invex, b);
}
}
return true;
+4 -4
View File
@@ -381,7 +381,7 @@ class RatboxIRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
@@ -505,11 +505,11 @@ bool event_bmask(const Anope::string &source, const std::vector<Anope::string> &
{
Anope::string b = myStrGetToken(bans, ' ', i);
if (ban && params[2].equals_cs("b"))
c->SetModeInternal(ban, b);
c->SetModeInternal(NULL, ban, b);
else if (except && params[2].equals_cs("e"))
c->SetModeInternal(except, b);
c->SetModeInternal(NULL, except, b);
if (invex && params[2].equals_cs("I"))
c->SetModeInternal(invex, b);
c->SetModeInternal(NULL, invex, b);
}
}
return true;
+4 -4
View File
@@ -879,19 +879,19 @@ class Unreal32IRCdMessage : public IRCdMessage
if (keep_their_modes && ban && buf[0] == '&')
{
buf.erase(buf.begin());
c->SetModeInternal(ban, buf);
c->SetModeInternal(NULL, ban, buf);
}
/* Except */
else if (keep_their_modes && except && buf[0] == '"')
{
buf.erase(buf.begin());
c->SetModeInternal(except, buf);
c->SetModeInternal(NULL, except, buf);
}
/* Invex */
else if (keep_their_modes && invex && buf[0] == '\'')
{
buf.erase(buf.begin());
c->SetModeInternal(invex, buf);
c->SetModeInternal(NULL, invex, buf);
}
else
{
@@ -930,7 +930,7 @@ class Unreal32IRCdMessage : public IRCdMessage
* This will enforce secureops etc on the user
*/
for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
c->SetModeInternal(*it, buf);
c->SetModeInternal(NULL, *it, buf);
/* Now set whatever modes this user is allowed to have on the channel */
chan_set_correct_modes(u, c, 1);
+1 -1
View File
@@ -199,7 +199,7 @@ class BotServCore : public Module
"name with one of the following characters: %s."), Config->ChanServ.c_str(), Config->BSFantasyCharacter.c_str());
}
EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string &param) anope_override
EventReturn OnChannelModeSet(Channel *c, User *setter, ChannelModeName Name, const Anope::string &param) anope_override
{
if (Config->BSSmartJoin && Name == CMODE_BAN && c->ci && c->ci->bi && c->FindUser(c->ci->bi))
{
+15 -13
View File
@@ -332,17 +332,18 @@ std::pair<Channel::ModeList::iterator, Channel::ModeList::iterator> Channel::Get
}
/** Set a mode internally on a channel, this is not sent out to the IRCd
* @param setter The user who is setting the mode
* @param cm The mode
* @param param The param
* @param EnforeMLock true if mlocks should be enforced, false to override mlock
*/
void Channel::SetModeInternal(ChannelMode *cm, const Anope::string &param, bool EnforceMLock)
void Channel::SetModeInternal(User *setter, ChannelMode *cm, const Anope::string &param, bool EnforceMLock)
{
if (!cm)
return;
EventReturn MOD_RESULT;
FOREACH_RESULT(I_OnChannelModeSet, OnChannelModeSet(this, cm->Name, param));
FOREACH_RESULT(I_OnChannelModeSet, OnChannelModeSet(this, setter, cm->Name, param));
/* Setting v/h/o/a/q etc */
if (cm->Type == MODE_STATUS)
@@ -406,17 +407,18 @@ void Channel::SetModeInternal(ChannelMode *cm, const Anope::string &param, bool
}
/** Remove a mode internally on a channel, this is not sent out to the IRCd
* @param setter The user who is unsetting the mode
* @param cm The mode
* @param param The param
* @param EnforceMLock true if mlocks should be enforced, false to override mlock
*/
void Channel::RemoveModeInternal(ChannelMode *cm, const Anope::string &param, bool EnforceMLock)
void Channel::RemoveModeInternal(User *setter, ChannelMode *cm, const Anope::string &param, bool EnforceMLock)
{
if (!cm)
return;
EventReturn MOD_RESULT;
FOREACH_RESULT(I_OnChannelModeUnset, OnChannelModeUnset(this, cm->Name, param));
FOREACH_RESULT(I_OnChannelModeUnset, OnChannelModeUnset(this, setter, cm->Name, param));
/* Setting v/h/o/a/q etc */
if (cm->Type == MODE_STATUS)
@@ -535,7 +537,7 @@ void Channel::SetMode(BotInfo *bi, ChannelMode *cm, const Anope::string &param,
}
ModeManager::StackerAdd(bi, this, cm, true, param);
SetModeInternal(cm, param, EnforceMLock);
SetModeInternal(bi ? finduser(bi->nick) : NULL, cm, param, EnforceMLock);
}
/**
@@ -587,7 +589,7 @@ void Channel::RemoveMode(BotInfo *bi, ChannelMode *cm, const Anope::string &para
}
ModeManager::StackerAdd(bi, this, cm, false, realparam);
RemoveModeInternal(cm, realparam, EnforceMLock);
RemoveModeInternal(bi ? finduser(bi->nick) : NULL, cm, realparam, EnforceMLock);
}
/**
@@ -725,9 +727,9 @@ void Channel::SetModesInternal(User *setter, const Anope::string &mode, bool Enf
if (cm->Type == MODE_REGULAR)
{
if (add)
this->SetModeInternal(cm, "", EnforceMLock);
this->SetModeInternal(setter, cm, "", EnforceMLock);
else
this->RemoveModeInternal(cm, "", EnforceMLock);
this->RemoveModeInternal(setter, cm, "", EnforceMLock);
continue;
}
else if (cm->Type == MODE_PARAM)
@@ -736,7 +738,7 @@ void Channel::SetModesInternal(User *setter, const Anope::string &mode, bool Enf
if (!add && cmp->MinusNoArg)
{
this->RemoveModeInternal(cm, "", EnforceMLock);
this->RemoveModeInternal(setter, cm, "", EnforceMLock);
continue;
}
}
@@ -750,9 +752,9 @@ void Channel::SetModesInternal(User *setter, const Anope::string &mode, bool Enf
paramstring += " " + token;
if (add)
this->SetModeInternal(cm, token, EnforceMLock);
this->SetModeInternal(setter, cm, token, EnforceMLock);
else
this->RemoveModeInternal(cm, token, EnforceMLock);
this->RemoveModeInternal(setter, cm, token, EnforceMLock);
}
else
Log() << "warning: Channel::SetModesInternal() recieved more modes requiring params than params, modes: " << mode;
@@ -874,7 +876,7 @@ void Channel::ChangeTopicInternal(const Anope::string &user, const Anope::string
Log(LOG_DEBUG) << "Topic of " << this->name << " changed by " << user << " to " << newtopic;
FOREACH_MOD(I_OnTopicUpdated, OnTopicUpdated(this, this->topic));
FOREACH_MOD(I_OnTopicUpdated, OnTopicUpdated(this, u, this->topic));
if (this->ci)
{
@@ -891,7 +893,7 @@ void Channel::ChangeTopic(const Anope::string &user, const Anope::string &newtop
ircdproto->SendTopic(this->ci->WhoSends(), this);
FOREACH_MOD(I_OnTopicUpdated, OnTopicUpdated(this, this->topic));
FOREACH_MOD(I_OnTopicUpdated, OnTopicUpdated(this, u, this->topic));
if (this->ci)
{
+1 -1
View File
@@ -70,7 +70,7 @@ Server::Server(Server *uplink, const Anope::string &name, unsigned hops, const A
if (cm == NULL)
cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_modes[j]));
if (cm && cm->Type == MODE_STATUS)
c->SetModeInternal(cm, bi->nick);
c->SetModeInternal(bi ? finduser(bi->nick) : NULL, cm, bi->nick);
}
}
}