App-KGB
view release on metacpan or search on metacpan
script/kgb-bot view on Meta::CPAN
push @info, colorize( module => $module ) if $module ne '';
push @info, colorize( branch => $branch ) if $branch ne '';
push @info, "$rev_prefix" . colorize( revision => $commit_id )
if defined $commit_id;
push @info, colorize( author => $author ) if $author ne '';
push @info, colorize( path => "$common_dir/" ) if $common_dir ne '';
if ( $changed_files > $MAGIC_MAX_FILES ) {
my %dirs;
for my $c (@$changes) {
my $dir = dirname( $c->path );
$dirs{$dir}++;
}
my $dirs = scalar( keys %dirs );
my $path_string = join( ' ',
( $dirs > 1 )
? sprintf( "(%d files in %d dirs)", $changed_files, $dirs )
: sprintf( "(%d files)", $changed_files ) );
push @info, colorize( path => $path_string );
}
else {
push @info, join( ' ', map { colorize_change($_) } @$changes )
if @$changes;
}
my @string = join( ' ', @info );
my $web_string
= defined( $data->{extra}{web_link} )
? colorize( web => $data->{extra}{web_link} )
: undef;
my $use_notices = $data->{extra}{use_irc_notices};
# one-line notifications result in:
# user branch commit module changes log link
# multi-line notifications look like:
# user branch commit module changes link
# log line 1
# log line 2 ...
if ( 1 == @log and length($log[0]) <= 80 ) {
$string[0] .= ' ' . colorize( separator => '*' ) . ' ' . $log[0];
}
else {
push @string, @log;
}
$string[0] .= ' ' . colorize( separator => '*' ) . ' ' . $web_string
if defined($web_string);
@string = trim_lines(\@chanids, $repo_id, @string);
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 (@string) {
$fh->print("$chanid $_\n");
}
$fh->close;
}
else {
my ( $net, $chan ) = KGB->get_net_chan($chanid);
$kernel->yield(
irc_notify => $net, $chan, \@string,
$use_notices ? 'notice' : 'privmsg'
);
}
}
}
sub do_commit_v0 {
my ( $kernel, $repo_id, $passwd, $rev, $paths, $log, $author )
= @_;
throw Error::Simple("Unknown repository '$repo_id'\n")
unless $KGB::config->{repositories}{$repo_id};
throw Error::Simple("Invalid password for repository $repo_id\n")
if $KGB::config->{repositories}{$repo_id}{password}
and $KGB::config->{repositories}{$repo_id}{password} ne $passwd;
do_commit_msg(
$kernel,
$repo_id,
{ rev_prefix => 'r',
commit_id => $rev,
changes => $paths,
commit_log => $log,
author => $author
}
);
}
sub do_commit_v1 {
my ($kernel, $repo_id, $checksum, $rev,
$paths, $log, $author, $branch, $module
) = @_;
# v1 is the same as v2, but has no rev_prefix parameter
return do_commit_v2(
$kernel, $repo_id, $checksum, 'r', $rev,
$paths, $log, $author, $branch, $module
);
}
sub do_commit_v2 {
my ($kernel, $repo_id, $checksum,
$rev_prefix, $rev, $paths, $log,
$author, $branch, $module,
) = @_;
throw Error::Simple("Repository $repo_id is unknown\n")
unless $KGB::config->{repositories}{$repo_id};
# Protocol v2 always uses UTF-8
script/kgb-bot view on Meta::CPAN
}
sub do_json_commit_v4 {
my ( $self, $kernel, $repo_id, $data ) = @_;
KGB::SOAP::do_commit_msg( $kernel, $repo_id, $data );
}
sub do_json_relay_message {
my ( $self, $kernel, $repo_id, $message, $opts ) = @_;
$opts ||= {};
defined $repo_id or die "Missing repo_id argument\n";
exists $KGB::config->{repositories}{$repo_id} or die "Invalid repository '$repo_id'\n";
my $repo = $KGB::config->{repositories}{$repo_id};
my @chanids = @{ $repo->{chanids} };
push @chanids, @{ $KGB::config->{broadcast_channels} }
unless $repo->{private};
die("Repository $repo_id has no associated channels.\n") unless @chanids;
my @messages;
defined($message) or die "No message parameter";
if (ref($message) ) {
ref($message) eq 'ARRAY'
or die "Unsupported ref ("
. ref($message)
. ") for the message parameter";
for (@$message) {
defined($_) and not ref($_) or die "Invalid message";
length($_) or die "Empty message";
}
KGB->debug(
sprintf( "Received a batch of %d messages\n", scalar(@$message) )
);
@messages = @$message;
}
else {
length($message) or die "Empty message";
push @messages, $message;
}
die "Too much messages (rate limit overflow)"
if $KGB::config->{queue_limit}
and $KGB::IRC::irc_object
and $KGB::config->{queue_limit}
< ( $KGB::IRC::irc_object->send_queue + scalar(@messages) );
foreach my $msg (@messages) {
foreach my $chanid ( @chanids ) {
for my $line ( split( /\n/, $msg ) ) {
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');
$fh->print("$chanid $line\n");
$fh->close;
}
else {
my ( $net, $chan ) = KGB->get_net_chan($chanid);
$kernel->yield(
irc_notify => $net, $chan, [$line],
$opts->{use_irc_notices} ? 'notice' : 'privmsg'
);
}
}
}
}
return 'OK';
}
package KGB::WebHook;
use strict;
use warnings;
use File::Basename;
use HTTP::Status;
use JSON;
use App::KGB::Painter;
use List::MoreUtils qw(any);
use List::Util qw(max);
use Net::IP;
use POE;
use Text::Glob qw(match_glob);
sub colorize { KGB::SOAP::colorize( @_ ) };
sub colorize_change { KGB::SOAP::colorize_change( @_ ) };
sub webhook_error(\@$;$$) {
my $env = shift;
my $message = shift;
my $code = shift // HTTP::Status::HTTP_BAD_REQUEST;
my $content = shift // $message;
KGB->debug("Webhook error: $message");
my $response = $env->[ARG1];
$response->code($code);
$response->message($message);
$response->content($content);
return $env->[KERNEL]->post( $env->[SENDER], DONE => $response);
}
=head2 webhook_request I<request> I<response> I<dirmatch>
Handler for webhook HTTP request.
This handler only processes the HTTP part, parsing URI parameters and POST
contents.
The actual processing and IRC notification is done via an appropriate
C<gitlab_webhook_*> event, asynchronously.
=cut
script/kgb-bot view on Meta::CPAN
unless ($ok) {
KGB->out("Unable to load URL shortening service '$service': $@");
return $url;
}
$shortener_loaded = $service;
}
my $short_url;
my $ok = eval { $short_url = short_link($url); 1 };
unless ($ok) {
KGB->out(
"Failure while calling URL shortening service '$service' for '$url': $@"
);
return monkey_shorten_git_hashes($url);
}
unless ( defined $short_url ) {
KGB->out(
"URL shortening service '$service' failed to shorten '$url'.");
return monkey_shorten_git_hashes($url);
}
KGB->count('URLs_shortened');
return $short_url;
}
sub trim_lines(\@$@) {
my ( $chanids, $repoid, @strings ) = @_;
# Standard says 512 (minus \r\n), anyway that's further trimmed when
# 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¶m=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.
script/kgb-bot view on Meta::CPAN
sub gitlab_webhook_build {
my ( $kernel, $chanids, $body, $opts ) = @_[ KERNEL, ARG0 .. ARG2 ];
my $module = $body->{project_name};
KGB->debug("Handling webhook build event for project $module");
local $KGB::painter = $KGB::painter_dummy
if exists $opts->{use_color} and not $opts->{use_color};
my $log = sprintf(
'Build #%d (%s) stage: %s, status: %s',
$body->{build_id}, $body->{build_name},
$body->{build_stage} // 'UNKNOWN',
$body->{build_status} // 'UNKNOWN'
);
$log .= ". Duration: " . human_duration( $body->{build_duration} )
if defined $body->{build_duration};
my @info;
push @info, colorize( module => $module ) if $module ne '';
push @info, colorize( branch => 'builds' );
push @info, colorize( author => $body->{user}{name} );
push @info, colorize( revision => substr( $body->{sha}, 0, 7 ) )
if $body->{object_attributes}{id};
push @info, colorize( separator => '*' ) . ' ' . $log;
my @string = join( ' ', @info );
webhook_to_irc(
{ kernel => $kernel,
chanids => $chanids,
strings => \@string,
repository => $body->{project}{namespace},
opts => $opts,
}
);
}
package main;
use strict;
use warnings;
use POE;
use POE::Component::Server::SOAP;
use POE::Component::IRC::State;
use POE::Component::IRC::Plugin::AutoJoin;
use POE::Component::IRC::Plugin::BotAddressed;
use POE::Component::IRC::Plugin::Connector;
use POE::Component::IRC::Plugin::NickReclaim;
use POE::Component::IRC::Plugin::NickServID;
use POE::Component::IRC::Plugin::CTCP;
use Getopt::Long;
use YAML ();
use Proc::PID::File;
KGB::save_progname();
$KGB::out = \*STDERR;
binmode( $KGB::out, ':utf8' );
my $conf_file = '/etc/kgb-bot/kgb.conf';
my $conf_dir = '/etc/kgb-bot/kgb.conf.d';
$KGB::foreground = 0;
$KGB::simulate = 0;
$KGB::simulate_color = 0;
$KGB::debug = 0;
Getopt::Long::Configure("bundling");
GetOptions(
'c|config=s' => \$conf_file,
'cd|config-dir=s' => \$conf_dir,
'f|foreground' => \$KGB::foreground,
'simulate=s' => \$KGB::simulate,
'simulate-color!' => \$KGB::simulate_color,
'debug!' => \$KGB::debug,
) or die 'Invalid parameters';
@ARGV and die "No command line arguments supported\n";
KGB::load_conf($conf_file);
use Cwd;
$KGB::simulate = Cwd::realpath($KGB::simulate) if $KGB::simulate;
$KGB::painter
= App::KGB::Painter->new( { item_colors => $KGB::config->{colors} } );
our $pid_keeper;
unless ($KGB::foreground) {
pipe IN, OUT or die "pipe: $!\n";
my $pid = fork();
die "Can't fork: $!" unless ( defined $pid );
if ($pid) {
close OUT;
my $r = join( "", <IN> );
close IN or die $!;
if ( $r =~ /^OK$/ ) {
exit 0;
}
else {
die $r;
}
die "Should not happen";
}
$poe_kernel->has_forked;
close IN;
eval {
$pid_keeper = Proc::PID::File->new(
verify => 1,
dir => $KGB::config->{pid_dir},
);
die "Already running\n" if $pid_keeper->alive;
$pid_keeper->write;
POSIX::setsid() or die "setsid: $!\n";
umask(0022);
( run in 1.426 second using v1.01-cache-2.11-cpan-97f6503c9c8 )