zeroclickinfo-goodies/lib/DDG/Goodie/VIN.pm

150 lines
3.4 KiB
Perl

package DDG::Goodie::VIN;
# ABSTRACT: extract information about vehicle identification numbers
use strict;
use DDG::Goodie;
zci is_cached => 1;
zci answer_type => "vin";
triggers query_lc => qr/([\d+a-z]{17})|
(^\d+$)
/x;
# Regex for VIN.
my $vin_qr = qr/v(?:ehicle|)i(?:dentification|)n(?:umber|)/oi;
my $tracking_qr = qr/package|track(?:ing|)|num(?:ber|)|\#/i;
# Checksum for VIN.
my %vin_checksum = (
'A' => 1,
'B' => 2,
'C' => 3,
'D' => 4,
'E' => 5,
'F' => 6,
'G' => 7,
'H' => 8,
'I' => 'X',
'J' => 1,
'K' => 2,
'L' => 3,
'M' => 4,
'N' => 5,
'O' => 'X',
'P' => 7,
'Q' => 'X',
'R' => 9,
'S' => 2,
'T' => 3,
'U' => 4,
'V' => 5,
'W' => 6,
'X' => 7,
'Y' => 8,
'Z' => 9,
);
my %vin_checksum_weight = (
'1' => 8,
'2' => 7,
'3' => 6,
'4' => 5,
'5' => 4,
'6' => 3,
'7' => 2,
'8' => 10,
'9' => 0,
'10' => 9,
'11' => 8,
'12' => 7,
'13' => 6,
'14' => 5,
'15' => 4,
'16' => 3,
'17' => 2,
);
# VIN numbers.
# 2008.07.29 force some letters because
# "Alvin and the Chipmunks songs" passes checksum.
# See http://en.wikipedia.org/wiki/Vehicle_identification_number#Check_digit_calculation
# 2012.03.22 remove ^ and $ from 2nd regex term to also allow
# 'vin <vin>, etc' -- a regular vin just triggers w.js?
handle query_nowhitespace_nodash => sub {
my ($query) = @_;
# If a VIN number (2 for exclusively).
my $is_vin = 0;
# VIN number.
my $vin_number = '';
# Exclsuive trigger.
if ($query =~ /^$vin_qr.*?([A-Z\d]{17,})$/i || $query =~ /^([A-Z\d]{17,}).*?$vin_qr$/i) {
$vin_number = uc $1;
$is_vin = 2;
# No exclusive trigger, do checksum.
# Since the vin numbers are just numbers,
# we are more strict in regex (e.g. than UPS).
} elsif($query =~ /^(?:$tracking_qr|$vin_qr|)*([A-Z\d]{17})(?:$tracking_qr|$vin_qr|)*$/io || $query =~ /^(?:$tracking_qr|$vin_qr|)*([A-Z\d]{17})(?:$tracking_qr|$vin_qr|)*$/io) {
$vin_number = uc $1;
my $checksum = 0;
my @chars = split( //, $vin_number );
my $length = scalar(@chars);
my $char_count = 0;
my $sum = 0;
my $letter_count = 0;
foreach my $char (@chars) {
$char_count++;
$letter_count++ if $char =~ /[A-Z]/;
# Grab number.
my $char_num = $char;
$char_num = $vin_checksum{$char} if exists $vin_checksum{$char};
# Make sure number.
if ( $char_num eq 'X' ) {
$checksum = -1;
last;
}
# Use weight.
$sum += $char_num * $vin_checksum_weight{$char_count};
}
$checksum = $sum % 11;
$checksum = 'X' if $checksum == 10;
if ($checksum eq $chars[8] && $letter_count > 3) {
$is_vin = 1;
}
}
return unless $is_vin;
my $moreUrl = 'http://www.decodethis.com/VIN-Decoded/vin/' . $vin_number;
return "Decode VIN ($vin_number) at Decode This: $moreUrl",
structured_answer => {
data => {
title => "Vehicle Identification Number: $vin_number",
href => $moreUrl
},
templates => {
group => 'text',
options => {
subtitle_content => 'DDH.vin.subtitle'
}
}
};
return;
};
1;