rss2mail 0.1.2 → 0.1.3
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 +1 -1
- data/lib/rss2mail/feed.rb +73 -64
- data/lib/rss2mail/rss.rb +18 -9
- data/lib/rss2mail/version.rb +1 -1
- metadata +2 -2
data/README
CHANGED
data/lib/rss2mail/feed.rb
CHANGED
@@ -25,6 +25,7 @@
|
|
25
25
|
#++
|
26
26
|
|
27
27
|
require 'erb'
|
28
|
+
require 'open3'
|
28
29
|
require 'nuggets/file/which'
|
29
30
|
require 'nuggets/string/evaluate'
|
30
31
|
|
@@ -34,9 +35,15 @@ module RSS2Mail
|
|
34
35
|
|
35
36
|
include Util
|
36
37
|
|
37
|
-
|
38
|
+
unless MAIL = File.which(mail = 'mail'.freeze)
|
39
|
+
class << MAIL; self; end.send(:define_method, :to_s) { mail }
|
40
|
+
end
|
41
|
+
|
42
|
+
HOST = ENV['HOSTNAME'] || ENV['HOST'] || %x{hostname}.chomp.freeze
|
43
|
+
|
44
|
+
FROM = "From: rss2mail@#{HOST}".freeze
|
38
45
|
|
39
|
-
|
46
|
+
KEEP = 100
|
40
47
|
|
41
48
|
def initialize(feed, options = {})
|
42
49
|
raise TypeError, "Hash expected, got #{feed.class}" unless feed.is_a?(Hash)
|
@@ -50,19 +57,21 @@ module RSS2Mail
|
|
50
57
|
@debug = options[:debug]
|
51
58
|
|
52
59
|
required = [:url, :to, :title]
|
53
|
-
required.delete_if { |
|
60
|
+
required.delete_if { |key| feed.has_key?(key) }
|
54
61
|
|
55
|
-
|
62
|
+
unless required.empty?
|
63
|
+
raise ArgumentError, "Feed incomplete: #{required.join(', ')} missing"
|
64
|
+
end
|
56
65
|
end
|
57
66
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
67
|
+
attr_reader :feed, :simple, :updated,
|
68
|
+
:reload, :verbose, :debug,
|
69
|
+
:content, :rss
|
62
70
|
|
63
|
-
|
71
|
+
def deliver(templates)
|
72
|
+
raise "Mail command not found: #{MAIL}" unless MAIL
|
64
73
|
|
65
|
-
if to.empty?
|
74
|
+
if (to = Array(feed[:to])).empty?
|
66
75
|
log 'No one to send to'
|
67
76
|
return
|
68
77
|
end
|
@@ -72,58 +81,38 @@ module RSS2Mail
|
|
72
81
|
return
|
73
82
|
end
|
74
83
|
|
75
|
-
if rss.items.empty?
|
84
|
+
if (items = rss.items).empty?
|
76
85
|
log 'No new items'
|
77
86
|
return
|
78
87
|
end
|
79
88
|
|
80
|
-
|
81
|
-
encoding
|
82
|
-
|
83
|
-
feed[:sent] ||= []
|
89
|
+
type = feed[:content_type] || 'text/html'
|
90
|
+
encoding = feed[:encoding] || 'UTF-8'
|
84
91
|
|
85
|
-
|
92
|
+
type_header = "Content-type: #{type}; charset=#{encoding}"
|
86
93
|
|
87
|
-
unless template = templates[
|
88
|
-
log "Template not found: #{
|
94
|
+
unless template = templates[type[/\/(.*)/, 1]]
|
95
|
+
log "Template not found: #{type}"
|
89
96
|
return
|
90
97
|
end
|
91
98
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
sent = 0
|
102
|
-
|
103
|
-
rss.items.each { |item|
|
104
|
-
title = item.title
|
105
|
-
link = item.link
|
106
|
-
description = item.description(feed[:unescape_html])
|
107
|
-
date = item.date
|
108
|
-
author = item.author
|
109
|
-
body = item.body(feed[:body])
|
110
|
-
subject = item.subject
|
111
|
-
|
112
|
-
log "#{title} / #{date} [#{author}]", debug
|
113
|
-
log "<#{link}>", debug
|
114
|
-
|
115
|
-
send_mail(cmd.evaluate(binding), ERB.new(template).result(binding)) {
|
116
|
-
feed[:sent] << link
|
117
|
-
sent += 1
|
99
|
+
sent = feed[:sent] ||= []
|
100
|
+
count, title = 0, feed[:title]
|
101
|
+
|
102
|
+
items.each { |item|
|
103
|
+
link, subject, body = render(feed, item, template)
|
104
|
+
|
105
|
+
send_mail(type_header, to, title, subject, body) {
|
106
|
+
sent << link
|
107
|
+
count += 1
|
118
108
|
}
|
119
109
|
}
|
120
110
|
|
121
|
-
|
122
|
-
|
123
|
-
feed[:sent].slice!(0...-100)
|
111
|
+
sent.uniq!
|
112
|
+
sent.slice!(0...-KEEP)
|
124
113
|
|
125
|
-
log "#{
|
126
|
-
|
114
|
+
log "#{count} items sent"
|
115
|
+
count
|
127
116
|
end
|
128
117
|
|
129
118
|
private
|
@@ -169,25 +158,25 @@ module RSS2Mail
|
|
169
158
|
error err, 'while getting feed'
|
170
159
|
end
|
171
160
|
|
172
|
-
|
161
|
+
content
|
173
162
|
end
|
174
163
|
|
175
164
|
def parse(reload = reload)
|
176
165
|
@rss = nil if reload
|
177
166
|
|
178
|
-
if content
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
167
|
+
if content
|
168
|
+
@rss ||= RSS.feed(content, simple) { |err|
|
169
|
+
error err, 'while parsing feed'
|
170
|
+
}
|
171
|
+
|
172
|
+
if rss && !reload
|
173
|
+
sent = feed[:sent]
|
184
174
|
|
185
|
-
|
186
|
-
@rss.items.delete_if { |item|
|
175
|
+
rss.items.delete_if { |item|
|
187
176
|
if updated && date = item.date
|
188
177
|
date <= updated
|
189
|
-
|
190
|
-
sent
|
178
|
+
elsif sent
|
179
|
+
sent.include?(item.link)
|
191
180
|
end
|
192
181
|
}
|
193
182
|
end
|
@@ -195,15 +184,35 @@ module RSS2Mail
|
|
195
184
|
log 'Nothing to parse'
|
196
185
|
end
|
197
186
|
|
198
|
-
|
187
|
+
rss
|
199
188
|
end
|
200
189
|
|
201
|
-
def
|
190
|
+
def render(feed, item, template)
|
191
|
+
title = item.title
|
192
|
+
link = item.link
|
193
|
+
description = item.description(feed[:unescape_html])
|
194
|
+
date = item.date
|
195
|
+
author = item.author
|
196
|
+
body = item.body(feed[:body])
|
197
|
+
subject = item.subject
|
198
|
+
|
199
|
+
log "#{title} / #{date} [#{author}]", debug
|
200
|
+
log "<#{link}>", debug
|
201
|
+
|
202
|
+
[link, subject, ERB.new(template).result(binding)]
|
203
|
+
end
|
204
|
+
|
205
|
+
def send_mail(type_header, to, title, subject, body)
|
202
206
|
return if debug
|
203
207
|
|
204
|
-
|
208
|
+
Open3.popen3(MAIL, '-e', '-a', type_header, '-a', FROM,
|
209
|
+
'-s', "[#{title}] #{subject}", *to) { |mail, _, _|
|
210
|
+
mail.puts body
|
211
|
+
mail.close
|
212
|
+
}
|
213
|
+
|
205
214
|
yield if block_given?
|
206
|
-
rescue Errno::EPIPE => err
|
215
|
+
rescue Errno::EPIPE, IOError => err
|
207
216
|
error err, 'while sending mail', cmd
|
208
217
|
end
|
209
218
|
|
data/lib/rss2mail/rss.rb
CHANGED
@@ -43,20 +43,28 @@ module RSS2Mail
|
|
43
43
|
|
44
44
|
include Util
|
45
45
|
|
46
|
-
|
46
|
+
SUB = {
|
47
47
|
'–' => '--',
|
48
48
|
'«' => '<<',
|
49
49
|
'&' => '&'
|
50
50
|
}
|
51
51
|
|
52
|
-
|
52
|
+
SUB_RE = Regexp.union(*SUB.keys)
|
53
53
|
|
54
|
-
|
54
|
+
KEEP = %w[a p br h1 h2 h3 h4]
|
55
55
|
|
56
|
-
|
56
|
+
class << self
|
57
|
+
|
58
|
+
def parse(url, *args)
|
59
|
+
new(open_feed(url), *args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def feed(*args)
|
63
|
+
new(*args)
|
64
|
+
rescue ::SimpleRSSError, ::RSS::NotWellFormedError => err
|
65
|
+
yield err if block_given?
|
66
|
+
end
|
57
67
|
|
58
|
-
def self.parse(url, *args)
|
59
|
-
new(open_feed(url), *args)
|
60
68
|
end
|
61
69
|
|
62
70
|
def initialize(content, simple = false)
|
@@ -66,6 +74,8 @@ module RSS2Mail
|
|
66
74
|
@rss = simple ? simple_parse : parse
|
67
75
|
end
|
68
76
|
|
77
|
+
attr_reader :content, :rss
|
78
|
+
|
69
79
|
def simple?
|
70
80
|
@simple
|
71
81
|
end
|
@@ -172,7 +182,7 @@ module RSS2Mail
|
|
172
182
|
else raise ArgumentError, "don't know how to handle tag of type #{tag.class}"
|
173
183
|
end
|
174
184
|
|
175
|
-
body.gsub!(/<\/?(.*?)>/) { |m| m if
|
185
|
+
body.gsub!(/<\/?(.*?)>/) { |m| m if KEEP.include?($1.split.first.downcase) }
|
176
186
|
body.gsub!(/<a\s+href=['"](?!http:).*?>(.*?)<\/a>/mi, '\1')
|
177
187
|
|
178
188
|
encoding ? Iconv.conv('UTF-8', encoding, body) : body
|
@@ -189,8 +199,7 @@ module RSS2Mail
|
|
189
199
|
|
190
200
|
def clean_subject(str)
|
191
201
|
str.replace_diacritics!
|
192
|
-
str.gsub!(
|
193
|
-
str.gsub!(/'/, "'\\\\''")
|
202
|
+
str.gsub!(SUB_RE) { |m| SUB[m] }
|
194
203
|
str.to_ascii
|
195
204
|
end
|
196
205
|
|
data/lib/rss2mail/version.rb
CHANGED
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.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -107,7 +107,7 @@ rdoc_options:
|
|
107
107
|
- --line-numbers
|
108
108
|
- --all
|
109
109
|
- --title
|
110
|
-
- rss2mail Application documentation (v0.1.
|
110
|
+
- rss2mail Application documentation (v0.1.3)
|
111
111
|
- --main
|
112
112
|
- README
|
113
113
|
require_paths:
|