I’m a big fan of meetup.com, but they’re so tragically unhip when it comes to mashups/integration/web 2.0. One of my biggest gripes until about 6 months ago was that they had no facility (besides API) for exporting a calendar of meetups to my calendar app (I use Google Calendar), or any other calendar app for that matter.
They introduced an export feature recently, but it’s pretty useless. Here’s why: they offer two calendars
- [Calendar A] contains all upcoming items in all your meetup groups
- [Calendar B] contains upcoming items which you have RSVP’d with “yes” or “maybe”.
That’s it. The calendars exported don’t even contain links that allow you to RSVP from directly inside your calendar — you have click through to the meetup.com site, log in, then RSVP. Ugh.
Come on, product guys. What’s really called for is 4 separate calendars.
- [Calendar "yes"] All groups, “yes” events
- [Calendar "maybe"] All groups, “maybe” events
- [Calendar "no"] All groups, “no” events
- [Calendar "none"] All groups, events to which I have not yet submitted an RSVP.
I was finally just pissed off enough about the status quo that I fixed it for myself, and below I share the code. You can try it out here: http://spicylogic.com/allenday/cgi-bin/mu.cgi?key=<your_api_key>&cal=<calendar>
where <your_api_key> can be found here and <calendar> is one of “yes”, “no”, “none”, “maybe”.
Okay, here’s the code. Install it on your own machine if possible, my ISP will appreciate it. If you find fuckups, let me know and I’ll update the post.
#!/usr/bin/perl use strict; use CGI qw(:standard); use Date::Manip qw(ParseDate ParseDateString ParseDateDelta DateCalc UnixDate); use Date::Parse; use HTML::Entities; use LWP::Simple qw(get); use XML::DOM; use constant URL_EVENTS => 'http://api.meetup.com/events?key=%s&member_id=%d&format=xml'; print header(q(text/calendar)); my $parser = new XML::DOM::Parser (); my $mode = param( 'cal' ); my $key = param( 'key' ); my $user = param( 'user' ); if ( ! $mode || ! $key || ! $user ) { die } my $events_url = sprintf( URL_EVENTS, $key, $user ); #warn $events_url; my $events_txt = get( $events_url ); #warn $events_txt; my $events_dom = $parser->parse( $events_txt ); #warn $events_dom; print qq(BEGIN:VCALENDAR\nPRODID:-//Meetup Inc//RemoteApi//EN\nVERSION:2.0\nMETHOD:PUBLISH\nCALSCALE:GREGORIAN\nX-ORIGINAL-URL:http://www.meetup.com/\nX-WR-CALNAME:mu $mode\n); my $events = $events_dom->getElementsByTagName( 'item' ); for ( my $i = 0 ; $i < $events->getLength() ; $i++ ) { my $event = $events->item( $i ); my $n_id = $event->getElementsByTagName( 'id' )->item( 0 )->getFirstChild(); my $n_rsvp = $event->getElementsByTagName( 'myrsvp' )->item( 0 )->getFirstChild(); my $n_addr0 = $event->getElementsByTagName( 'venue_name' )->item( 0 )->getFirstChild(); my $n_addr1 = $event->getElementsByTagName( 'venue_address1' )->item( 0 )->getFirstChild(); my $n_addr2 = $event->getElementsByTagName( 'venue_address2' )->item( 0 )->getFirstChild(); my $n_addr3 = $event->getElementsByTagName( 'venue_address3' )->item( 0 )->getFirstChild(); my $n_addr4 = $event->getElementsByTagName( 'venue_city' )->item( 0 )->getFirstChild(); my $n_addr5 = $event->getElementsByTagName( 'venue_state' )->item( 0 )->getFirstChild(); my $n_addr6 = $event->getElementsByTagName( 'venue_zip' )->item( 0 )->getFirstChild(); my $n_desc = $event->getElementsByTagName( 'description' )->item( 0 )->getFirstChild(); my $n_link = $event->getElementsByTagName( 'event_url' )->item( 0 )->getFirstChild(); my $n_name = $event->getElementsByTagName( 'name' )->item( 0 )->getFirstChild(); my $n_lat = $event->getElementsByTagName( 'venue_lat' )->item( 0 )->getFirstChild(); my $n_lon = $event->getElementsByTagName( 'venue_lon' )->item( 0 )->getFirstChild(); my $n_start_time = $event->getElementsByTagName( 'time' )->item( 0 )->getFirstChild(); my $start_time; my $end_time; #my $dummy_time = "20000101T000000Z"; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time()); my $dummy_time = sprintf( q(%04d%02d%02dT%02d%02d%02dZ), $year + 1900, $mon + 1, $mday, $hour, $min, $sec ); if ( $n_start_time ) { my ($ss,$mm,$hh,$day,$month,$year,$zone); ($ss,$mm,$hh,$day,$month,$year,$zone) = strptime( $n_start_time->toString() ); $start_time = sprintf( q(%04d%02d%02dT%02d%02d%02dZ), $year + 1900, $month + 1, $day, $hh, $mm, $ss ); my $eday = $day; if ( $hh == 23 ) { $eday = $day + 1; } my $ehh = ($hh + 1) % 24; $end_time = sprintf( q(%04d%02d%02dT%02d%02d%02dZ), $year + 1900, $month + 1, $eday, $ehh, $mm, $ss ); } else { $start_time = ''; $end_time = ''; } if ( $mode eq $n_rsvp->toString() ) { my $id = $n_id->toString(); my $name = $n_name ? $n_name->toString() : ""; my $desc = $n_desc ? $n_desc->toString() : ""; my $addr = ( $n_addr0 ? $n_addr0->toString().', ' : "" ) . ( $n_addr1 ? $n_addr1->toString().', ' : "" ) . ( $n_addr2 ? $n_addr2->toString().', ' : "" ) . ( $n_addr3 ? $n_addr3->toString().', ' : "" ) . ( $n_addr4 ? $n_addr4->toString().', ' : "" ) . ( $n_addr5 ? $n_addr5->toString().', ' : "" ) . ( $n_addr6 ? $n_addr6->toString() : "" ); #$desc =~ s/(.)/(ord($1) > 127) ? "" : $1/egs; $name = HTML::Entities::decode_entities( $name ); $desc = HTML::Entities::decode_entities( $desc ); $addr = HTML::Entities::decode_entities( $addr ); $name =~ s/,/\\,/g; $desc =~ s/,/\\,/g; $addr =~ s/,/\\,/g; $desc =~ s# #\\n#gs; $desc .= "\\n\\n\\nGoing?\\n\\n"; foreach my $response ( qw( yes no maybe ) ) { $desc .= uc($response).qq(: http://api.meetup.com/rsvp?event_id=$id&key=$key&rsvp=$response\\n); } my $geo = $n_lat && $n_lon ? "GEO:" . $n_lat->toString() . ";" . $n_lon->toString() . "\n" : undef; #print sprintf( qq(BEGIN:VEVENT\nSUMMARY:%s\nDESCRIPTION:%s\nLAST-MODIFIED:%s\nUID:%s\nCLASS:%s\nCREATED:%s\nDTSTAMP:%s\nDTSTART:%s\nDTEND:%s\nLOCATION:%s\n\nURL:%s\nEND:VEVENT\n), print sprintf( qq(BEGIN:VEVENT\nSUMMARY:%s\nDESCRIPTION:%s\nLAST-MODIFIED:%s\nUID:%s\nCLASS:%s\nCREATED:%s\nDTSTAMP:%s\nDTSTART:%s\nDTEND:%s\n%sLOCATION:%s\nURL:%s\nEND:VEVENT\n), $name, $desc, $start_time, "event_$id\@meetup.com", "PUBLIC", $dummy_time, $dummy_time, $start_time, $end_time, $geo, $addr, $n_link ? $n_link->toString() : "", ); } } print qq(END:VCALENDAR\n);
Steve Luyben | 27-Jan-10 at 8:26 am | Permalink
Hi. Meetup’s Export is worse than you describe for me, as is their Help file which contains at least two errors. I don’t even see an option for RSVP’s.
My question: where on my machine do I install your code?
Thanks for writing it.