root/misc/30boxes.pl

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

fixed POD

  • 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     chmod 0600, $conf;
124 }
125
126 sub dow {
127     my @dow  = qw(Sun Mon Tue Wed Thu Fri Sat);
128     my $date = shift;
129     my($y, $m, $d) = split /-/, $date;
130     return $dow[ Date_DayOfWeek($m, $d, $y) ];
131 }
132
133 sub list_events {
134     my %param;
135     $param{start} = $args{start} if $args{start};
136     $param{end}   = $args{end}   if $args{end};
137     if ($args{date}) {
138         $param{start} = $param{end} = $args{date};
139     }
140
141     my $res = call_api("events.Get", %param);
142
143     unless ($res->{eventList}->{event}) {
144         print "No event found.\n";
145         exit;
146     }
147
148     my @events = @{ $res->{eventList}->{event} };
149     for my $event (sort { $a->{start} cmp $b->{start} } @events) {
150         my($date, $time) = split / /, $event->{start};
151         printf "%8s %s (%s) %s (%s)\n",
152             $event->{id},
153             $date,
154             dow($date),
155             $event->{summary},
156             ($event->{allDayEvent} ? 'All day' : $time),
157     }
158 }
159
160 sub add_event {
161     my $summary = join ' ', @ARGV;
162     my $res = call_api("events.AddByOneBox", event => encode_utf8($summary));
163
164     print "Event ", $res->{eventList}->{event}->[0]->{id}, " created.\n";
165 }
166
167 sub delete_event {
168     for my $id (@ARGV) {
169         my $res = call_api("events.Delete", eventId => $id);
170         print "Event $id deleted.\n";
171     }
172 }
173
174 sub update_event {
175     my $id = shift @ARGV;
176     my $summary = join ' ', @ARGV;
177     # XXX this breaks other datetime fields than summary
178     my $res = call_api("events.Update", eventId => $id, summary => $summary);
179     print "Event $id updated.\n";
180 }
181
182 sub call_api {
183     my($method, %opt) = @_;
184
185     my $url = URI->new("http://30boxes.com/api/api.php");
186     $url->query_form(
187         method => $method,
188         apiKey => $config{apikey},
189         authorizedUserToken => $config{auth_token},
190         %opt,
191     );
192
193     my $res  = $ua->get($url);
194     my $data = XML::Simple::XMLin($res->content, ForceArray => [ 'event', 'tags' ], KeyAttr => undef);
195     if ($data->{stat} ne 'ok') {
196         die "call API failed. You might need to remove $conf to redo the authentication.";
197     }
198
199     return $data;
200 }
201
202 __END__
203
204 =head1 NAME
205
206 30boxes.pl - a command-line interface to 30boxes
207
208 =head1 SYNOPSIS
209
210   30boxes.pl [options] list
211   30boxes.pl add <summary of the event>
212   30boxes.pl del <event-id>
213
214     Options:
215       --from, --start           Start date of events to search for
216       --to, --end               End date of events to search for
217       --month                   Month of events to search for
218       --date                    Date of events to search for
219
220   30boxes.pl list
221         List all events in your calendar, starting from today to 90 days later.
222
223   30boxes.pl --from "2 weeks ago" --to today
224         List all events in your calendar, starting from 2 weeks ago to today.
225
226   30boxes.pl --month "2006 September"
227         List all events in your calendar scheduled on September, 2006.
228
229   30boxes.pl --date tomorrow
230         List all events in your calendar tomorrow.
231
232   30boxes.pl add Meeting with Bob tomorrow 3pm
233         Add new event titled "Meeting with Bob" on 3pm tomorrow.
234
235   30boxes.pl del 100
236         Deletes event with id 100.
237
238 =cut
Note: See TracBrowser for help on using the browser.