From c0e8f01a25ed8f81daa4f6a56fee81c2053477bd Mon Sep 17 00:00:00 2001 From: Peter Palfrader Date: Wed, 23 May 2007 21:25:21 +0000 Subject: Add nsa git-svn-id: svn+ssh://asteria.noreply.org/svn/weaselutils/trunk@270 bc3d92e2-beff-0310-a7cd-cc87d7ac0ede --- config.sample | 15 +++ do-email | 25 ++++ nsa | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ parse-mail | 132 ++++++++++++++++++++ parse-nagios | 88 +++++++++++++ parse-wiki | 111 +++++++++++++++++ run-both | 4 + 7 files changed, 763 insertions(+) create mode 100644 config.sample create mode 100755 do-email create mode 100755 nsa create mode 100755 parse-mail create mode 100755 parse-nagios create mode 100755 parse-wiki create mode 100755 run-both diff --git a/config.sample b/config.sample new file mode 100644 index 0000000..5fb761e --- /dev/null +++ b/config.sample @@ -0,0 +1,15 @@ +--- + irc: + nick: kgb + username: kgb + server: irc.oftc.net + realname: CVS Commit 2 IRC + nickservpassword: xxxxxxxxxxxxxxxx + nickserv_is_smart: true + port: 6667 + mailin: /home/commit/Maildir + projects: + 'oftc': [ '#oftc' ] + 'weasel': [ 'weasel' ] + '*': [ '#commits' ] +# vim:syn=yaml diff --git a/do-email b/do-email new file mode 100755 index 0000000..ff1574e --- /dev/null +++ b/do-email @@ -0,0 +1,25 @@ +#!/bin/sh + +set -e +cd $HOME + +while [ 1 ] ; do + #for project in mixminion or freehaven debian.de oftc; do + for project in mixminion or debian.de oftc; do + for i in `( cd Maildir.$project/new ; ls -1)` ; do + bin/parse-mail $project Maildir.$project/new/$i && rm Maildir.$project/new/$i + done + done + for project in or oftc; do + for i in `( cd Maildir.$project-wiki/new ; ls -1)` ; do + bin/parse-wiki $project Maildir.$project-wiki/new/$i && rm Maildir.$project-wiki/new/$i + done + done + for project in weasel oftc; do + for i in `( cd Maildir.$project-nagios/new ; ls -1)` ; do + bin/parse-nagios $project Maildir.$project-nagios/new/$i && rm Maildir.$project-nagios/new/$i + done + done + sleep 10 +done + diff --git a/nsa b/nsa new file mode 100755 index 0000000..47c484e --- /dev/null +++ b/nsa @@ -0,0 +1,388 @@ +#!/usr/bin/ruby + +# (c) 2005 Peter Palfrader + +require 'socket' +require 'yaml' +require 'monitor' + +default_irc = { + 'server' => 'irc.oftc.net', + 'port' => 6667, + 'username' => 'unknown_nsa', + 'nick' => 'unknown_nsa', + 'realname' => 'Unknown NSA instance' +} + +CONFIG = YAML::load( File.open( 'config' ) ) +default_irc.each_pair do |k,v| + CONFIG['irc'][k] = v unless CONFIG['irc'][k] +end +CONFIG['mailin'] = '/home/commit/Maildir' unless CONFIG['mailin']; + +Log = Object.new +class << Log + def init + @ignore_list = [] + @ignore_list << "dispatch thread" + @ignore_list << "connection" + @ignore_list << "waituntilonline - waiter" + @ignore_list << "waituntilonline - runthread" + end + + def log section, message + puts message unless @ignore_list.include?(section) + end +end + + +class Connection + def initialize + @inQueue = [] + @inQueue.extend(MonitorMixin) + @inEmpty = @inQueue.new_cond + + @outQueue = [] + @outQueue.extend(MonitorMixin) + @outEmpty = @outQueue.new_cond + + @sock = TCPSocket.new(CONFIG['irc']['server'], CONFIG['irc']['port']); + puts "Connected!" + + createInThread + createOutThread + end + + def print line + @outQueue.synchronize do + @outQueue << line + @outEmpty.signal + end + end + + def getline + line = "" + @inQueue.synchronize do + @inEmpty.wait_while { @inQueue.empty? } + line = @inQueue.shift + end + return line + end + + private + + def createInThread + @inThread = Thread.new { + begin + while true + line = @sock.readline + Log.log "connection", "[connection] <<< " + line + line.chop! + + @inQueue.synchronize do + @inQueue << line + @inEmpty.signal + end + end + rescue => e + puts e.class.to_s+": "+e.message + puts e.backtrace + end + Thread.main.exit + } + end + + def createOutThread + @outThread = Thread.new { + begin + while true + @outQueue.synchronize do + @outEmpty.wait_while { @outQueue.empty? } + line = @outQueue.shift + @sock.puts line + Log.log "connection", "[connection] >>> " + line + end + end + rescue => e + puts e.class.to_s+": "+e.message + puts e.backtrace + end + Thread.main.exit + } + end +end + +IrcHandleDevNull = Object.new +class << IrcHandleDevNull + def handle(irc, parsed) + end +end + +IrcHandlePing = Object.new +class << IrcHandlePing + def handle(irc, parsed) + irc.print "PONG "+irc.getNick + end +end + +IrcHandle001 = Object.new +class << IrcHandle001 + def handle(irc, parsed) + irc.weAreOnline + end +end + +IrcHandlePickAnotherNick = Object.new +class << IrcHandlePickAnotherNick + def handle(irc, parsed) + irc.useADifferentNick + end +end + +IrcHandleMode = Object.new +class << IrcHandleMode + def handle(irc, parsed) + irc.modeUpdate(parsed) + end +end + +class Irc + IRC_NOT_CONNECTED = 0 + IRC_CONNECTED = 1 + IRC_SENT_NICK = 2 + IRC_PICK_NEW_NICK = 3 + IRC_ONLINE = 4 + + def initialize + @handler = {} + @nick = "#{CONFIG['irc']['nick']}" + @onlineMonitor = Monitor.new; + @onlineCond = @onlineMonitor.new_cond + + + @handler['PING'] = IrcHandlePing # PING + @handler['NOTICE'] = IrcHandleDevNull # NOTICE + @handler['MODE'] = IrcHandleMode # NOTICE + @handler['001'] = IrcHandle001 # w :Welcome to the OFTC Internet + @handler['002'] = IrcHandleDevNull # w :Your host is neutron.oftc.net.. + @handler['003'] = IrcHandleDevNull # w :This server was created Fri .... + @handler['004'] = IrcHandleDevNull # w neutron.oftc.net hybrid-7.1+oftc1.... + @handler['005'] = IrcHandleDevNull # w WALLCHOPS KNOCK EXCEPTS INVEX.... + @handler['375'] = IrcHandleDevNull # RPL_MOTDSTART + @handler['372'] = IrcHandleDevNull # RPL_MOTD + @handler['376'] = IrcHandleDevNull # RPL_ENDOFMOTD + @handler['250'] = IrcHandleDevNull # Highest connection count: 2 (2 clients) (7 connections received) + @handler['251'] = IrcHandleDevNull # RPL_LUSERCLIENT + @handler['252'] = IrcHandleDevNull # RPL_LUSEROP + @handler['253'] = IrcHandleDevNull # 1 :unknown connection(s) + @handler['254'] = IrcHandleDevNull # 575 :channels formed + @handler['255'] = IrcHandleDevNull # RPL_LUSERME + @handler['265'] = IrcHandleDevNull # Current local users: 2 Max: 2 + @handler['266'] = IrcHandleDevNull # Current global users: 2 Max: 2 + @handler['353'] = IrcHandleDevNull # RAB = #rab :RAB @weasel· + @handler['366'] = IrcHandleDevNull # RAB #rab :End of /NAMES list. + @handler['433'] = IrcHandlePickAnotherNick # * oftc-bot :Nickname is already in use. + + dispatchToHandlers + run + end + + def print line + @connection.print line + end + + def getNick + @nick + end + + def weAreOnline + throw "Current run_state is @{run_state}. that's unexpected" unless @run_state == IRC_SENT_NICK + @run_state = IRC_ONLINE + @run_thread.wakeup + end + + def useADifferentNick + throw "Current run_state is @{run_state}. that's unexpected" unless @run_state == IRC_SENT_NICK + @run_state = IRC_PICK_NEW_NICK + @run_thread.wakeup + end + + def modeUpdate(message) + puts "[irc] Received mode update: " + message.to_yaml.gsub("\n","\n ") + end + + + def waitUntilOnline + Log.log "waituntilonline - waiter", "[waituntilonline] entering monitor" + @onlineMonitor.synchronize do + Log.log "waituntilonline - waiter", "[waituntilonline] entered monitor" + @onlineCond.wait_while { @run_state != IRC_ONLINE } + Log.log "waituntilonline - waiter", "[waituntilonline] waiting done" + @onlineCond.signal + Log.log "waituntilonline - waiter", "[waituntilonline] woke up the rest" + end + Log.log "waituntilonline - waiter", "[waituntilonline] left monitor" + end + + private + + def run + @run_thread = Thread.new { + @run_state = IRC_NOT_CONNECTED + while true + puts "[run thread] state '#{@run_state}'" + case @run_state + when IRC_NOT_CONNECTED + @connection = Connection.new + @dispatch_thread.wakeup + @run_state = IRC_CONNECTED + when IRC_CONNECTED + @connection.print "USER #{CONFIG['irc']['username']} . . :#{CONFIG['irc']['realname']}" + issueNick + @run_state = IRC_SENT_NICK + when IRC_SENT_NICK + sleep + when IRC_PICK_NEW_NICK + @nick.succ! + issueNick + @run_state = IRC_SENT_NICK + when IRC_ONLINE + @connection.print "MODE #{@nick} +w" + + Log.log "waituntilonline - runthread", "[run thread] entering monitor onlineMonitor" + @onlineMonitor.synchronize do + Log.log "waituntilonline - runthread", "[run thread] entered monitor onlineMonitor" + @onlineCond.signal + Log.log "waituntilonline - runthread", "[run thread] sent signal on onlineCond" + end + Log.log "waituntilonline - runthread", "[run thread] left monitor onlineMonitor" + sleep + end + end + } + end + + def dispatchToHandlers + @dispatch_thread = Thread.new { + while true + while not @connection + Log.log "dispatch thread", "[dispatch thread] waiting for connection" + sleep + Log.log "dispatch thread", "[dispatch thread] waiting for connection done" + end + + Log.log "dispatch thread", "[dispatch thread] waiting for line" + line = @connection.getline + Log.log "dispatch thread", "[dispatch thread] waiting for line done" + + parsed = parseLine line + + if @handler.has_key?(parsed['command']) + Log.log "dispatch thread", "[dispatch thread] dispatching #{parsed['command']}: " + parsed['params'].join(' ') + @handler[parsed['command']].handle( self, parsed ) + else + Log.log "dispatch thread - unhandled", "[dispatch thread] Unhandled: #{line}" + end + end + } + end + + def issueNick + @connection.print "NICK #{@nick}" + end + + def parseLine line + source = nil + (source, line) = line.split(' ', 2) if line[0,1] == ':' + source = source[1,source.length-1] if source + (command, line) = line.split(' ', 2) + params = [] + while line and line[0,1] != ':' + (middle, line) = line.split(' ', 2) + params << middle + end + params << line[1,line.length-1] if line and line[0,1] == ':' + throw "hmmmm. line is '#{line}'." if line and line[0,1] != ':' + + return { + 'source' => source, + 'command' => command, + 'params' => params + } + end +end + +Thread.abort_on_exception = true + +Dir.chdir( CONFIG['mailin'] + "/new" ) + +Log.init +bot = Irc.new +bot.waitUntilOnline +sleep 1 +if CONFIG['irc']['nickserv_is_smart'] + bot.print "NICKSERV :identify #{CONFIG['irc']['nickservpassword']} #{CONFIG['irc']['nick']}" +else + bot.print "NICKSERV :identify #{CONFIG['irc']['nickservpassword']}" +end +sleep 5 + +channels = {} +CONFIG['projects'].each_value do |cl| + cl.each do |c| + channels[c] = true + end +end +channels.each_key do |c| + bot.print "JOIN #{c}" +end + +while (1) do + oldcommitmsg = nil + Dir.foreach('.') { |filename| + next if filename == "." + next if filename == ".." + + in_headers = true + fh = File.open(filename, "r") + project = nil + lines = [] + commitmsg = "" + fh.readlines.each { |line| + line.chomp! + in_headers = false if line == "" + if (in_headers and not line =~ /^\s/) + (header, content) = line.split(':', 2); + content.strip! + if header.upcase == "SUBJECT" + project = /Announce\s+([A-Za-z0-9_-]+)/.match(content)[1]; + end + elsif (not in_headers) + lines.push line + commitmsg = commitmsg + line + end + } + fh.close + File.unlink(filename) + + puts "Project "+project + puts "commitmsg "+commitmsg + + if project and commitmsg != oldcommitmsg + pr = "%c"%(002) + project + "%c: "%(002) + lines.each{ |line| + if (line != '') + line = pr + line + channellist = CONFIG['projects'].has_key?(project) ? CONFIG['projects'][project] : CONFIG['*'] + channellist.each do |c| + bot.print "PRIVMSG #{c} :#{line}" + end + end + } + end + oldcommitmsg = commitmsg + } + sleep 5 +end + +Thread.stop diff --git a/parse-mail b/parse-mail new file mode 100755 index 0000000..4c64f57 --- /dev/null +++ b/parse-mail @@ -0,0 +1,132 @@ +#!/usr/bin/perl -wT + +use strict; +use English; +use File::Basename; + +my $HOME = '/home/commit/'; +my $MAX_LINES = 4; +my $ENVELOPE_FROM = 'nobody@commit.noreply.org'; +my $HEADER_FROM = 'nobody@commit.noreply.org'; +my $BOT_ADDRESS = 'commit@commit.noreply.org'; +my $SENDMAIL = '/usr/sbin/sendmail'; +my $UNPARSEABLE = $HOME.'/Maildir.unparseable'; + +$ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +die ("Usage: $PROGRAM_NAME \n") unless (scalar @ARGV == 2); +my $project = $ARGV[0]; + +umask 077; + +my $TMPDIR = "$HOME/cvs-tmp/"; +-d $TMPDIR or + mkdir $TMPDIR or + die ("Cannot mkdir $TMPDIR: $0\n"); + +$project =~ m/^([a-zA-Z-]+(\.[a-zA-Z-]+)*)+$/; +$project = $1; +die ("Project is not a nice name.\n") unless defined $project; + +sub whine_and_store_away($) { + my ($complaint) = @_; + + $ARGV[1] =~ m#(Maildir.[a-z]+/new/[0-9]+\.[0-9_]+\.[a-z]+)#; + my $clean_filename = $1; + die ("Unable to clean filename '$ARGV[1]' for taint mode.") unless defined $clean_filename; + + my $newname = $UNPARSEABLE.'/new/'.basename($clean_filename); + link ($clean_filename, $newname) or die ("Cannot link $clean_filename to $newname: $!\n"); + unlink ($clean_filename) or die ("Cannot unlink $clean_filename: $!\n"); + print $complaint; + exit (1); +}; + +my $from; +my $message; +my $in_msg; +my $found_update_of; +my $tag; +my $cvs_mailcommit_directory; + +open (MAIL, $ARGV[1]) || die ("Cannot open $ARGV[1]: $!\n"); +my @mail = ; +close(MAIL); + +my $line; +while (defined($line = shift @mail)) { + if ($line =~ m/^From:.*CVS User ([a-zA-Z0-9-.]*)/ && !defined $from) { + $from = $1; + } elsif ($line =~ m/^From:.*<(\S*?)@/ && !defined $from) { + $from = $1; + } elsif ($line =~ m/^From:\s*(\S*?)@/ && !defined $from) { + $from = $1; + # Subject: [oftc-cvs-commits] CVS oftc-hybrid + } elsif ($line =~ m/^Subject: (?:\[\S*\] )?CVS\s*(.*)/) { + $cvs_mailcommit_directory = $1; + } elsif ($line =~ m/^Update of /) { + $found_update_of = 1; + } elsif ($line =~ m/^\s*Tag:\s*(.*)$/) { + $tag = $1; + } elsif ($line =~ m/^Log Message:/) { + $in_msg = 1; + last; + } +}; +die ("$PROGRAM_NAME - $project: No author found.\n") unless defined $from; +whine_and_store_away("$PROGRAM_NAME - $project: 'Update of ' not found.\n") unless defined $found_update_of; + +my $cur_line = 0; +my $last_line = ''; +if ($in_msg) { + while (defined($line = shift @mail)) { + if ($line =~ m/^Index: / || + $line =~ m/^--- NEW FILE/ || + $line =~ m/^--/ || + $line =~ m/^\*\*\*\*\*\*\*\*/ + ) { + last; + } + next if ($line =~ m/^\s*$/); + if ($cur_line == $MAX_LINES) { + $last_line = $_; + } elsif ($cur_line > $MAX_LINES) { + $last_line = "...\n"; + last; + } else { + $message .= $_; + }; + $cur_line++; + } +} +$message .= $last_line; + +die ("No message found.\n") unless defined $message; +my $notice = "commit by $from: $message"; + +my $old_notice = ''; +my $filename = $TMPDIR.'/'.$project; +if (-e $filename) { + open(F, $filename) or die ("cannot open $filename: $!\n"); + local $/ = undef; + $old_notice = ; + close(F); +} +if ($notice eq $old_notice) { + exit(0); +} +open(F, ">".$filename) or die ("cannot open $filename for writing: $!\n"); +print F $notice; +close(F); + +#open(MAIL, "|cat") or +open(MAIL, "|$SENDMAIL -t -oi -f $ENVELOPE_FROM") or + die ("Cannot exec sendmail: $!\n"); +print MAIL "From: $HEADER_FROM\n"; +print MAIL "To: $BOT_ADDRESS\n"; +print MAIL "Subject: Announce $project\n"; +print MAIL "Precedence: junk\n"; +print MAIL "\n"; +print MAIL (defined $tag ? "[$tag] " : '') . (defined $cvs_mailcommit_directory && length ($cvs_mailcommit_directory) ? "[$cvs_mailcommit_directory] " : ''). $notice; +close(MAIL); diff --git a/parse-nagios b/parse-nagios new file mode 100755 index 0000000..3edb127 --- /dev/null +++ b/parse-nagios @@ -0,0 +1,88 @@ +#!/usr/bin/perl -wT + +use strict; +use English; +use File::Basename; + +my $MAX_LINES = 4; +my $ENVELOPE_FROM = 'nobody@commit.noreply.org'; +my $HEADER_FROM = 'nobody@commit.noreply.org'; +my $BOT_ADDRESS = 'commit@commit.noreply.org'; +my $SENDMAIL = '/usr/sbin/sendmail'; + +$ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +die ("Usage: $PROGRAM_NAME \n") unless (scalar @ARGV == 2); +my $project = $ARGV[0]; + +umask 077; + + +$project =~ m/^([a-zA-Z-]+(\.[a-zA-Z-]+)*)+$/; +$project = $1; +die ("Project is not a nice name.\n") unless defined $project; + + +my $type; +my $service; +my $host; +my $state; +my $info; +my $running_number; +my $ack_by; +my $comment; + +open (MAIL, $ARGV[1]) || die ("Cannot open $ARGV[1]: $!\n"); +my @mail = ; +close(MAIL); + +my $line; +while (defined($line = shift @mail)) { + if ($line =~ m/^Subject:.*?alert ([0-9]+) - /) { + $running_number = $1; + } elsif ($line =~ m/^Notification Type:\s*(.*)/) { + $type = $1; + } elsif ($line =~ m/^Service:\s*(.*)/) { + $service = $1; + } elsif ($line =~ m/^Host:\s*(.*)/) { + $host = $1; + } elsif ($line =~ m/^State:\s*(.*)/) { + $state = $1; + } elsif ($line =~ m/^ACK by:\s*(.*)/) { + $ack_by = $1; + } elsif ($line =~ m/^Comment:\s*(.*)/) { + $comment = $1; + } elsif ($line =~ m/^(Additional )?Info:/) { + while (defined($line = shift @mail) && !($line =~ /\S/)) { + # null + } + if (defined $line) { + chomp $line; + $info = $line; + }; + } +}; +die ("$PROGRAM_NAME - $project: No type found.\n") unless defined $type; +# die ("$PROGRAM_NAME - $project: No service found.\n") unless defined $service; +die ("$PROGRAM_NAME - $project: No host found.\n") unless defined $host; +die ("$PROGRAM_NAME - $project: No state found.\n") unless defined $state; +die ("$PROGRAM_NAME - $project: No info found.\n") unless defined $info; + +$type =~ tr/A-Z/a-z/; +$service = $host unless defined $service; + +my $extra = (defined $running_number) ? " (#$running_number)" : ""; + +#open(MAIL, "|cat") or +open(MAIL, "|$SENDMAIL -t -oi -f $ENVELOPE_FROM") or + die ("Cannot exec sendmail: $!\n"); +print MAIL "From: $HEADER_FROM\n"; +print MAIL "To: $BOT_ADDRESS\n"; +print MAIL "Subject: Announce $project\n"; +print MAIL "Precedence: junk\n"; +print MAIL "\n"; +print MAIL "[$type\@$host] $service is $state$extra: $info\n"; +print MAIL "ACK by: $ack_by\n" if ($ack_by); +print MAIL "Comment: $comment\n" if ($comment); +close(MAIL); diff --git a/parse-wiki b/parse-wiki new file mode 100755 index 0000000..822857b --- /dev/null +++ b/parse-wiki @@ -0,0 +1,111 @@ +#!/usr/bin/perl -wT + +use strict; +use English; +use MIME::Base64; + + +my $HOME = '/home/commit/'; +my $MAX_LINES = 4; +my $ENVELOPE_FROM = 'nobody@commit.noreply.org'; +my $HEADER_FROM = 'nobody@commit.noreply.org'; +my $BOT_ADDRESS = 'commit@commit.noreply.org'; +my $SENDMAIL = '/usr/sbin/sendmail'; + +$ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +die ("Usage: $PROGRAM_NAME \n") unless (scalar @ARGV == 2); +my $project = $ARGV[0]; + +umask 077; + +my $TMPDIR = "$HOME/cvs-tmp/"; +-d $TMPDIR or + mkdir $TMPDIR or + die ("Cannot mkdir $TMPDIR: $0\n"); + +$project =~ m/^([a-zA-Z-]+(\.[a-zA-Z-]+)*)+$/; +$project = $1; +die ("Project is not a nice name.\n") unless defined $project; + + +my $from; +my $what; +my $is_base64 = 0; + + +open (MAIL, $ARGV[1]) || die ("Cannot open $ARGV[1]: $!\n"); +my @mail = ; +close(MAIL); +my $line; + +my @headers; +my $lastline = undef; +while (defined($line = shift @mail)) { + my $chomped = $line; + chomp $chomped; + if ($chomped =~ m/^\s+/) { + $lastline .= ' '.$line; + } elsif ($chomped =~ m/^$/) { + push @headers, $lastline if defined $lastline; + push @headers, $line; + push @headers, @mail; + last + } else { + push @headers, $lastline if defined $lastline; + $lastline = $line; + } +} + +while (defined($line = shift @headers)) { + if ($line =~ m/^Subject:\s+(?:\[.*?\]\s+)?(?:Trivial )?Update\s+of\s+"(.*)"\s+by\s+(.*?)\s*$/s) { + $what = $1; + $from = $2; + # =?utf-8?q?=5BOFTC=5D_Update_of_=22OFTC=22_by_PeterPalfrader?= + # =?utf-8?q?=5BOFTC=5D_Update_of_=22Staff=22_by_SethArnold?= + } elsif ($line =~ m/^Subject: (?:\[.*?\] )?=\?utf-8\?q\?=5BOFTC=5D_(?:Trivial_)?Update_of_=22(.*)=22_by_(.*?)\?=$/s) { + $what = $1; + $from = $2; + } elsif ($line =~ m/^Content-Transfer-Encoding: base64$/s) { + $is_base64 = 1; + } elsif ($line =~ m/^$/) { + last; + } +}; + +$from =~ s/\?=\s*=\?utf-8\?q\?//sg; + +die ("$PROGRAM_NAME - $project: No author found.\n") unless defined $from; +die ("$PROGRAM_NAME - $project: No what found.\n") unless defined $what; + +my $body = join '', @mail; +$body = decode_base64($body) if ($is_base64); + +my $message; +my $url; +my @lines = split(/\n/, $body); +while (defined($line = shift @lines)) { + if ($line =~ m/^The following page has been changed by /) { + $url = shift @lines + } elsif ($line =~ m/^The comment on the change is:/) { + $message = shift @lines; + last; + }; +}; + +my $notice = (defined $message) ? + "$from updated $what: $message" : + "$from updated $what"; +$notice .= " - $url" if defined $url; + +#open(MAIL, "|cat") or +open(MAIL, "|$SENDMAIL -t -oi -f $ENVELOPE_FROM") or + die ("Cannot exec sendmail: $!\n"); +print MAIL "From: $HEADER_FROM\n"; +print MAIL "To: $BOT_ADDRESS\n"; +print MAIL "Subject: Announce $project\n"; +print MAIL "Precedence: junk\n"; +print MAIL "\n"; +print MAIL "[wiki] $notice\n"; +close(MAIL); diff --git a/run-both b/run-both new file mode 100755 index 0000000..d6d6a8d --- /dev/null +++ b/run-both @@ -0,0 +1,4 @@ +#!/bin/sh + +./bin/do-email & +while [ 1 ] ; do ./bin/nsa; sleep 10; done -- cgit v1.2.3