Category:
Author/Contact Info steven fountain (cider@compulsion.org) http://enraptured.compulsion.org
Description: perl script that uses expect.pm and the command line gnutella client "gnut", attempts to automate:
  • identifying what files by this group you already have
  • logging in and waiting for at least 500 neighbors (users)
  • search for this group and wait a little
  • pull out and parse the responses
  • build unique list of songs that you dont have
  • fires off a ton of get requests
  • drops you in interactive mode to watch the rest
  • #!/usr/bin/perl
    
    # autonuts v2.1 - automation of gnut using expect.pm
    # by steven fountain (cider@compulsion.org)
    # web: http://enraptured.compulsion.org/autonuts
    # web: http://enraptured.compulsion.org/code/
    #
    # autonuts is designed to pillage and data-mine gnutella using the
    # command line gnutella client "gnut", using perl and Expect.pm
    # in a very hacked fashion.
    #
    # this tool succeeds at identifying unique file names
    # when given enough responses to chew through.
    #
    # in order to have it not grab files you already have, you must
    # change the $archive variable to point to where your mp3's are,
    #      ie, /mp3
    # then you must move the contents of a retrieval for "the fish"
    #      into /mp3/the_fish
    # or "fish" as
    #      /mp3/fish
    #
    # after identifying available unique songs by a band that you
    # dont already have, you can then queue them all..
    # take the path of no return and jump into interactive mode
    # to watch the files download.
    #
    # usage: autonuts your simple text bandname here
    #    ie: autonuts sneakerpimps &
    #        autonuts the cure &
    #        autonuts fugazi &
    #                             ;)
    #
    # requirements:
    #    expect perl module w/stty/pty dependencies (deb users: libexpect-
    +perl)
    #  gnut (deb users: gnut)
    #  some bandwidth
    #  some harddrive space
    #
    
    # where your files are mostly stored.. for re-running
    $archive = "/ok/muse"; # change to /mp3 or something if i had drives?
    
    use Term::ANSIColor; # allways like to have this on my plate...
    use Expect; # gnut's kinda like ftp. *wink*
    use Text::Soundex; # for assistance in finding unique filenames
    
    $|++; # i like autoflushing too much.
    
    $*{STDERR} = \*{STDOUT}; # wouldnt want to miss anything now
    $SIG{INT} = sub {    $nut -> hard_close(); die "exiting.\n"; }; # hand
    +le ctl-C
    
    $Expect::Debug=0;
    $Expect::Exp_Internals=0;
    $Expect::Log_Stdout=0; # turn this on for annoying debugging...
    $Expect::Multiline_Matching = 0;
    $Expect::Exp_Max_Accum = 0;
    $Expect::Manual_Stty = 1;
    
    # take a band name from command line.
    $band = join ' ', @ARGV;
    $band = lc($band);
    if ($band =~ /^\w/) { print "Good bandname found from commandline: $ba
    +nd\n"; }
    else { undef $band; }
    
    # coloring debugging output routines
    sub red { print color 'red'; }
    sub pred { $_ = shift; print color 'red'; print "$_"; print color 'res
    +et'; print "\n"; }
    sub pgreen { $_ = shift; print color 'green'; print "$_"; print color 
    +'reset'; print "\n"; }
    sub pbgreen { $_ = shift; print color 'bold green'; print "$_"; print 
    +color 'reset'; print "\n"; }
    sub white { print color 'bold white'; }
    sub creset { print color 'reset'; }
    sub pret { print "\n"; }
    
    sub wait_for_prompt {
        $quiet = @_;
        print "waiting for prompt.. ";
        unless($nut -> expect(15, "autonuts>")) {
            print "NOT ok.\n";
            return 0;
        }
        print "ok.\n";
        return 1;
    }
    
    sub waitfor {
        $waitfor = shift;
        $howlong = shift || 60;
        print "expecting \"$waitfor\" for ${howlong}s.. ";
        unless($nut -> expect($howlong, '-re', "$waitfor")) {
            print "NOT found.\n";
            return 0;
        }
        print "ok.\n";
        return 1;
    }
    
    sub start {
        print "spawning gnut process.. ";
        ($nut = Expect->spawn("gnut 2>&1")) || die "Couldn't spawn gnut: $
    +!";
        print "ok.\n";
        print "waiting for gnut to start.. ";
        unless($nut -> expect(15, "at your service")) {
        die "gnut didn't start successfully.\n"; }
        print "ok.\n";
    }
    
    
    @settings = (
        "set prompt autonuts>\\n", # without this, wait_for_prompt would b
    +reak
        "set response_format sz:{S} :: sp:{s} :: {f#} :: {N} ::", # needed
    + for "resp"
        "set paginate 0", # gets rid of requests to more every 25 lines
        "set stats_format 1", # one line serverstats instead of two, i thi
    +nk
        "set max_downloads 100",
        "set max_incoming 100",
        "set sort_order p", # sort by speed, show the fastest ones at bott
    +om
    );
    
    sub settings {
        foreach $setting (@settings) {
            print "$setting.. ";
            $nut->send_slow(0, "$setting\r\n");
            print "ok. ";
            wait_for_prompt();
        }
    }
    
    sub sane {
        print "waiting for network to become ready..\n";
            until($network_is_ready) {
            print "gathering... ";
            $nut->clear_accum();
            $nut->send_slow(0, "info\r\n");
            unless($nut -> expect(2, '-re', "HOST STATS:") ) {
                print "blah.\n";
            }
        
            $host_stats = $nut->exp_after();
            @host_stats = ();
            @host_stats = split /\n/, $host_stats;
            foreach (@host_stats) {
                if (/Unique GUIDs in memory: (\d+)/) {
                    $users = $1;
                    print "unique users: $users (not >500)";
                    $network_is_ready++ if ($users > 500);
                }
            }
            print " resting 20s" unless $network_is_ready;
            sleep 20 unless $network_is_ready;
            print ";\n";
        }
        wait_for_prompt(quiet);
        $nut->send_slow(0, "info\r\n");
        wait_for_prompt(quiet);
        pgreen "sane connectivity scenario reached.";
    }
    
    #until($cmd eq "stop"||$cmd eq "quit"||(!$nut)) {
    #    print "autonuts: ";
    #    chomp($cmd = <STDIN>) unless $auto;
    #    $auto=0 if $auto; # disable if previously auto...
    
    sub info {
            $nut->clear_accum();
            $nut->send_slow(0, "info\r\n");
            waitfor "HOST STATS:";
            pgreen "playing back...";
            $info = $nut->exp_after();
            @info = ();
            @info = split /\n/, $info;
            foreach (@info) {
                chomp;
                $l++;
                pgreen "$l: $_";
            }
            undef $l;
            wait_for_prompt();
    }
    
    sub band {
            print "band presently set to: $band\n";
    }
    
    sub bandset {
        $band = shift;
        $band = lc($band);
        band();
    }
                    
    sub search {
            pred("searching for stuff by ${band}... 75 second headstart...
    + ");
            $nut->clear_accum();
            $nut->send_slow(0, "search $band\r");
            sleep 75;
            # the funny thing is that after 60s this picks up ~365, 495, e
    +tc.
            waitfor("responses received.", 120) ? $results=0 : $results=1 
    +;
            $nut->send_slow(0, "\r");
            search() unless $results;
            resp() if $results;
    }
    
    sub resp {
            pred("showing responses...");
            $nut->clear_accum();
            $nut->send_slow(0, "resp\r");
            $results = wait_for_prompt();
            if($results) {
            $responses = $nut->exp_before();
            @responses = ();
            @responses = split /\n/, $responses;
            $fmp3=0;
            %leech = ();
            %titles = ();
            %sounds = ();
            foreach (@responses) {
                chomp;
                $l++;
                pgreen "$l: $_";
                if (/mp3/i) {
                    $fmp3++;
                    /sz:(.*?)\s+::\ssp:\s{0,}(\d+)\s::\s(\d+)\s::\s(.*?)\s
    +::/;
                    $song   = $4; # and print "song: $song\n";
                    $number = $3; # and print "number: $number\n";
                    $speed  = $2; # and print "speed: $speed\n";
                    $size   = $1; # and print "size: $size\n";
                    
                    $isize = int($size);
                    next unless ($size =~ /m$/i); # should be in the meg b
    +allpark
                    next unless ($isize > 2); # should be bigger then two 
    +megs
                    next unless ($isize < 10); # should be less than ten m
    +egs
                    next unless ($speed > 200); # should get it faster tha
    +n isdn
    
                    $song = lc($song); # change the song to lowercase
                    $song =~ s/_/ /g; # change underscores to spaces
                    $song =~ s/\.mp3//gi; # remove the file extension
                    $song =~ s/\s{0,}[\(\[].*?[\[\(].*?[\]\)].*?[\]\)]\s{0
    +,}//g; # remove double variant comments eg: (remix(its phat))
                    $song =~ s/\s{0,}[\(\[].*?[\]\)]\s{0,}//g; # remove va
    +riant comments eg: (remix)
                    $song =~ s/${band} - .* - (.*?)$/${band} - $1/g; # gre
    +edy attempt at removing album names
                    next unless ($song =~ /$band - .*?/i); # should resemb
    +le band with a title
                    
                    $title = $1 if ($song =~ /${band} - (.*?)$/);
    
    
                    $code = soundex $title;
    
                    $already_have = 0;
                    foreach (@already_have) {
                        $ihave = $_;
                        $ihavecode = $1 if ($ihave =~ /^(.*?) /);
                        $uhave = "$code $title";
                        # if it sounds like something we already have,
                        # we probably dont want it.
                        if ($uhave =~ /^$ihavecode /) {
                            $already_have = 1;
                            print "passing by: $code $title\n";
                        }
                    }
    
                    if ($already_have eq 0) {
                        print "marking: $code $title\n";
                        $sounds{$code}{title} = $title;
                        $sounds{$code}{number} = $number;
                    }
                    
                };
            }
            
            unless ($l > 20 && $number / 2 < $l) {
                print "not happy with these results, trying again.\n";
                $auto=1;
                $resp_sucks++;
                if ($resp_sucks > 5) {
                    undef $resp_sucks;
                    print "hrm. you might want to try typing search again.
    +\n";
                    print "searching again...\n";
                    search(); # ha figured it out heh
                    }
                search();
            } else {
                print "i'm happy with the results. you should now type lis
    +t\n";
            }
            undef $l;
    
        }
            $auto=1 unless $fmp3;
    }
    
    sub list {        
            foreach $codes (sort keys %sounds) {
                $uniq++;
                print "$codes [$sounds{$codes}{number}]\t$sounds{$codes}{t
    +itle}\n";
            }
            print "$uniq uniq.\n";
            search() unless ($uniq =~ /^\d+/);
            undef $uniq;
    }
        
    sub getuniq {    
            print "i'll take";
            foreach $codes (sort keys %sounds) {
                $num = $sounds{$codes}{number};
                print " ${num},";
                $nut->send_slow(0, "g $num\r\n");
            }
            print " and that aughta do it.\n";
            print "requested all unique files.\n";
    }
        
    sub log {                                                            
            if ($log) { pred "turning log off."; $log=0; $Expect::Log_Stdo
    +ut=0; }
            unless ($log) { pred "turning log on."; $log=1; $Expect::Log_S
    +tdout=1; }
    }
    
    sub before {
            print $nut->exp_before();
    }
    
    sub after {    
            print $nut->exp_after();
    }
        
    sub interact {
            print "please do not return to autonuts, i'm changing your pro
    +mpt...\n";
            print "setting prompt.. ";
            $nut->send_slow(0, "set prompt gnut+autonutted>\r\n");
            print "ok.\n";
            $nut->interact();
            $cmd = "stop"; # eh who knows might work...
    }
    
    sub help {    
            print "commands..\nband info search resp list getuniq log befo
    +re after interact\nif you run them in no specific order your mileage 
    +will vary\n";
    }
    
    
    sub check_existing_songs {
        $group = shift;
        $group =~ s/ /_/g;
        print "opening existing $group...\n";
        opendir OFF, "$archive/$group" or return "no archive directory fou
    +nd.\n";
        while($song = readdir OFF) {
            next if ($song =~ /^\.|\.\.$/);
            next unless ($song =~ /\.mp3$/i);
            $song = lc($song); # change the song to lowercase
            $song =~ s/_/ /g; # change underscores to spaces
            $song =~ s/\.mp3//gi; # remove the file extension
            $song =~ s/\s{0,}[\(\[].*?[\[\(].*?[\]\)].*?[\]\)]\s{0,}//g; #
    + remove double variant comments eg: (remix(its phat))
            $song =~ s/\s{0,}[\(\[].*?[\]\)]\s{0,}//g; # remove variant co
    +mments eg: (remix)
            $song =~ s/${band} - .* - (.*?)$/${band} - $1/g; # greedy atte
    +mpt at removing album names
            next unless ($song =~ /$band - .*?/i); # should resemble band 
    +with a title
            $title = $1 if ($song =~ /${band} - (.*?)$/);
            $code = soundex $title;
            print "already have: $code $title\n";
            push @already_have, "$code $title";
        }
        closedir OFF;
    }
    
    
    
    
    # roadmap..
    print "band: " unless(defined($band));
        chomp($band = <STDIN>) unless(defined($band));
    
    start();
    settings();
    check_existing_songs($band);
    sane();
    bandset($band) if(defined($band));
    search();
    list();
    getuniq();
    print "abandoning you now to the gnut program, press enter once inside
    +.\n";
    interact(); # lost into gnut...
    
    
    # shouldnt get here...
    print "aah, they got me.\n";
    print "i'm dying.\n";
    $nut->hard_close();
    die "dead.\n";