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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f18bbfd4e8a09cb263f70c7966af0fa9e6602a3c
4
- data.tar.gz: fe00a0445e6010eca1d847861f33687beb7a8dc7
3
+ metadata.gz: 51069bce6917475aa8499384d330396eebde34c1
4
+ data.tar.gz: cde703782a959046c866aa459123d050e6df5fc2
5
5
  SHA512:
6
- metadata.gz: 8fddbfd1416024208e32e65ba16a5eeb51c3e6537d6b39d0e49ba945ae8ab5c9bf9c8c14f3e976b173da9f06976e15b54ebc18ac73c62250727f2e3a58eafa1b
7
- data.tar.gz: 0f1cbbdcba1768b9f75ad29c21b3c5c4d17cd55446dbaa1aed49fc72d9b63ec58a3c333dd1d8a0001e31f9cc6555ac106a8c40db764bcc127b004a2e0605f69c
6
+ metadata.gz: 702e5ab54cd401d097b572bfdea2acfc48af045382ddc988135a1f93d8b100790c950b1cf1c32a0405feab4ae8d746c289fffd3ed328ff6974e2418eef87f383
7
+ data.tar.gz: 271c7c5d97acbc461dcdf24ac481255d0ddd6a387535c8fb9dc1fef58fcb6783aed9235c87d7a089de6e625036e06ae6399ec2f3b861486af257c697bb526f0f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ### 0.8.0
2
+
3
+ * Command-line interface for managing feeds (c20bf0c, e5c6dec)
4
+ * Perform feed autodiscovery in `add` command (46523ee)
5
+ * Store feed metadata in feed list, so no more feed files (cfbd725)
6
+ * Add `f2e` symlink to `feed2email` binary for running convenience (6add6b9)
7
+ * Improve send delay between entry processing (2118aef)
8
+ * Fix feed fetching exception handling (8eb68cf)
9
+ * Sync feed metadata only if all entries are processed (73f0947)
10
+ * Record entry to history only if email was sent (db59e77)
11
+ * Always fetch feed when permanently redirected (4fb147f)
12
+ * Ignore redirections to the same location (26513d1e)
13
+ * Major rewrite of README file with new instructions
14
+
1
15
  ### 0.7.0
2
16
 
3
17
  * Prevent simultaneous running instances
@@ -11,7 +25,7 @@
11
25
  * Render text/plain body as Markdown
12
26
  * Cache feed fetching with Last-Modified and ETag HTTP headers
13
27
  * Update feed URI on permanent redirect
14
- * Make sender a required config option
28
+ * Make `sender` a required config option
15
29
  * Maintain a separate history file per feed
16
30
 
17
31
  ### 0.5.0
data/README.md CHANGED
@@ -1,22 +1,25 @@
1
1
  # feed2email [![Gem Version](https://badge.fury.io/rb/feed2email.svg)](http://badge.fury.io/rb/feed2email)
2
2
 
3
- RSS/Atom feed updates in your email
4
-
5
- ## Why
6
-
7
- I don't like having a separate application for feeds when I'm already checking
8
- my email. I also never read a thing when feeds are kept in a separate place.
9
-
10
3
  feed2email is a [headless][] RSS/Atom feed aggregator that sends feed entries
11
- via email. It was written primarily as a replacement of [rss2email][] and aims
4
+ via email. It was initially written as a replacement of [rss2email][] and aims
12
5
  to be simple, fast and easy to use.
13
6
 
14
7
  [headless]: http://en.wikipedia.org/wiki/Headless_software
15
8
  [rss2email]: http://www.allthingsrss.com/rss2email/
16
9
 
10
+ ## Features
11
+
12
+ * Easy command-line feed management (add, remove, enable/disable)
13
+ * Feed fetching caching
14
+ * Feed autodiscovery
15
+ * Support for sending email with SMTP or a local MTA (e.g. sendmail)
16
+ * _text/html_ and _text/plain_ (Markdown) multipart emails
17
+ * Support for feed permanent redirections
18
+ * Auto-fixing relative feed entry permalinks
19
+
17
20
  ## Installation
18
21
 
19
- Install as a [gem][] from [RubyGems][]:
22
+ As a [gem][] from [RubyGems][]:
20
23
 
