feedigest 0.0.1 → 0.1.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 +53 -0
- data/README.md +117 -0
- data/bin/feedigest +23 -0
- data/lib/feedigest.rb +5 -191
- data/lib/feedigest/feed_fetcher.rb +55 -0
- data/lib/feedigest/mail_composer.rb +103 -0
- data/lib/feedigest/mail_sender.rb +52 -0
- data/lib/feedigest/options.rb +23 -0
- data/lib/feedigest/version.rb +3 -0
- metadata +26 -21
- data/bin/feedigest-generate +0 -9
- data/bin/feedigest-send +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 401be82539f92da17495ec1bbdc38e9308ee2c1d2bf1d991dd2440f8c7ce9dfd
|
4
|
+
data.tar.gz: 9ed6fdf1eb2ddfef7862cf3de2cf0052ed38d02eeb3f7a2e054692163234dc07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52813c1c6d54b757a5173673d65d436ce5418222314ddac2ddeb7c9b811089e72efbf6a194bf55e0c761cb7ee8d45a7fd86db180c867c8fa98ed160ef3ebc483
|
7
|
+
data.tar.gz: b8bb424b7a4fd0b7fcede8ccea5709b4ec88f666de8faf5fe8e5916123ae82c70e4f66a3c8960a677e06e6662bf8f3521b408fdef41da02762fcfb4c53544815
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog][] and this project adheres to
|
6
|
+
[Semantic Versioning][].
|
7
|
+
|
8
|
+
## [Unreleased][]
|
9
|
+
|
10
|
+
## [0.1.0][] - 2018-07-04
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Bump version to 0.1.0 to signify 0.0.2 (which should have been 0.1.0) has
|
15
|
+
breaking changes.
|
16
|
+
|
17
|
+
## [0.0.3][] - 2018-07-03 [YANKED]
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- Use subcommands instead of symlinks to feedigest binary.
|
22
|
+
- Do not require SMTP options when printing the email.
|
23
|
+
|
24
|
+
## [0.0.2][] - 2018-06-29 [YANKED]
|
25
|
+
|
26
|
+
### Added
|
27
|
+
|
28
|
+
- README file.
|
29
|
+
- This CHANGELOG file.
|
30
|
+
- Signature in digest email.
|
31
|
+
- `bundler-audit` as a development dependency.
|
32
|
+
|
33
|
+
### Removed
|
34
|
+
|
35
|
+
- `FEEDIGEST_DELIVERY_METHOD` option. Now only SMTP is supported.
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
- Clean up and refactor code.
|
40
|
+
- `dotenv` to be a development dependency.
|
41
|
+
- `FEEDIGEST_SMTP_HOST` option to `FEEDIGEST_SMTP_ADDRESS`
|
42
|
+
- `feedigest-generate` command to `feedigest-print`
|
43
|
+
|
44
|
+
## 0.0.1 - 2018-06-12
|
45
|
+
|
46
|
+
Initial release.
|
47
|
+
|
48
|
+
[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/
|
49
|
+
[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
|
50
|
+
[0.0.2]: https://github.com/agorf/feedigest/compare/0.0.1...0.0.2
|
51
|
+
[0.0.3]: https://github.com/agorf/feedigest/compare/0.0.2...0.0.3
|
52
|
+
[0.1.0]: https://github.com/agorf/feedigest/compare/0.0.3...0.1.0
|
53
|
+
[Unreleased]: https://github.com/agorf/feedigest/compare/0.1.0...HEAD
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# feedigest [](http://badge.fury.io/rb/feedigest)
|
2
|
+
|
3
|
+
feedigest is a [headless][] [RSS][]/[Atom][] feed aggregator that sends feed
|
4
|
+
updates as a single digest email.
|
5
|
+
|
6
|
+
It was written as a simpler alternative to [feed2email][].
|
7
|
+
|
8
|
+
[headless]: http://en.wikipedia.org/wiki/Headless_software
|
9
|
+
[RSS]: http://www.rssboard.org/rss-specification
|
10
|
+
[Atom]: https://tools.ietf.org/html/rfc4287
|
11
|
+
[feed2email]: https://github.com/agorf/feed2email
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
As a [gem][] from [RubyGems][]:
|
16
|
+
|
17
|
+
~~~ sh
|
18
|
+
gem install feedigest
|
19
|
+
~~~
|
20
|
+
|
21
|
+
If the above command fails, make sure the following system packages are
|
22
|
+
installed. For Debian/Ubuntu, issue as root:
|
23
|
+
|
24
|
+
~~~ sh
|
25
|
+
apt-get install build-essential patch ruby-dev zlib1g-dev liblzma-dev
|
26
|
+
~~~
|
27
|
+
|
28
|
+
[gem]: http://rubygems.org/gems/feedigest
|
29
|
+
[RubyGems]: http://rubygems.org/
|
30
|
+
|
31
|
+
## Configuration
|
32
|
+
|
33
|
+
feedigest is configured through shell environment variables. Instead of passing
|
34
|
+
them one by one when you run it, you can store them in a separate file and
|
35
|
+
source it to make them available:
|
36
|
+
|
37
|
+
~~~ sh
|
38
|
+
$ mkdir -p ~/.feedigest
|
39
|
+
$ touch ~/.feedigest/env
|
40
|
+
$ chmod 600 ~/.feedigest/env
|
41
|
+
$ cat > ~/.feedigest/env
|
42
|
+
FEEDIGEST_EMAIL_RECIPIENT=me@mydomain.com
|
43
|
+
...
|
44
|
+
^D
|
45
|
+
~~~
|
46
|
+
|
47
|
+
**Note:** In the example above, `^D` stands for pressing `Ctrl-D`.
|
48
|
+
|
49
|
+
The following environment variables are supported:
|
50
|
+
|
51
|
+
* `FEEDIGEST_ENTRY_WINDOW` (default: `86400`) the maximum age, in seconds, of
|
52
|
+
entries to include in the digest
|
53
|
+
* `FEEDIGEST_EMAIL_SENDER` (default: `feedigest@hostname`) the "from" address in
|
54
|
+
the email
|
55
|
+
* `FEEDIGEST_EMAIL_RECIPIENT` the email address to send the email to
|
56
|
+
|
57
|
+
feedigest uses SMTP to send emails. You can get a free plan from [Mailgun][].
|
58
|
+
The relevant environment variables are:
|
59
|
+
|
60
|
+
[Mailgun]: http://www.mailgun.com/
|
61
|
+
|
62
|
+
* `FEEDIGEST_SMTP_ADDRESS`
|
63
|
+
* `FEEDIGEST_SMTP_PORT` (default: `587`)
|
64
|
+
* `FEEDIGEST_SMTP_USERNAME`
|
65
|
+
* `FEEDIGEST_SMTP_PASSWORD`
|
66
|
+
* `FEEDIGEST_SMTP_AUTH` (default: `plain`)
|
67
|
+
* `FEEDIGEST_SMTP_STARTTLS` (default: `true`)
|
68
|
+
|
69
|
+
Finally, you need to provide to the standard input (stdin) of feedigest, a
|
70
|
+
line-separated list of feed URLs:
|
71
|
+
|
72
|
+
~~~ sh
|
73
|
+
$ cat > ~/.feedigest/feeds.txt
|
74
|
+
https://github.com/agorf/feed2email/commits.atom
|
75
|
+
https://github.com/agorf.atom
|
76
|
+
...
|
77
|
+
^D
|
78
|
+
~~~
|
79
|
+
|
80
|
+
## Use
|
81
|
+
|
82
|
+
~~~ sh
|
83
|
+
export $(cat ~/.feedigest/env | xargs) && feedigest send < ~/.feedigest/feeds.txt
|
84
|
+
~~~
|
85
|
+
|
86
|
+
You can run this with [cron][] e.g. once per day at 10 am:
|
87
|
+
|
88
|
+
[cron]: https://en.wikipedia.org/wiki/Cron
|
89
|
+
|
90
|
+
~~~
|
91
|
+
0 10 * * * export $(cat ~/.feedigest/env | xargs) && feedigest send < ~/.feedigest/feeds.txt
|
92
|
+
~~~
|
93
|
+
|
94
|
+
Alternatively, you can have feedigest simply print the generated email so that
|
95
|
+
you can send it yourself e.g. by piping it to sendmail. To do that, you simply
|
96
|
+
replace `feedigest send` with `feedigest print`:
|
97
|
+
|
98
|
+
~~~ sh
|
99
|
+
feedigest print < ~/.feedigest/feeds.txt
|
100
|
+
~~~
|
101
|
+
|
102
|
+
## Contributing
|
103
|
+
|
104
|
+
Using feedigest and want to help? Please [let me know][contact] how you use it
|
105
|
+
and if you have any ideas on how to improve it.
|
106
|
+
|
107
|
+
[contact]: https://agorf.gr/contact/
|
108
|
+
|
109
|
+
## License
|
110
|
+
|
111
|
+
Licensed under the MIT license (see [LICENSE.txt][license]).
|
112
|
+
|
113
|
+
[license]: https://github.com/agorf/feedigest/blob/master/LICENSE.txt
|
114
|
+
|
115
|
+
## Author
|
116
|
+
|
117
|
+
Angelos Orfanakos, <https://agorf.gr/>
|
data/bin/feedigest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if !%w[send print].include?(ARGV[0])
|
4
|
+
puts "#$0 <send|print>"
|
5
|
+
exit 1
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'feedigest'
|
9
|
+
|
10
|
+
puts 'Please provide a list of feed URLs (one per line) and press Ctrl-D'
|
11
|
+
|
12
|
+
feed_urls = $stdin.readlines.map(&:strip)
|
13
|
+
feeds = Feedigest::FeedFetcher.new(feed_urls).feeds
|
14
|
+
|
15
|
+
exit if feeds.empty?
|
16
|
+
|
17
|
+
mail = Feedigest::MailComposer.new(feeds).mail
|
18
|
+
mail_sender = Feedigest::MailSender.new(mail)
|
19
|
+
|
20
|
+
case ARGV[0]
|
21
|
+
when 'send' then mail_sender.deliver
|
22
|
+
when 'print' then puts mail_sender
|
23
|
+
end
|
data/lib/feedigest.rb
CHANGED
@@ -1,192 +1,6 @@
|
|
1
|
-
|
2
|
-
require 'feedjira'
|
3
|
-
require 'mail'
|
4
|
-
require 'nokogiri'
|
5
|
-
require 'reverse_markdown'
|
1
|
+
module Feedigest; end
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
"feedigest@#{`hostname`.strip}"
|
12
|
-
)
|
13
|
-
EMAIL_RECIPIENT = ENV.fetch('FEEDIGEST_EMAIL_RECIPIENT')
|
14
|
-
DELIVERY_METHOD = ENV.fetch('FEEDIGEST_DELIVERY_METHOD', 'sendmail').to_sym
|
15
|
-
|
16
|
-
Feed = Struct.new(:url, :title, :entries, :error)
|
17
|
-
|
18
|
-
attr_reader :feed_urls
|
19
|
-
|
20
|
-
def initialize(feed_urls)
|
21
|
-
@feed_urls = feed_urls
|
22
|
-
end
|
23
|
-
|
24
|
-
def send_email
|
25
|
-
build_email.deliver if send_email?
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_s
|
29
|
-
build_email.to_s
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def send_email?
|
35
|
-
feeds.any?
|
36
|
-
end
|
37
|
-
|
38
|
-
def build_email
|
39
|
-
return @build_email if @build_email
|
40
|
-
|
41
|
-
mail = Mail.new
|
42
|
-
mail.from = EMAIL_SENDER
|
43
|
-
mail.to = EMAIL_RECIPIENT
|
44
|
-
mail.subject = email_subject
|
45
|
-
mail.text_part = build_email_text_part
|
46
|
-
mail.html_part = build_email_html_part
|
47
|
-
|
48
|
-
setup_delivery_method(mail)
|
49
|
-
|
50
|
-
@build_email = mail
|
51
|
-
end
|
52
|
-
|
53
|
-
def setup_delivery_method(mail)
|
54
|
-
mail.delivery_method(DELIVERY_METHOD, delivery_method_options)
|
55
|
-
end
|
56
|
-
|
57
|
-
def delivery_method_options
|
58
|
-
case DELIVERY_METHOD
|
59
|
-
when :smtp
|
60
|
-
{
|
61
|
-
address: ENV.fetch('FEEDIGEST_SMTP_HOST'),
|
62
|
-
port: ENV.fetch('FEEDIGEST_SMTP_PORT', '587').to_i,
|
63
|
-
user_name: ENV.fetch('FEEDIGEST_SMTP_USERNAME'),
|
64
|
-
password: ENV.fetch('FEEDIGEST_SMTP_PASSWORD'),
|
65
|
-
authentication: ENV.fetch('FEEDIGEST_SMTP_AUTH', 'plain'),
|
66
|
-
enable_starttls: ENV.fetch('FEEDIGEST_SMTP_STARTTLS', 'true') == 'true'
|
67
|
-
}
|
68
|
-
else
|
69
|
-
{}
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def email_subject
|
74
|
-
sprintf(
|
75
|
-
'Digest for %s in %s',
|
76
|
-
pluralize(entries_count, 'entry', 'entries'),
|
77
|
-
pluralize(feeds_without_error.size, 'feed')
|
78
|
-
)
|
79
|
-
end
|
80
|
-
|
81
|
-
def build_email_text_part
|
82
|
-
Mail::Part.new.tap do |p|
|
83
|
-
p.content_type 'text/plain; charset=utf-8'
|
84
|
-
p.body email_body_text
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def build_email_html_part
|
89
|
-
Mail::Part.new.tap do |p|
|
90
|
-
p.content_type 'text/html; charset=utf-8'
|
91
|
-
p.body email_body_html
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def email_body_html
|
96
|
-
@email_body_html ||=
|
97
|
-
Nokogiri::HTML::Builder.new(encoding: 'utf-8') { |builder|
|
98
|
-
builder.div do
|
99
|
-
feeds.each do |feed|
|
100
|
-
feed_html(builder, feed)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
}.to_html
|
104
|
-
end
|
105
|
-
|
106
|
-
def email_body_text
|
107
|
-
ReverseMarkdown.convert(email_body_html)
|
108
|
-
end
|
109
|
-
|
110
|
-
def feed_html(builder, feed)
|
111
|
-
builder.div do
|
112
|
-
if feed.error
|
113
|
-
builder.h2 feed.url
|
114
|
-
builder.p "Error: #{feed.error}"
|
115
|
-
else
|
116
|
-
builder.h2 feed.title
|
117
|
-
entries_html(builder, feed.entries)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def entries_html(builder, entries)
|
123
|
-
entries.group_by { |e| e.published.to_date }.each do |date, date_entries|
|
124
|
-
builder.h3 date
|
125
|
-
|
126
|
-
date_entries.each do |entry|
|
127
|
-
entry_html(builder, entry)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def entry_html(builder, entry)
|
133
|
-
builder.p do
|
134
|
-
builder.a(entry.title, href: entry.url)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def feeds
|
139
|
-
@feeds ||= all_feeds.select { |f| process_feed?(f) }
|
140
|
-
end
|
141
|
-
|
142
|
-
def feeds_without_error
|
143
|
-
feeds.select { |feed| feed[:error].nil? }
|
144
|
-
end
|
145
|
-
|
146
|
-
def process_feed?(feed)
|
147
|
-
feed[:error] || feed[:entries].any?
|
148
|
-
end
|
149
|
-
|
150
|
-
def all_feeds
|
151
|
-
feed_urls.map { |url| feed_from_url(url) }
|
152
|
-
end
|
153
|
-
|
154
|
-
def feed_from_url(url)
|
155
|
-
feed, error = fetch_and_parse_feed(url)
|
156
|
-
Feed.new(
|
157
|
-
url,
|
158
|
-
feed&.title,
|
159
|
-
error ? [] : feed_entries(feed),
|
160
|
-
error
|
161
|
-
)
|
162
|
-
end
|
163
|
-
|
164
|
-
def fetch_and_parse_feed(url)
|
165
|
-
feed = Feedjira::Feed.fetch_and_parse(url)
|
166
|
-
[feed, nil]
|
167
|
-
rescue StandardError => e
|
168
|
-
[nil, e.message]
|
169
|
-
end
|
170
|
-
|
171
|
-
def feed_entries(feed)
|
172
|
-
feed.entries.
|
173
|
-
select { |e| process_entry?(e) }.
|
174
|
-
sort_by { |e| -e.published.to_i }
|
175
|
-
end
|
176
|
-
|
177
|
-
def process_entry?(entry)
|
178
|
-
!entry.published.nil? && entry.published >= window_start
|
179
|
-
end
|
180
|
-
|
181
|
-
def window_start
|
182
|
-
@window_start ||= Time.now - ENTRY_WINDOW
|
183
|
-
end
|
184
|
-
|
185
|
-
def entries_count
|
186
|
-
feeds.reduce(0) { |s, f| s + f.entries.size }
|
187
|
-
end
|
188
|
-
|
189
|
-
def pluralize(count, singular, plural = singular + 's')
|
190
|
-
[count, count == 1 ? singular : plural].join(' ')
|
191
|
-
end
|
192
|
-
end
|
3
|
+
require 'feedigest/options'
|
4
|
+
require 'feedigest/feed_fetcher'
|
5
|
+
require 'feedigest/mail_composer'
|
6
|
+
require 'feedigest/mail_sender'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'feedigest/options'
|
2
|
+
require 'feedjira'
|
3
|
+
|
4
|
+
class Feedigest::FeedFetcher
|
5
|
+
Feed = Struct.new(:url, :title, :entries, :error)
|
6
|
+
|
7
|
+
attr_reader :feed_urls
|
8
|
+
|
9
|
+
def initialize(feed_urls)
|
10
|
+
@feed_urls = feed_urls
|
11
|
+
end
|
12
|
+
|
13
|
+
def feeds
|
14
|
+
@feeds ||= all_feeds.select { |f| process_feed?(f) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def process_feed?(feed)
|
20
|
+
feed[:error] || feed[:entries].any?
|
21
|
+
end
|
22
|
+
|
23
|
+
def all_feeds
|
24
|
+
feed_urls.map { |url| feed_from_url(url) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def feed_from_url(url)
|
28
|
+
feed, error = fetch_and_parse_feed(url)
|
29
|
+
Feed.new(
|
30
|
+
url,
|
31
|
+
feed&.title,
|
32
|
+
error ? [] : feed_entries(feed),
|
33
|
+
error
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_and_parse_feed(url)
|
38
|
+
feed = Feedjira::Feed.fetch_and_parse(url)
|
39
|
+
[feed, nil]
|
40
|
+
rescue StandardError => e
|
41
|
+
[nil, e.message]
|
42
|
+
end
|
43
|
+
|
44
|
+
def feed_entries(feed)
|
45
|
+
feed.entries.select { |e| process_entry?(e) }.sort_by(&:published).reverse
|
46
|
+
end
|
47
|
+
|
48
|
+
def process_entry?(entry)
|
49
|
+
!entry.published.nil? && entry.published >= window_start
|
50
|
+
end
|
51
|
+
|
52
|
+
def window_start
|
53
|
+
@window_start ||= Time.now - Feedigest.options[:entry_window]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'feedigest/options'
|
2
|
+
require 'feedigest/version'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'reverse_markdown'
|
5
|
+
|
6
|
+
class Feedigest::MailComposer
|
7
|
+
Mail = Struct.new(:from, :to, :subject, :html_body, :text_body)
|
8
|
+
|
9
|
+
attr_reader :feeds
|
10
|
+
|
11
|
+
def initialize(feeds)
|
12
|
+
@feeds = feeds
|
13
|
+
end
|
14
|
+
|
15
|
+
def mail
|
16
|
+
@mail ||= Mail.new(
|
17
|
+
Feedigest.options[:email_sender],
|
18
|
+
Feedigest.options[:email_recipient],
|
19
|
+
subject,
|
20
|
+
html_body,
|
21
|
+
text_body
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def subject
|
28
|
+
sprintf(
|
29
|
+
'Digest for %s in %s',
|
30
|
+
pluralize(entries_count, 'entry', 'entries'),
|
31
|
+
pluralize(feeds_without_error.size, 'feed')
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def html_body
|
36
|
+
@html_body ||=
|
37
|
+
Nokogiri::HTML::Builder.new(encoding: 'utf-8') { |builder|
|
38
|
+
builder.div do
|
39
|
+
feeds.each do |feed|
|
40
|
+
feed_html(builder, feed)
|
41
|
+
end
|
42
|
+
|
43
|
+
signature_html(builder)
|
44
|
+
end
|
45
|
+
}.to_html
|
46
|
+
end
|
47
|
+
|
48
|
+
def text_body
|
49
|
+
ReverseMarkdown.convert(html_body)
|
50
|
+
end
|
51
|
+
|
52
|
+
def feed_html(builder, feed)
|
53
|
+
builder.div do
|
54
|
+
if feed.error
|
55
|
+
builder.h2 feed.url
|
56
|
+
builder.p "Error: #{feed.error}"
|
57
|
+
else
|
58
|
+
builder.h2 feed.title
|
59
|
+
entries_html(builder, feed.entries)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def entries_html(builder, entries)
|
65
|
+
entries.group_by { |e| e.published.to_date }.each do |date, date_entries|
|
66
|
+
builder.h3 date
|
67
|
+
|
68
|
+
date_entries.each do |entry|
|
69
|
+
entry_html(builder, entry)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def entry_html(builder, entry)
|
75
|
+
builder.p do
|
76
|
+
builder.a(entry.title, href: entry.url)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def signature_html(builder)
|
81
|
+
builder.p do
|
82
|
+
builder.text '--'
|
83
|
+
builder.br
|
84
|
+
builder.text 'Sent by '
|
85
|
+
builder.a(
|
86
|
+
"feedigest #{Feedigest::VERSION}",
|
87
|
+
href: 'https://github.com/agorf/feedigest'
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def feeds_without_error
|
93
|
+
feeds.select { |feed| feed[:error].nil? }
|
94
|
+
end
|
95
|
+
|
96
|
+
def entries_count
|
97
|
+
feeds.reduce(0) { |count, feed| count + feed.entries.size }
|
98
|
+
end
|
99
|
+
|
100
|
+
def pluralize(count, singular, plural = singular + 's')
|
101
|
+
[count, count == 1 ? singular : plural].join(' ')
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'feedigest/options'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
class Feedigest::MailSender
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :mail_data
|
9
|
+
|
10
|
+
delegate %i[to_s] => :mail
|
11
|
+
|
12
|
+
def initialize(mail_data)
|
13
|
+
@mail_data = mail_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def deliver
|
17
|
+
setup_delivery_method!(mail)
|
18
|
+
mail.deliver
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def mail
|
24
|
+
return @mail if @mail
|
25
|
+
|
26
|
+
@mail = Mail.new
|
27
|
+
@mail.from = mail_data.from
|
28
|
+
@mail.to = mail_data.to
|
29
|
+
@mail.subject = mail_data.subject
|
30
|
+
@mail.html_part = html_part
|
31
|
+
@mail.text_part = text_part
|
32
|
+
@mail
|
33
|
+
end
|
34
|
+
|
35
|
+
def html_part
|
36
|
+
Mail::Part.new.tap do |p|
|
37
|
+
p.content_type 'text/html; charset=utf-8'
|
38
|
+
p.body mail_data.html_body
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def text_part
|
43
|
+
Mail::Part.new.tap do |p|
|
44
|
+
p.content_type 'text/plain; charset=utf-8'
|
45
|
+
p.body mail_data.text_body
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def setup_delivery_method!(mail)
|
50
|
+
mail.delivery_method(:smtp, Feedigest.smtp_options)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Feedigest
|
2
|
+
def self.options
|
3
|
+
@options ||= {
|
4
|
+
entry_window: ENV.fetch('FEEDIGEST_ENTRY_WINDOW', 60 * 60 * 24), # Seconds
|
5
|
+
email_sender: ENV.fetch(
|
6
|
+
'FEEDIGEST_EMAIL_SENDER',
|
7
|
+
"feedigest@#{`hostname`.strip}"
|
8
|
+
),
|
9
|
+
email_recipient: ENV.fetch('FEEDIGEST_EMAIL_RECIPIENT')
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.smtp_options
|
14
|
+
@smtp_options ||= {
|
15
|
+
address: ENV.fetch('FEEDIGEST_SMTP_ADDRESS'),
|
16
|
+
port: ENV.fetch('FEEDIGEST_SMTP_PORT', '587').to_i,
|
17
|
+
user_name: ENV.fetch('FEEDIGEST_SMTP_USERNAME'),
|
18
|
+
password: ENV.fetch('FEEDIGEST_SMTP_PASSWORD'),
|
19
|
+
authentication: ENV.fetch('FEEDIGEST_SMTP_AUTH', 'plain'),
|
20
|
+
enable_starttls: ENV.fetch('FEEDIGEST_SMTP_STARTTLS', 'true') == 'true'
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feedigest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Angelos Orfanakos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: dotenv
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '2.4'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '2.4'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: feedjira
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,18 +66,37 @@ dependencies:
|
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
68
|
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler-audit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.6'
|
83
83
|
description:
|
84
84
|
email: me@agorf.gr
|
85
85
|
executables:
|
86
|
-
- feedigest
|
87
|
-
- feedigest-generate
|
86
|
+
- feedigest
|
88
87
|
extensions: []
|
89
88
|
extra_rdoc_files: []
|
90
89
|
files:
|
90
|
+
- CHANGELOG.md
|
91
91
|
- LICENSE.txt
|
92
|
-
-
|
93
|
-
- bin/feedigest
|
92
|
+
- README.md
|
93
|
+
- bin/feedigest
|
94
94
|
- lib/feedigest.rb
|
95
|
+
- lib/feedigest/feed_fetcher.rb
|
96
|
+
- lib/feedigest/mail_composer.rb
|
97
|
+
- lib/feedigest/mail_sender.rb
|
98
|
+
- lib/feedigest/options.rb
|
99
|
+
- lib/feedigest/version.rb
|
95
100
|
homepage: https://github.com/agorf/feedigest
|
96
101
|
licenses:
|
97
102
|
- MIT
|
@@ -112,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
117
|
version: '0'
|
113
118
|
requirements: []
|
114
119
|
rubyforge_project:
|
115
|
-
rubygems_version: 2.7.
|
120
|
+
rubygems_version: 2.7.6
|
116
121
|
signing_key:
|
117
122
|
specification_version: 4
|
118
123
|
summary: RSS/Atom feed updates as a digest email
|
data/bin/feedigest-generate
DELETED
data/bin/feedigest-send
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'feedigest'
|
4
|
-
|
5
|
-
if ENV['FEEDIGEST_EMAIL_RECIPIENT'].nil?
|
6
|
-
puts 'Please set $FEEDIGEST_EMAIL_RECIPIENT and try again'
|
7
|
-
exit 1
|
8
|
-
end
|
9
|
-
|
10
|
-
puts 'Please provide a list of feed URLs (one per line) and press Ctrl-D'
|
11
|
-
|
12
|
-
feed_urls = $stdin.readlines.map(&:strip)
|
13
|
-
|
14
|
-
Feedigest.new(feed_urls).send_email
|