App-KGB

 view release on metacpan or  search on metacpan

script/kgb-bot  view on Meta::CPAN

contents.

The actual processing and IRC notification is done via an appropriate
C<gitlab_webhook_*> event, asynchronously.

=cut

my @array_webhook_params =
    qw( channel pipeline_only_status always_squash_branch only_branch
        only_tag always_squash_outside_dir );
my %array_webhook_params = map( ( $_ => 1 ), @array_webhook_params );

sub webhook_request {
    my ( $kernel, $request, $response, $dirmatch ) = @_[ KERNEL, ARG0 .. ARG2 ];

    unless ( $KGB::config->{webhook}{enabled} ) {
        return webhook_error(@_, 'WebHook support not enabled',
            HTTP::Status::HTTP_PRECONDITION_FAILED);
    }

    my $client_ip = $response->connection->remote_ip;
    # fix ipv4-as-ipv6 represenation
    $client_ip =~ s/^::ffff:(\d+\.\d+\.\d+\.\d+)$/$1/;
    $client_ip = Net::IP->new($client_ip);

    my $allowed = 0;

    my $allowed_nets = $KGB::config->{webhook}{allowed_networks};
    if ($allowed_nets) {
        for my $net (@$allowed_nets) {
            next unless $net->version == $client_ip->version;

            $allowed = 1, last
                if $net->overlaps($client_ip) != $Net::IP::IP_NO_OVERLAP;
        }
    }

    unless ($allowed) {
        return webhook_error(@_, 'Client IP ' . $client_ip->ip . ' is not whitelisted',
            HTTP::Status::HTTP_FORBIDDEN);
    }

    KGB->debug( "got a webhook request from " . $client_ip->ip );

    unless( $request->method eq 'POST' ) {
        return webhook_error(@_, 'Request method must be "POST"');
    }

    KGB->debug('DEBUG: method check passed');

    my $json;
    my $ok = eval {
        $json = JSON::from_json( $request->content, { utf8 => 1 } );
        1;
    };

    unless ($ok) {
        return webhook_error(@_, 'Error decoding JSON body', undef, $@);
    }

    KGB->debug('Body decoded');

    unless (
            $json
        and ref($json)
        and ref($json) eq 'HASH'
        and (   $json->{project}
            and defined $json->{project}{name}
            and length $json->{project}{name}
            or defined $json->{project_name} and length $json->{project_name} )
        and defined $json->{object_kind}
        and length $json->{object_kind}
        )
    {
        return webhook_error(@_, 'Empty on invalid JSON body');
    }

    my $module = $json->{project}{name};

    my $hook_handler;
    if ( $json->{object_kind} eq 'push' ) {
        $hook_handler = 'gitlab_webhook_push';
    }
    elsif ( $json->{object_kind} eq 'tag_push' ) {
        $hook_handler = 'gitlab_webhook_tag_push';
    }
    elsif ( $json->{object_kind} eq 'wiki_page' ) {
        $hook_handler = 'gitlab_webhook_wiki_page';
    }
    elsif ( $json->{object_kind} eq 'issue' ) {
        $hook_handler = 'gitlab_webhook_issue';
    }
    elsif ( $json->{object_kind} eq 'note' ) {
        $hook_handler = 'gitlab_webhook_note';
    }
    elsif ( $json->{object_kind} eq 'merge_request' ) {
        $hook_handler = 'gitlab_webhook_merge_request';
    }
    elsif ( $json->{object_kind} eq 'pipeline' ) {
        $hook_handler = 'gitlab_webhook_pipeline';
    }
    elsif ( $json->{object_kind} eq 'build' ) {
        $hook_handler = 'gitlab_webhook_build';
    }
    else {
        return webhook_error(@_,
            "Unsupported object_kind: $json->{object_kind}");
    }

    KGB->debug('Body check passed');

    # form a CGI-like param hash
    my $param = { map( ( $_ => [] ), @array_webhook_params ) };
    $param->{always_squash_branch} = [qw( upstream upstream/master )];
    $param->{shorten_urls}         = 1;
    $param->{use_irc_notices}      = 1;
    $param->{network}              = 'oftc';
    my $uri = URI->new( $request->url );
    my @query = $request->uri->query_form;
    while ( my( $key, $value ) = splice( @query, 0, 2 ) ) {
        if ( $array_webhook_params{$key} ) {

script/kgb-bot  view on Meta::CPAN

    # resending to clients because of prefix.
    # Let's trim on 400, to be safe
    my $MAGIC_MAX_LINE = ( 400 - length("PRIVMSG ")
        - max( map( length($KGB::config->{chanidx}{$_}{name}), @$chanids ) ) );

    my @tmp;
    while ( $_ = shift @strings ) {
        if ( length($_) > $MAGIC_MAX_LINE ) {
            push @tmp, substr( $_, 0, $MAGIC_MAX_LINE );
            unshift @strings, substr( $_, $MAGIC_MAX_LINE );
        }
        else {
            push @tmp, $_;
        }
    }
    return @tmp;
}

sub webhook_to_irc {
    my ( $p ) = @_;

    my @chanids = @{ $p->{chanids} };
    my @strings = trim_lines(@chanids, $p->{repository}, @{ $p->{strings} });

    foreach my $chanid (@chanids) {
        if ( $KGB::simulate ) {
            my $fh = IO::File->new(">> $KGB::simulate")
                or die "Error opening $KGB::simulate for writing: $!\n";
            $fh->autoflush(1);
            $fh->binmode(':utf8');
            for (@strings) {
                $fh->print("$chanid $_\n");
            }
            $fh->close;
        }
        else {
            my ( $net, $chan ) = KGB->get_net_chan($chanid);
            my $method = $p->{opts}{use_irc_notices} ? 'notice' : 'privmsg';
            if ( not exists $KGB::joining_channels{$chanid} )
            {
                $p->{kernel}->yield(
                    irc_notify => $net, $chan, \@strings, $method
                );
            }
            else {
                KGB->out(
                    "Delaying a message to $chanid for after it is joined."
                );
                my $stash = { message => \@strings, method  => $method };
                push (@{ $KGB::joining_channels{$chanid}{pending_messages} },
                    $stash);
            }
        }
    }
}

=head2 gitlab_webhook_push

Handle a gitlab webhook call for the C<push> event (branch update).

Expects the body of the POST request (decoded, as a hash reference) in I<ARG0>
and all the request parameters in I<ARG1>.

The request is expected to conform to the GitLab webhook documentation at
L<https://salsa.debian.org/help/user/project/integrations/webhooks.md#push-events>.

The request parameters should look like the result of the CGI's param() method.

Supported parameters (?param=value&param=value2...)

=over

=item channel

The name of the channel to post notifications to. Leading hash sign is optional
and should be URL-encoded if present (%23).

=item network

The name of the IRC network, servicing the channel. Supported networks are
configured by the bot's admin.

=item private

A boolean flag, indicating that the notifications shouldn't also be posted to
the C<#commits> channel on Freenode.

=item use_color

A boolean flag enabling colors. Defaults to true.

=item rev_prefix

Optional text to prepend to the commit ID.

=item use_irc_notices

If true, IRC notification uses C<notice> messages, instead of C<privmsg>.
Defaults to C<1>.

C<notice> messages are usually less intrusive.

=item squash_threshold I<number>

For I<push> events, limit the commit notifications to the given I<number>. If a
branch update contains more commits, the usual notifications are replaced by a
single notification about the number of the pushed commits.

B<Default>: 20

=back

=cut

sub gitlab_webhook_push {
    my ( $kernel, $chanids, $body, $opts ) = @_[ KERNEL, ARG0 .. ARG2 ];

    my $module = $body->{project}{name};

    KGB->debug("Handling webhook push request for project $module");



( run in 2.256 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )