我们公司使用Skype进行通讯,我希望能够在Jenkins构建失败(以及恢复时)向Skype聊天室发送警报。
我怎样才能做到这一点?
我已经使用Skype公共API完成了此操作
我所做的是编写一个Perl脚本,该脚本使用SkypeAPI CPAN模块来处理与Skype的通信。这有点笨拙,因为脚本需要在运行Skype的桌面上运行。我在始终可以在自己的桌面上运行它,但这确实意味着该机器人在我们团队的其他成员看来都是“我”。
最终结果是惊人的-每当詹金斯人构建改变状态时,该机器人就会通过键入 alert将消息发送到任何已注册兴趣的Skype聊天。此外,任何开发人员都可以通过键入 jenkins来查看和共享最新的构建状态
现在,SkypeAPI模块非常基础。它在listen()方法中具有消息循环,该消息循环检查来自Skype客户端的新事件,如果没有,则休眠一会儿。
我希望我的脚本进入此循环,以便我的机器人可以定期检查Jenkins RSS feed,因此在用ActiveState软件包管理器安装后,我对SkypeAPI.pm进行了以下修改:
我声明了新属性“ idler”以及现有属性…
__PACKAGE__->mk_accessors( qw/api handler_list stop_listen idler/ );
我添加了一种方法来设置模块将调用而不是休眠的“ idler”回调
sub register_idler { my $self = shift; my $ref_sub = shift; $self->idler($ref_sub); }
最后,我修改了消息循环以调用惰轮(如果已设置)
sub listen { my $self = shift; my $idler=$self->idler(); $self->stop_listen(0); while (!$self->stop_listen) { my $message; { lock @message_list; $message = shift @message_list; } if (not defined $message) { if ($idler) { $self->idler->($self); } else { sleep 0.1; } next; } for my $id (sort keys %{$self->handler_list}) { $self->handler_list->{$id}->($self, $message); } } }
现在该模块的功能更加强大,只需要编写一个脚本来充当机器人即可。这是我的-我对原始文件进行了一些编辑,因为它包含其他不相关的功能,但它应该为您提供一个起点。
所有从属模块都可以与ActiveState软件包管理器一起安装。
use strict; use SkypeAPI; use LWP::Simple; use Data::Dumper; use dirtyRSS; use Time::Local 'timegm'; use Math::Round; use Storable; #CHANGE THIS - where to get jenkins status from my $jenkinsRss='http://username:password@jenkins.example.com/rssLatest'; my %commands=( 'jenkins' =>\&cmdJenkins, 'alert' =>\&cmdAlert, 'noalert' =>\&cmdNoAlert, 'help' =>\&cmdHelp, ); my $helpMessage=<<HELP; Who asked for help? Here's all the other special commands I know... *jenkins - show status of our platform tests *alert - add this room to get automatic notification of build status *noalert - cancel notifcations *help - displays this message HELP #status for jenkins tracking my %builds; my $lastJenkinsCheck=0; my $alertRoomsFile='alert.rooms'; my $alertRooms={}; #store jenkins state checkJenkins(); #because that was our first fetch, we'll have flagged everything as changed #but it hasn't really, so we reset those flags resetJenkinsChangeFlags(); #remember rooms we're supposed to alert loadAlertRooms(); #attach to skype and enter message loop my $skype = SkypeAPI->new(); my $attached=$skype->attach(); $skype->register_handler(\&onEvent); $skype->register_idler(\&onIdle); $skype->listen(); exit; #here are the command handlers sub cmdJenkins { my ($chatId, $args)=@_; my $message=""; foreach my $build (keys(%builds)) { $message.=formatBuildMessage($build)."\n"; #reset changed flag - we've just show the status $builds{$build}->{'changed'}=0; } chatmessage($chatId, $message); } sub cmdAlert { my ($chatId, $args)=@_; addChatroomToAlerts($chatId,1); } sub cmdNoAlert { my ($chatId, $args)=@_; addChatroomToAlerts($chatId,0); } sub cmdHelp { my ($chatId, $args)=@_; chatmessage($chatId, $helpMessage); } #simple helper to transmit a message to a specific chatroom sub chatmessage { my ($chatId, $message)=@_; my $commandstr="CHATMESSAGE $chatId $message"; my $command = $skype->create_command( { string => $commandstr} ); $skype->send_command($command); } #refreshes our copy of jenkins state, and will flag any builds #which have changed state since the last check sub checkJenkins{ my $xml = get($jenkinsRss); my $tree = parse($xml); my $items=$tree->{'channel'}->[0]->{'item'}; foreach my $item (@{$items}) { my $title=$item->{'title'}; my $link=$item->{'link'}; my $built=$item->{'lastbuilddate'}; #print Dumper($item); if ($title=~m/^(.*?) #(\d+)\s*(.*)$/) { my $build=$1; my $buildnumber=$2; my $status=$3; #print "$build\n$buildnumber\n$status\n$link\n$built\n\n"; #build in progress, ignore if (!exists($builds{$build})) { $builds{$build}={}; $builds{$build}->{'status'}=''; $builds{$build}->{'changed'}=0; } $builds{$build}->{'name'}=$build; if ($status eq '(?)') { $builds{$build}->{'in_progress'}=1; next; #don't update until complete } else { $builds{$build}->{'in_progress'}=0; } #is this status different to last status? if ($builds{$build}->{'status'} ne $status) { $builds{$build}->{'changed'}=1; } $builds{$build}->{'status'}=$status; $builds{$build}->{'build_number'}=$buildnumber; $builds{$build}->{'link'}=$link; $builds{$build}->{'built'}=$built; } } #print Dumper(\%builds); } #generates a string suitable for displaying build status in skype sub formatBuildMessage { my ($build)=@_; my $status=$builds{$build}->{'status'}; my $smiley=":)"; if ($status=~m/broken/) { $smiley="(devil)"; } elsif ($status=~m/\?/) { #this means the build is being retested, we should skip it $smiley=":|"; } my $message=''; if ($builds{$build}->{'in_progress'}) { $message=":| $build - rebuild in progress..." } else { my ($y,$mon,$d,$h,$m,$s) = $builds{$build}->{'built'} =~ m/(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z/; my $time = timegm($s,$m,$h,$d,$mon-1,$y); my $age=time()-$time; my $mins=round($age/60); my $hrs=round($age/3600); my $days=round($age/86400); my $niceage; if ($mins<=2) { $niceage="a few moments ago"; } elsif ($mins<120) { $niceage="$mins minutes ago"; } elsif ($hrs<48) { $niceage="$hrs hours ago"; } else { $niceage="$days days ago"; } $message="$smiley $build last built $niceage $status"; } return $message; } #forget any changes we've flagged sub resetJenkinsChangeFlags { foreach my $build (keys(%builds)) { $builds{$build}->{'changed'}=0; } } #checks for builds which have changed state. Can be called #often, it will only kick in if 60 seconds have elapsed since #last check sub checkForJenkinsChanges { my $now=time(); if (($now-$lastJenkinsCheck) < 60) { #no need, we fetched it recently return; } checkJenkins(); my $message=''; foreach my $build (keys(%builds)) { if ($builds{$build}->{'changed'}) { $builds{$build}->{'changed'}=0; $message.=formatBuildMessage($build)."\n"; } } if (length($message)) { foreach my $chatId (keys(%$alertRooms)) { chatmessage($chatId, $message); } } $lastJenkinsCheck=$now; } #adds or removes a room from the alerts sub addChatroomToAlerts { my($chatId, $add)=@_; if ($add) { if (exists($alertRooms->{$chatId})) { chatmessage($chatId, "/me says this room is already getting alerts"); } else { $alertRooms->{$chatId}=1; chatmessage($chatId, "/me added this chatroom to jenkins alerts"); } } else { delete($alertRooms->{$chatId}); chatmessage($chatId, "/me removed this chatroom from jenkins alerts"); } store $alertRooms, $alertRoomsFile; } sub loadAlertRooms { if (-e $alertRoomsFile) { $alertRooms = retrieve( $alertRoomsFile); } } # Skype event handler sub onEvent { my $skype = shift; my $msg = shift; #my $command = $skype->create_command( { string => "GET USERSTATUS"} ); #print $skype->send_command($command) , "\n"; #print "handler: $msg\n"; #an inbound chat message is either #MESSAGE 13021257 STATUS RECEIVED (from others) #MESSAGE 13021257 STATUS SENT (from us) if ($msg =~ m/MESSAGE (\d+) STATUS (SEND|RECEIVED)/) { my $msgId=$1; #get message body my $commandstr="GET CHATMESSAGE $msgId BODY"; my $command = $skype->create_command( { string => $commandstr} ); my $output=$skype->send_command($command); #if its a message for us... if ($output =~ m/MESSAGE $msgId BODY \*([^\s]*)\s*(.*)/i) { my $botcmd=$1; my $botargs=$2; $commandstr="GET CHATMESSAGE $msgId CHATNAME"; $command = $skype->create_command( { string => $commandstr} ); $output=$skype->send_command($command); if ($output =~ m/MESSAGE $msgId CHATNAME (.*)/) { my $chatId=$1; if (exists($commands{$botcmd})) { $commands{$botcmd}->($chatId, $botargs); } else { chatmessage($chatId, "/me suggests trying *help as the robot didn't understand *$botcmd"); } } } } } #skype idle handler #Note - SkypeAPI.pm was modified to support this sub onIdle { my $skype = shift; checkForJenkinsChanges(); sleep 0.1; }
如果已将其另存为robot.pl,则只需打开一个控制台窗口即可perl robot.pl运行它。Skype会询问您是否应允许perl.exe与之通信,一旦确认,就可以了!
perl robot.pl
进入团队聊天室并键入*jenkins最新版本的摘要,然后注册会议室以通过以下方式通知版本更改*alert
*jenkins
*alert
完美的:)