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