1
/**vim: ts=4, wrap, tw=80
2
* --- libkvistat.cpp ---
4
* This is a plugin for KVirc 2.0 Phoenix. It allows you to see
5
* statistics of your IRC sessions - total words said etc.
6
* More features are still to come.
8
* (c) Krzysztof Godlewski <kristoff@poczta.wprost.pl>
10
* This program is FREE software. You can redistribute it and/or
11
* modify it under the terms of the GNU General Public License
12
* as published by the Free Software Foundation; either version 2
13
* of the License, or (at your opinion) any later version.
15
* This program is distributed in the HOPE that it will be USEFUL,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
* See the GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program. If not, write to the Free Software Foundation,
22
* Inc. ,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
#define __KVIRC_PLUGIN__
31
#include <qpushbutton.h>
33
#include <qmessagebox.h>
34
#include <qtabdialog.h>
35
#include <qtextstream.h>
36
#include <qdatetime.h>
38
#include <qlistview.h>
39
#include <qfileinfo.h>
41
#include "kvi_toolbar.h"
42
#include "kvi_channel.h"
43
#include "kvi_input.h"
44
#include "kvi_error.h"
45
#include "kvi_config.h"
46
#include "kvi_locale.h"
48
#include "libkvistatwindow.h"
49
#include "libkvistatsystraywidget.h"
50
#include "libkvistatoptions.h"
52
#include "libkvistat.h"
53
#include "libkvistat.moc"
55
#include "libkvistaticon.h"
57
KviStatController * g_pStatPluginController = 0;
58
QPixmap * g_pStatPluginSysTrayIcon = 0;
59
KviStatOptions * g_pStatPluginOptionsWindow = 0;
60
KviStatWindow * g_pStatPluginStatWindow = 0;
63
KviStatController::KviStatController()
66
m_pSysTrayWidgetList = new QList<KviStatSysTrayWidget>;
67
m_pSysTrayWidgetList->setAutoDelete(false);
69
m_pStatChanList = new QList<KviStatChan>;
70
m_pStatChanList->setAutoDelete(true);
72
m_bShowConsoleStats = true;
78
KviStatController::~KviStatController()
83
while(KviStatSysTrayWidget * w = m_pSysTrayWidgetList->first())
84
kvirc_plugin_remove_systray_widget(w->frame(),w,true);
86
delete m_pSysTrayWidgetList;
87
delete m_pStatChanList;
90
void KviStatController::loadStats()
93
kvirc_plugin_get_config_file_path(tmp,"stat.db");
94
KviConfig cfg(tmp.ptr());
98
m_stats.queriesWords = cfg.readUIntEntry("QueryWords",0);
99
m_stats.queriesLetters = cfg.readUIntEntry("QueryLetters",0);
100
m_stats.dccsWords = cfg.readUIntEntry("DccWords",0);
101
m_stats.dccsLetters = cfg.readUIntEntry("DccLetters",0);
102
m_stats.chansWords = cfg.readUIntEntry("ChannelWords",0);
103
m_stats.chansLetters = cfg.readUIntEntry("ChannelLetters",0);
104
m_stats.totalWords = cfg.readUIntEntry("TotalWords",0);
105
m_stats.totalLetters = cfg.readUIntEntry("TotalLetters",0);
106
m_stats.kicks = cfg.readUIntEntry("Kicks",0);
107
m_stats.bans = cfg.readUIntEntry("Bans",0);
108
m_stats.onIrc = cfg.readUIntEntry("IrcSessions",0);
109
m_stats.joins = cfg.readUIntEntry("Joins",0);
110
m_stats.topics = cfg.readUIntEntry("Topics",0);
111
m_stats.sessionWords = cfg.readUIntEntry("SessionWords",0);
112
m_stats.wordsRecord = cfg.readUIntEntry("WordsRecord",0);
113
KviStr date = QDateTime::currentDateTime().toString();
114
m_stats.startDate = cfg.readEntry("StartDate",date.ptr());
116
int idx = cfg.readIntEntry("NumChannels",0);
117
for(int i = 0;i<idx;i++)
119
KviStr tmp(KviStr::Format,"Chan%d",i);
120
cfg.setGroup(tmp.ptr());
122
KviStr chName = cfg.readEntry("Name","");
125
unsigned int jo = cfg.readUIntEntry("Joins",0);
126
unsigned int wo = cfg.readUIntEntry("Words",0);
127
unsigned int ki = cfg.readUIntEntry("Kicks",0);
128
unsigned int ba = cfg.readUIntEntry("Bans",0);
129
unsigned int to = cfg.readUIntEntry("Topics",0);
130
unsigned int ac = cfg.readUIntEntry("Actions",0);
132
KviStatChan * c = new KviStatChan(chName.ptr(),jo,wo,ki,ba,to,ac);
133
m_pStatChanList->append(c);
138
void KviStatController::saveStats() const
141
kvirc_plugin_get_config_file_path(tmp,"stat.db");
142
KviConfig cfg(tmp.ptr());
144
cfg.setGroup("Main");
146
cfg.writeEntry("QueryWords",m_stats.queriesWords);
147
cfg.writeEntry("QueryLetters",m_stats.queriesLetters);
148
cfg.writeEntry("DccWords",m_stats.dccsWords);
149
cfg.writeEntry("DccLetters",m_stats.dccsLetters);
150
cfg.writeEntry("ChannelWords",m_stats.chansWords);
151
cfg.writeEntry("ChannelLetters",m_stats.chansLetters);
152
cfg.writeEntry("TotalWords",m_stats.totalWords);
153
cfg.writeEntry("TotalLetters",m_stats.totalLetters);
154
cfg.writeEntry("Kicks",m_stats.kicks);
155
cfg.writeEntry("Bans",m_stats.bans);
156
cfg.writeEntry("IrcSessions",m_stats.onIrc);
157
cfg.writeEntry("Joins",m_stats.joins);
158
cfg.writeEntry("Topics",m_stats.topics);
159
cfg.writeEntry("SessionWords",m_stats.sessionWords);
160
cfg.writeEntry("WordsRecord",m_stats.wordsRecord);
161
cfg.writeEntry("StartDate",m_stats.startDate.ptr());
165
for(KviStatChan * c=m_pStatChanList->first();c;c = m_pStatChanList->next())
167
KviStr tmp(KviStr::Format,"Chan%d",idx);
169
cfg.setGroup(tmp.ptr());
170
cfg.writeEntry("Name",c->name());
171
cfg.writeEntry("Joins",c->joins());
172
cfg.writeEntry("Words",c->words());
173
cfg.writeEntry("Kicks",c->kicks());
174
cfg.writeEntry("Bans",c->bans());
175
cfg.writeEntry("Topics",c->topics());
176
cfg.writeEntry("Actions",c->actions());
179
cfg.setGroup("Main");
180
cfg.writeEntry("NumChannels",idx);
183
void KviStatController::loadConfig()
186
kvirc_plugin_get_config_file_path(tmp,"stat.cfg");
187
KviConfig cfg(tmp.ptr());
189
m_config.joinStatType= cfg.readIntEntry("JoinStatType",ShowFullStatsOnJoin);
190
// on channel join show:
191
m_config.showJoins = cfg.readBoolEntry("ShowJoins",true);
192
m_config.showWords = cfg.readBoolEntry("ShowWords",true);
193
m_config.showKicks = cfg.readBoolEntry("ShowKicks",false);
194
m_config.showBans = cfg.readBoolEntry("ShowBans",false);
195
m_config.showTopics = cfg.readBoolEntry("ShowTopics",false);
196
// enable systray on startup:
197
m_config.sysTrayOnStartup = cfg.readBoolEntry("STWOnStartup",true);
198
// in systray widget show:
199
m_config.STWshowJoins = cfg.readBoolEntry("STWShowJoins",true);
200
m_config.STWshowWords = cfg.readBoolEntry("STWShowWords",true);
201
m_config.STWshowKicks = cfg.readBoolEntry("STWShowKicks",true);
202
m_config.STWshowBans = cfg.readBoolEntry("STWShowBans",true);
203
m_config.STWshowTopics = cfg.readBoolEntry("STWShowTopics",true);
204
// systray widget scrolling options:
205
m_config.ScrollingWhen = cfg.readIntEntry("ScrollingWhen", \
206
ScrollingWhenNeeded);
207
m_config.ScrollingDelay = cfg.readIntEntry("ScrollingDelay",200);
208
m_config.ScrollingTextJump = cfg.readIntEntry("ScrollingStep",3);
209
m_config.ScrollingDirLeft = cfg.readBoolEntry("ScrollingLeft",true);
212
void KviStatController::saveConfig() const
215
kvirc_plugin_get_config_file_path(tmp,"stat.cfg");
216
KviConfig cfg(tmp.ptr());
218
cfg.writeEntry("JoinStatType",m_config.joinStatType);
219
// on channel join show:
220
cfg.writeEntry("ShowJoins",m_config.showJoins);
221
cfg.writeEntry("ShowWords",m_config.showWords);
222
cfg.writeEntry("ShowKicks",m_config.showKicks);
223
cfg.writeEntry("ShowBans",m_config.showBans);
224
cfg.writeEntry("ShowTopics",m_config.showTopics);
225
// enable systray on startup:
226
cfg.writeEntry("STWOnStartup", m_config.sysTrayOnStartup );
227
// in systray widget show:
228
cfg.writeEntry("STWShowJoins",m_config.STWshowJoins);
229
cfg.writeEntry("STWShowWords",m_config.STWshowWords);
230
cfg.writeEntry("STWShowKicks",m_config.STWshowKicks);
231
cfg.writeEntry("STWShowBans",m_config.STWshowBans);
232
cfg.writeEntry("STWShowTopics",m_config.STWshowTopics);
233
// systray widget scrolling options:
234
cfg.writeEntry("ScrollingWhen",m_config.ScrollingWhen);
235
cfg.writeEntry("ScrollingDelay",m_config.ScrollingDelay);
236
cfg.writeEntry("ScrollingStep",m_config.ScrollingTextJump);
237
cfg.writeEntry("ScrollingLeft",m_config.ScrollingDirLeft);
240
KviStatChan * const KviStatController::findStatChan(const char * channame) const
242
for(KviStatChan * c=m_pStatChanList->first();c;c = m_pStatChanList->next())
244
if(kvi_strEqualCI(channame, c->name()))
250
void KviStatController::setCurrentChan(KviStatChan * ptr)
252
m_pStatChanList->setAutoDelete(false);
254
if(m_pStatChanList->removeRef(ptr))
255
m_pStatChanList->insert(0,ptr); // list head!
257
m_pStatChanList->setAutoDelete(true);
260
void KviStatController::slotShowConfig()
262
if(g_pStatPluginOptionsWindow)
264
if(!g_pStatPluginOptionsWindow->isVisibleToTLW() )
265
g_pStatPluginOptionsWindow->show();
269
g_pStatPluginOptionsWindow = new KviStatOptions();
271
g_pStatPluginOptionsWindow->setOptions( m_config.joinStatType,
272
m_config.showJoins, m_config.showWords, m_config.showKicks,
273
m_config.showBans, m_config.showTopics );
275
g_pStatPluginOptionsWindow->setSysTrayOptions(m_config.sysTrayOnStartup,
276
m_config.STWshowWords, m_config.STWshowJoins, m_config.STWshowKicks,
277
m_config.STWshowBans, m_config.STWshowTopics );
279
g_pStatPluginOptionsWindow->setScrollingOptions( m_config.ScrollingWhen,
280
m_config.ScrollingDelay, m_config.ScrollingTextJump,
281
m_config.ScrollingDirLeft );
283
connect(g_pStatPluginOptionsWindow,SIGNAL(applyButtonPressed()),
284
SLOT(slotApplyOptions()));
285
connect(g_pStatPluginOptionsWindow,SIGNAL(defaultButtonPressed()),
286
SLOT(slotApplyOptions()));
287
connect(g_pStatPluginOptionsWindow,SIGNAL(cancelButtonPressed()),
288
SLOT(slotKillOptionsWindow()));
290
g_pStatPluginOptionsWindow->show();
293
void KviStatController::slotShowStats()
295
if( g_pStatPluginStatWindow )
296
return; // WE WANT IT TO EXIST ONLY WHEN NEEDED!!!!!
299
g_pStatPluginStatWindow = new KviStatWindow();
300
connect( g_pStatPluginStatWindow, SIGNAL(applyButtonPressed()),
301
SLOT(slotKillStatWindow()) );
304
g_pStatPluginStatWindow->updateStats();
305
g_pStatPluginStatWindow->show();
308
void KviStatController::slotReset()
313
void KviStatController::slotRemoveAllChans()
315
if( m_pStatChanList->isEmpty() )
318
if( QMessageBox::warning(0, _CHAR_2_QSTRING(__tr("Remove all channels")),
319
_CHAR_2_QSTRING(__tr("Are you sure you want to remove all channels from your statistics ?")),
320
_CHAR_2_QSTRING(__tr("Yeah, whatever")), _CHAR_2_QSTRING(__tr("No way !")), 0, 1 ) == 0 )
322
m_pStatChanList->clear();
323
g_pStatPluginStatWindow->updateStats();
327
bool KviStatController::doReset()
329
if( QMessageBox::warning(0,_CHAR_2_QSTRING(__tr("Reset stats")),
330
_CHAR_2_QSTRING(__tr("Are you sure you want to reset your statistics ?")), _CHAR_2_QSTRING(__tr("Yes")),
331
_CHAR_2_QSTRING(__tr("No")), 0, 1 ) == 0 )
333
m_stats.startDate = QDateTime::currentDateTime().toString();
334
m_stats.chansWords = m_stats.chansLetters = m_stats.queriesWords = \
335
m_stats.queriesLetters = 0;
336
m_stats.dccsWords = m_stats.dccsLetters = m_stats.totalWords = \
337
m_stats.totalLetters = 0;
338
m_stats.onIrc = m_stats.bans = m_stats.kicks = m_stats.joins = \
340
m_stats.wordsRecord = 0;
342
m_pStatChanList->clear();
344
QListIterator<KviStatSysTrayWidget> it(*m_pSysTrayWidgetList);
345
for( ; it.current(); ++it )
346
it.current()->setStartDate( m_stats.startDate.ptr() );
355
void KviStatController::slotKillOptionsWindow()
357
if(!g_pStatPluginOptionsWindow)
360
delete g_pStatPluginOptionsWindow;
361
g_pStatPluginOptionsWindow = 0;
364
void KviStatController::slotKillStatWindow()
366
if( !g_pStatPluginStatWindow )
369
delete g_pStatPluginStatWindow;
370
g_pStatPluginStatWindow = 0;
373
void KviStatController::slotApplyOptions()
375
if(!g_pStatPluginOptionsWindow)
378
g_pStatPluginOptionsWindow->getOptions( m_config.joinStatType,
379
m_config.showJoins, m_config.showWords, m_config.showKicks,
380
m_config.showBans, m_config.showTopics );
382
g_pStatPluginOptionsWindow->getSysTrayOptions(m_config.sysTrayOnStartup,
383
m_config.STWshowWords, m_config.STWshowJoins, m_config.STWshowKicks,
384
m_config.STWshowBans, m_config.STWshowTopics );
386
g_pStatPluginOptionsWindow->getScrollingOptions( m_config.ScrollingWhen,
387
m_config.ScrollingDelay, m_config.ScrollingTextJump,
388
m_config.ScrollingDirLeft );
390
QListIterator<KviStatSysTrayWidget> it(*m_pSysTrayWidgetList);
392
for( ; it.current(); ++it )
394
it.current()->setOptions( m_config.STWshowWords, m_config.STWshowJoins,
395
m_config.STWshowKicks,m_config.STWshowBans, m_config.STWshowTopics);
397
it.current()->setScrollingOptions( m_config.ScrollingWhen,
398
m_config.ScrollingDelay, m_config.ScrollingTextJump,
399
m_config.ScrollingDirLeft );
402
slotKillOptionsWindow();
408
bool KviStatController::eventFilter(QObject *obj,QEvent *e)
410
KviWindow *win = (KviWindow*)obj->parent();
412
if( e->type() == QEvent::FocusIn )
416
if(kvi_strEqualCI(win->className(),"KviChannel"))
418
KviStatChan * c = findStatChan(win->caption());
422
m_bShowConsoleStats = false;
425
m_bShowConsoleStats = true;
433
void KviStatController::setSysTrayWidgetOptions()
435
QListIterator<KviStatSysTrayWidget> it(*m_pSysTrayWidgetList);
436
for( ; it.current(); ++it )
438
it.current()->setOptions(
439
m_config.STWshowJoins,m_config.STWshowWords,
440
m_config.STWshowKicks,m_config.STWshowBans,m_config.STWshowTopics);
441
it.current()->setStartDate( m_stats.startDate.ptr() );
442
it.current()->setScrollingOptions(
443
m_config.ScrollingWhen,m_config.ScrollingDelay,
444
m_config.ScrollingTextJump,m_config.ScrollingDirLeft );
448
void KviStatController::registerStatTray(KviStatSysTrayWidget * ptr)
450
m_pSysTrayWidgetList->append(ptr);
453
void KviStatController::unregisterStatTray(KviStatSysTrayWidget * ptr)
455
m_pSysTrayWidgetList->removeRef(ptr);
461
const char *usageText = __tr(
462
"\nUsage:\nSTATS [option] - see and manipulate your statistics.\n" \
463
"If used without options it shows number of words for current window type, " \
464
"and if it is typed in a chan window it shows the number of words said on " \
466
"If used with an option it shows:\n" \
467
" -h this help message.\n" \
468
" -t number of words spoken since last stats reset\n" \
469
" -c number of words spoken on channels\n" \
470
" -q number of words spoken on queries\n" \
471
" -d number of words spoken on DCCs\n" \
472
" -k number of kicks given\n" \
473
" -b number of bans set\n" \
474
" -w a window with complete statistics\n" \
475
" -j number of times you have joined the channel\n" \
476
" -m a message to a channel telling how many words you've got :)\n"\
477
" -M same as -m, but shows number of words on channel you are " \
478
"currently on (not total)\n" \
479
" -o number of topic changes\n" \
480
" -D detailed stats of current channel\n" \
481
" -S current session stats\n" \
482
" -r session record (words)\n" \
484
" -x str Delete stats for channel 'str'. 'Str' can be in '#chan' or " \
486
" -s Save stats file NOW\n" \
487
" -C Show configuration dialog\n" \
488
" -R Reset stats (after confirmation)\n" \
489
"See plugin help page for more details\n" \
490
"This plugin was written by Krzysztof \"Kristoff\" Godlewski " \
491
"<kristoff@poczta.wprost.pl, krzygod@kki.net.pl>\n" \
492
"See you on IRCNet !\n");
498
// --- global stuff ---
500
void stat_plugin_processJoinStats(KviStatChan * c,KviWindow *window)
502
if( window->type() != KVI_WND_TYPE_CHANNEL )return;
504
switch( g_pStatPluginController->joinStatType() )
506
case ShowFullStatsOnJoin:
507
window->output(KVI_OUT_INFO,(__tr("You have joined %s for %u time.")),
508
window->caption(),c->joins());
509
window->output(KVI_OUT_INFO,(__tr("Full stats for %s:")),
511
window->output(KVI_OUT_INFO,
512
(__tr("Words: %d. Kicks: %u.\nBans: %u Topic changes: %u.")),
513
c->words(),c->kicks(),c->bans(),c->topics());
516
case ShowCustomStatsOnJoin:
517
if( g_pStatPluginController->showJoins() )
518
window->output(KVI_OUT_INFO,
519
(__tr("You have joined %s for %u time.")),window->caption(),
521
if( g_pStatPluginController->showKicks() ||
522
g_pStatPluginController->showWords() ||
523
g_pStatPluginController->showBans() ||
524
g_pStatPluginController->showTopics() )
526
window->output(KVI_OUT_INFO,(__tr("Stats for %s:")),
529
if( g_pStatPluginController->showWords() )
530
window->output(KVI_OUT_INFO,(__tr("Words spoken: %d.")),
532
if( g_pStatPluginController->showKicks() )
533
window->output(KVI_OUT_INFO,(__tr("Kicks given: %d.")),
535
if( g_pStatPluginController->showBans() )
536
window->output(KVI_OUT_INFO,(__tr("Bans set: %d.")),c->bans());
537
if( g_pStatPluginController->showTopics() )
538
window->output(KVI_OUT_INFO,(__tr("Topic changes: %d.")),
546
bool stat_plugin_hook_addstat(KviPluginCommandStruct *cmd)
551
if(cmd->params->count() >= 2)
553
QListIterator<KviStr> it(*cmd->params);
554
it += 2; // omit event and window name
555
for( ; it.current(); ++it )len += it.current()->len();
556
numWords = cmd->params->count() - 2;
559
// if the channel is not in stats add it
561
KviStatChan * c = g_pStatPluginController->findStatChan(
562
cmd->window->caption() );
564
switch( cmd->window->type() ) // determine the window type
566
case KVI_WND_TYPE_CHANNEL: // Channel window
567
g_pStatPluginController->addChansWords(numWords);
568
g_pStatPluginController->addChansLetters(len);
571
g_pStatPluginController->addTotalJoin();
573
KviStatChan *stch = new KviStatChan(cmd->window->caption(),1,
575
g_pStatPluginController->addChan(stch);
577
cmd->window->output(KVI_OUT_INFO,__tr("Added %s to stats."),
578
cmd->window->caption());
583
c->addWords(numWords);
587
case KVI_WND_TYPE_QUERY: // Query window
588
g_pStatPluginController->addQueriesWords( numWords );
589
g_pStatPluginController->addQueriesLetters( len );
592
case KVI_WND_TYPE_CHAT: // Dcc Chat window
593
g_pStatPluginController->addDccsWords( numWords );
594
g_pStatPluginController->addDccsLetters( len );
598
g_pStatPluginController->addTotalWords( numWords );
599
g_pStatPluginController->addSessionWords( numWords );
600
g_pStatPluginController->addTotalLetters( len );
605
bool stat_plugin_command_stats(KviPluginCommandStruct *cmd)
608
KviStatChan *chan = g_pStatPluginController->findStatChan(
609
cmd->window->caption());
611
if( cmd->params->count() > 1 )
613
switch( cmd->params->at( 1 )->ptr()[1] ) // check argument
615
case 'C': // show config dialog
616
g_pStatPluginController->slotShowConfig();
619
case 'D': // detailed stats of current chan
620
if(cmd->window->type() == 1 ) // this is a chan win
622
if( chan ) // got the chan in stats
624
s.sprintf( __tr("Stats for %s:\n"), cmd->window->caption() );
625
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
626
s.sprintf( __tr("Joins: %u. Words: %d. Kicks: %u.\n"),
627
chan->joins(),chan->words(),chan->kicks());
628
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
629
s.sprintf( __tr("Bans: %u. Topics: %u.\n"),
630
chan->bans(), chan->topics() );
631
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
633
else // don't have it
635
s.sprintf(__tr("No stats for %s.\n"),cmd->window->caption() );
636
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
639
else // current window is not a chan window
641
if( cmd->window->type() == 0 ) // @console
643
s.sprintf(__tr("Total number of:\n* IRC sessions: %d.\n"),
644
g_pStatPluginController->numOnIrc() );
645
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
646
s.sprintf( __tr("* joins to channels: %d.\n"),
647
g_pStatPluginController->numJoins() );
648
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
649
s.sprintf( __tr("* kicks given: %d.\n"),
650
g_pStatPluginController->numKicks());
651
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
652
s.sprintf( __tr("* bans set: %d.\n* topic changes: %d.\n"),
653
g_pStatPluginController->numBans(),
654
g_pStatPluginController->numTopics() );
655
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
658
if( cmd->window->type() == 2 ) // query
661
__tr("Total queries stats:\n* number of words: %d.\n"),
662
g_pStatPluginController->queriesWords());
663
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
664
s.sprintf( __tr("* number of letters: %d.\n"),
665
g_pStatPluginController->queriesLetters() );
666
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
669
if( cmd->window->type() == 3 ) // dcc
671
s.sprintf(__tr("Total DCCs stats:\n* number of words: %d.\n"),
672
g_pStatPluginController->dccsWords());
673
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
674
s.sprintf( __tr("* number of letters: %d.\n"),
675
g_pStatPluginController->dccsLetters() );
676
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
683
case 'o': // show total number of topics
684
s.sprintf( __tr("You have changed topic %d times.\n"),
685
g_pStatPluginController->numTopics() );
686
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
689
case 'm': // show channel message
690
s.sprintf( __tr("/me has spoken %d words since %s !"),
691
g_pStatPluginController->totalWords(),
692
g_pStatPluginController->startDate());
693
kvirc_plugin_simulate_typing(cmd->window, s);
696
case 'M': // show channel message, but with words from this chan
699
s.sprintf( __tr("No stats for %s."), cmd->window->caption());
700
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
704
s.sprintf( __tr("/me has spoken %d words on %s since %s !"),
705
chan->words(), cmd->window->caption(),
706
g_pStatPluginController->startDate());
707
kvirc_plugin_simulate_typing(cmd->window, s);
711
case 'h': // print help message
712
s.sprintf( usageText );
713
cmd->window->outputNoFmt( KVI_OUT_HELP, s.ptr());
716
case 't': // just echo total words
717
s.sprintf(__tr("Total number of words %d.\n"),
718
g_pStatPluginController->totalWords());
719
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
722
case 'c': // channels words
723
s.sprintf( __tr("Words spoken on channels: %d.\n"),
724
g_pStatPluginController->chansWords());
725
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
728
case 'q': // queries words
729
s.sprintf( __tr("Words spoken on queries: %d.\n"),
730
g_pStatPluginController->queriesWords());
731
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
734
case 'd': // dccsWords
735
s.sprintf( __tr("Words spoken on DCCs: %d.\n"),
736
g_pStatPluginController->dccsWords());
737
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
740
case 'k': // kicks given
741
s.sprintf( __tr("Total number of kicks given: %d.\n"),
742
g_pStatPluginController->numKicks());
743
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
746
case 'b': // bans set
747
s.sprintf( __tr("Total number of bans set: %d.\n"),
748
g_pStatPluginController->numBans());
749
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
752
case 'j': // joins to the channel
754
s.sprintf( __tr("No stats for %s.\n"), cmd->window->caption() );
756
s.sprintf( __tr("Number of times you have joined %s: %u"),
757
cmd->window->caption(), chan->joins());
759
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
762
case 'w': // show complete stats window
763
g_pStatPluginController->slotShowStats();
766
case 's': // save stats to file right NOW ! :>
767
g_pStatPluginController->slotSaveStats();
768
s.sprintf( __tr("Stats file saved.\n") );
769
cmd->window->outputNoFmt( KVI_OUT_PLUGIN, s.ptr());
772
case 'R': // reset stats
773
if( g_pStatPluginController->doReset())
775
s.sprintf(__tr("All stats cleared.\n"));
776
cmd->window->outputNoFmt(KVI_OUT_INFO, s.ptr());
780
case 'x': // delete stats for given channel
781
if( cmd->params->count() < 3 )
783
s.sprintf( __tr("Missing argument to /STATS -x !.\n" \
784
"Usage: /STATS -x #channel.") );
785
cmd->window->outputNoFmt( KVI_OUT_ERROR, s.ptr());
790
QString qs( cmd->params->at(2)->ptr());
791
if( !qs.contains( "#" ) )
793
KviStatChan *sc = g_pStatPluginController->findStatChan(
797
s.sprintf( __tr("Channel %s is not in stats !"), qs.latin1() );
798
cmd->window->outputNoFmt( KVI_OUT_ERROR, s.ptr());
802
g_pStatPluginController->removeChan(sc);
803
s.sprintf(__tr("Channel %s removed from stats."), qs.latin1());
804
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
809
case 'S': // this session stats
810
s.sprintf( __tr("Words spoken in this session: %u."),
811
g_pStatPluginController->sessionWords());
812
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
815
case 'r': // session record
816
s.sprintf( __tr("Biggest number of words spoken in one session: %u."),
817
g_pStatPluginController->wordsRecord() );
818
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
822
s.sprintf( __tr("STATS: Unknown option !\n") );
823
cmd->window->outputNoFmt( KVI_OUT_ERROR, s.ptr());
826
else // show stats for current window type
828
switch( cmd->window->type() )
831
s.sprintf( __tr("Total number of IRC sessions: %d.\n"),
832
g_pStatPluginController->numOnIrc() );
833
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
836
case 1: // chan window, show stats for this chan
839
s.sprintf( __tr("No stats for %s.\n"), cmd->window->caption());
840
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
844
s.sprintf(__tr("Words spoken on %s: %d.\n"),cmd->window->caption()
846
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
850
case 2: // query window
851
s.sprintf( __tr("Words spoken on queries: %d.\n"),
852
g_pStatPluginController->queriesWords());
853
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
856
case 3: // dcc window
857
s.sprintf( __tr("Words spoken on DCCs: %d.\n"),
858
g_pStatPluginController->dccsWords());
859
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr());
867
bool stat_plugin_hook_on_topic(KviPluginCommandStruct *cmd)
870
KviStr myNick = cmd->frame->m_global.szCurrentNick.ptr();
871
KviStr sourceNick = cmd->params->at( 2 )->ptr();
872
if( myNick == sourceNick )
874
g_pStatPluginController->addTotalTopic();
876
KviStatChan *c = g_pStatPluginController->findStatChan(
877
cmd->window->caption());
882
KviStatChan *kvsc = new KviStatChan( cmd->window->caption());
884
g_pStatPluginController->addChan(kvsc);
885
g_pStatPluginController->addTotalJoin();
888
s.sprintf( __tr("Added %s to stats."), cmd->window->caption() );
889
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
891
g_pStatPluginController->setCurrentChan(kvsc);
898
bool stat_plugin_hook_on_irc(KviPluginCommandStruct *cmd)
900
g_pStatPluginController->addOnIrc();
901
g_pStatPluginController->resetSessionWords();
906
bool stat_plugin_hook_on_kick(KviPluginCommandStruct *cmd)
908
KviStr myNick = cmd->frame->m_global.szCurrentNick.ptr();
909
KviStr kickerNick = cmd->params->at( 3 )->ptr();
910
if( myNick == kickerNick )
912
g_pStatPluginController->addTotalKick();
913
KviStatChan *c = g_pStatPluginController->findStatChan(
914
cmd->window->caption() );
919
KviStatChan *kvsc = new KviStatChan(cmd->window->caption());
921
g_pStatPluginController->addChan(kvsc);
922
g_pStatPluginController->addTotalJoin();
925
s.sprintf( __tr("Added %s to stats."), cmd->window->caption() );
926
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
928
g_pStatPluginController->setCurrentChan(kvsc);
935
bool stat_plugin_hook_on_ban(KviPluginCommandStruct *cmd)
937
KviStr myNick = cmd->frame->m_global.szCurrentNick.ptr();
938
KviStr banningNick= cmd->params->at( 2 )->ptr();
939
if( myNick == banningNick )
941
g_pStatPluginController->addTotalBan();
943
KviStatChan *c = g_pStatPluginController->findStatChan(
944
cmd->window->caption() );
949
KviStatChan *kvsc = new KviStatChan(cmd->window->caption());
951
g_pStatPluginController->addChan(kvsc);
952
g_pStatPluginController->addTotalJoin();
955
s.sprintf( __tr("Added %s to stats."), cmd->window->caption() );
956
cmd->window->outputNoFmt( KVI_OUT_INFO, s.ptr() );
958
g_pStatPluginController->setCurrentChan(kvsc);
966
bool stat_plugin_hook_on_me_join(KviPluginCommandStruct *cmd)
968
// Does it make sense ? :)))
969
if( cmd->window->type() != 1 ) // not a chan
972
g_pStatPluginController->addTotalJoin();
973
KviStr myNick = cmd->frame->m_global.szCurrentNick.ptr();
974
KviStr joinChan = *cmd->params->at(1);
976
KviStatChan * c = g_pStatPluginController->findStatChan( joinChan.ptr() );
980
stat_plugin_processJoinStats(c,cmd->window);
981
// g_pStatPluginController->setCurrentChan(c);
985
KviStatChan *tmpchan = new KviStatChan( joinChan.ptr() );
986
g_pStatPluginController->addChan( tmpchan );
987
g_pStatPluginController->addTotalJoin();
990
tmps.sprintf( __tr("Added %s to stats.\n"), tmpchan->name());
991
cmd->window->outputNoFmt( KVI_OUT_INFO, tmps.ptr());
994
// systray widget update stuff
995
KviChannel * kvic = cmd->frame->findChannel(cmd->params->at(1)->ptr());
998
QObjectList *list = kvic->queryList("KviInput",0,false,true);
999
QObjectListIt it(*list);
1000
KviInput *i = (KviInput *)it.current();
1001
i->installEventFilter(g_pStatPluginController);
1007
bool stat_plugin_hook_on_startup(KviPluginCommandStruct *cmd)
1009
//#warning "OnStartup - a little more work to be done here !"
1010
//#warning "currently this event is DISABLED"
1016
// This things below should now work fine, as soon as we know what is wrong with
1019
s.sprintf(__tr("/echo -i=$icon(kvirc) Running $b\\KviStat$o\\ plugin "\
1020
"version $b\\%s$o\\. Have fun !!"), LIBKVISTAT_VERSION );
1021
kvirc_plugin_simulate_typing(cmd->window, s);
1023
if( g_pStatPluginController->sysTrayOnStartup() )
1026
// How about this little ugly hack :) ?
1027
s.sprintf( "/stattray dock" );
1028
kvirc_plugin_simulate_typing(cmd->window, s );
1029
debug("[stats]: Systray ON (startup)");
1032
KviStatSysTrayWidget *st = new KviStatSysTrayWidget(
1033
kvirc_plugin_get_systray(cmd->frame),cmd->frame );
1034
kvirc_plugin_add_systray_widget(cmd->handle, cmd->frame, st,
1036
g_pStatPluginController->setSysTrayWidgetOptions();
1040
KviConsole *con = cmd->console;
1044
QObjectList *li = con->queryList("KviInput",0,false,true);
1045
QObjectListIt it_(*li);
1046
KviInput *i = (KviInput *)it_.current();
1049
debug( "[stat]: Installing event filter on @console" );
1050
i->installEventFilter(g_pStatPluginController);
1056
bool stat_plugin_hook_on_disconnect(KviPluginCommandStruct *cmd)
1058
if( g_pStatPluginController->sessionWords() >
1059
g_pStatPluginController->wordsRecord() )
1061
g_pStatPluginController->setWordsRecord(
1062
g_pStatPluginController->sessionWords());
1063
cmd->window->outputNoFmt(KVI_OUT_INFO,__tr("[stats] : You've just beat " \
1064
"your session record !"));
1065
cmd->window->output(KVI_OUT_INFO,__tr("[stats] : Now it is %c%d%c " \
1066
"words!"),KVI_TEXT_BOLD,g_pStatPluginController->wordsRecord(),
1072
bool stat_plugin_hook_on_shutdown(KviPluginCommandStruct *cmd)
1074
if( g_pStatPluginController->sessionWords() >
1075
g_pStatPluginController->wordsRecord() )
1076
g_pStatPluginController->setWordsRecord(
1077
g_pStatPluginController->sessionWords());
1081
bool stat_plugin_command_stattray(KviPluginCommandStruct *cmd)
1085
// /stattray [on] - dock the stattray into current frame
1086
// /stattray <off> <undock> - undock it
1091
// This is temporary, all event filters will be installed on startup
1092
//#warning "The event filter on @console doesn't get installed !"
1093
QObjectList *li = cmd->console->queryList("KviInput",0,false,true);
1094
QObjectListIt it_(*li);
1095
KviInput *i = (KviInput *)it_.current();
1097
i->installEventFilter(g_pStatPluginController);
1102
KviStr param = kvirc_plugin_param( cmd, 1 );
1104
if( kvi_strEqualCI( param.ptr(), "undock") ||
1105
kvi_strEqualCI( param.ptr(), "off" ))
1107
KviSysTrayWidget *w = kvirc_plugin_lookup_systray_widget(
1108
cmd->frame, "KviStatSysTrayWidget" );
1111
cmd->error = KVI_ERROR_InvalidParameter;
1112
cmd->errorstr = __tr("No StatTray to undock");
1116
kvirc_plugin_remove_systray_widget( cmd->frame, w, true );
1121
KviSysTrayWidget *w = kvirc_plugin_lookup_systray_widget(
1122
cmd->frame, "KviStatSysTrayWidget" );
1125
cmd->error = KVI_ERROR_InvalidParameter;
1126
cmd->errorstr = __tr("StatTray already docked in this frame");
1131
KviStatSysTrayWidget *sw = new KviStatSysTrayWidget(
1132
kvirc_plugin_get_systray(cmd->frame),cmd->frame);
1133
QObject::connect( sw, SIGNAL(wantOptions()),g_pStatPluginController,
1134
SLOT( setSysTrayWidgetOptions()));
1136
kvirc_plugin_add_systray_widget(cmd->handle, cmd->frame, sw, true );
1138
g_pStatPluginController->setSysTrayWidgetOptions();
1143
bool stat_plugin_hook_on_action(KviPluginCommandStruct * cmd)
1149
bool stat_plugin_init(KviPluginCommandStruct *cmd)
1151
g_pStatPluginController = new KviStatController();
1152
g_pStatPluginSysTrayIcon = new QPixmap(stat_tray_icon_xpm);
1154
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnAction,
1155
stat_plugin_hook_on_action );
1156
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnChannelInput,
1157
stat_plugin_hook_addstat );
1158
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnQueryInput,
1159
stat_plugin_hook_addstat );
1160
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnDCCInput,
1161
stat_plugin_hook_addstat );
1162
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnIrc,
1163
stat_plugin_hook_on_irc );
1164
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnTopic,
1165
stat_plugin_hook_on_topic );
1166
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnKick,
1167
stat_plugin_hook_on_kick );
1168
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnBan,
1169
stat_plugin_hook_on_ban );
1170
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnMeJoin,
1171
stat_plugin_hook_on_me_join );
1173
// This has problems.....need to take a look at this
1174
// Yeah...I think the same thing ;-) -- Kristoff
1176
// For a moment I thought everything works fine, but sometimes it crashes..still
1177
// I'll leave it disabled until everything clears up. User can put "stattray" in
1178
// his onStartup event in "Script Center"
1179
// kvirc_plugin_add_hook(cmd->handle, KviEvent_OnStartup,
1180
// stat_plugin_hook_on_startup);
1182
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnDisconnect,
1183
stat_plugin_hook_on_disconnect);
1184
kvirc_plugin_add_hook(cmd->handle, KviEvent_OnShutdown,
1185
stat_plugin_hook_on_shutdown);
1186
kvirc_plugin_register_command(cmd->handle,"STATS",
1187
stat_plugin_command_stats);
1188
kvirc_plugin_register_command(cmd->handle,"STATTRAY",
1189
stat_plugin_command_stattray);
1193
if(!(cmd->frame->m_pWinList))
1197
for(KviWindow * wnd = cmd->frame->m_pWinList->first();wnd;
1198
wnd = cmd->frame->m_pWinList->next())
1200
if(wnd->type() == KVI_WND_TYPE_CHANNEL)
1202
KviStatChan * c = g_pStatPluginController->findStatChan(
1206
c = new KviStatChan(wnd->caption());
1207
g_pStatPluginController->addChan(c);
1209
wnd->output(KVI_OUT_INFO,__tr("Added %s to stats.\n"),
1213
if(wnd->m_pInput)wnd->m_pInput->installEventFilter(
1214
g_pStatPluginController);
1217
g_pStatPluginController->addTotalJoin();
1219
g_pStatPluginController->setCurrentChan(c);
1221
stat_plugin_processJoinStats(c,wnd);
1225
//#warning "Look here :>"
1226
// Also we need to install the event filter on @CONSOLE
1227
// FIXME: This doesn't work also :(
1228
// QObjectList *li = cmd->console->queryList("KviInput",0,false,true);
1229
// QObjectListIt it_(*li);
1230
// KviInput *i = (KviInput *)it_.current();
1232
// i->installEventFilter(g_pStatPluginController);
1236
if(cmd->console->m_pInput)cmd->console->m_pInput->installEventFilter(
1237
g_pStatPluginController);
1243
void stat_plugin_cleanup()
1245
if(g_pStatPluginOptionsWindow)
1247
delete g_pStatPluginOptionsWindow;
1248
g_pStatPluginOptionsWindow = 0;
1250
delete g_pStatPluginController;
1251
g_pStatPluginController = 0;
1252
delete g_pStatPluginSysTrayIcon;
1253
g_pStatPluginSysTrayIcon = 0;
1255
kvirc_plugin_unregister_meta_object("KviStatController");
1256
kvirc_plugin_unregister_meta_object("KviStatOptions");
1257
kvirc_plugin_unregister_meta_object("KviStatSysTrayWidget");
1258
kvirc_plugin_unregister_meta_object("KviStatWindow");
1262
@document: doc_plugin_stat.kvihelp
1263
@title: The KVIrc Stat plugin ver. 0.2.0
1264
This is a plugin which makes statistics of your IRC sessions,
1265
which means you can check anytime how many words you have said,
1266
how many times you have joined specific channel etc.<br>
1267
The plugin exports commands: <b>/STATS</b> and <b>
1269
Command <b>/STATS</b> can be invoked with quite a big number of option.
1271
at a time is allowed though. Every option except the first one is
1274
Here are all the options with descriptions:<br>
1276
<ul><b>Options that show something:
1277
<li><i>-h</i> - a help message with all the options available.</li>
1278
<li><i>-t</i> - total number of words spoken</li>
1279
<li><i>-c</i> - total number of words spoken on all channels</li>
1280
<li><i>-q</i> - total number of words spoken on all queries</li>
1281
<li><i>-d</i> - total number of words spoken on all DCC Chats</li>
1282
<li><i>-k</i> - total number of kicks given (on all channels)</li>
1283
<li><i>-b</i> - total number of kicks given (all channels also)</li>
1284
<li><i>-o</i> - total number of topic changes</li>
1285
<li><i>-j</i> - number of times you have joined the channel you are
1287
<li><i>-m</i> - a message to current channel telling how many words
1288
you have said since last reset. It looks just like you'd written:
1289
/me has spoken <i>(total_words)</i> words since <i>(reset_date)</i>
1291
<li><i>-M</i> - similar to <i>-m</i>, but shows how many words you
1293
on the channel you are currently on. Example:<br>
1294
<i>Kristoff has spoken 143 words on #linuxpl since Wed Mar
1295
29 02:10:21 2000 !</i></li>
1296
<li><i>-D</i> - detailed statistics for current <i>window type</i>.
1298
if you type <b>/STATS -D</b> in <i>@CONSOLE</i> you will get
1299
"totals" list - total words, IRC sessions, kicks etc. If you type
1300
the same command in a <i>channel window</i> you'll get this
1301
channel's detailed statistics</li>
1302
<li><i>-S</i> - current session stats
1303
<li><i>-r</i> - your "high score" of words spoken in one session
1304
<li><i>-w</i> - a window with <b>complete</b> stats. Everything is
1305
there - "totals", channel list, and actions such as kicks etc.
1306
In the <i>Channels</i> tab you can remove specific channel from
1307
stats, or remove all the channels (without
1308
resetting total words etc.). There is also a <i>Reset</i> button
1310
you to reset <b>all</b> the statistics. You will have to confirm
1311
resetting statistics as well as removing a chan.</li>
1314
<ul><b>Other options:</b>
1315
<li><i>-R</i> - reset statistics, after confirmation of course.</li>
1316
<li><i>-s</i> - save statistics to file <b>now</b>.</li>
1317
<li><i>-x <channel></i> - remove specified channel from your
1318
stats. You aren't asked if you are sure you want to do this, so be
1319
careful ;-). It isn't necessary to enter a <b>#</b> in front of
1320
the channel's name.</li>
1321
<li><i>-C</i> - configure the plugin. Here you can configure stats
1323
<b>OnMeJoin</b> Event (read more about events
1324
<a href="events.kvihelp">here</a>). You can choose to see either
1325
no, full or customized stats. This is quite simple really, so
1326
there isn't much to explain. Another thing you can configure is the
1327
<b>StatTray</b>.</li>
1330
If <b>/STATS</b> is invoked without any options it
1331
shows information depending on <i>window type</i>. In <i>channel
1332
windows</i> it shows number of words on the channel, on <i>query</i>
1333
and <i>DCC Chat windows</i> you will see number of words you've said
1334
there. In <i>@CONSOLE</i> it'll show number of IRC sessions.<br><br>
1335
Option <b>/STATTRAY</b> can be invoked with <i>ON</i>
1336
or <i>OFF</i> option. They do exactly what you think - turn the StatTray
1337
widget on or off. This widget's properties can be set in the plugin's
1338
configuration dialog.<br><br>
1340
<ul><b>Scrolling Options</b>
1341
<li><i>When is it enabled</i> - You have 3 possibilities: <i>Never
1342
</i>, <i>Always</i> and <i>When Needed</i>. <i>When Needed</i> means
1343
that scrolling will be enabled only if text won't fit into the
1345
<li><i>Direction</i> - Left or Right - plenty to choose from ;-)
1346
<li><i>Delay</i> - time between each text movement
1347
<li><i>Text Movement</i> - ammount of pixels the text moves every
1348
time <- set by the <i>Delay</i> slider
1350
It is generally better to have the <i>Text Movement</i> slider set more
1351
to the right... Although scrolling doesn't look as nice as if it was
1352
moved max to the left, but at least it isn't so lagging...<br>
1355
<b>KviStat</b> was written by Krzysztof <b>"Kristoff"</b> Godlewski
1356
<<i>kristoff@poczta.wprost.pl</i>><br>
1358
If you notice any bugs, or have suggestions / ideas how to make this
1360
please e-mail me.<br>
1362
<u><b>BIG</b></u> Thanks to <b>Szymon Stefanek</b> for his support.
1366
void stat_plugin_help()
1368
kvirc_plugin_show_help_page("doc_plugin_stat.kvihelp");
1371
void stat_plugin_config()
1373
g_pStatPluginController->slotShowConfig();
1376
KviPlugin kvirc_plugin =
1379
__tr("Stats plugin") ,
1380
LIBKVISTAT_VERSION ,
1382
"Krzysztof Godlewski\n<kristoff@poczta.wprost.pl>" ,
1383
// description field:
1384
__tr("The plugin makes and saves your sessions statistics."
1385
"/STATS - check your statistics.\n"
1386
"/STATTRAY - control the StatTray widget.\n"
1387
"See help page or /STATS -h for more information."),
1388
stat_plugin_init , // init function
1389
stat_plugin_cleanup , // cleanup function
1390
stat_plugin_config , // config function
1391
stat_plugin_help // help function