21
24
  ~~~ sh
22
25
  $ gem install feed2email
@@ -27,19 +30,24 @@ $ gem install feed2email
27
30
 
28
31
  ## Configuration
29
32
 
30
- Through a [YAML][] file at `~/.feed2email/config.yml`.
33
+ Through a [YAML][] file that you create at `~/.feed2email/config.yml`.
31
34
 
32
35
  [YAML]: http://en.wikipedia.org/wiki/YAML
33
36
 
34
37
  Each line in the configuration file contains a key-value pair. Each key-value
35
- pair is separated with a colon: `foo: bar`
38
+ pair is separated with a colon, e.g.: `foo: bar`
36
39
 
37
- ### Generic options
40
+ ### General options
38
41
 
39
42
  * `recipient` (required) is the email address to send email to
40
43
  * `sender` (required) is the email address to send email from (can be any)
41
44
  * `send_delay` (optional) is the number of seconds to wait between each email to
42
45
  avoid SMTP server throttling errors (default is `10`; use `0` to disable)
46
+ * `max_entries` (optional) is the maximum number of entries to process per feed
47
+ (default is `20`; use `0` for unlimited)
48
+
49
+ ### Logging options
50
+
43
51
  * `log_path` (optional) is the _absolute_ path to the log file (default is
44
52
  `true` which logs to standard output; use `false` to disable logging)
45
53
  * `log_level` (optional) is the logging verbosity level and can be `fatal`
@@ -50,15 +58,15 @@ pair is separated with a colon: `foo: bar`
50
58
  * `log_shift_size` (optional) is the maximum log file size in _megabytes_ and it
51
59
  only applies when `log_shift_age` is a number greater than zero (default is
52
60
  `1`)
53
- * `max_entries` (optional) is the maximum number of entries to process per feed
54
- (default is `20`; use `0` for unlimited)
61
+
62
+ ### Delivery options
55
63
 
56
64
  It is possible to send email via SMTP or an [MTA][] (default). If `config.yml`
57
65
  contains options for both, feed2email will use SMTP.
58
66
 
59
67
  [MTA]: http://en.wikipedia.org/wiki/Message_transfer_agent
60
68
 
61
- ### SMTP
69
+ #### SMTP
62
70
 
63
71
  For this method you need to have access to an SMTP service. [Mailgun][] has a
64
72
  free plan.
@@ -76,11 +84,11 @@ free plan.
76
84
  **Warning:** Unless it has correct restricted permissions, anyone with access in
77
85
  your system will be able to read `config.yml` and your password. To prevent
78
86
  this, feed2email will not run and complain if it detects the wrong permissions.
79
- To set the correct permissions, issue `chmod 600 ~/.feed2email/config.yml`
87
+ To set the correct permissions, issue `chmod 600 ~/.feed2email/config.yml`.
80
88
 
81
89
  [Mailgun]: http://www.mailgun.com/
82
90
 
83
- ### MTA
91
+ #### MTA
84
92
 
85
93
  For this method you need to have an [MTA][] with a [Sendmail][]-compatible
86
94
  interface set up and working in your system like [msmtp][] or [Postfix][].
@@ -96,68 +104,125 @@ interface set up and working in your system like [msmtp][] or [Postfix][].
96
104
 
97
105
  ### Managing feeds
98
106
 
99
- Create or edit `~/.feed2email/feeds.yml` and add the URL of the feed you want to
100
- subscribe to, prefixed with a dash and a space:
107
+ First, add some feeds:
108
+
109
+ ~~~ sh
110
+ $ feed2email add https://github.com/agorf.atom
111
+ Added feed https://github.com/agorf.atom at index 0
112
+ $ feed2email add https://github.com/agorf/feed2email/commits.atom
113
+ Added feed https://github.com/agorf/feed2email/commits.atom at index 1
114
+ ~~~
115
+
116
+ **Tip:** You only have to type a feed2email command until it is unambiguous e.g.
117
+ instead of `feed2email list`, you can simply issue `feed2email l` as long as
118
+ there is no other command beginning with an `l`.
101
119
 
