aboutsummaryrefslogtreecommitdiff
path: root/units.pl
blob: 927d5415b485c027c638a0389d382dc45b30cdba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/perl

# Unit conversions (weight/measure/etc) for an irssi bot.

use warnings;
no strict;

use POSIX 'round';
use Irssi qw/signal_add/;

$helpmsg = "Usage: !units <number> <unit> [[to] <to-unit>]. Examples: !units 50 miles km; !units 20C to F. With no <to-unit>, tries to guess based on <unit> (e.g. C will convert to F)";

# default output unit based on input unit
%default_unit = (
	F => 'C',
	C => 'F',
	f => 'c',
	c => 'f',
	mi => 'km',
	mile => 'km',
	km => 'mile',
	ft => 'meter',
	foot => 'm',
	feet => 'm',
	yd => 'm',
	yard => 'm',
	in => 'cm',
	inch => 'cm',
	inches => 'cm',
	lb => 'kg',
	pound => 'kg',
	lbs => 'kg',
	gal => 'l',
	gallon => 'l',
	cc => 'floz',
	floz => 'cc',
);

# round to nearest 1/100, e.g. 1.004 is 1, 1.005 is 1.01.
sub round_result {
	return round($_[0] * 100) / 100;
}

sub convert {
	for($_[0]) {
		s/°([cfk])\b/$1/i;

		if(/[^\s0-9a-zA-Z\/\^]/) {
			return "malformed request";
		}

		s/\s+to\b//;
		s/fl\w*\.?\s?o[uz]\w*/floz/;

		my ($num, $from, $to) = (/(\d+|\.\d+|\d+\.\d+)\s*([\w\^\/]+)(?:\s+([\w+\^\/]+))?$/);

		return "malformed request" unless defined $num && defined $from;

		$to = $default_unit{$from} unless defined $to;
		unless(defined $to) {
			my $f = $from;
			$f =~ s/s$//;
			$to = $default_unit{$f};
		}
		return "no default conversion for $from" unless defined $to;

		#warn "was: num $num, from $from, to $to\n";
		my $realfrom = $from;
		my $realto = $to;
		my $realnum = $num;
		my $space = " ";

		if($from =~ /^([cfk])$/i) {
			$realfrom = uc $1;
			$from = "temp" . uc($1) . "($num)";
			$num = "";
			$space = "";
		}

		if($to =~ /^([cfk])$/i) {
			$realto = uc $1;
			$to = "temp" . uc($1);
			$space = "";
		}

		#warn "now: num $num, from $from, to $to\n";

		# was using: -o'%.1f', but it doesn't do rounding, plus it
		# prints 1 as 1.0.

		chomp($result = `units -1t '$num$from' '$to' 2>&1`);
		if($?) {
			if($result =~ /conformability error/) {
				return "Can't convert $realfrom to $realto";
			} else {
				return $result;
			}
		} else {
			$result = round_result($result);
			return "$realnum$space$realfrom is $result$space$realto";
		}
	}
}

sub on_public_msg {
	my ($server, $msg, $nick, $address, $target) = @_;
	my $mynick = $server->{nick};

	unless(length $target) {
		$target = $nick;
		$nick = $mynick;
	}

	if($target eq $mynick) {
		# private message... send response to sender
		$target = $nick;
	}

	if($msg !~ /^!units\s+(.+)/) {
		return;
	}

	my $input = $1;
	my $result = convert($input);
	$server->command("msg $target $result");
}

signal_add("message public", "on_public_msg");