feed2email 0.7.0 → 0.8.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +124 -59
- data/bin/f2e +9 -0
- data/bin/feed2email +4 -6
- data/bin/feed2email-migrate-feedlist +36 -0
- data/lib/feed2email.rb +16 -11
- data/lib/feed2email/cli.rb +158 -0
- data/lib/feed2email/config.rb +8 -3
- data/lib/feed2email/configurable.rb +7 -0
- data/lib/feed2email/feed.rb +87 -99
- data/lib/feed2email/feed_autodiscoverer.rb +55 -0
- data/lib/feed2email/feed_history.rb +63 -9
- data/lib/feed2email/feed_list.rb +147 -0
- data/lib/feed2email/lazy_smtp_connection.rb +6 -5
- data/lib/feed2email/loggable.rb +7 -0
- data/lib/feed2email/mail.rb +23 -39
- data/lib/feed2email/redirection_checker.rb +38 -0
- data/lib/feed2email/version.rb +1 -1
- metadata +61 -15
- data/TODO.md +0 -16
- data/lib/feed2email/feed_data_file.rb +0 -65
- data/lib/feed2email/feed_meta.rb +0 -28
- data/lib/feed2email/feeds.rb +0 -77
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'thor'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Feed2Email
|
6
|
+
class FeedList
|
7
|
+
class DuplicateFeedError < StandardError; end
|
8
|
+
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@path = path
|
13
|
+
@dirty = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def_delegators :data, :size, :each, :each_with_index, :empty?, :[]
|
17
|
+
|
18
|
+
def <<(uri)
|
19
|
+
if index = find_feed_by_uri(uri)
|
20
|
+
raise DuplicateFeedError, "Feed #{uri} already exists at index #{index}"
|
21
|
+
end
|
22
|
+
|
23
|
+
mark_dirty
|
24
|
+
data << { uri: uri, enabled: true }
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear_fetch_cache(index)
|
28
|
+
mark_dirty
|
29
|
+
data[index].delete(:last_modified)
|
30
|
+
data[index].delete(:etag)
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_at(index)
|
34
|
+
if deleted = data.delete_at(index)
|
35
|
+
mark_dirty
|
36
|
+
deleted
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def include?(uri)
|
41
|
+
!find_feed_by_uri(uri).nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def process
|
45
|
+
require 'feed2email/feed'
|
46
|
+
|
47
|
+
begin
|
48
|
+
each do |meta|
|
49
|
+
next unless meta[:enabled]
|
50
|
+
|
51
|
+
feed = Feed.new(meta)
|
52
|
+
processed = feed.process
|
53
|
+
sync_feed_meta(feed.meta, meta, processed)
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
smtp_connection.finalize
|
57
|
+
end
|
58
|
+
|
59
|
+
sync
|
60
|
+
end
|
61
|
+
|
62
|
+
def sync
|
63
|
+
if dirty?
|
64
|
+
open(path, 'w') {|f| f.write(to_yaml) } > 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
return 'Empty feed list' if empty?
|
70
|
+
|
71
|
+
justify = size.to_s.size
|
72
|
+
disabled = Thor::Shell::Color.new.set_color('DISABLED', :red)
|
73
|
+
|
74
|
+
each_with_index.map do |feed, i|
|
75
|
+
'%{index}: %{disabled}%{uri}' % {
|
76
|
+
index: i.to_s.rjust(justify),
|
77
|
+
disabled: feed[:enabled] ? '' : "#{disabled} ",
|
78
|
+
uri: feed[:uri]
|
79
|
+
}
|
80
|
+
end.join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
def toggle(index)
|
84
|
+
if data[index]
|
85
|
+
mark_dirty
|
86
|
+
data[index][:enabled] = !data[index][:enabled]
|
87
|
+
true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def data
|
94
|
+
return @data if @data
|
95
|
+
|
96
|
+
if File.exist?(path)
|
97
|
+
@data = YAML.load(read_file)
|
98
|
+
else
|
99
|
+
@data = []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def dirty?; @dirty end
|
104
|
+
|
105
|
+
def find_feed_by_uri(uri)
|
106
|
+
data.index {|feed| feed[:uri] == uri }
|
107
|
+
end
|
108
|
+
|
109
|
+
def mark_dirty
|
110
|
+
@dirty = true
|
111
|
+
end
|
112
|
+
|
113
|
+
def path; @path end
|
114
|
+
|
115
|
+
def read_file
|
116
|
+
File.read(path)
|
117
|
+
end
|
118
|
+
|
119
|
+
def smtp_connection
|
120
|
+
Feed2Email.smtp_connection # delegate
|
121
|
+
end
|
122
|
+
|
123
|
+
def sync_feed_meta(src, dest, processed)
|
124
|
+
if processed
|
125
|
+
if src[:last_modified]
|
126
|
+
mark_dirty
|
127
|
+
dest[:last_modified] = src[:last_modified]
|
128
|
+
end
|
129
|
+
|
130
|
+
if src[:etag]
|
131
|
+
mark_dirty
|
132
|
+
dest[:etag] = src[:etag]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Check for permanent redirection and persist
|
137
|
+
if dest[:uri] != src[:uri]
|
138
|
+
mark_dirty
|
139
|
+
dest[:uri] = src[:uri]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_yaml
|
144
|
+
data.to_yaml # delegate
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'net/smtp'
|
2
|
+
require 'feed2email/configurable'
|
2
3
|
|
3
4
|
module Feed2Email
|
4
5
|
class LazySMTPConnection
|
5
|
-
|
6
|
-
Feed2Email.config # delegate
|
7
|
-
end
|
6
|
+
include Configurable
|
8
7
|
|
9
8
|
def connect
|
10
9
|
smtp.start('localhost', config['smtp_user'], config['smtp_pass'],
|
@@ -19,11 +18,13 @@ module Feed2Email
|
|
19
18
|
smtp.finish if connected?
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
21
|
+
def sendmail(*args, &block)
|
23
22
|
connect unless connected?
|
24
|
-
smtp.
|
23
|
+
smtp.sendmail(*args, &block) # delegate
|
25
24
|
end
|
26
25
|
|
26
|
+
private
|
27
|
+
|
27
28
|
def smtp
|
28
29
|
return @smtp if @smtp
|
29
30
|
@smtp = Net::SMTP.new(config['smtp_host'], config['smtp_port'])
|
data/lib/feed2email/mail.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
require 'mail'
|
2
|
+
require 'feed2email/configurable'
|
2
3
|
require 'feed2email/version'
|
3
4
|
|
4
5
|
module Feed2Email
|
5
6
|
class Mail
|
7
|
+
include Configurable
|
8
|
+
|
6
9
|
def initialize(entry, feed_title)
|
7
10
|
@entry = entry
|
8
11
|
@feed_title = feed_title
|
9
12
|
end
|
10
13
|
|
11
14
|
def send
|
12
|
-
|
13
|
-
send_with_smtp
|
14
|
-
else
|
15
|
-
send_with_sendmail
|
16
|
-
end
|
15
|
+
build_mail.deliver!
|
17
16
|
end
|
18
17
|
|
19
18
|
private
|
@@ -43,31 +42,37 @@ module Feed2Email
|
|
43
42
|
body_html.to_markdown
|
44
43
|
end
|
45
44
|
|
46
|
-
def
|
47
|
-
Feed2Email.config # delegate
|
48
|
-
end
|
49
|
-
|
50
|
-
def entry; @entry end
|
51
|
-
|
52
|
-
def feed_title; @feed_title end
|
53
|
-
|
54
|
-
def mail
|
45
|
+
def build_mail
|
55
46
|
::Mail.new.tap do |m|
|
56
47
|
m.from = %{"#{feed_title}" <#{config['sender']}>}
|
57
48
|
m.to = config['recipient']
|
58
49
|
m.subject = entry.title.strip_html
|
59
|
-
m.html_part =
|
60
|
-
m.text_part =
|
61
|
-
|
50
|
+
m.html_part = build_mail_part('text/html', body_html)
|
51
|
+
m.text_part = build_mail_part('text/plain', body_text)
|
52
|
+
|
53
|
+
m.delivery_method(*delivery_method_params)
|
54
|
+
end
|
62
55
|
end
|
63
56
|
|
64
|
-
def
|
57
|
+
def build_mail_part(content_type, body)
|
65
58
|
part = ::Mail::Part.new
|
66
59
|
part.content_type = "#{content_type}; charset=UTF-8"
|
67
60
|
part.body = body
|
68
61
|
part
|
69
62
|
end
|
70
63
|
|
64
|
+
def delivery_method_params
|
65
|
+
if config.smtp_configured?
|
66
|
+
[:smtp_connection, connection: Feed2Email.smtp_connection]
|
67
|
+
else
|
68
|
+
[:sendmail, location: config['sendmail_path']]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def entry; @entry end
|
73
|
+
|
74
|
+
def feed_title; @feed_title end
|
75
|
+
|
71
76
|
def published
|
72
77
|
return nil unless entry.author || entry.published
|
73
78
|
text = 'Published'
|
@@ -75,26 +80,5 @@ module Feed2Email
|
|
75
80
|
text << " at #{entry.published}" if entry.published
|
76
81
|
text
|
77
82
|
end
|
78
|
-
|
79
|
-
def send_with_sendmail
|
80
|
-
open("|#{config['sendmail_path']} #{config['recipient']}", 'w') do |f|
|
81
|
-
f.write(mail)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def send_with_smtp
|
86
|
-
smtp_connection.send_message(mail, config['sender'], config['recipient'])
|
87
|
-
end
|
88
|
-
|
89
|
-
def smtp_configured?
|
90
|
-
config['smtp_host'] &&
|
91
|
-
config['smtp_port'] &&
|
92
|
-
config['smtp_user'] &&
|
93
|
-
config['smtp_pass']
|
94
|
-
end
|
95
|
-
|
96
|
-
def smtp_connection
|
97
|
-
Feed2Email.smtp_connection
|
98
|
-
end
|
99
83
|
end
|
100
84
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Feed2Email
|
5
|
+
class RedirectionChecker
|
6
|
+
def initialize(uri)
|
7
|
+
@uri = uri
|
8
|
+
check
|
9
|
+
end
|
10
|
+
|
11
|
+
def location; @location end
|
12
|
+
|
13
|
+
def permanently_redirected?
|
14
|
+
redirected? && code == 301
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def check
|
20
|
+
parsed_uri = URI.parse(uri)
|
21
|
+
http = Net::HTTP.new(parsed_uri.host, parsed_uri.port)
|
22
|
+
http.use_ssl = (parsed_uri.scheme == 'https')
|
23
|
+
response = http.head(parsed_uri.request_uri)
|
24
|
+
@code = response.code.to_i
|
25
|
+
@location = response['location']
|
26
|
+
end
|
27
|
+
|
28
|
+
def code; @code end
|
29
|
+
|
30
|
+
def redirected?
|
31
|
+
[301, 302].include?(code) &&
|
32
|
+
location != uri && # prevent redirection to the same location
|
33
|
+
location =~ %r{\Ahttps?://} # sanitize location
|
34
|
+
end
|
35
|
+
|
36
|
+
def uri; @uri end
|
37
|
+
end
|
38
|
+
end
|
data/lib/feed2email/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feed2email
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aggelos Orfanakos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: feedzirra
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.6.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.19.1
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.19.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: nokogiri
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.6.5
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.6.5
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: rspec
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,6 +167,8 @@ dependencies:
|
|
139
167
|
description:
|
140
168
|
email: me@agorf.gr
|
141
169
|
executables:
|
170
|
+
- f2e
|
171
|
+
- feed2email-migrate-feedlist
|
142
172
|
- feed2email-migrate-history
|
143
173
|
- feed2email
|
144
174
|
extensions: []
|
@@ -147,21 +177,25 @@ files:
|
|
147
177
|
- CHANGELOG.md
|
148
178
|
- LICENSE.txt
|
149
179
|
- README.md
|
150
|
-
-
|
180
|
+
- bin/f2e
|
151
181
|
- bin/feed2email
|
182
|
+
- bin/feed2email-migrate-feedlist
|
152
183
|
- bin/feed2email-migrate-history
|
153
184
|
- lib/feed2email.rb
|
185
|
+
- lib/feed2email/cli.rb
|
154
186
|
- lib/feed2email/config.rb
|
187
|
+
- lib/feed2email/configurable.rb
|
155
188
|
- lib/feed2email/core_ext.rb
|
156
189
|
- lib/feed2email/entry.rb
|
157
190
|
- lib/feed2email/feed.rb
|
158
|
-
- lib/feed2email/
|
191
|
+
- lib/feed2email/feed_autodiscoverer.rb
|
159
192
|
- lib/feed2email/feed_history.rb
|
160
|
-
- lib/feed2email/
|
161
|
-
- lib/feed2email/feeds.rb
|
193
|
+
- lib/feed2email/feed_list.rb
|
162
194
|
- lib/feed2email/lazy_smtp_connection.rb
|
195
|
+
- lib/feed2email/loggable.rb
|
163
196
|
- lib/feed2email/logger.rb
|
164
197
|
- lib/feed2email/mail.rb
|
198
|
+
- lib/feed2email/redirection_checker.rb
|
165
199
|
- lib/feed2email/version.rb
|
166
200
|
homepage: https://github.com/agorf/feed2email
|
167
201
|
licenses:
|
@@ -169,18 +203,30 @@ licenses:
|
|
169
203
|
metadata: {}
|
170
204
|
post_install_message: |2+
|
171
205
|
|
172
|
-
|
206
|
+
Important changes since version 0.6.0:
|
207
|
+
|
208
|
+
* Each feed has its own history file. Please run the provided migration script
|
209
|
+
to migrate your history before using feed2email: feed2email-migrate-history
|
210
|
+
If you don't, feed2email will think it is run for the first time and will
|
211
|
+
treat all entries as old (thus no email will be sent and you may miss some
|
212
|
+
entries).
|
213
|
+
|
214
|
+
* `sender` is a required config option. Please update your config file
|
215
|
+
(~/.feed2email/config.yml) to set it to an email address to send email from.
|
216
|
+
|
217
|
+
Important changes since version 0.8.0:
|
218
|
+
|
219
|
+
* Feed metadata is stored in the feed list (~/.feed2email/feeds.yml). Please run
|
220
|
+
the provided migration script before using feed2email:
|
221
|
+
`feed2email-migrate-feedlist`
|
173
222
|
|
174
|
-
|
175
|
-
using feed2email. This will split an existing single history file to many small
|
176
|
-
ones, one for each feed.
|
223
|
+
It is safe to remove any feed meta files: rm ~/.feed2email/meta-*.yml
|
177
224
|
|
178
|
-
|
179
|
-
|
180
|
-
|
225
|
+
* A command-line interface is available. Running feed2email without any options
|
226
|
+
will display some help text. To run it so that it processes your feed list,
|
227
|
+
issue: `feed2email process`
|
181
228
|
|
182
|
-
|
183
|
-
file by setting it to an email address to send email from.
|
229
|
+
Don't forget to update any cron jobs too!
|
184
230
|
|
185
231
|
rdoc_options: []
|
186
232
|
require_paths:
|