# - Google.pl use strict; use Irssi qw(); use URI::Escape; use HTML::Parser; use Data::Dumper::Names; use vars qw($VERSION %IRSSI); $VERSION = '3.0'; %IRSSI = ( authors => 'Tamara Temple', contact => 'tamara@tamaratemple.com', name => 'google', description => 'Add functions to return google searches and implement lmgtfy builder', license => 'Public Domain', url => 'http://public.tamaratemple.com/irssi/google-'.$VERSION.'.pl.txt', ); my $debug=0; my $empty_re = qr/^\s*$/; ## Usage: ## /google [-say] [-] search words ## ## -say -- send results to current channel/query window, otherwise just print on current window ## - -- number of results to show (always <= 10, google's default) ## ## /goggledebug [on|off] ## ## on - turn debugging on ## off - turn debugging off ## - show current state ## ## Customization: ## google_number_sites (defaults to 3) ## google_prefix (message that gets printed before showing results (empty) ## # ############################################## # CONSTANTS AND DEFAULT CONFIGURATION SETTINGS # ############################################## my $max_results = 10; # maximum number of results google will return in a bare query my $default_results = 3; my $default_prefix = ''; my $default_connect_timeout = 30; my $default_max_timeout = 120; # The configured number of results to show, defaults to 10 Irssi::settings_add_int('misc','google_num_results',$default_results); # The configured prefix message to print before showing results Irssi::settings_add_str('misc','google_prefix',$default_prefix); # Curl connection timeout Irssi::settings_add_int('misc','google_connect_timeout',$default_connect_timeout); # Curl overall timeout Irssi::settings_add_int('misc','google_max_timeout',$default_max_timeout); # #################### # GUI COMMANDS HERE # #################### # /google command sub cmd_google { my ($data, $server, $witem) = @_; # Pick up settings my $num_show = Irssi::settings_get_int('google_num_results') ? Irssi::settings_get_int('google_num_results') : $default_results; my $prefix = Irssi::settings_get_str('google_prefix') ? Irssi::settings_get_str('google_prefix') : $default_prefix; my $to_window = 0; my @words = split(/\s+/,$data); while ($words[0] =~ m|^-|) { my $arg = shift @words; SWITCH: { $arg =~ /-sa?y?/i && do { $to_window = 1; last SWITCH;}; $arg =~ /-\d+/ && do { $num_show = abs($arg); last SWITCH;}; } } $num_show = $max_results if $num_show > $max_results; dbg("Showing $num_show results to ".($to_window ? "current channel/query" : "screen")); if ((!$witem) && ($to_window)) { showerror("No channel/query window currently active"); return undef; } my @results = get_google_results(join(' ',@words)); map { dbg("result: ".irssi_safe($_)); } @results; if (!defined(@results)) { if ($to_window) { $witem->command("/say No results"); } else { irssi_print("No results"); } } if ($prefix !~ $empty_re) { if ($to_window) { $witem->command("/say $prefix"); } else { irssi_print("$prefix"); } } if ($to_window) { $witem->command("/say google results for: ".irssi_safe(join(' ',@words))); } for (my $i=0; $i < $num_show; $i++) { if ($to_window) { $witem->command("/say ".irssi_safe($results[$i])); } else { irssi_print(irssi_safe($results[$i])); } } } Irssi::command_bind('google', 'cmd_google'); # simple function to write a lmgtfy response (snark) sub cmd_lmgtfy { my ( $data, $server, $witem ) = @_; return 0 if ( !$witem ); $witem->command(sprintf("/say http://lmgtfy.com/?q=%s", make_search_safe($data))); return 1; } Irssi::command_bind( 'lmgtfy', 'cmd_lmgtfy' ); # ################### # WORKING CODE HERE # ################### # global used to collect results -- not really used elsewhere and shouldn't be my @results_g; # collect the google page of results, parse it, and return the pertinant results. sub get_google_results { my $html = google_query(shift @_); return undef if (!defined($html)); @results_g = (); # reset the results for this query my $p = HTML::Parser->new( api_version => 3, start_h => [\&start, "tagname, attr"], ); $p->parse($html) or return undef; return undef if ($#results_g < 0); return @results_g; } # callback for html parser sub start { my ($tag, $attr_r) = @_; my %attr = %$attr_r; # derefence hash if (lc($tag) eq 'a') { if ((defined $attr{'href'}) && (defined $attr{'class'}) && ($attr{'class'} eq 'l')) { push @results_g, ($attr{'href'}); } } } # perform a google query and retrieve the html sub google_query { my $url="http://www.google.com/search?q=".make_search_safe(shift @_); dbg("google search url = ".irssi_safe($url)); my $outputfile = '/tmp/curl.'.time; # cheap way to make a unique file? unlink($outputfile); # just to be sure my $curlopt_connect_timeout = Irssi::settings_get_int('google_connect_timeout') ? Irssi::settings_get_int('google_connect_timeout') : $default_connect_timeout; my $curlopt_max_timeout = Irssi::settings_get_int('google_max_timeout') ? Irssi::settings_get_int('google_max_timeout') : $default_max_timeout; my $curl_cmd = "curl"; my @curlopts = ( "--fail", "--insecure", # don't worry if a certificate doesn't pass "-o $outputfile", #"--max-filesize 10240", # this tends to cause some requests to fail strangely '-A "Mozilla"', "--connect-timeout $curlopt_connect_timeout", "--location", # follow 302 returns "--max-time $curlopt_max_timeout", "--silent", "--show-error" ); my $cmd = $curl_cmd . ' ' . join( ' ', @curlopts ) . ' ' . "'" . $url . "'".' 2>&1'; dbg("cmd=".irssi_safe($cmd)); my @result = `$cmd`; if ($!) { ## OOPS, curl command returned an error my $curl_error=$!; showerror("Curl command: $cmd - returned an error: $curl_error"); return undef; } dbg("Value of \$#result=$#result"); if ($#result >= 0) { shownotice("curl said something..."); for (my $i = 0; $i <= $#result; $i++) { shownotice("[$i] $result[$i]"); } return undef; } dbg("Results in $outputfile"); my $html; { local $/ = undef; local *FILE; open FILE, "<", $outputfile or showerror("Unable to open $outputfile: $?"); $html = ; close FILE } unlink($outputfile) unless $debug; dbg("size of html retrieved: ".length($html)); return $html; } # ######################## # SOME USEFUL FUNCTIONS # ######################## # Useful for debug messages; leave in debug messages, control via $debug variable sub dbg { my $msg = shift; return if (!$debug); return if (!defined($msg) || $msg =~ $empty_re); irssi_print("%RDEBUG%n ".$msg); } # make a string safe to print on the irssi screen sub irssi_safe { my $s = shift; return if (!defined($s) || $s =~ $empty_re); $s =~ s/%/%%/g; return $s; } # print a message on the active window sub irssi_print { my $s = shift; return if (!defined($s) || $s =~ $empty_re); Irssi::active_win()->print("%Y[IRSSI{'name'}]%n $s"); } # print an error message on whatever window is in front sub showerror { my $msg = shift; return if (!defined($msg) || $msg =~ $empty_re); Irssi::print("%Y[$IRSSI{'name'}]%n %RERROR!! $msg"); } # send a message to the target sub say_message { my ($server, $target, $msg) = @_; return if (!defined($server) || (!defined($target)) || $target =~ $empty_re || !defined($msg) || $msg =~ $empty_re); $server->command("msg $target [$IRSSI{'name'}] $msg"); } # send an action to the target sub me_action { my ($server, $target, $msg) = @_; return if (!defined($server) || (!defined($target)) || $target =~ $empty_re || !defined($msg) || $msg =~ $empty_re); $server->command("ACTION $target $msg"); } # Print on the window that the signal came from sub printOnThisWindow { my ($server, $target, $msg) = @_; return if (!defined($server) || (!defined($target)) || $target =~ $empty_re || !defined($msg) || $msg =~ $empty_re); my $witem = $server->window_find_item($target); $witem->print("[$IRSSI{'name'}] $msg"); } # function to make a string search-safe for a URL # # escape common characters # convert spaces to pluses # sub make_search_safe { my $s = shift @_; return if (!defined($s)); dbg("in make_search_safe: ".irssi_safe($s)); $s = uri_escape($s); $s =~ s/%20/+/g; dbg("out of make_search_safe: ".irssi_safe($s)); return $s; } # turn debugging on or off via the command line # # Usage: /googledebug [on|off] # on - turn debugging on # off - turn debugging off # omitted - show current debug status # sub cmd_setdebug { my ($data, $server, $witem) = @_; SWITCH: { $data =~ /^on$/i && do { $debug = 1; last SWITCH;}; $data =~ /^off$/i && do { $debug = 0; last SWITCH;}; # don't bother if it's anything else } irssi_print("debug is ".($debug?'ON':'OFF')); } Irssi::command_bind($IRSSI{'name'}.'debug', 'cmd_setdebug'); irssi_print($IRSSI{'name'}.' loaded');