All pastes #794135 Raw Edit

googlesearch.pl

public text v1 · immutable
·published 2007-11-22 22:28 UTC
rendered paste body
# googlesearch.pl: !google search trigger for irssi
#
# Copyright (C) 2007 Tristan Willy <tristan.willy at gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

use strict;
use warnings;
use Text::ParseWords;
use HTML::Entities;
use Compress::Zlib;
use URI::Escape;
use JSON;
use LWP;

use vars qw($VERSION %IRSSI);
use Irssi;
$VERSION = '0.01';
%IRSSI = (
    'authors'     => 'Tristan Willy',
    'contact'     => 'tristan.willy at gmail.com',
    'name'        => 'Google Search',
    'description' => 'Google Search for irssi. Uses Google\'s AJAX API.',
    'license'     => 'GPL v2'
    );

Irssi::settings_add_str($IRSSI{'name'}, 'gsearch_channels', '');
Irssi::settings_add_str($IRSSI{'name'}, 'gsearch_site_url', '');
Irssi::settings_add_str($IRSSI{'name'}, 'gsearch_key', '');

Irssi::signal_add('event privmsg', 'irc_privmsg');

sub irc_privmsg {
  my ($server, $data, $from, $address) = @_;
  my ($to, $message) = split(/\s+:/, $data, 2);
  my ($me, $target) = ($server->{'nick'}, find_target($to, $from));
  my (%gsearch_active_chans, $site_url, $ajax_key);

  # Are we triggered?
  if($message !~ /^\s*!google\s+(.+)/i){
    return 1;
  }
  my $encoded_query = uri_escape($1, "^A-Za-z0-9\-_.!~*'()+");

  # pull in settings
  map { $gsearch_active_chans{uc($_)} = 1 }
    quotewords(',', 0, Irssi::settings_get_str('gsearch_channels'));
  $site_url = Irssi::settings_get_str('gsearch_site_url');
  $ajax_key = Irssi::settings_get_str('gsearch_key');

  # verify settings and trigger perms
  if(not $site_url or not $ajax_key){
    Irssi::print("Google search: search detected, but settings not set.");
    return 1;
  }
  if(not (uc($to) eq uc($me) or $gsearch_active_chans{uc($to)})){
    return 1;
  }

  my $agent = LWP::UserAgent->new(
      'agent' =>
        'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) ' .
        'Gecko/20060124 Firefox/1.5.0.1');
  my $reply = $agent->get(
      'http://www.google.com/uds/GwebSearch?' .
      'callback=GwebSearch.RawCompletion&' .
      'context=0&lstkp=0&rsz=large&hl=en&gss=.com&' .
      'sig=827f423b91df4dc97aaeb3a17d5711a0&' .
      'q=' . $encoded_query . '&' .
      'key=' . $ajax_key . '&v=1.0',
      'Referer' => $site_url);

  my $results;
  if($reply->is_success){
    if($reply->content_encoding and
        $reply->content_encoding eq 'gzip'){
      $results = Compress::Zlib::memGunzip($reply->content);
    } else {
      # assume they're plain-text...
      $results = $reply->content;
    }
  } else {
    $server->command("msg $target Error: Google died. The end is near.");
    return 1;
  }

  # Cut out the JS call and parse
  $results =~ s/^GwebSearch.RawCompletion\('\d+',//;
  my $resh = jsonToObj($results);

  if(not $resh){
    $server->command("msg $target Error: Google is speaking gibberish.");
    return 1;
  }

  if($#{$resh->{results}} < 0){
    $server->command("msg $target No results.");
    return 1;
  }

  my $first = $resh->{results}[0];
  $server->command("msg $target " .
    untag(decode_entities($first->{title})) . " <$first->{unescapedUrl}>\n");
  return 1;
}

sub find_target {
  my ($to, $from) = @_;

  if($to =~ /^#/){
    return $to; # from a channel
  }
  return $from; # from a private message
}

# powerman's HTML striper: http://www.perlmonks.org/?node_id=161281
sub untag {
  local $_ = $_[0] || $_;
# ALGORITHM:
#   find < ,
#       comment <!-- ... -->,
#       or comment <? ... ?> ,
#       or one of the start tags which require correspond
#           end tag plus all to end tag
#       or if \s or ="
#           then skip to next "
#           else [^>]
#   >
  s{
    <               # open tag
    (?:             # open group (A)
      (!--) |       #   comment (1) or
      (\?) |        #   another comment (2) or
      (?i:          #   open group (B) for /i
        ( TITLE  |  #     one of start tags
          SCRIPT |  #     for which
          APPLET |  #     must be skipped
          OBJECT |  #     all content
          STYLE     #     to correspond
        )           #     end tag (3)
      ) |           #   close group (B), or
      ([!/A-Za-z])  #   one of these chars, remember in (4)
    )               # close group (A)
    (?(4)           # if previous case is (4)
      (?:           #   open group (C)
        (?!         #     and next is not : (D)
          [\s=]     #       \s or "="
          ["`']     #       with open quotes
        )           #     close (D)
        [^>] |      #     and not close tag or
        [\s=]       #     \s or "=" with
        `[^`]*` |   #     something in quotes ` or
        [\s=]       #     \s or "=" with
        '[^']*' |   #     something in quotes ' or
        [\s=]       #     \s or "=" with
        "[^"]*"     #     something in quotes "
      )*            #   repeat (C) 0 or more times
    |               # else (if previous case is not (4))
      .*?           #   minimum of any chars
    )               # end if previous char is (4)
    (?(1)           # if comment (1)
      (?<=--)       #   wait for "--"
    )               # end if comment (1)
    (?(2)           # if another comment (2)
      (?<=\?)       #   wait for "?"
    )               # end if another comment (2)
    (?(3)           # if one of tags-containers (3)
      </            #   wait for end
      (?i:\3)       #   of this tag
      (?:\s[^>]*)?  #   skip junk to ">"
    )               # end if (3)
    >               # tag closed
   }{}gsx;          # STRIP THIS TAG
  return $_ ? $_ : "";
}