root/misc/30boxes.pl

Revision 1968 (checked in by miyagawa, 14 years ago)

rename (again) the script to 30boxes.pl to make it less confusing

  • Property svn:executable set to *
Line 
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4
5 =head1 DESCRIPTION
6
7 This is a simple command-line interface to 30boxes that can be used
8 like Lifehacker.com's todo.sh script.
9
10 =cut
11
12 use Date::Manip;
13 use Encode;
14 use ExtUtils::MakeMaker ();
15 use File::HomeDir;
16 use File::Spec;
17 use Getopt::Long;
18 use YAML;
19 use LWP::UserAgent;
20 use Pod::Usage;
21 use URI;
22 use XML::Simple;
23
24 our $conf = File::Spec->catfile(File::HomeDir->my_home, ".30boxes");
25 our $ua = LWP::UserAgent->new;
26 our %config = ();
27 our %args   = ();
28 our $changed;
29
30 $ua->env_proxy;
31
32 my $encoding;
33 eval {
34     require Term::Encoding;
35     $encoding = Term::Encoding::get_encoding();
36 };
37 $encoding ||= "utf-8";
38 binmode STDOUT, ":encoding($encoding)";
39 binmode STDIN, ":encoding($encoding)";
40
41 main();
42
43 END {
44     save_config() if $changed;
45 }
46
47 sub prompt {
48     my $value = ExtUtils::MakeMaker::prompt($_[0]);
49     $changed++;
50     return $value;
51 }
52
53 sub main {
54     GetOptions(\%args,
55                "start=s",
56                "from=s",
57                "end=s",
58                "to=s",
59                "month=s",
60                "help",
61                "config=s")
62         or pod2usage(2);
63
64     $conf = $args{config} if $args{config};
65     pod2usage(0) if $args{help};
66
67     # alias from/start, to/end
68     $args{start} ||= $args{from};
69     $args{end}   ||= $args{to};
70
71     # Human readable one
72     $args{start} = parse_date($args{start}) if $args{start};
73     $args{end}   = parse_date($args{end})   if $args{end};
74
75     # map month to start/end
76     if ($args{month}) {
77         my $target = parse_date($args{month});
78         my($year, $month, $day) = split /-/, $target;
79         my $end = Date_DaysInMonth($month, $year);
80         $args{start} = "$year-$month-1";
81         $args{end}   = "$year-$month-$end";
82     }
83
84     setup_config();
85
86     my %commands = (
87         list => \&list_events,
88         add  => \&add_event,
89         del  => \&delete_event,
90         rm   => \&delete_event,
91 #        update => \&update_event,
92     );
93
94     my $command = shift @ARGV || "list";
95     $commands{$command} or pod2usage(-message => "Unknown command: $command", -exitval => 2);
96     $commands{$command}->();
97 }
98
99 sub parse_date {
100     my $time = UnixDate(ParseDateString($_[0]), "%s") or die "Can't parse '$_[0]' as a date string\n";
101     my @date = localtime($time);
102
103     return join '-', $date[5] + 1900, $date[4] + 1, $date[3];
104 }
105
106 sub setup_config {
107     my $config = eval { YAML::LoadFile($conf) } || {};
108     %config = %$config;
109     $config{apikey}     ||= prompt("30boxes API Key:");
110     $config{auth_token} ||= prompt(<<PROMPT);
111 You need to login 30boxes to authorize this app.
112 Go to the following URL and paste the result token here.
113   http://30boxes.com/api/api.php?method=user.Authorize&apiKey=$config{apikey}&applicationName=30boxes
114 Your token:
115 PROMPT
116 }
117
118 sub save_config {
119     YAML::DumpFile($conf, \%config);
120 }
121
122 sub dow {
123     my @dow  = qw(Sun Mon Tue Wed Thu Fri Sat);
124     my $date = shift;
125     my($y, $m, $d) = split /-/, $date;
126     return $dow[ Date_DayOfWeek($m, $d, $y) ];
127 }
128
129 sub list_events {
130     my $res = call_api("events.Get",
131                        $args{start} ? (start => $args{start}) : (),
132                        $args{end}   ? (end => $args{end}) : ());
133
134     unless ($res->{eventList}->{event}) {
135         print "No event found.\n";
136         exit;
137     }
138
139     my @events = @{ $res->{eventList}->{event} };
140     for my $event (sort { $a->{start} cmp $b->{start} } @events) {
141         my($date, $time) = split / /, $event->{start};
142         printf "%8s %s (%s) %s (%s)\n",
143             $event->{id},
144             $date,
145             dow($date),
146             $event->{summary},
147             ($event->{allDayEvent} ? 'All day' : $time),
148     }
149 }
150
151 sub add_event {
152     my $summary = join ' ', @ARGV;
153     my $res = call_api("events.AddByOneBox", event => encode_utf8($summary));
154
155     print "Event ", $res->{eventList}->{event}->[0]->{id}, " created.\n";
156 }
157
158 sub delete_event {
159     for my $id (@ARGV) {
160         my $res = call_api("events.Delete", eventId => $id);
161         print "Event $id deleted.\n";
162     }
163 }
164
165 sub update_event {
166     my $id = shift @ARGV;
167     my $summary = join ' ', @ARGV;
168     # XXX this breaks other datetime fields than summary
169     my $res = call_api("events.Update", eventId => $id, summary => $summary);
170     print "Event $id updated.\n";
171 }
172
173 sub call_api {
174     my($method, %opt) = @_;
175
176     my $url = URI->new("http://30boxes.com/api/api.php");
177     $url->query_form(
178         method => $method,
179         apiKey => $config{apikey},
180         authorizedUserToken => $config{auth_token},
181         %opt,
182     );
183
184     my $res  = $ua->get($url);
185     my $data = XML::Simple::XMLin($res->content, ForceArray => [ 'event', 'tags' ], KeyAttr => undef);
186     if ($data->{stat} ne 'ok') {
187         die "call API failed. You might need to remove $conf to redo the authentication.";
188     }
189
190     return $data;
191 }
192
193 __END__
194
195 =head1 NAME
196
197 30boxes.pl - a command-line interface to 30boxes
198
199 =head1 SYNOPSIS
200
201   30boxes.pl [options] list
202   30boxes.pl add <text try of the event>
203   30boxes.pl del <event-id>
204
205     Options:
206       --from, --start           Start date of events to search for
207       --to, --end               End date of events to search for
208       --month                   Month of events to search for
209
210   30boxes.pl list
211         List all events in your calendar, starting from today to 90 days later.
212
213   30boxes.pl --from "2 weeks ago" --to today
214         List all events in your calendar, starting from 2 weeks ago to today.
215
216   30boxes.pl --month "2006 September"
217         List all events in your calendar scheduled on September, 2006.
218
219   30boxes.pl add Meeting with Bob tomorrow 3pm
220         Add new event titled "Meeting with Bob" on 3pm tomorrow.
221
222   30boxes.pl del 100
223         Deletes event with id 100.
224
225 =cut
Note: See TracBrowser for help on using the browser.