rss2mail 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  == VERSION
4
4
 
5
- This documentation refers to rss2mail version 0.0.1
5
+ This documentation refers to rss2mail version 0.0.2
6
6
 
7
7
 
8
8
  == DESCRIPTION
@@ -26,7 +26,7 @@ Rubyforge project:: <http://rubyforge.org/projects/rss2mail>
26
26
 
27
27
  == LICENSE AND COPYRIGHT
28
28
 
29
- Copyright (C) 2007-2008 Jens Wille
29
+ Copyright (C) 2007-2009 Jens Wille
30
30
 
31
31
  rss2mail is free software: you can redistribute it and/or modify it under
32
32
  the terms of the GNU General Public License as published by the Free Software
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ begin
15
15
  :summary => %q{Send RSS feeds as e-mail},
16
16
  :homepage => %q{http://rss2mail.rubyforge.org/},
17
17
  :files => FileList['lib/**/*.rb', 'bin/*'].to_a,
18
- :extra_files => FileList['[A-Z]*', 'example/*'].to_a,
18
+ :extra_files => FileList['[A-Z]*', 'templates/*', 'example/*'].to_a,
19
19
  :dependencies => %w[simple-rss hpricot unidecode ruby-nuggets]
20
20
  }
21
21
  }}
data/bin/rss2mail CHANGED
@@ -5,7 +5,7 @@
5
5
  # #
6
6
  # rss2mail -- Send RSS feeds as e-mail #
7
7
  # #
8
- # Copyright (C) 2007-2008 Jens Wille #
8
+ # Copyright (C) 2007-2009 Jens Wille #
9
9
  # #
10
10
  # Authors: #
11
11
  # Jens Wille <ww@blackwinter.de> #
@@ -40,7 +40,8 @@ abort USAGE if ARGV.empty?
40
40
  options = {
41
41
  :files => nil,
42
42
  :reload => false,
43
- :verbose => false
43
+ :verbose => false,
44
+ :debug => false
44
45
  }
45
46
 
