App-RoboBot
view release on metacpan or search on metacpan
lib/App/RoboBot/Plugin/API/Github.pm view on Meta::CPAN
package App::RoboBot::Plugin::API::Github;
$App::RoboBot::Plugin::API::Github::VERSION = '4.004';
use v5.20;
use namespace::autoclean;
use Moose;
use MooseX::SetOnce;
use AnyEvent;
use Data::Dumper;
use HTTP::Request;
use JSON;
use LWP::UserAgent;
use URI;
use App::RoboBot::Channel;
use App::RoboBot::Response;
extends 'App::RoboBot::Plugin';
=head1 api.github
Provides functions for interacting with Github APIs, including watching for
repository related events.
A poor man's alternative to simply enabling Github's built-in chat notifiers.
=cut
has '+name' => (
default => 'API::Github',
);
has '+description' => (
default => 'Provides functions for interacting with Github APIs, including watching for repository related events.',
);
=head2 github-watch
=head3 Description
Adds a watcher for the current channel on the given Github project. The watcher
will periodically poll the Github APIs for commit, issue, and other events and
report them in the channel when they occur. If multiple events have occurred
since the last reporting, they will be bundled together.
=head3 Usage
<project url>
=head3 Examples
(github-watch https://github.com/jsime/robobot)
=head2 github-unwatch
=head3 Description
Removes the watcher for the given Github project in the current channel. If the
same project is being watched in other channels as well, it will need to be
removed from them separately.
=head3 Usage
<project url>
=head3 Examples
(github-unwatch https://github.com/jsime/robobot)
=head2 github-list
lib/App/RoboBot/Plugin/API/Github.pm view on Meta::CPAN
# server-side with parsing incoming <link|label> strings and
# displays them literally instead of parsing.
push(@notices, sprintf('[https://github.com/%s/%s] %d new commit%s by %s.',
$repo->{'owner_name'}, $repo->{'repo_name'},
scalar(@commits), (scalar(@commits) == 1 ? '' : 's'),
join(', ', sort { $a cmp $b } values %commiters)));
push(@notices, sprintf('> *%s*: %s - _%s_',
$_->{'id'}, $_->{'comment'}, $_->{'author'})) foreach @commits;
if (defined $oldest_commit) {
push(@notices, sprintf('View diff on Github: https://github.com/%s/%s/compare/%s...%s',
$repo->{'owner_name'}, $repo->{'repo_name'},
substr($oldest_commit, 0, 16),
substr($commits[0]{'sha'}, 0, 16)));
}
}
} else {
# If we didn't get a valid response back, set the polled_at out several
# minutes so we don't recheck too soon in the case of API usage limits.
$self->bot->config->db->do(q{
update github_repos set polled_at = now() + interval '5 min' where repo_id = ?
}, $repo->{'repo_id'});
}
return @notices;
}
sub short_commit_comment {
my ($self, $comment) = @_;
return '' unless defined $comment && length($comment) > 0;
# Remove anything past the first newline if this was a multi-line comment.
$comment = (grep { $_ =~ m{.+} } split(/\n/, $comment))[0];
# Truncate and add ellipses.
if (length($comment) > 64) {
$comment = substr($comment, 0, 62) . '...';
}
return $comment;
}
sub make_gh_api_call {
my ($self, $path, $args) = @_;
my $uri = URI->new;
$uri->scheme('https');
$uri->host('api.github.com');
if (ref($path) eq 'ARRAY') {
$uri->path_segments(@{$path});
} else {
$uri->path($path);
}
if (defined $args && ref($args) eq 'HASH' && scalar(keys(%{$args})) > 0) {
$uri->query_form($args);
}
my $req = HTTP::Request->new( GET => $uri->as_string );
if (exists $self->bot->config->plugins->{'github'}{'user'} && exists $self->bot->config->plugins->{'github'}{'token'}) {
$req->authorization_basic(
$self->bot->config->plugins->{'github'}{'user'},
$self->bot->config->plugins->{'github'}{'token'}
);
}
my $response = $self->ua->request($req);
return unless $response->is_success;
my $json;
eval {
$json = decode_json($response->decoded_content);
};
return if $@;
return $json;
}
sub _run_watcher {
my ($self, $bot) = @_;
# Ensure that we only get repositories that are currently associated with
# channels, to suppress pointless API calls. Also, limit the results to
# those repos which have either a NULL polled_at (newly-watched repos that
# we've not yet checked) or those with a polled_at in the past (allows API
# calling method to set a polled_at in the future to delay our next check
# in the event of errors or API usage limits).
my $repos = $bot->config->db->do(q{
select r.repo_id, r.owner_name, r.repo_name, r.last_pr, r.last_issue,
to_char(coalesce(r.polled_at, now() - interval '5 min') at time zone 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') as polled_at,
count(distinct(c.id)) as num_channels, array_agg(c.id) as channels
from github_repos r
join github_repo_channels rc on (rc.repo_id = r.repo_id)
join channels c on (c.id = rc.channel_id)
where r.polled_at is null or r.polled_at < now() - interval '10 seconds'
group by r.repo_id, r.owner_name, r.repo_name, r.polled_at, r.last_pr, r.last_issue
});
if ($repos) {
while ($repos->next) {
my @notices = $self->get_repo_notices($repos);
next unless @notices > 0;
foreach my $channel_id (@{$repos->{'channels'}}) {
my $channel = App::RoboBot::Channel->find_by_id($self->bot, $channel_id);
next unless defined $channel;
my $response = App::RoboBot::Response->new(
network => $channel->network,
channel => $channel,
bot => $bot,
);
$response->push(@notices);
$response->send;
}
( run in 2.030 seconds using v1.01-cache-2.11-cpan-f56aa216473 )