Love me or hate me for it, I'm a mac user. So of course, I use Adium as my chat client. I think the log searching is horrible in the app, so I wrote my own in perl. the first script sits in my local cgi-bin dir, and I made a firefox keyword so I can just type 'adm cheese' to search my logs for "cheese" and it will give me a nice pretty display of the search results. I use prep from CPAN to make things a little more portable than if I used system's grep command instead.

adiumLog.pl, the main cgi:

#!/usr/bin/perl $|++; use strict; use CGI; my $username = 'josh'; my $logpath = "/Users/$username/Library/Application Support/Adium 2.0/ +Users/Default/Logs/"; my $query = new CGI; print $query->header; my $keyword = $query->param('keyword'); print <<__HTML__; <style type='text/css'> li { color: #666666; } .date, .time { color: blue; } .from { color: green; } .to { color: red; } .msg { color: black; } .dump { color: orange; } a,a:visited,a:active { color:#345678; } </style> <script language='javascript'> function toggle(id) { var ele = document.getElementById(id); if (ele.style.display=='none') { ele.style.display='inline'; } else { ele.style.display='none'; } } </script> <form method='post'> Search Keyword(s): <input type='text' name='keyword' value='$keyword'/ +> <br> <input type='submit' name='submit' value='submit'> </form> <hr> __HTML__ if ($keyword) { my $pwd = `pwd`; chomp $pwd; my $cmd = "/usr/local/bin/perl $pwd/adiumLog_parser.pl \"$logp +ath\" \"$keyword\""; print `$cmd`; } print "<br>done.<br>";

adiumLog_parser.pl, the search utility. My first version was a cmdline-only search utility, so that's why it loops over a pipe...originally it took in output from `grep` on STDIN.

#!/usr/bin/perl $|++; use strict; my $logpath = $ARGV[0]; my $keyword = $ARGV[1]; $logpath =~ s/\ /\\ /g; my $cmd = "perl ./prep.pl -r \"$keyword\" $logpath*"; open IN, "$cmd|"; my %msgs; my $lines; while (<IN>) { my ($file,$prot,$me,$user,$y,$m,$d,$t,$from,$msg) = $_ =~ /($l +ogpath(.+?)\.(.+?)\/(.+?)\/.+?\((\d{4})\|(\d{2})\|(\d{2})\)\.adiumLog +):\((\d\d:\d\d:\d\d)\)\ (.+?):(.+)$/; $msgs{$y}{$m}{$d}{$t}{$prot}{$me}{$user}{to} = $me eq $from ? +$user : $me; $msgs{$y}{$m}{$d}{$t}{$prot}{$me}{$user}{from} = $me eq $from +? $me : $user; $msgs{$y}{$m}{$d}{$t}{$prot}{$me}{$user}{msg} = $msg; $msgs{$y}{$m}{$d}{$t}{$prot}{$me}{$user}{file} = $file; } close IN; $/=undef; print "<ul>"; my %seen; foreach my $y (sort keys %msgs) { my $year = $msgs{$y}; foreach my $m (sort keys %$year) { my $month = $$year{$m}; foreach my $d (sort keys %$month) { my $day = $$month{$d}; foreach my $t (sort keys %$day) { my $time = $$day{$t}; foreach my $prot (sort keys %$time) { my $proto = $$time{$prot}; foreach my $me (sort keys %$proto) { my $mee = $$proto{$me}; foreach my $user (sort keys %$mee) { my $userr = $$mee{$user}; next if $seen{$$userr{file}};$seen{$$userr{file}}=1; next unless length $$userr{from}; open F, $$userr{file}; my $dump = <F>; close F; $dump =~ s/$keyword/<span class='msg'>$keyword<\/span> +/g; $dump =~ s/\n/<br>/gsm; print <<__MSG__; <li><span class='date'>$y-$m-$d</span> <span class='time'>$t</span> From <span class='from'>$$userr{from}</span> To <span class='to'>$$userr{to}</span><br> <a onclick='toggle("$$userr{file}");return false;'>sho +w log</a><br> <span class='msg'>$$userr{msg}</span> <br> <div class='dump' id="$$userr{file}" style='display:no +ne'>$dump</div> <br> </li> __MSG__ } } } } } } } print "</ul>";

