modules/stats.php
changeset 20 e2f6e3af9959
parent 17 b07b23187b17
equal deleted inserted replaced
19:eb92dc5d9fb4 20:e2f6e3af9959
     1 <?php
     1 <?php
     2 
     2 
     3 // most of the code in here goes towards keeping track of the list of members currently in the various channels we're in.
     3 require('statsincludes/stats_core.php');
       
     4 require('statsincludes/stats_logger.php');
       
     5 require('statsincludes/stats_frontend.php');
     4 
     6 
     5 $stats_memberlist = array();
       
     6 $stats_prefixes = array(
       
     7   'o' => '@',
       
     8   'v' => '+'
       
     9 );
       
    10 $stats_data = array('anonymous' => array(), 'messages' => array());
       
    11 $stats_day = gmdate('Ymd');
       
    12 @include("./stats/stats-data-$stats_day.php");
       
    13 unset($stats_data['members']);
       
    14 $stats_data['members'] =& $stats_memberlist;
       
    15 
       
    16 eb_hook('event_self_join', 'stats_init_channel($this);');
       
    17 eb_hook('event_raw_message', 'stats_process_message($chan, $message);');
       
    18 eb_hook('snippet_dynamic', 'if ( $snippet === "memberlist" ) return stats_list_members($chan, $message); if ( $snippet === "deluser" ) return stats_del_user($chan, $message);');
       
    19 eb_hook('event_other', 'stats_handle_other_event($message);');
       
    20 eb_hook('event_privmsg', 'stats_handle_privmsg($message);');
       
    21 
       
    22 function stats_init_channel(&$chan)
       
    23 {
       
    24   global $stats_memberlist, $stats_prefixes, $stats_data;
       
    25   
       
    26   $channel_name = $chan->get_channel_name();
       
    27   $stats_memberlist[$channel_name] = array();
       
    28   $prefixes_regexp = '/^([' . preg_quote(implode('', $stats_prefixes)) . '])+/';
       
    29   $prefixes_flipped = array_flip($stats_prefixes);
       
    30   $prefixes_regexp_notlist = '/[^' . preg_quote(implode('', $prefixes_flipped)) . ']/';
       
    31   
       
    32   if ( !isset($stats_data['messages'][$channel_name]) )
       
    33   {
       
    34     $stats_data['messages'][$channel_name] = array();
       
    35   }
       
    36   
       
    37   // read list of members from channel
       
    38   @stream_set_timeout($chan->parent->sock, 3);
       
    39   while ( $msg = $chan->parent->get() )
       
    40   {
       
    41     if ( $ml = strstr($msg, ' 353 ') )
       
    42     {
       
    43       $memberlist = trim(substr(strstr($ml, ':'), 1));
       
    44       $stats_memberlist[$channel_name] = explode(' ', $memberlist);
       
    45       $stats_memberlist[$channel_name] = array_flip($stats_memberlist[$channel_name]);
       
    46       foreach ( $stats_memberlist[$channel_name] as $nick => $_ )
       
    47       {
       
    48         $stats_memberlist[$channel_name][$nick] = '';
       
    49         while ( preg_match($prefixes_regexp, $nick) )
       
    50         {
       
    51           $prefix = substr($nick, 0, 1);
       
    52           $add = preg_replace($prefixes_regexp_notlist, '', strval($stats_memberlist[$channel_name][$nick]));
       
    53           unset($stats_memberlist[$channel_name][$nick]);
       
    54           $nick = substr($nick, 1);
       
    55           $stats_memberlist[$channel_name][$nick] = $prefixes_flipped[$prefix] . $add;
       
    56         }
       
    57       }
       
    58       break;
       
    59     }
       
    60   }
       
    61 }
       
    62 
       
    63 function stats_process_message(&$chan, $message)
       
    64 {
       
    65   global $stats_memberlist, $stats_data;
       
    66   $channel_name = $chan->get_channel_name();
       
    67   if ( !isset($stats_memberlist[$channel_name]) )
       
    68   {
       
    69     return false;
       
    70   }
       
    71   
       
    72   $ml =& $stats_memberlist[$channel_name];
       
    73   
       
    74   // we need to change statistics accordingly depending on the event
       
    75   if ( $message['action'] == 'JOIN' )
       
    76   {
       
    77     // member joined - init their flags and up the member count by one
       
    78     $ml[$message['nick']] = '';
       
    79   }
       
    80   else if ( $message['action'] == 'PART' )
       
    81   {
       
    82     // member left - clear flags and decrement the total member count
       
    83     unset($ml[$message['nick']]);
       
    84     $ml = array_values($ml);
       
    85   }
       
    86   else if ( $message['action'] == 'MODE' )
       
    87   {
       
    88     // update member list (not sure why this would be useful, but export it anyway - display scripts might find it useful)
       
    89     list($mode, $target) = explode(' ', $message['message']);
       
    90     $action = substr($mode, 0, 1);
       
    91     
       
    92     global $stats_prefixes;
       
    93     $ml[$target] = str_replace(substr($mode, 1), '', $ml[$target]);
       
    94     if ( $action == '+' )
       
    95     {
       
    96       $ml[$target] .= substr($mode, 1);
       
    97     }
       
    98   }
       
    99   else if ( $message['action'] == 'PRIVMSG' )
       
   100   {
       
   101     // private message into $channel_name - mark the user active and log the message time
       
   102     if ( isset($stats_data['anonymous'][$message['nick']]) )
       
   103       $message['nick'] = 'Anonymous';
       
   104     
       
   105     $messages =& $stats_data['messages'][$channel_name];
       
   106     
       
   107     $messages[] = array(
       
   108         'time' => time(),
       
   109         'nick' => $message['nick']
       
   110       );
       
   111   }
       
   112   
       
   113   stats_cron();
       
   114 }
       
   115 
       
   116 function stats_list_members(&$chan, &$message)
       
   117 {
       
   118   global $stats_memberlist;
       
   119   $channel_name = $chan->get_channel_name();
       
   120   if ( !isset($stats_memberlist[$channel_name]) )
       
   121   {
       
   122     return false;
       
   123   }
       
   124   
       
   125   $ml =& $stats_memberlist[$channel_name];
       
   126   
       
   127   $chan->parent->privmsg($message['nick'], "memberlist:\n" . str_replace("\n", ' ', print_r($ml, true)));
       
   128   
       
   129   return true;
       
   130 }
       
   131 
       
   132 function stats_del_user(&$chan, &$message)
       
   133 {
       
   134   global $stats_memberlist, $privileged_list, $irc, $stats_data;
       
   135   
       
   136   // remove a user from the DB
       
   137   $targetuser = trim(substr(strstr($message['message'], '|'), 1));
       
   138   if ( empty($targetuser) )
       
   139     $targetuser = $message['nick'];
       
   140   
       
   141   if ( $targetuser != $message['nick'] && !in_array($message['nick'], $privileged_list) )
       
   142   {
       
   143     $irc->privmsg($message['nick'], "Sorry, you need to be a moderator to delete statistics for users other than yourself.");
       
   144     return true;
       
   145   }
       
   146   
       
   147   // we should be good - delete the user
       
   148   foreach ( $stats_data['messages'] as $channel => &$messages )
       
   149   {
       
   150     foreach ( $messages as $i => &$currentmessage )
       
   151     {
       
   152       if ( $currentmessage['nick'] == $targetuser )
       
   153       {
       
   154         unset($messages[$i]);
       
   155       }
       
   156     }
       
   157     $messages = array_values($messages);
       
   158   }
       
   159   unset($users, $currentmessage, $messages);
       
   160   
       
   161   global $nick;
       
   162   $greeting = ( $targetuser == $message['nick'] ) ? "All of your statistics data" : "All of {$targetuser}'s statistic data";
       
   163   $irc->privmsg($message['nick'], "$greeting has been removed from the database for all channels. The changes will show up in the next commit to disk, which is usually no more than once every two minutes.");
       
   164   $irc->privmsg($message['nick'], "Want your stats to be anonymized in the future? Type /msg $nick anonymize to make me keep all your stats anonymous in the future. This only applies to your current nick though - for example if you change your nick to \"{$message['nick']}|sleep\" or similar your information will not be anonymous.");
       
   165   $irc->privmsg($message['nick'], "You can't clear your logs if you're anonymous. Type /msg $nick denonymize to remove yourself from the anonymization list. Anonymized logs can't be converted back to their original nicks.");
       
   166   
       
   167   return true;
       
   168 }
       
   169 
       
   170 function stats_handle_privmsg(&$message)
       
   171 {
       
   172   global $irc, $stats_data, $nick;
       
   173   static $poll_list = array();
       
   174   
       
   175   $message['message'] = strtolower($message['message']);
       
   176   
       
   177   if ( trim($message['message']) === 'anonymize' )
       
   178   {
       
   179     $stats_data['anonymous'][$message['nick']] = true;
       
   180     $poll_list[$message['nick']] = true;
       
   181     $irc->privmsg($message['nick'], "Anonymization complete. Any further statistics recorded about you will be anonymous.");
       
   182     $irc->privmsg($message['nick'], "Do you want to also anonymize any past statistics about you? (type \"yes\" or \"no\")");
       
   183   }
       
   184   else if ( trim($message['message']) === 'denonymize' )
       
   185   {
       
   186     $stats_data['anonymous'][$message['nick']] = false;
       
   187     unset($stats_data['anonymous'][$message['nick']]);
       
   188     $irc->privmsg($message['nick'], "Denonymization complete. Any further statistics recorded about you will bear your nick. Remember that you can always change this with /msg $nick anonymize.");
       
   189   }
       
   190   else if ( trim($message['message']) === 'yes' && isset($poll_list[$message['nick']]) )
       
   191   {
       
   192     // anonymize logs for this user
       
   193     // we should be good - delete the user
       
   194     $targetuser = $message['nick'];
       
   195     
       
   196     foreach ( $stats_data['messages'] as $channel => &$messages )
       
   197     {
       
   198       foreach ( $messages as $i => &$currentmessage )
       
   199       {
       
   200         if ( $currentmessage['nick'] == $targetuser )
       
   201         {
       
   202           $currentmessage['nick'] = 'Anonymous';
       
   203         }
       
   204       }
       
   205       $messages = array_values($messages);
       
   206     }
       
   207     unset($users, $currentmessage, $messages);
       
   208     $irc->privmsg($message['nick'], "Anonymization complete. All past statistics on your nick are now anonymous.");
       
   209     
       
   210     unset($poll_list[$message['nick']]);
       
   211   }
       
   212   stats_cron();
       
   213 }
       
   214 
       
   215 function stats_handle_other_event(&$message)
       
   216 {
       
   217   global $stats_memberlist;
       
   218   
       
   219   if ( $message['action'] == 'NICK' )
       
   220   {
       
   221     // we have a nick change; go through all channels and replace the old nick with the new
       
   222     foreach ( $stats_memberlist as &$ml )
       
   223     {
       
   224       if ( isset($ml[$message['nick']]) )
       
   225       {
       
   226         $ml[$message['message']] = $ml[$message['nick']];
       
   227         unset($ml[$message['nick']]);
       
   228       }
       
   229     }
       
   230   }
       
   231   stats_cron();
       
   232 }
       
   233 
       
   234 function stats_cron()
       
   235 {
       
   236   static $commit_time = 0;
       
   237   $now = time();
       
   238   // commit to disk every 1 minute
       
   239   if ( $commit_time + 60 < $now )
       
   240   {
       
   241     $commit_time = $now;
       
   242     stats_commit();
       
   243   }
       
   244 }
       
   245 
       
   246 function stats_commit()
       
   247 {
       
   248   global $stats_data, $stats_day;
       
   249   
       
   250   ob_start();
       
   251   var_export($stats_data);
       
   252   $stats_data_exported = ob_get_contents();
       
   253   ob_end_clean();
       
   254   
       
   255   $fp = @fopen("./stats/stats-data-$stats_day.php", 'w');
       
   256   if ( !$fp )
       
   257     return false;
       
   258   fwrite($fp, "<?php\n\$stats_data = $stats_data_exported;\n");
       
   259   fclose($fp);
       
   260   
       
   261   if ( $stats_day != gmdate('Ymd') )
       
   262   {
       
   263     // it's a new day! flush all our logs
       
   264     foreach ( $stats_data['messages'] as &$data )
       
   265     {
       
   266       $data = array();
       
   267     }
       
   268   }
       
   269   
       
   270   $stats_day = gmdate('Ymd');
       
   271 }
       
   272