#!/usr/bin/perl # # This script is kind of big compared to others. That's because it is # not just a sample script, I use this script daily to sell stuff on eBay. # Hence a lot of things here are tailored to my own use of eBay. This # script does not let you control every parameter of ebay's AddItem call, # and sets some defaults that I like (like using UPS if I specify shipping # weight). Use it as a guide to write your own scripts, no more. # use IgorBusinessRules; use Net::eBay; use Data::Dumper; use Cwd; my $usage = "usage: $0 {options} category minbid name"; my $command = "$0 '" . join( "' '", @ARGV ) . "'"; my $subtitle = undef; my $siteid = 0; my $fake = 0; my $debug = 0; my $quantity = 1; my $high_fee = undef; my $sitehosted = undef; my $shipping = undef; my $freeonly = undef; my $zipcode = "60163"; my $handlingtime = 4; my $returnpolicy = "No returns unless the auction says otherwise"; my $returnsaccepted = "ReturnsNotAccepted"; my $listingType = undef; my $duration = $ENV{DEFAULT_DURATION} || 7; my $done = 1; sub get_argument { my ($name,$ref) = @_; if( $ARGV[0] eq "--$name" ) { shift @ARGV; $$ref = shift @ARGV; die "--$name requires an argument!" unless defined $$ref; return 1; } return undef; } while( $done ) { $done = 0; #print STDERR "A0=$ARGV[0].\n"; next if $done = get_argument( 'type', \$listingType ); next if $done = get_argument( 'subtitle', \$subtitle ); next if $done = get_argument( 'duration', \$duration ); next if $done = get_argument( 'quantity', \$quantity ); next if $done = get_argument( 'zipcode', \$zipcode ); next if $done = get_argument( 'shipping', \$shipping ); next if $done = get_argument( 'siteid', \$siteid ); next if $done = get_argument( 'handlingtime', \$handlingtime ); next if $done = get_argument( 'returnsaccepted', \$returnsaccepted ); next if $done = get_argument( 'returnpolicy', \$returnpolicy ); next if $done = get_argument( 'sitehosted', \$sitehosted ); if( $ARGV[0] eq '--debug' ) { shift @ARGV; $debug = 1; $done = 1; next; } if( $ARGV[0] eq '--high_fee' ) { shift @ARGV; $high_fee = 1; $done = 1; next; } if( $ARGV[0] eq '--freeonly' ) { shift @ARGV; $freeonly = 1; $done = 1; next; } if( $ARGV[0] eq '--fake' ) { shift @ARGV; $fake = 1; $done = 1; next; } } my $category = shift @ARGV || die $usage; die "$usage (cat=$category)" unless $category =~ /^\d+(,\d+)*$/; my $minimumBid = shift @ARGV || die $usage; my $bin; if( $minimumBid =~ /(\d+(\.\d+)?)(\/\d+)?/ ) { $bin = $3; $bin =~ s/\///g; $minimumBid =~ s/\/.*$//; } else { die $usage; } my $name = join( " ", @ARGV) || die $usage; my $ptitle = $name; $ptitle =~ s/\"/\\\"/g; die "Inventory number in form INV=XXXX is required" unless $ptitle =~ /INV=\d+\b/; ###################################################################### Check args if( defined $listingType ) { my $legal = { Chinese => 1, Dutch => 1, Auction => 1, StoresFixedPrice => 1, FixedPriceItem => 1, }; die "Illegal auction type '$listingType'. Legal codes are: " . join( ', ', sort keys %$legal ) unless $legal->{$listingType}; } open( INDEX, "index.html" ) || die "Cannot open index.html"; my $index = ""; $index .= "$_" while ; close( INDEX ); my $picurl; if( -f ".picurl" ) { open( PIC, ".picurl" ); $picurl = ; chomp $picurl; close( PIC ); } my $appData = `pwd`; { chomp $appData; my @d = split( /\//, $appData ); $appData = pop @d; } my $eBay = new Net::eBay(); $eBay->setDefaults( { siteid => $siteid, debug => $debug } ); # use new eBay API $eBay->setDefaults( { API => 2, debug => $debug } ); ######################################### # Now interpret shipping! # # Format: [weight[-size1xsize2xsize3]]+handling # # no weight means fixed shipping cost equal to 'handling' # # Examples: # # 70-12x22x13+15 -- 70 lbs, 12x22x13 in, 15 dollars handling # +15 -- $15 fixed fee # 40+15 -- no size (default 12x12x12_-- DISABLED to due to mistakes) # # Note that I use UPS ground only as my shipping method # my $shippingDetails; if( $index =~ /FIXED_SHIPPING_COST=(\d+(\.\d*)?)/ ) { my $new_shipping = "+$1"; # flat from auction text die "Shipping mismatch: $shipping vs. $new_shipping" if defined $shipping && $shipping && $shipping ne $1; $shipping = $new_shipping; } { my $s = $shipping; my $additional = undef; if ( $shipping eq 'local' ) { $shippingDetails = { ShippingServiceOptions => { ShippingService => "LocalDelivery", FreeShipping => 'true', #ShippingServiceID => 150, }, #ShippingType => 'Flat', }; } elsif ( $shipping eq 'free' ) { $shippingDetails = { ShippingServiceOptions => { ShippingService => "Other", FreeShipping => 'true', }, ShippingType => 'Flat', }; } elsif ( $shipping eq 'freight' ) { $shippingDetails = { ShippingServiceOptions => { ShippingService => "FreightShipping", }, ShippingType => 'FreightFlat', }; } else { my $weight = undef; my $isflatrate = undef; $weight = $1 if $s =~ s/^(\d+)//; my ($d1, $d2, $d3); if ( $weight && $s =~ s/^-(\d+)x(\d+)x(\d+)// ) { ($d1, $d2, $d3) = ($1, $2, $3); } my $handling = 0; $handling = $1 if $s =~ s/^\+(\d+(\.\d+)?)//; if ( $s =~ s/^\@(\d+(\.\d+)?)// ) { $additional = $1; } print STDERR "SHIPPING: Weight = $weight, dimensions = $d1-$d2-$d3, handling = $handling, Additional Shipping is $additional.\n"; die "Incorrectly specified shipping '$shipping' => '$s'" unless $s eq ""; if ( $s =~ s/^\@(\d+(\.\d+)?)// ) { $additional = $1; } if( $weight ) { # Calculated Rate my $service = "UPSGround"; $service = "Freight" if $weight > 150; unless ( $d1 && $d2 && d3 ) { die "Shipping dimensions not specified."; } $shippingDetails = { CalculatedShippingRate => { OriginatingPostalCode => $zipcode, PackageDepth => $d1, PackageLength => $d2, PackageWidth => $d3, PackagingHandlingCosts => { _attributes => { currencyID => 'USD' }, _value => $handling }, WeightMajor => $weight, ShippingPackage => 'PackageThickEnvelope', }, ShippingServiceOptions => { ShippingService => $service, }, ShippingType => 'Calculated', }; } else { # Flat Rate # # If handling is not specified, no shipping details will be provided! # $isflatrate = 1; if( $handling ) { $shippingDetails = { ShippingServiceOptions => { ShippingService => 'Other', ShippingServiceCost => $handling }, ShippingType => 'Flat', }; } } if ( defined $additional ) { $shippingDetails->{ShippingServiceOptions}->{ShippingServiceAdditionalCost} = $additional; } # Business rule check if ( $isflatrate && $quantity > 1 && !defined $additional ) { print "\n\b*** WARNING *** Please provide additional cost!\n"; exit 1; } } #print STDERR "\n\nShipping $shippingDetails: \n" . Dumper( $shippingDetails ) . "\n"; #exit 0; } # Now verify that the item either has shipping, or is local pickup only, or is freight. { my $noship_ok = undef; $noship_ok = 1 if $index =~ /can only be shipped via freight/s; $noship_ok = 1 if $index =~ /Local pickup ONLY/s; die "You have not specified shipping cost, local pickup, or freight. Aborting." unless $shipping || $noship_ok; } my $listingDuration = undef; if ( $listingType eq 'FixedPriceItem' ) { $listingDuration = 'GTC'; } else { $listingDuration = "Days_$duration"; } my $condition = 3000; # Used $condition = 1500 if $ptitle =~ /^NEW,/i; my $args = { Item => { debug => $debug, #BuyItNowPrice => 6.0, Title => $ptitle, Country => "US", Currency => "USD", ConditionID => $condition, UseTaxTable => 'true', Description => "", ListingDuration => $listingDuration, 'BuyerRequirements' => { 'MaximumUnpaidItemStrikes' => 'true', 'ShipToRegistrationCountry' => 'true', 'MinimumFeedbackScore' => '-1' }, BuyerRequirementDetails => { MaximumUnpaidItemStrikesInfo => { Count => 3, Period => Days_180, }, ShipToRegistrationCountry => true, VerifiedUser => 1, VerifiedUserRequirements => { MinimumFeedbackScore => 2, } }, Location => "Berkeley, IL", DispatchTimeMax => $handlingtime, ReturnPolicy => { Description => $returnpolicy, ReturnsAccepted => $returnpolicy, ReturnsAcceptedOption => $returnsaccepted, }, PostalCode => $zipcode, PaymentMethods => [ 'PayPal', 'CashOnPickup'], PayPalEmailAddress => 'ichudov@gmail.com', PrimaryCategory => { CategoryID => [ split( /,/, $category ) ] }, Quantity => $quantity, RegionID => 0, StartPrice => $minimumBid, HitCounter => 'GreenLED' } }; #$args->{Item}->{ApplicationData} = $appData if defined $appData && $appData; $args->{Item}->{BuyItNowPrice} = $bin if $bin; $args->{Item}->{ListingType} = $listingType if defined $listingType; if( $shippingDetails ) { $args->{Item}->{ShippingDetails} = $shippingDetails; } if ( $listingType eq 'FixedPriceItem' ) { $args->{Item}->{BestOfferDetails}->{BestOfferEnabled} = 'true'; $args->{Item}->{ListingDetails} = { BestOfferAutoAcceptPrice => sprintf( "%.2f", $minimumBid * igor_ebay_min_autoaccept_factor ), MinimumBestOfferPrice => sprintf( "%.2f", $minimumBid * igor_ebay_max_autoreject_factor ), }; } if( defined $subtitle ) { $args->{Item}->{SubTitle} = $subtitle; } if( $picurl ) { if( 0 ) { $args->{Item}->{VendorHostedPicture} = { GalleryType => 'Gallery', GalleryURL => $picurl, PictureURL => $picurl, SelfHostedURL => $picurl, }; } else { $args->{Item}->{PictureDetails} = { GalleryType => 'Gallery', GalleryURL => $picurl, PictureURL => $picurl, }; } } if( $sitehosted ) { $args->{Item}->{SiteHostedPicture}->{PictureURL} = $sitehosted; } print " ________________________________________ R E V I E W Y O U R A U C T I O N ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Title: $ptitle ################################################################################ (" . length( $ptitle ) . "/80) "; if( $subtitle ) { print " SubTitle: $subtitle ################################################################################ (" . length( $subtitle ) . "/80) "; } print " Category : $category Starting Price: $minimumBid BIN : $bin Shipping : $shipping "; if( length( $ptitle ) > 80 ) { print STDERR "Error, title too long.\n"; exit; } { my $verify = $eBay->submitRequest( 'VerifyAddItem', $args ); print Dumper( $verify ) if $debug; my $total = 0; foreach my $fee (@{$verify->{Fees}->{Fee}}) { my $amount = $fee->{Fee}->{content}; if( $amount && $freeonly ) { print STDERR "ERROR, --freeonly specified but fees are not zero\n"; exit 1; } if( $amount > 12 && !$high_fee ) { print STDERR "Error, listing fee '$fee->{Name}' is $amount, TOO HIGH and no --high_fee argument. \n"; exit 1; } next unless $amount > 0 && $fee->{Name} ne 'ListingFee'; print sprintf( "%16s", $fee->{Name} ) . ": $amount.\n"; $total += $amount; } print " TOTAL FEE: $total <===** \n\n"; unless( $total ) { print Dumper( $verify ); exit 1; } } exit 0 if $debug; if( !$fake && -t STDIN ) { print "Press Enter to continue:\n"; my $dummy = ; } unless( $fake ) { open( SAVE, ">relist.sh.new" ); print SAVE "$command\n\n"; close( SAVE ); rename "relist.sh.new", "relist.sh"; chmod 0755, "relist.sh"; } ######################################## Bunching my $fn = "$ENV{HOME}/.ebay-bunch.sh"; if( -t STDIN && -f $fn && ($listingDuration ne 'GTC' || $ENV{BUNCH_ALL_EBAY_LISTINGS}) ) { open( BUNCH, ">>$fn" ) || die "Cannot append to $fn"; print BUNCH "cd " . getcwd . "; ./relist.sh\n"; close( BUNCH ); print "Added to bunch $fn.\n"; exit( 0 ); } my $request = $fake ? "VerifyAddItem" : "AddItem"; my $result = $eBay->submitRequest( $request, $args ); if( ref $result ) { if( $result->{ItemID} ) { unless( $fake ) { open( ITEM, ">item.txt" ); print ITEM "$result->{ItemID}\n"; close( ITEM ); } if( $fake ) { print "FAKE LISTING ATTEMPT SUCCEEDED!\n\n"; } else { #print "Result: " . Dumper( $result ) . "\n"; print "----------------------------------------------------------------- CONGRATS Your item was listed: $eBay->{public_url}?ViewItem&item=$result->{ItemID} "; } my $total = 0; foreach my $fee (@{$result->{Fees}->{Fee}}) { my $amount = $fee->{Fee}->{content}; next unless $amount > 0 && $fee->{Name} ne 'ListingFee'; print "Fee: $fee->{Name}: $amount.\n"; $total += $amount; } print "----------------------------- TOTAL FEE: $total "; unless ( $listingType eq 'FixedPriceItem' ) { system( "bash -i -c 'ms bash -i -c reset-ebay' " ); } if( open( ANN, ">annotation.txt" ) ) { print ANN $ptitle; close( ANN ); system( "cpak annotation.txt" ); print STDERR "Wrote and COPIED annotation.txt\n"; } else { print STDERR "FAILED TO WRITE annotation.txt\n"; } } else { print "ERROR in Result: " . Dumper( $result ) . "\n"; } } else { print "Unparsed result: \n$result\n\n"; }