Replies are listed 'Best First'.
Re: Search Adium 2.0 Logs
by yacoubean (Scribe) on Nov 23, 2004 at 04:07 UTC
    That's pretty durn cool. I've been a long time user of Mozilla/Firefox, and I love the keyword stuff. I have a few setup myself, but I never thought of using a local perl script to leverage the ease/power of Firefox for such an app. This specific app is not very useful to me though, as I'm a Linux user, plus I don't do much IRC or IM. But I'll definitely remember the perl/firefox one two combo for future stuff. :)
      ages ago I wrote a similar script for searching my browser history and presenting the results on a webpage using firefox keyword bookmarks. Very similar as a matter of fact...just dug through my browser history files instead of my chat logs :-)
        See, now that's a script I could use. I'll have to whip one up like that for myself.
Re: Search Adium 2.0 Logs
by bageler (Hermit) on Dec 04, 2004 at 05:09 UTC
    I've cleaned up the code a lot, and merged it all into one file and no longer piping to prep.pl. The module HTML::SmallTemplate is just a simple little template engine I concocted for myself, but is off topic for this discussion. I also put the loading of the full document in a separate CGI to take a little load off this one. My template has a link to that cgi to go in a popup window.
    #!/usr/bin/perl $|++; use strict; use CGI; use Data::Dumper; use File::Find; use Time::HiRes qw(gettimeofday); use URI::Escape; use HTML::SmallTemplate qw(readtemplate); $HTML::SmallTemplate::BaseDir = './templates'; my $username = 'josh'; my $logpath = "/Users/$username/Library/Application Support/Adium 2.0/ +Users/Default/Logs/"; my $query = new CGI; print $query->header; my $keyword = $query->param('keyword'); my $data; my $grepmatch = qr/($logpath(.+?)\.(.+?)\/(.+?)\/.+?\((\d{4})\|(\d{2}) +\|(\d{2})\)\.adiumLog):\((\d\d:\d\d:\d\d)\)\ (.+?):(.+)$/; my @matches; if ($keyword) { $$data{adiumLog}{keyword} = $keyword; $$data{results} = &search; } # load template my $page = HTML::SmallTemplate->new(name => 'adiumLog', template => re +adtemplate('adiumLog'), data => $data); $page->parse; print $page->output; sub search { my %msgs; my $lines; scan($logpath); my (@date,@time,@from,@to,@file,@snip,@id); for ( map $_->[0] => sort { $a->[1] cmp $b->[1] } map [$_,"$$_[4]$$_[5]$$_[6]$$_[7]$$_[3]"] => @matches ) { my ($file,$prot,$me,$user,$y,$m,$d,$t,$from,$msg) = @{ +$_}; my $snip = snippit($file,$keyword); $snip =~ s/$keyword/<span class='msg'>$keyword<\/span> +/g; $snip =~ s/\n/<br>/gsm; my $id = join '',gettimeofday; push @date, "$y-$m-$d"; push @time, $t; push @from, $me eq $from ? $me : $user; push @file, uri_escape($file); push @snip, $snip; push @id, $id; push @to, $me eq $from ? $user : $me; } return { date => [@date], time => [@time], from => [@from], file => [@file], snip => [@snip], id => [@id], to => [@to], }; } sub snippit { my ($file,$keyword,$in,@catch) = @_; open F, $file; my $match = qr/$keyword/; while (<F>) { push @catch,tell F; if ($_ =~ $match) { $in=1; } $in++ if $in; if ($in == 4) { last; } elsif (!$in && $#catch > 2) { shift @catch; } } seek F,$catch[0],0; my $snip; read F,$snip,$catch[$#catch]-$catch[0],0; close F; $snip; } sub scan { my @path = @_; my $regex = qr/$keyword/; find( sub { return unless -T; open my $fh, $File::Find::name; domatch($fh, $File::Find::name,$regex); close $fh; }, @path ); } sub domatch { my $fh = shift; my $name = shift; my $regex = shift; while( <$fh> ) { if ( /$regex/o ) { push @matches, ["$name:$_" =~ /$grepmatch/o] a +nd last; } } }