102
- ~~~ yaml
103
- - https://github.com/agorf/feed2email/commits.atom
120
+ It is also possible to pass a website URL and let feed2email autodiscover any
121
+ feeds:
122
+
123
+ ~~~ sh
124
+ $ feed2email add http://www.rubyinside.com/
125
+ 0: http://www.rubyinside.com/feed/ "Ruby Inside" (application/rss+xml)
126
+ Please enter a feed to subscribe to: 0
127
+ Added feed http://www.rubyinside.com/feed/ at index 2
128
+ $ feed2email add http://thechangelog.com/137/
129
+ 0: http://thechangelog.com/137/feed/ "The Changelog » #137: Better GitHub Issues with HuBoard and Ryan Rauh Comments Feed" (application/rss+xml)
130
+ 1: http://thechangelog.com/feed/ "RSS 2.0 Feed" (application/rss+xml)
131
+ Please enter a feed to subscribe to: 1
132
+ Added feed http://thechangelog.com/feed/ at index 3
133
+ $ feed2email add http://thechangelog.com/137/
134
+ 0: http://thechangelog.com/137/feed/ "The Changelog » #137: Better GitHub Issues with HuBoard and Ryan Rauh Comments Feed" (application/rss+xml)
135
+ Please enter a feed to subscribe to: ^C
136
+ ~~~
137
+
138
+ Note that on the last command, feed2email autodiscovers the same two feeds as in
139
+ the second command, but only lists the ones that haven't been already added.
140
+ Autodiscovery is then cancelled by pressing `Ctrl-C`.
141
+
142
+ The feed list so far:
143
+
144
+ ~~~ sh
145
+ $ feed2email list
146
+ 0: https://github.com/agorf.atom
147
+ 1: https://github.com/agorf/feed2email/commits.atom
148
+ 2: http://www.rubyinside.com/feed/
149
+ 3: http://thechangelog.com/feed/
150
+ ~~~
151
+
152
+ To disable a feed so that it is not processed with `feed2email process`, toggle
153
+ it:
154
+
155
+ ~~~ sh
156
+ $ feed2email toggle 1
157
+ Disabled feed at index 1
158
+ $ feed2email list
159
+ 0: https://github.com/agorf.atom
160
+ 1: DISABLED https://github.com/agorf/feed2email/commits.atom
161
+ 2: http://www.rubyinside.com/feed/
162
+ 3: http://thechangelog.com/feed/
104
163
  ~~~
105
164
 
106
- To disable a feed, comment its line by prefixing it with a hash symbol:
165
+ It is also possible to remove it from the list:
107
166
 
108
- ~~~ yaml
109
- #- https://github.com/agorf/feed2email/commits.atom
167
+ ~~~ sh
168
+ $ feed2email remove 1
169
+ Removed feed at index 1
170
+ Warning: Feed list indices have changed!
110
171
  ~~~
111
172
 
112
- ### Running for the first time
173
+ It has been removed, but what is that weird warning?
113
174
 
114
- When feed2email runs for the first time or after adding a new feed:
175
+ Since the feed that got removed was at index 1, every feed below it got
176
+ reindexed. So feed2email warns you that the feed indices have changed: the feed
177
+ at index 2 is now at index 1 and the feed at index 3 is now at index 2.
115
178
 
116
- * All feed entries are skipped (no email sent)
117
- * `~/.feed2email/history-<digest>.yml` is created for each feed containing these
118
- (old) entries, where `<digest>` is the MD5 hex digest of the feed URL
179
+ Indeed:
119
180
 
120
- **Warning:** Versions prior to 0.6.0 used a single history file for all feeds.
121
- Before using version 0.6.0 for the first time, please make sure you run the
122
- provided migration script: `feed2email-migrate-history` If you don't, feed2email
123
- will think it's run for the first time and will treat all entries as old (thus
124
- no email will be sent and you may miss some entries).
181
+ ~~~ sh
182
+ $ feed2email list
183
+ 0: https://github.com/agorf.atom
184
+ 1: http://www.rubyinside.com/feed/
185
+ 2: http://thechangelog.com/feed/
186
+ ~~~
125
187
 
126
- ### Receiving specific entries from a feed
188
+ **Tip:** feed2email installs `f2e` as a symbolic link to the feed2email binary,
189
+ so you can use that to avoid typing the whole name every time, e.g.: `f2e list`
190
+ or even `f2e l`.
127
191
 
