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 )