| App-PM-Announce documentation | Contained in the App-PM-Announce distribution. |
App::PM::Announce - Announce your PM meeting via Meetup and LinkedIn
Version 0.025
# Initialize and edit the config (only need to do this once)
pm-announce config edit
# Generate a template for the event
pm-announce template > event.txt
# Edit event.txt with your editor of choice...
# Announce the event
pm-announce announce < event.txt
App::PM::Announce is a tool for creating and advertising PM meetings (on Meetup, LinkedIn, and blog software)
-v, -d, --verbose Debugging mode. Be verbose when reporting
-h, -?, --help This help screen
config Check the config file ($HOME/.app-pm-announce/config)
config edit Edit the config file using $EDITOR
history Show announcement history
history <query> Show announcement history for event <query>, where <query> should be enough of the uuid to be unambiguous
template Print out a template to be used for input to the 'announce' command
--image <image> Attach <image> (can be either a local file or remote URL) to the Meetup event
announce Read STDIN for the event information and make a post for each feed
-n, --dry-run Don't actually login and announce, just show what would be done
test Post a bogus event to a test meetup account, test linkedin account, and test greymatter account
help This help screen
Robert Krimen, <rkrimen at cpan.org>
Please report any bugs or feature requests to bug-app-pm-announce at rt.cpan.org, or through
the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-PM-Announce. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
You can find documentation for this module with the perldoc command.
perldoc App::PM::Announce
You can also look for information at:
Copyright 2009 Robert Krimen, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| App-PM-Announce documentation | Contained in the App-PM-Announce distribution. |
package App::PM::Announce; use warnings; use strict;
our $VERSION = '0.025'; use Moose; #with 'MooseX::LogDispatch'; use File::HomeDir; use Path::Class; use Config::JFDI; use Config::General; use String::Util qw/trim/; use Data::UUID; use Document::TriPart; use DateTimeX::Easy; use Log::Dispatch; use Log::Dispatch::Screen; use Log::Dispatch::File; use App::PM::Announce::History; use App::PM::Announce::Feed::meetup; use App::PM::Announce::Feed::linkedin; use App::PM::Announce::Feed::greymatter; use App::PM::Announce::Feed::useperl; sub BUILD { my $self = shift; $self->startup; } has debug => qw/is ro lazy_build 1/; sub _build_debug { return $ENV{APP_PM_ANNOUNCE_DEBUG} ? 1 : 0; } has verbose => qw/is ro lazy_build 1/; sub _build_verbose { return 0; } has dry_run => qw/is ro lazy_build 1/; sub _build_dry_run { return 0; } has home_dir => qw/is ro lazy_build 1/; sub _build_home_dir { my @home_dir; @home_dir = map { exists $ENV{$_} && defined $ENV{$_} ? $ENV{$_} : () } qw/APP_PM_ANNOUNCE_HOME/; # Don't want to write $ENV{...} twice @home_dir = ( File::HomeDir->my_data, '.app-pm-announce' ) unless @home_dir; return dir( @home_dir ); } has config_file => qw/is ro lazy_build 1/; sub _build_config_file { return shift->home_dir->file( 'config' ); } has config_default => qw/is ro isa HashRef lazy_build 1/; sub _build_config_default { return {}; } #has _config => qw/is ro isa Config::JFDI lazy_build 1/; #sub _build__config { # my $self = shift; # return Config::JFDI->new(file => $self->config_file); #} #sub config { # return shift->_config->get; #} has config => qw/is ro isa HashRef lazy_build 1/; sub _build_config { my $self = shift; if ($self->config_file) { return { Config::General->new( -ConfigFile => $self->config_file, )->getall }; } else { return $self->config_default, } } has log_file => qw/is ro lazy_build 1/; sub _build_log_file { return shift->home_dir->file( 'log' ); } has logger => qw/is ro isa Log::Dispatch lazy_build 1/; sub _build_logger { my $self = shift; my $logger = Log::Dispatch->new( callbacks => sub { my $message = join ' ', "[@{[ DateTime->now->set_time_zone( 'local' ) ]}]", "[$_[3]]", "$_[1]\n", ; # $message = "# $message" if $_[3] eq 'debug'; return $message; } ); $logger->add( Log::Dispatch::Screen->new( name => 'screen', min_level => $self->debug ? 'debug' : 'info', stderr => 1 ) ) if $self->debug; # $logger->add( Log::Dispatch::File->new( name => 'file', mode => 'append', min_level => 'info', filename => $self->log_file.'' ) ); return $logger; } has feed => qw/is ro isa HashRef lazy_build 1/; sub _build_feed { my $self = shift; return { meetup => $self->_build_meetup_feed, linkedin => $self->_build_linkedin_feed, greymatter => $self->_build_greymatter_feed, useperl => $self->_build_useperl_feed, }; } sub _build_meetup_feed { my $self = shift; return undef unless my $given = $self->config->{feed}->{meetup}; return App::PM::Announce::Feed::meetup->new( app => $self, username => $given->{username}, password => $given->{password}, uri => $given->{uri}, venue => $given->{venue}, ); } sub _build_linkedin_feed { my $self = shift; return undef unless my $given = $self->config->{feed}->{linkedin}; return App::PM::Announce::Feed::linkedin->new( app => $self, username => $given->{username}, password => $given->{password}, uri => $given->{uri}, ); } sub _build_greymatter_feed { my $self = shift; return undef unless my $given = $self->config->{feed}->{greymatter}; return App::PM::Announce::Feed::greymatter->new( app => $self, username => $given->{username}, password => $given->{password}, uri => $given->{uri}, ); } sub _build_useperl_feed { my $self = shift; return undef unless my $given = $self->config->{feed}->{useperl}; return App::PM::Announce::Feed::useperl->new( app => $self, username => $given->{username}, password => $given->{password}, promote => $given->{promote}, ); } has history => qw/is ro isa App::PM::Announce::History lazy_build 1/; sub _build_history { my $self = shift; return App::PM::Announce::History->new( app => $self ); } sub startup { my $self = shift; $self->logger->debug( "debug = " . $self->debug ); $self->logger->debug( "verbose = " . $self->verbose ); $self->logger->debug( "dry-run = " . $self->dry_run ); my $home_dir = $self->home_dir; $self->logger->debug( "home_dir = $home_dir" ); unless (-d $home_dir) { $self->logger->debug( "Making $home_dir because it does not exist" ); $home_dir->mkpath; } # Gotta do this here $self->logger->add( Log::Dispatch::File->new( name => 'file', mode => 'append', min_level => 'info', filename => $self->log_file.'' ) ); my $log_file = $self->log_file; $self->logger->debug( "log_file = $log_file" ); my $config_file = $self->config_file; if (defined $config_file) { $self->logger->debug( "config_file = $config_file" ); unless (-f $config_file) { $self->logger->debug( "Making $config_file stub because it does not exist" ); $config_file->openw->print( <<_END_ ); # vim: set filetype=configgeneral: # Replace 'An-Example-Group' with the real resource for your Meetup group # Replace <venue> with the venue number you want to be the default #<feed meetup> # username # password # uri http://www.meetup.com/An-Example-Group/calendar/?action=new # venue <venue> #</feed> # Replace <gid> with the gid of your group #<feed linkedin> # username # password # uri http://www.linkedin.com/groupAnswers?start=&gid=<gid> #</feed> # Replace 'example.com' with a real host #<feed greymatter> # username # password # uri http://example.com/cgi-bin/greymatter/gm.cgi #</feed> #<feed useperl> # username # password #</feed> _END_ } } } sub announce { my $self = shift; my %event; if (ref $_[0]) { my $document = $self->parse( @_ ); %event = %{ $document->header }; $event{description} = $document->body; } else { %event = @_; } { # Validate, parse, and filter. $event{$_} = trim $event{$_} for qw/title venue/; die "Wasn't given a UUID for the event\n" unless $event{uuid}; die "Wasn't given a title for the event\n" unless $event{title}; # die "Wasn't given a venue for the event\n" unless $event{venue}; die "Wasn't given a date & time for the event\n" unless $event{datetime}; die "The date & time isn't a DateTime object\n" unless $event{datetime}->isa( 'DateTime' ); } my (@report, $event, $result); my $uuid = $event{uuid}; $event = $self->history->find_or_insert( $uuid )->{data}; $self->history->update( $uuid => %event ); eval { if ($event->{did_meetup}) { $self->logger->debug( "Already posted to meetup, skipping" ); $self->logger->debug( "The Meetup link is " . $event->{meetup_link} ) if $event->{meetup_link}; push @report, "Already announced on meetup"; } elsif ($self->feed->{meetup}) { unless ($self->dry_run) { die "Didn't announce on meetup" unless $result = $self->feed->{meetup}->announce( %event ); my $meetup_link = $event->{meetup_link} = $result->{meetup_link}; $self->logger->debug( "Meetup link is " . $meetup_link ); $self->logger->info( "\"$event{title}\" ($uuid) announced to meetup ($meetup_link) " ); $self->history->update( $uuid => did_meetup => 1, meetup_link => "$meetup_link" ); push @report, "Announced on meetup"; } else { push @report, "Would announce on meetup"; } } else { $self->logger->debug( "No feed configured for meetup" ); } die "Don't have a Meetup link" unless $self->dry_run || $event->{meetup_link}; # $event{description} = [ # $event{description}, # "\nRSVP at Meetup - <a href=\"$event->{meetup_link}\">$event->{meetup_link}</a>" # ]; if ($event->{did_linkedin}) { $self->logger->debug( "Already posted to linkedin, skipping" ); push @report, "Already announced on linkedin"; } elsif ($self->feed->{linkedin}) { unless ($self->dry_run) { die "Didn't announce on linkedin" unless $result = $self->feed->{linkedin}->announce( %event, description => [ $event{description}, "RSVP at Meetup - $event->{meetup_link}", ], ); $self->logger->info( "\"$event{title}\" ($uuid) announced to linkedin" ); $result = $self->history->update( $uuid => did_linkedin => 1 ); push @report, "Announced on linkedin"; } else { push @report, "Would announce on linkedin"; } } else { $self->logger->debug( "No feed configured for linkedin" ); } if ($event->{did_greymatter}) { $self->logger->debug( "Already posted to greymatter, skipping" ); push @report, "Already announced on greymatter"; } elsif ($self->feed->{greymatter}) { unless ($self->dry_run) { die "Didn't announce on greymatter" unless $result = $self->feed->{greymatter}->announce( %event, description => [ $event{description}, "\nRSVP at Meetup - <a href=\"$event->{meetup_link}\">$event->{meetup_link}</a>" ], ); $self->logger->info( "\"$event{title}\" ($uuid) announced to greymatter" ); $result = $self->history->update( $uuid => did_greymatter => 1 ); push @report, "Announced on greymatter"; } else { push @report, "Would announce on greymatter"; } } else { $self->logger->debug( "No feed configured for greymatter" ); } if ($event->{did_useperl}) { $self->logger->debug( "Already posted to useperl, skipping" ); push @report, "Already announced on useperl"; } elsif ($self->feed->{useperl}) { unless ($self->dry_run) { die "Didn't announce on useperl" unless $result = $self->feed->{useperl}->announce( %event, description => [ $event{description}, "\nRSVP at Meetup - <a href=\"$event->{meetup_link}\">$event->{meetup_link}</a>" ], ); $self->logger->info( "\"$event{title}\" ($uuid) announced to useperl" ); $result = $self->history->update( $uuid => did_useperl => 1 ); push @report, "Announced on useperl"; } else { push @report, "Would announce on useperl"; } } else { $self->logger->debug( "No feed configured for useperl" ); } }; if ($@) { warn "Unable to announce \"$event{title}\" ($uuid)\n"; die $@; } $event = $self->history->fetch( $uuid )->{data}; # $self->logger->info("\"$event{title}\" is announced on", join ', ', map { $event->{"did_$_"} ? $_ : () } qw/meetup linkedin greymatter/); # $self->logger->info("Meetup link is $event->{meetup_link}") if $event->{meetup_link}; return $event, \@report; # $result{done} = 1; # return \%result; } use Data::Dump qw/dd pp/; sub parse { my $self = shift; die "Couldn't parse" unless my $document = Document::TriPart->read(shift); my $datetime = $document->header->{datetime}; die "You didn't give a datetime" unless $datetime; die "Unable to parse ", $document->header->{datetime} unless $datetime = DateTimeX::Easy->parse( $datetime ); $document->header->{datetime} = $datetime; return $document; } sub template { my $self = shift; my %given = @_; my $uuid = Data::UUID->new->create_str; my $datetime = DateTimeX::Easy->parse( '4th tuesday' ); my $venue = $self->config->{venue} || ''; $datetime = DateTimeX::Easy->parse( '3rd tuesday' ) unless $datetime; $datetime->set(hour => 20, minute => 0, second => 0); return <<_END_; # App-PM-Announce # You can leave 'venue' blank to use the default venue (per @{[ $self->config_file ]}) # The 'datetime' field is the date & time that the event will take place. Any reasonable string should do (parsed via DateTimeX::Easy) --- title: The title of the event venue: $venue datetime: $datetime image: $given{image} uuid: $uuid --- Put your multi-line description for the event here. Everything below the '---' is considered the description. _END_ }
1; # End of App::PM::Announce