46
47
  OptionParser.new { |opts|
@@ -61,6 +62,10 @@ OptionParser.new { |opts|
61
62
  opts.on('-v', '--verbose', 'Be verbose') {
62
63
  options[:verbose] = true
63
64
  }
65
+
66
+ opts.on('-D', '--debug', "Print debug output and don't send any mails") {
67
+ options[:debug] = true
68
+ }
64
69
  }.parse!
65
70
 
66
71
  if target = ARGV.shift
@@ -70,16 +75,15 @@ else
70
75
  end
71
76
 
72
77
  templates = Hash.new { |h, k|
73
- t = File.join(base, 'templates', "#{k}.erb")
74
-
75
- begin
76
- h[k] = File.read(t)
78
+ h[k] = begin
79
+ File.read(File.join(base, 'templates', "#{k}.erb"))
77
80
  rescue Errno::ENOENT
78
81
  # silently ignore
79
82
  end
80
83
  }
81
84
 
82
85
  feeds_files = options.delete(:files) || [File.join(base, 'feeds.yaml')]
86
+
83
87
  feeds_files.each { |feeds_file|
84
88
  feeds = begin
85
89
  YAML.load_file(feeds_file)
@@ -98,7 +102,5 @@ feeds_files.each { |feeds_file|
98
102
  }
99
103
 
100
104
  # write updated feed information
101
- File.open(feeds_file, 'w') { |file|
102
- YAML.dump(feeds, file)
103
- }
105
+ File.open(feeds_file, 'w') { |file| YAML.dump(feeds, file) } unless options[:debug]
104
106
  }
data/example/feeds.yaml CHANGED
@@ -28,3 +28,8 @@
28
28
  - :url: http://rss.slashdot.org/Slashdot/slashdotBookReviews
29
29
  :to: my.secret@e.mail
30
30
  :title: "Slashdot: Book Reviews"
31
+ :test:
32
+ - :url: http://github.com/blackwinter.atom
33
+ :to: my.secret@e.mail
34
+ :title: GitHub/blackwinter
35
+ :unescape_html: true
data/lib/rss2mail/feed.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # #
4
4
  # A component of rss2mail, the RSS to e-mail forwarder. #
5
5
  # #
6
- # Copyright (C) 2007-2008 Jens Wille #
6
+ # Copyright (C) 2007-2009 Jens Wille #
7
7
  # #
8
8
  # Authors: #
9
9
  # Jens Wille <ww@blackwinter.de> #
@@ -28,9 +28,6 @@ require 'open-uri'
28
28
  require 'erb'
29
29
 
30
30
  require 'rubygems'
31
- require 'hpricot'
32
- require 'unidecode'
33
- require 'nuggets/util/i18n'
34
31
  require 'nuggets/string/evaluate'
35
32
 
36
33
  require 'rss2mail/rss'
@@ -39,17 +36,9 @@ module RSS2Mail
39
36
 
40
37
  class Feed
41
38
 
42
- SUBSTITUTIONS = {
43
- '–' => '--',
44
- '«' => '<<',
45
- '&amp;' => '&'
46
- }
39
+ HOST = ENV['HOSTNAME'] || ENV['HOST'] || %x{hostname}.chomp
47
40
 
48
- SUBSTITUTIONS_RE = %r{Regexp.union(*SUBSTITUTIONS.keys)}o
49
-
50
- TAGS_TO_KEEP = %w[a p br h1 h2 h3 h4]
51
-
52
- attr_reader :feed, :verbose, :reload, :simple, :updated, :content, :rss
41
+ attr_reader :feed, :reload, :verbose, :debug, :simple, :updated, :content, :rss
53
42
 
54
43
  def initialize(feed, options = {})
55
44
  raise TypeError, "Hash expected, got #{feed.class}" unless feed.is_a?(Hash)
@@ -58,33 +47,34 @@ module RSS2Mail
58
47
  @simple = feed[:simple]
59
48
  @updated = feed[:updated]
60
49
 
61
- @verbose = options[:verbose]
62
50
  @reload = options[:reload]
51
+ @verbose = options[:verbose]
52
+ @debug = options[:debug]
63
53
 
64
54
  required = [:url, :to, :title]
65
55
  required.delete_if { |i| feed.has_key?(i) }
66
56
 
67
- raise ArgumentError, "feed incomplete: #{required.join(', ')} missing" unless required.empty?
57
+ raise ArgumentError, "Feed incomplete: #{required.join(', ')} missing" unless required.empty?
68
58
  end
69
59
 
70
60
  def deliver(templates)
71
- unless get && parse
72
- warn "[#{feed[:title]}] Nothing to send" if verbose
61
+ to = [*feed[:to]]
62
+
63
+ if to.empty?
64
+ log 'No one to send to'
73
65
  return
74
66
  end
75
67
 
76
- if rss.items.empty?
77
- warn "[#{feed[:title]}] No new items" if verbose
68
+ unless get && parse
69
+ log 'Nothing to send'
78
70
  return
79
71
  end
80
72
 
81
- to = [*feed[:to]]
82
- if to.empty?
83
- warn "[#{feed[:title]}] No one to send to" if verbose
73
+ if rss.items.empty?
74
+ log 'No new items'
84
75
  return
85
76
  end
86
77
 
87
- feed_title = feed[:title]
88
78
  content_type = feed[:content_type] || 'text/html'
89
79
  encoding = feed[:encoding] || 'UTF-8'
90
80
 
@@ -93,7 +83,7 @@ module RSS2Mail
93
83
  content_type_header = "Content-type: #{content_type}; charset=#{encoding}"
94
84
 
95
85
  unless template = templates[content_type[/\/(.*)/, 1]]
96
- warn "[#{feed[:title]}] Template not found: #{content_type}" if verbose
86
+ log "Template not found: #{content_type}"
97
87
  return
98
88
  end
99
89
 
@@ -101,8 +91,8 @@ module RSS2Mail
101
91
  '/usr/bin/mail',
102
92
  '-e',
103
93
  "-a '#{content_type_header}'",
104
- "-a 'From: rss2mail@blackwinter.de'",
105
- "-s '[#{feed_title}] \#{subject}'",
94
+ "-a 'From: rss2mail@#{HOST}'",
95
+ "-s '[#{feed[:title]}] \#{subject}'",
106
96
  *to
107
97
  ].join(' ')
108
98
 
