root/misc/30boxes.pl

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

document --date option

  • 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                "date=s",
61                "help",
62                "config=s")
63         or pod2usage(2);
64
65     $conf = $args{config} if $args{config};
66     pod2usage(0) if $args{help};
67
68     # alias from/start, to/end
69     $args{start} ||= $args{from};
70     $args{end}   ||= $args{to};
71
72     # Human readable one
73     $args{start} = parse_date($args{start}) if $args{start};
74     $args{end}   = parse_date($args{end})   if $args{end};
75
76     # map month to start/end
77     if ($args{month}) {
78         my $target = parse_date($args{month});
79         my($year, $month, $day) = split /-/, $target;
80         my $end = Date_DaysInMonth($month, $year);
81         $args{start} = "$year-$month-1";
82         $args{end}   = "$year-$month-$end";
83     }
84
85     $args{date} = parse_date($args{date}) if $args{date};
86
87     setup_config();
88
89     my %commands = (
90         list => \&list_events,
91         add  => \&add_event,
92         del  => \&delete_event,
93         rm   => \&delete_event,
94 #        update => \&update_event,
95     );
96
97     my $command = shift @ARGV || "list";
98     $commands{$command} or pod2usage(-message => "Unknown command: $command", -exitval => 2);
99     $commands{$command}->();
100 }
101
102 sub parse_date {
103     my $time = UnixDate(ParseDateString($_[0]), "%s") or die "Can't parse '$_[0]' as a date string\n";
104     my @date = localtime($time);
105
106     return join '-', $date[5] + 1900, $date[4] + 1, $date[3];
107 }
108
109 sub setup_config {
110     my $config = eval { YAML::LoadFile($conf) } || {};
111     %config = %$config;
112     $config{apikey}     ||= prompt("30boxes API Key:");
113     $config{auth_token} ||= prompt(<<PROMPT);
114 You need to login 30boxes to authorize this app.
115 Go to the following URL and paste the result token here.
116   http://30boxes.com/api/api.php?method=user.Authorize&apiKey=$config{apikey}&applicationName=30boxes
117 Your token:
118 PROMPT
119 }
120
121 sub save_config {
122     YAML::DumpFile($conf, \%config);
123 }
124
125 sub dow {
126     my @dow  = qw(Sun Mon Tue Wed Thu Fri Sat);
127     my $date = shift;
128     my($y, $m, $d) = split /-/, $date;
129     return $dow[ Date_DayOfWeek($m, $d, $y) ];
130 }
131
132 sub list_events {
133     my %param;
134     $param{start} = $args{start} if $args{start};
135     $param{end}   = $args{end}   if $args{end};
136     if ($args{date}) {
137         $param{start} = $param{end} = $args{date};
138     }
139
140     my $res = call_api("events.Get", %param);
141
142     unless ($res->{eventList}->{event}) {
143         print "No event found.\n";
144         exit;
145     }
146
147     my @events = @{ $res->{eventList}->{event} };
148     for my $event (sort { $a->{start} cmp $b->{start} } @events) {
149         my($date, $time) = split / /, $event->{start};
150         printf "%8s %s (%s) %s (%s)\n",
151             $event->{id},
152             $date,
153             dow($date),
154             $event->{summary},
155             ($event->{allDayEvent} ? 'All day' : $time),
156     }
157 }
158
159 sub add_event {
160     my $summary = join ' ', @ARGV;
161     my $res = call_api("events.AddByOneBox", event => encode_utf8($summary));
162
163     print "Event ", $res->{eventList}->{event}->[0]->{id}, " created.\n";
164 }
165
166 sub delete_event {
167     for my $id (@ARGV) {
168         my $res = call_api("events.Delete", eventId => $id);
169         print "Event $id deleted.\n";
170     }
171 }
172
173 sub update_event {
174     my $id = shift @ARGV;
175     my $summary = join ' ', @ARGV;
176     # XXX this breaks other datetime fields than summary
177     my $res = call_api("events.Update", eventId => $id, summary => $summary);
178     print "Event $id updated.\n";
179 }
180
181 sub call_api {
182     my($method, %opt) = @_;
183
184     my $url = URI->new("http://30boxes.com/api/api.php");
185     $url->query_form(
186         method => $method,
187         apiKey => $config{apikey},
188         authorizedUserToken => $config{auth_token},
189         %opt,
190     );
191
192     my $res  = $ua->get($url);
193     my $data = XML::Simple::XMLin($res->content, ForceArray => [ 'event', 'tags' ], KeyAttr => undef);
194     if ($data->{stat} ne 'ok') {
195         die "call API failed. You might need to remove $conf to redo the authentication.";
196     }
197
198     return $data;
199 }
200
201 __END__
202
203 =head1 NAME
204
205 30boxes.pl - a command-line interface to 30boxes
206
207 =head1 SYNOPSIS
208
209   30boxes.pl [options] list
210   30boxes.pl add <text try of the event>
211   30boxes.pl del <event-id>
212
213     Options:
214       --from, --start           Start date of events to search for
215       --to, --end               End date of events to search for
216       --month                   Month of events to search for
217       --date                    Date of events to search for
218
219   30boxes.pl list
220         List all events in your calendar, starting from today to 90 days later.
221
222   30boxes.pl --from "2 weeks ago" --to today
223         List all events in your calendar, starting from 2 weeks ago to today.
224
225   30boxes.pl --month "2006 September"
226         List all events in your calendar scheduled on September, 2006.
227
228   30boxes.pl --date tomorrow
229         List all events in your calendar tomorrow.
230
231   30boxes.pl add Meeting with Bob tomorrow 3pm
232         Add new event titled "Meeting with Bob" on 3pm tomorrow.
233
234   30boxes.pl del 100
235         Deletes event with id 100.
236
237 =cut
Note: See TracBrowser for help on using the browser.