128
- 1. Add the feed URL to `~/.feed2email/feeds.yml`
129
- 1. Run feed2email once so that the feed's history file is generated
130
- 1. Remove the entries you want to receive from the feed's history (i.e. with
131
- your text editor)
132
- 1. Remove the feed's meta file (`meta-<digest>.yml`, where `<digest>` is the MD5
133
- hex digest of the feed URL) to bust feed fetching caching
192
+ ### Running
134
193
 
135
- Next time feed2email runs, these entries will be treated as new and will be
136
- processed (sent as email).
194
+ ~~~ sh
195
+ $ feed2email process
196
+ ~~~
137
197
 
138
- ### Permanent redirections
198
+ When run, feed2email will go through your feed list, fetch each feed (if
199
+ necessary) and send an email for each new entry. Any output is logged to the
200
+ standard output, unless configured otherwise.
139
201
 
140
- Before processing each feed, feed2email issues a [HEAD request][] to check
141
- whether it has been permanently moved by looking for a _301 Moved Permanently_
142
- HTTP status and its respective _Location_ header. In such case, feed2email
143
- updates `~/.feed2email/feeds.yml` with the new location and all feed entries are
144
- skipped (no email sent). If you do want to have some of them sent as email, see
145
- [Receiving specific entries from a feed](#receiving-specific-entries-from-a-feed).
202
+ **Warning:** Prior to version 0.8.0 where a command-line interface was
203
+ introduced, the way to run feed2email was simply `feed2email`. Now this will
204
+ just print helpful text on how to use it.
146
205
 
147
- [HEAD request]: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
206
+ When a new feed is detected (which is the case when feed2email runs for the
207
+ first time on your feed list), all of its entries are skipped and no email is
208
+ sent. This is so that you don't get spammed when you add a feed for the first
209
+ time.
148
210
 
149
- ### Feed caching
211
+ If you want to receive a specific entry from a newly added feed, edit the feed's
212
+ history file with `feed2email history` and remove the entry. Then issue
213
+ `feed2email fetch` to clear the feed's fetch cache. Next time
214
+ `feed2email process` runs, the entry will be treated as new and will be
215
+ processed.
150
216
 
151
- feed2email caches fetched feeds with the _Last-Modified_ and _Etag_ HTTP
152
- headers. If you want to force a feed to be fetched, remove the feed's meta file
153
- (`~/.feed2email/meta-<digest>.yml`, where `<digest>` is the MD5 hex digest of
154
- the feed URL). Next time feed2email runs, the feed will be fetched.
217
+ ### Getting help
155
218
 
156
- ### Automating
219
+ Issue `feed2email` or `feed2email help` at any point to get helpful text on how
220
+ to use feed2email.
157
221
 
158
- You can use [cron][] to run feed2email automatically e.g. once every hour.
222
+ ## Contributing
159
223
 
160
- [cron]: http://en.wikipedia.org/wiki/Cron
224
+ Using feed2email and want to help? Just [contact me](http://agorf.gr/) and
225
+ describe how you use it and if you have any ideas on how it can be improved.
161
226
 
162
227
  ## License
163
228
 
data/bin/f2e ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if File.new('/tmp/feed2email.lock', 'w').flock(File::LOCK_NB | File::LOCK_EX)
4
+ require 'feed2email/cli'
5
+ Feed2Email::Cli.start(ARGV)
6
+ else
7
+ abort 'An instance of feed2email is already running. Exiting...'
8
+ end
9
+
data/bin/feed2email CHANGED
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'feed2email'
4
- require 'feed2email/feed'
5
-
6
3
  if File.new('/tmp/feed2email.lock', 'w').flock(File::LOCK_NB | File::LOCK_EX)
7
- Feed2Email::Feed.process_all
4
+ require 'feed2email/cli'
5
+ Feed2Email::Cli.start(ARGV)
8
6
  else
9
- $stderr.puts 'An instance of feed2email is already running. Exiting...'
10
- exit 1
7
+ abort 'An instance of feed2email is already running. Exiting...'
11
8
  end
9
+
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+
5
+ CONFIG_DIR = File.expand_path('~/.feed2email')
6
+ FEEDS_FILE = File.join(CONFIG_DIR, 'feeds.yml')
7
+
8
+ if !File.exist?(FEEDS_FILE)
9
+ $stderr.puts "Missing feed list file #{FEEDS_FILE}"
10
+ exit 1
11
+ end
12
+
13
+ feeds_data = YAML.load(open(FEEDS_FILE))
14
+
15
+ unless feeds_data.is_a?(Array) && feeds_data.all? {|data| data.is_a?(String) }
16
+ $stderr.puts "Invalid data type for feed list file #{FEEDS_FILE}"
17
+ exit 2
18
+ end
19
+
20
+ File.rename(FEEDS_FILE, "#{FEEDS_FILE}.bak")
21
+
22
+ begin
23
+ open(FEEDS_FILE, 'w') do |f|
24
+ f.write(
25
+ feeds_data.map {|uri|
26
+ { uri: uri, enabled: true }
27
+ }.to_yaml
28
+ )
29
+ end
30
+
31
+ puts "Migrated feed list file #{FEEDS_FILE}"
32
+ puts "Renamed #{FEEDS_FILE} to #{FEEDS_FILE}.bak"
33
+ rescue
34
+ File.rename("#{FEEDS_FILE}.bak", FEEDS_FILE)
35
+ raise
36
+ end
data/lib/feed2email.rb CHANGED
@@ -1,22 +1,27 @@
1
1
  require 'feed2email/config'
2
+ require 'feed2email/feed_list'
2
3
  require 'feed2email/lazy_smtp_connection'
3
4
  require 'feed2email/logger'
4
5
 
5
6
  module Feed2Email
6
- CONFIG_DIR = File.expand_path('~/.feed2email')
7
+ CONFIG_DIR = File.join(ENV['HOME'], '.feed2email')
7
8
 
8
- def self.config; @config end
9
-
10
- def self.logger
11
- @logger.logger # delegate
9
+ def self.config
10
+ @config ||= Config.new(File.join(CONFIG_DIR, 'config.yml'))
12
11
  end
13
12
 
14
- def self.smtp_connection; @smtp_connection end
15
-
16
- @config = Config.new(File.join(CONFIG_DIR, 'config.yml'))
13
+ def self.feed_list
14
+ @feed_list ||= FeedList.new(File.join(CONFIG_DIR, 'feeds.yml'))
15
+ end
17
16
 
18
- @logger = Logger.new(config['log_path'], config['log_level'],
19
- config['log_shift_age'], config['log_shift_size'])
17
+ def self.logger
18
+ @logger ||= Logger.new(
19
+ config['log_path'], config['log_level'], config['log_shift_age'],
20
+ config['log_shift_size']
21
+ ).logger
22
+ end
20
23
 
21
- @smtp_connection = LazySMTPConnection.new
24
+ def self.smtp_connection
25
+ @smtp_connection ||= LazySMTPConnection.new
26
+ end
22
27
  end
@@ -0,0 +1,158 @@
1
+ require 'thor'
2
+ require 'feed2email'
3
+ require 'feed2email/feed_autodiscoverer'
4
+ require 'feed2email/redirection_checker'
5
+
6
+ module Feed2Email
7
+ class Cli < Thor
8
+ desc 'add URL', 'subscribe to feed at URL'
9
+ def add(uri)
10
+ uri = handle_permanent_redirection(uri)
11
+ uri = perform_feed_autodiscovery(uri)
12
+
13
+ begin
14
+ feed_list << uri
15
+ rescue FeedList::DuplicateFeedError => e
16
+ abort e.message
17
+ end
18
+
19
+ if feed_list.sync
20
+ puts "Added feed #{uri} at index #{feed_list.size - 1}"
21
+ else
22
+ abort 'Failed to add feed'
23
+ end
24
+ end
25
+
26
+ desc 'fetch FEED', 'clear fetch cache for feed at index FEED'
27
+ def fetch(index)
28
+ index = check_feed_index(index, in: (0...feed_list.size))
29
+ feed_list.clear_fetch_cache(index)
30
+
31
+ if feed_list.sync
32
+ puts "Cleared fetch cache for feed at index #{index}"
33
+ else
34
+ abort "Failed to clear fetch cache for feed at index #{index}"
35
+ end
36
+ end
37
+
38
+ desc 'history FEED', 'edit history file of feed at index FEED with $EDITOR'
39
+ def history(index)
40
+ abort '$EDITOR not set' unless ENV['EDITOR']
41
+
42
+ index = check_feed_index(index, in: (0...feed_list.size))
43
+ require 'feed2email/feed_history'
44
+ history_path = FeedHistory.new(feed_list[index][:uri]).path
45
+ exec(ENV['EDITOR'], history_path)
46
+ end
47
+
48
+ desc 'remove FEED', 'unsubscribe from feed at index FEED'
49
+ def remove(index)
50
+ index = check_feed_index(index, in: (0...feed_list.size))
51
+ deleted = feed_list.delete_at(index)
52
+
53
+ if deleted && feed_list.sync
54
+ puts "Removed feed at index #{index}"
55
+
56
+ if feed_list.size != index # feed was not the last
57
+ puts 'Warning: Feed list indices have changed!'
58
+ end
59
+ else
60
+ abort "Failed to remove feed at index #{index}"
61
+ end
62
+ end
63
+
64
+ desc 'toggle FEED', 'enable/disable feed at index FEED'
65
+ def toggle(index)
66
+ index = check_feed_index(index, in: (0...feed_list.size))
67
+ toggled = feed_list.toggle(index)
68
+ enabled = feed_list[index][:enabled]
69
+
70
+ if toggled && feed_list.sync
71
+ puts "#{enabled ? 'En' : 'Dis'}abled feed at index #{index}"
72
+ else
73
+ abort "Failed to #{enabled ? 'en' : 'dis'}able feed at index #{index}"
74
+ end
75
+ end
76
+
77
+ desc 'list', 'list feed subscriptions'
78
+ def list
79
+ puts feed_list
80
+ end
81
+
82
+ desc 'process', 'process feed subscriptions'
83
+ def process
84
+ feed_list.process
85
+ end
86
+
87
+ desc 'version', 'show feed2email version'
88
+ def version
89
+ require 'feed2email/version'
90
+ puts "feed2email #{Feed2Email::VERSION}"
91
+ end
92
+
93
+ no_commands do
94
+ def check_feed_index(index, options = {})
95
+ if index.to_i.to_s != index ||
96
+ (options[:in] && !options[:in].include?(index.to_i))
97
+ puts if index.nil? # Ctrl-D
98
+ abort 'Invalid index'
99
+ end
100
+
101
+ index.to_i
102
+ end
103
+
104
+ def feed_list
105
+ Feed2Email.feed_list # delegate
106
+ end
107
+
108
+ def handle_permanent_redirection(uri)
109
+ checker = RedirectionChecker.new(uri)
110
+
111
+ if checker.permanently_redirected?
112
+ puts "Got permanently redirected to #{checker.location}"
113
+ checker.location
114
+ else
115
+ uri
116
+ end
117
+ end
118
+
119
+ def perform_feed_autodiscovery(uri)
120
+ discoverer = FeedAutodiscoverer.new(uri)
121
+
122
+ discovered_feeds = discoverer.feeds.reject {|feed|
123
+ feed_list.include?(feed[:uri])
124
+ }
125
+
126
+ if discovered_feeds.empty?
127
+ if discoverer.content_type == 'text/html'
128
+ puts 'Could not find any new feeds'
129
+ exit
130
+ end
131
+
132
+ return uri
133
+ end
134
+
135
+ justify = discovered_feeds.size.to_s.size
136
+
137
+ discovered_feeds.each_with_index do |feed, i|
138
+ puts '%{index}: %{uri} %{title}(%{content_type})' % {
139
+ index: i.to_s.rjust(justify),
140
+ uri: feed[:uri],
141
+ title: feed[:title] ? "\"#{feed[:title]}\" " : '',
142
+ content_type: feed[:content_type]
143
+ }
144
+ end
145
+
146
+ begin
147
+ response = ask('Please enter a feed to subscribe to:')
148
+ rescue Interrupt # Ctrl-C
149
+ puts
150
+ exit
151
+ end
152
+
153
+ index = check_feed_index(response, in: (0...discovered_feeds.size))
154
+ discovered_feeds[index][:uri]
155
+ end
156
+ end
157
+ end
158
+ end