@@ -111,45 +101,25 @@ module RSS2Mail
111
101
  rss.items.each { |item|
112
102
  title = item.title
113
103
  link = item.link
114
- description = item.description
104
+ description = item.description(feed[:unescape_html])
115
105
  date = item.date
116
106
  author = item.author
107
+ body = item.body(feed[:body])
108
+ subject = item.subject
117
109
 
118
- if description && feed[:unescape_html]
119
- description.gsub!(/&lt;/, '<')
120
- description.gsub!(/&gt;/, '>')
121
- end
122
-
123
- if tag = feed[:body]
124
- body = case tag
125
- when true: open(link).read
126
- else Hpricot(open(link)).at(tag).to_s
127
- end.gsub(/<\/?(.*?)>/) { |m|
128
- m if TAGS_TO_KEEP.include?($1.split.first.downcase)
129
- }.gsub(/<a\s+href=['"](?!http:).*?>(.*?)<\/a>/mi, '\1')
110
+ log "#{title} / #{date} [#{author}]", debug
111
+ log "<#{link}>", debug
130
112
 
131
- if body_encoding = feed[:body_encoding]
132
- body = Iconv.conv('UTF-8', body_encoding, body)
133
- end
134
- end
135
-
136
- subject = title ? clean_subject(title) : 'NO TITLE'
137
-
138
- _cmd = cmd.evaluate(binding)
139
-
140
- begin
141
- IO.popen(_cmd, 'w') { |mail| mail.puts ERB.new(template).result(binding) }
113
+ send_mail(cmd.evaluate(binding), ERB.new(template).result(binding)) {
142
114
  feed[:sent] << link
143
115
  sent += 1
144
- rescue Errno::EPIPE => err
145
- warn "[#{feed[:title]}] Error while sending mail (#{err.class}): #{_cmd}"
146
- end
116
+ }
147
117
  }
148
118
 
149
119
  # only keep the last 100 entries
150
120
  feed[:sent].slice!(0...-100)
151
121
 
152
- warn "[#{feed[:title]}] #{sent} items sent" if verbose
122
+ log "#{sent} items sent"
153
123
  sent
154
124
  end
155
125
 
@@ -161,26 +131,30 @@ module RSS2Mail
161
131
  conditions = {}
162
132
  else
163
133
  conditions = case
164
- when etag = feed[:etag]: { 'If-None-Match' => etag }
165
- when mtime = feed[:mtime]: { 'If-Modified-Since' => mtime }
166
- else {}
134
+ when etag = feed[:etag] then { 'If-None-Match' => etag }
135
+ when mtime = feed[:mtime] then { 'If-Modified-Since' => mtime }
136
+ else {}
167
137
  end
168
138
  end
169
139
 
140
+ log conditions.inspect, debug
141
+
170
142
  begin
171
143
  open(feed[:url], conditions) { |uri|
172
144
  case
173
- when etag = uri.meta['etag']: feed[:etag] = etag
174
- when mtime = uri.last_modified: feed[:mtime] = mtime.rfc822
175
- else feed[:updated] = Time.now
145
+ when etag = uri.meta['etag'] then feed[:etag] = etag
146
+ when mtime = uri.last_modified then feed[:mtime] = mtime.rfc822
147
+ else feed[:updated] = Time.now
176
148
  end
177
149
 
178
150
  @content ||= uri.read
179
151
  }
152
+
153
+ log feed.values_at(:etag, :mtime, :updated).inspect, debug
180
154
  rescue OpenURI::HTTPError
181
- warn "[#{feed[:title]}] Feed not found or unchanged" if verbose
155
+ log 'Feed not found or unchanged'
182
156
  rescue Timeout::Error, Errno::ETIMEDOUT, Errno::ECONNRESET => err
183
- warn "[#{feed[:title]}] Error while getting feed: #{err} (#{err.class})"
157
+ error err, 'while getting feed'
184
158
  end
185
159
 
186
160
  @content
@@ -192,7 +166,7 @@ module RSS2Mail
192
166
  if content && @rss ||= begin
193
167
  RSS2Mail::RSS.new(content, simple)
194
168
  rescue SimpleRSSError => err
195
- warn "[#{feed[:title]}] Error while parsing feed: #{err} (#{err.class})"
169
+ error err, 'while parsing feed'
196
170
  end
197
171
  sent = feed[:sent]
198
172
 
@@ -206,18 +180,33 @@ module RSS2Mail
206
180
  }
207
181
  end
208
182
  else
209
- warn "[#{feed[:title]}] Nothing to parse" if verbose
183
+ log 'Nothing to parse'
210
184
  end
211
185
 
212
186
  @rss
213
187
  end
214
188
 
215
- def clean_subject(string)
216
- string.
217
- replace_diacritics.
218
- gsub(SUBSTITUTIONS_RE) { |m| SUBSTITUTIONS[m] }.
219
- to_ascii.
220
- gsub(/'/, "'\\\\''")
189
+ def send_mail(cmd, body)
190
+ return if debug
191
+
192
+ IO.popen(cmd, 'w') { |mail| mail.puts body }
193
+ yield if block_given?
194
+ rescue Errno::EPIPE => err
195
+ error err, 'while sending mail', cmd
196
+ end
197
+
198
+ def log(msg, verbose = verbose)
199
+ warn "[#{feed[:title]}] #{msg}" if verbose
200
+ end
201
+
202
+ def error(err = nil, occasion = nil, extra = nil)
203
+ msg = 'Error'
204
+
205
+ msg << " #{occasion}" if occasion
206
+ msg << ": #{err} (#{err.class})" if err
207
+ msg << " [#{extra}]" if extra
208
+
209
+ log msg, true
221
210
  end
222
211
 
223
212
  end
data/lib/rss2mail/rss.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # encoding: utf-8
2
+
1
3
  #--
2
4
  ###############################################################################
3
5
  # #
4
6
  # A component of rss2mail, the RSS to e-mail forwarder. #
5
7
  # #
6
- # Copyright (C) 2007-2008 Jens Wille #
8
+ # Copyright (C) 2007-2009 Jens Wille #
7
9
  # #
8
10
  # Authors: #
9
11
  # Jens Wille <ww@blackwinter.de> #
@@ -24,15 +26,34 @@
24
26
  ###############################################################################
25
27
  #++
26
28
 
29
+ require 'open-uri'
27
30
  require 'rss'
28
31
 
29
32
  require 'rubygems'
30
33
  require 'simple-rss'
34
+ require 'unidecode'
35
+ require 'nuggets/util/i18n'
36
+
37
+ begin
38
+ require 'hpricot'
39
+ rescue LoadError => err
40
+ warn err
41
+ end
31
42
 
32
43
  module RSS2Mail
33
44
 
34
45
  class RSS
35
46
 
47
+ SUBSTITUTIONS = {
48
+ '–' => '--',
49
+ '«' => '<<',
50
+ '&amp;' => '&'
51
+ }
52
+
53
+ SUBSTITUTIONS_RE = Regexp.union(*SUBSTITUTIONS.keys)
54
+
55
+ TAGS_TO_KEEP = %w[a p br h1 h2 h3 h4]
56
+
36
57
  attr_reader :content, :rss
37
58
 
38
59
  def initialize(content, simple = false)
@@ -60,32 +81,100 @@ module RSS2Mail
60
81
 
61
82
  class Item
62
83
 
63
- ALIASES = {
64
- :title => %w[],
65
- :link => %w[],
66
- :description => %w[summary content],
67
- :date => %w[pubDate updated],
68
- :author => %w[dc_creator]
69
- }
70
-
71
84
  def initialize(item)
72
85
  @item = item
73
86
  end
74
87
 
75
- def method_missing(method, *args, &block)
76
- if aliases = ALIASES[method]
77
- [method, *aliases].each { |name|
78
- begin
79
- res = @item.send(name)
80
- return res if res
81
- rescue NoMethodError
82
- end
88
+ def title
89
+ @title ||= value_for(:title, :content)
90
+ end
91
+
92
+ def link
93
+ @link ||= value_for(:link, :href)
94
+ end
95
+
96
+ def description(unescape_html = false)
97
+ @description ||= get_description(unescape_html)
98
+ end
99
+
100
+ def date
101
+ @date ||= value_for({ :date => %w[pubDate updated dc_date] }, :content) { |field, value|
102
+ field == 'updated' && value.respond_to?(:content) ? Time.at(value.content.to_i) : value
103
+ }
104
+ end
105
+
106
+ def author
107
+ @author ||= value_for({ :author => %w[contributor dc_creator] }, %w[name content])
108
+ end
109
+
110
+ def body(tag = nil, encoding = nil)
111
+ @body ||= get_body(tag, encoding)
112
+ end
113
+
114
+ def subject
115
+ @subject ||= title ? clean_subject(title) : 'NO TITLE'
116
+ end
117
+
118
+ private
119
+
120
+ def value_for(field, methods = nil, &block)
121
+ value = get_value_for(field, &block)
122
+
123
+ if methods
124
+ [*methods].each { |method|
125
+ break unless value.respond_to?(method)
126
+ value = value.send(method)
83
127
  }
128
+ end
129
+
130
+ value.respond_to?(:strip) ? value.strip : value
131
+ end
132
+
133
+ def get_value_for(fields, &block)
134
+ fields = fields.is_a?(Hash) ? fields.to_a.flatten : [*fields]
135
+
136
+ fields.each { |field|
137
+ begin
138
+ value = @item.send(field)
139
+ value = block[field, value] if block
140
+ return value if value
141
+ rescue NoMethodError
142
+ end
143
+ }
144
+
145
+ nil
146
+ end
84
147
 
85
- nil
86
- else
87
- super
148
+ def get_description(unescape_html)
149
+ description = value_for({ :description => %w[summary content] }, :content)
150
+
151
+ if description && unescape_html
152
+ description.gsub!(/&lt;/, '<')
153
+ description.gsub!(/&gt;/, '>')
154
+ end
155
+
156
+ description
157
+ end
158
+
159
+ def get_body(tag, encoding)
160
+ body = case tag
161
+ when nil then return
162
+ when true then open(link).read
163
+ else defined?(Hpricot) ? Hpricot(open(link)).at(tag).to_s : open(link).read
88
164
  end
165
+
166
+ body.gsub!(/<\/?(.*?)>/) { |m| m if TAGS_TO_KEEP.include?($1.split.first.downcase) }
167
+ body.gsub!(/<a\s+href=['"](?!http:).*?>(.*?)<\/a>/mi, '\1')
168
+
169
+ encoding ? Iconv.conv('UTF-8', encoding, body) : body
170
+ end
171
+
172
+ def clean_subject(string)
173
+ string.
174
+ replace_diacritics.
175
+ gsub(SUBSTITUTIONS_RE) { |m| SUBSTITUTIONS[m] }.
176
+ to_ascii.
177
+ gsub(/'/, "'\\\\''")
89
178
  end
90
179
 
91
180
  end
data/lib/rss2mail/util.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # #
4
4
  # A component of rss2mail, the RSS to e-mail forwarder. #
5
5
  # #
6
- # Copyright (C) 2007-2008 Jens Wille #
6
+ # Copyright (C) 2007-2009 Jens Wille #
7
7
  # #
8
8
  # Authors: #
9
9
  # Jens Wille <ww@blackwinter.de> #
@@ -4,7 +4,7 @@ module RSS2Mail
4
4
 
5
5
  MAJOR = 0
6
6
  MINOR = 0
7
- TINY = 1
7
+ TINY = 2
8
8
 
9
9
  class << self
10
10
 
data/lib/rss2mail.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # #
4
4
  # rss2mail -- Send RSS feeds as e-mail #
5
5
  # #
6
- # Copyright (C) 2007-2008 Jens Wille #
6
+ # Copyright (C) 2007-2009 Jens Wille #
7
7
  # #
8
8
  # Authors: #
9
9
  # Jens Wille <ww@blackwinter.de> #
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <body>
3
+ <%= title %><% if date %> / <%= date %><% end %><% if author %> [<%= author %>]<% end %><br />
4
+ <% if description %><br />
5
+ <%= description %><br />
6
+ <% end %><br />
7
+ <% if body %><br />
8
+ <%= body %><br />
9
+ <% end %><br />
10
+ &lt;<%= link %>&gt;
11
+ </body>
12
+ </html>
@@ -0,0 +1,8 @@
1
+ <%= title %><% if date %> / <%= date %><% end %><% if author %> [<%= author %>]<% end %>
2
+ <% if description %>
3
+ <%= description %>
4
+ <% end %>
5
+ <% if body %>
6
+ <%= body %>
7
+ <% end %>
8
+ <<%= link %>>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rss2mail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Wille
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-19 00:00:00 +01:00
12
+ date: 2009-03-02 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -73,17 +73,19 @@ files:
73
73
  - COPYING
74
74
  - ChangeLog
75
75
  - README
76
+ - templates/plain.erb
77
+ - templates/html.erb
76
78
  - example/feeds.yaml
77
79
  has_rdoc: true
78
80
  homepage: http://rss2mail.rubyforge.org/
79
81
  post_install_message:
80
82
  rdoc_options:
81
83
  - --line-numbers
84
+ - --main
85
+ - README
82
86
  - --inline-source
83
87
  - --title
84
88
  - rss2mail Application documentation
85
- - --main
86
- - README
87
89
  - --charset
88
90
  - UTF-8
89
91
  - --all