# duckduckgo.pl is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# 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 Lesser 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 .
use strict;
use POSIX;
use vars qw($VERSION %IRSSI);
use Irssi;
use LWP::UserAgent;
use HTML::Entities;
use URI::Escape;
$VERSION = '0.03';
%IRSSI = (
authors => 'bw1',
contact => 'bw1@aol.at',
name => 'duckduckgo',
description => 'search by https://duckduckgo.com/html/',
license => 'lgplv3',
url => 'http://scripts.irssi.org',
changed => '2021-10-09',
selfcheckcmd=> 'ddg -check',
);
my $url="https://duckduckgo.com/html?q={}";
#my $url="https://duckduckgo.com/html?q=irssi";
my $view_count=5;
my $browser="firefox '{}'";
my @res;
my $res_next;
# fork
my $read_handle;
my $write_handle;
my $forkcount=0;
my $pipe_tag;
sub www_get {
(my $url) =@_;
# Initialize LWP
my $ua = new LWP::UserAgent;
$ua->agent("duckduckgo.pl/0.1 " . $ua->agent);
# get
my $req = new HTTP::Request GET =>$url;
my $res = $ua->request($req);
return $res->content;
}
sub content2res {
(my $content) = @_;
my @content =split /\n/,$content;
my @res;
my $index=1;
foreach (@content) {
if ($_ =~ m/class="result__a"/) {
my %r;
$r{index}=$index;
# url
$_ =~ m/href="(.*?)"/;
$1 =~ m/uddg=(.*)&/;
my $u=uri_unescape($1);
$r{url}=$u;
# txt
$_ =~ m#">(.*?)#;
my $s =$1;
$r{txt_raw}=$s;
$s=~s//%U/g;
$s=~s##%U#g;
$r{txt}=$s;
# out
push @res,{%r};
$index++;
}
}
return @res;
}
sub backgroundf {
(my $url, my $write_handle) =@_;
print "child start";
my $res = www_get($url);
print "child fertig";
print $write_handle $res;
print $write_handle "\n";
close $write_handle;
}
sub view_results {
my ($start) =@_;
print "duckduckgo: results ";
for (my $c=$start; $c < $view_count+$start && $c <= $#res; $c ++) {
print $c,". ",$res[$c]->{txt};
if (length($res[$c]->{url}) <50) {
print " ",$res[$c]->{url};
} else {
print " ",substr($res[$c]->{url},0,20),"=>>";
}
}
}
sub sig_result {
print "sig_result";
my $r;
my $o_fh;
# input
$o_fh=select($read_handle);
local $/;
select($o_fh);
$r=readline($read_handle);
close($read_handle);
Irssi::input_remove($pipe_tag);
# filter
@res= content2res($r);
$res_next=0;
$forkcount--;
view_results($res_next);
}
sub sig_config {
$url = Irssi::settings_get_str('ddg_url');
$view_count = Irssi::settings_get_int('ddg_view_count');
}
sub cmd_ddg {
my ($args, $server, $witem) = @_;
my @alist =split / /,$args;
if ($alist[0] !~ m/^-/) {
cmd_searchf($args);
} else {
if ($alist[0] eq '-browser') {
cmd_browser($alist[1]);
} elsif ($alist[0] eq '-next') {
cmd_next();
} elsif ($alist[0] eq '-help') {
cmd_help('ddg');
} elsif ($alist[0] eq '-drop') {
cmd_drop($alist[1],$witem);
} elsif ($alist[0] eq '-check') {
@res=();
cmd_searchf("irssi");
Irssi::timeout_add_once(3000,\&self_check,'');
}
}
}
sub cmd_drop {
my ($args, $witem) =@_;
if ($witem) {
$witem->command("/say $res[$args*1]->{url}");
}
}
sub cmd_next {
$res_next+=$view_count;
view_results($res_next);
}
sub cmd_searchf {
my ($args) = @_;
my $url2=$url;
$args=~s/ /+/g;
$url2=~s/{}/$args/;
# fork
if ($forkcount==0) {
print "ddg:",$url2;
$forkcount++;
pipe($read_handle, $write_handle);
my $o_fh=select($write_handle);
local $|=1;
select($o_fh);
my $pid =fork();
if (not defined $pid) {
_error("Can't fork: Aborting");
close($read_handle);
close($write_handle);
return;
}
if ($pid == 0) {
# child
backgroundf($url2,$write_handle);
POSIX::_exit(1);
} else {
# parent
close ($write_handle);
Irssi::pidwait_add($pid);
$pipe_tag = Irssi::input_add(fileno($read_handle),
Irssi::INPUT_READ, \&sig_result, "");
}
}
}
sub cmd_browser {
my ($args) = @_;
my $b=$browser;
$b =~ s/{}/$res[$args*1]->{url}/;
system($b);
}
sub self_check {
my $s='ok';
Irssi::print("Result count: ".scalar(@res));
Irssi::print("Result url: ".$res[0]->{url});
Irssi::print("Result txt length: ".length($res[0]->{txt}));
if ( scalar ( @res ) <10 ) {
$s= "Error: result count (".scalar(@res).")";
} elsif ( $res[0]->{url} !~ m/^http.*\/$/ ) {
$s= "Error: url (".$res[0]->{url}.")";
} elsif ( length($res[0]->{txt}) < 5 ) {
$s= "Error: txt length (".length($res[0]->{txt}).")";
}
Irssi::print("Selfcheck $s");
my $schs = exists $Irssi::Script::{'selfcheckhelperscript::'};
Irssi::command("selfcheckhelperscript $s") if ( $schs );
}
sub cmd_help {
if ($_[0] eq 'ddg' || $_[0] eq 'duckduckgo') {
my $help = <<'END';
/ddg search for the keywords
/ddg -next display the next results
/ddg -browser give the url to firefox
/ddg -drop drop the url in a channel
/ddg -check self check
settings:
ddg_view_count,
ddg_url, ddg_browser (placeholder {} )
END
Irssi::print($help, MSGLEVEL_CLIENTCRAP);
Irssi::signal_stop;
}
}
Irssi::settings_add_str("duckduckgo", "ddg_url", $url);
Irssi::settings_add_str("duckduckgo", "ddg_browser", $browser);
Irssi::settings_add_int("duckduckgo", "ddg_view_count", $view_count);
Irssi::signal_add('setup changed', "sig_config");
Irssi::command_bind('help',\&cmd_help);
Irssi::command_bind("ddg", \&cmd_ddg);
Irssi::command_set_options('ddg','help browser next drop');
sig_config();