Etwas gegen die Flaute: Freshmeat-Newsletter parsen

"Josef 'Jupp' Schugt" <[email protected]> Fri, 14 Jan 2005 15:50:57 +0100
Newsgroups gmane.comp.lang.ruby.german
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------000700050401000407040705
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: quoted-printable
X-MIME-Autoconverted: from 8bit to quoted-printable by rubyforge.org id j0ELZwdS016379

Hi!

Bei mir ist seit 2004-11-23 nichts mehr =FCber diese Liste angekommen.=20
Andererseits wollte ich jetzt nicht die tausendunderste Testmail=20
verschicken, um die Erreichbarkeit der Liste zu testen. Also habe ich=20
mein neuestes Ruby-Machwerk angeh=E4ngt: Ein Programm, das den=20
Freshmeat-Newsletter in einzelne Mails zerlegt. Ich rufe es mit dem=20
folgenden procmail-Rezept auf:

:0 c
* ^From:.*[email protected]
* ^[email protected]
* ^Subject:.*[fm-news]
|/home/jupp/bin/fm.rb

Ob man es auch unter Windows nutzen kann wei=DF ich nicht, da ich schon=20
seit Jahren nur noch Linux einsetze. Na ja, nicht ganz; ich habe einige=20
Monate FreeBSD verwendet.

Josef 'Jupp' Schugt
--=20
The most severe crime in Germany is being on board of a plane that is
hijacked. Separation of powers is bypassed, the only punishment for it
is death, no legal proceedigs are necessary. It is the *only* crime for
which germans may sentence you to death. Ask German DoD for details.

--------------000700050401000407040705
Content-Type: text/plain;
 name="fm.rb"
Content-Disposition: inline;
 filename="fm.rb"
Content-Transfer-Encoding: 7bit

#!/usr/bin/env ruby
require 'net/smtp'
require 'yaml'

User = ENV['USER'].nil? ? ENV['LOGNAME'] : ENV['USER']
raise "User name unknown" if User.nil?

class Array
   def extract_next_part!
      if /::: ([^(]*) (\(\d+\) )?:::/.match(first)
         title = $1.gsub(/   /, "-").delete(' ').downcase!
         shift
         data = ''
         data << shift << "\n\n" until first =~ /^:::/ or empty?
         data.gsub!(/^[-. ]+\n$/, '')
	 [ title, data.strip.split(/- %  - %  - % - % -/).map{ |part| part.strip } ]
      else
	 shift
         [ nil, nil ]
      end
   end
end

class String
   def to_msg(type)
      time = Time.now.to_f
      body, head, msgId, name, title = [ ], [ ], '', '', ''
      case type
         when 'articles'
            body = split(/\n\n/)
            title = 'Article: ' + /(.+)^by/m.match(body.first)[1].gsub(/\n/, ' ')
            name  = /^URL: (http:\/\/freshmeat.net\/articles\/view\/(\d+)\/)/.match(self)[1]
            name = $2
            body = [ body[1], $1 ]
         when 'release-details'
            about, changes, license, url = '', '', '', '', ''
            name   = /^URL: http:\/\/freshmeat.net\/projects\/([^\\]+)\//.match(self)[1]
            split(/\n\n/).each_with_index { |part,i|
	       case i
		  when 1 
		     head = part.split(/\n/).map { |cat| "X-Trove-Category: #{cat}" }
		     head.push "X-Trove-Category: none provided" if head.empty?
		  else
		     case part
			when /^\[\d+\] - (.*)^  by/m then title   = $1.gsub(/\n/, ' ')
			when /^About: (.*)/m         then about   = $1.gsub(/\n/, ' ')
			when /^Changes: (.*)/m       then changes = $1.gsub(/\n/, ' ')
			when /^URL: (.*)/m           then url     = $1.gsub(/\n/, ' ')
			when /^License: (.*)/m       then license = $1.gsub(/\n/, ' ')
		     end
	       end
            }
            body += [ about, url ]
            body += [ '', '*Changes*', '', changes ] unless changes.empty?
            body += [ '', '*License*', '', license ] unless license.empty?
         else
	    return nil
      end
      msgId = "<#{time}-#{name}-#{type}-for-#{User}@freshmeat.net>"
      head.push "X-Freshmeat-Parser: fm.rb"
      head.push "From: #{User}@localhost"
      head.push "Message-Id: #{msgId}"        unless msgId.empty?
      head.push "Subject: #{title}"           unless title.empty?
      head.push "References: #{$refto[name]}" if $refto.include?(name)
      $refto[name] = msgId
      head.join("\n") + "\n\n" + body.join("\n")
   end
end

if File::exist?("#{ENV['HOME']}/.fm.yaml")
   $refto = YAML::load(File::open("#{ENV['HOME']}/.fm.yaml"))
else
   $refto = Hash.new
end

msgPart =  $stdin.readlines.join.split(/\n\n/)[1..-1]

list = Hash.new
until msgPart.empty?
   title, data = msgPart.extract_next_part!
   list[title] = data unless title.nil?
end

unless list['release-details'].nil? or list['release-details'].empty?
   fmSig = [ '_______________________________________________',
	     'The freshmeat daily newsletter',
	     'To unsubscribe, send email to [email protected]',
	     'or visit http://lists.freshmeat.net/mailman/listinfo/freshmeat-news']
   list['release-details'][-1].gsub!(Regexp.new(fmSig.join("\n")), '').strip! unless list['release-details'].empty?
end

Net::SMTP::start('localhost') { |smtp|
   list.each_pair { |type, value|
      next unless ['articles', 'release-details'].member?(type)
      next if value.nil?
      value.each { |body|
         smtp.ready("#{User}@localhost", "#{User}@localhost") { |msg| msg.write body.to_msg(type) }
      }
   }
}

File::open("#{ENV['HOME']}/.fm.yaml", 'w') { |f| f.puts $refto.to_yaml }

--------------000700050401000407040705
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

_______________________________________________
Ruby-de-talk mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/ruby-de-talk

--------------000700050401000407040705--