rss-reader 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +24 -0
- data/exe/rss-reader +5 -0
- data/lib/client.rb +144 -0
- data/lib/core.rb +142 -0
- data/lib/patch.rb +56 -0
- data/lib/utils.rb +27 -0
- data/rss-reader.gemspec +27 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91651a5c206fb1a61bf999096c56371739054035f3159b24d35be455c6c3240a
|
4
|
+
data.tar.gz: 2f61494a072baa0e3eb0f36d75ecd91b6c96758acf94cc1846ce3af16ec8e1f0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c6e70a3269a5b89e6d7a06453ffc2d490db113210154e4f053d171e3e1ac37fd0572e4e1d5d40de43f651297a635356ff41740319d23dd139ba50d700f817dd
|
7
|
+
data.tar.gz: b38b14167613eb6c76f2f4c4c7b11444912a2720daff9a11ac414d263dc67a88c03b4b455171915bf65a709d9b3634d65e5af267d2b7e3fb6e25aa7a698b731f
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# RSS Reader
|
2
|
+
|
3
|
+
CLI RSS reader by Ruby lang
|
4
|
+
|
5
|
+
## 安装
|
6
|
+
|
7
|
+
`gem install rss-reader`
|
8
|
+
|
9
|
+
## 使用
|
10
|
+
|
11
|
+
启动
|
12
|
+
|
13
|
+
`rss-reader`
|
14
|
+
|
15
|
+
查看帮助
|
16
|
+
|
17
|
+
`h` 或者 `help`
|
18
|
+
|
19
|
+
|
20
|
+
## 订阅链接
|
21
|
+
|
22
|
+
编辑 `~/subscribe-rss.list` 格式(目前只支持 RSS 格式订阅)
|
23
|
+
|
24
|
+
`#` 是注释符号
|
data/exe/rss-reader
ADDED
data/lib/client.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative './core'
|
5
|
+
require 'readline'
|
6
|
+
|
7
|
+
LIST = %w[
|
8
|
+
help
|
9
|
+
clear
|
10
|
+
quit
|
11
|
+
exit
|
12
|
+
update
|
13
|
+
reset
|
14
|
+
channel
|
15
|
+
].sort
|
16
|
+
|
17
|
+
HELP_DOC = <<~HELP
|
18
|
+
Help:
|
19
|
+
help/h: \t print help message
|
20
|
+
clear/c: \t clear screen
|
21
|
+
quit/exit: \t quit
|
22
|
+
exit: \t quit
|
23
|
+
update: \t update all channel data
|
24
|
+
reset/r: \t channel list
|
25
|
+
q: \t quit current view
|
26
|
+
|
27
|
+
Config:
|
28
|
+
~/subscribe-rss.list
|
29
|
+
|
30
|
+
HELP
|
31
|
+
comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
|
32
|
+
Readline.completion_append_character = ' '
|
33
|
+
Readline.completion_proc = comp
|
34
|
+
|
35
|
+
# RSS Client
|
36
|
+
class RssClient
|
37
|
+
# rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/AbcSize,Metrics/BlockLength,Metrics/PerceivedComplexity
|
38
|
+
def initialize
|
39
|
+
@rss = nil
|
40
|
+
@channel_index = -1
|
41
|
+
@item_index = -1
|
42
|
+
@debug = false
|
43
|
+
init_rss
|
44
|
+
end
|
45
|
+
|
46
|
+
def init_rss
|
47
|
+
puts 'RSS Reader'.green
|
48
|
+
puts 'loading....'.green
|
49
|
+
@rss = RSSManager.new
|
50
|
+
system('clear')
|
51
|
+
render
|
52
|
+
end
|
53
|
+
|
54
|
+
def render
|
55
|
+
# TODO: index check & catch
|
56
|
+
if @channel_index == -1 && @item_index == -1
|
57
|
+
# render channels list
|
58
|
+
puts '[RSS Reader]'.blue
|
59
|
+
puts '----'
|
60
|
+
puts @rss.channel_titles
|
61
|
+
elsif @channel_index >= 0 && @item_index == -1
|
62
|
+
# render channel item list
|
63
|
+
puts "[Channel] #{@rss.channel_title_by_index(@channel_index)}".blue
|
64
|
+
puts '----'
|
65
|
+
puts @rss.channel_items(@channel_index)
|
66
|
+
elsif @channel_index >= 0 && @item_index >= 0
|
67
|
+
# render item content
|
68
|
+
channel = @rss.channels[@channel_index]
|
69
|
+
content = channel.content_by_index(@item_index)
|
70
|
+
Utils.display_by_reader(content)
|
71
|
+
@item_index = -1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset_index
|
76
|
+
@channel_index = -1
|
77
|
+
@item_index = -1
|
78
|
+
end
|
79
|
+
|
80
|
+
def back_index
|
81
|
+
reset_index
|
82
|
+
end
|
83
|
+
|
84
|
+
def print_index
|
85
|
+
return unless @debug
|
86
|
+
|
87
|
+
puts "@channel_index: #{@channel_index}"
|
88
|
+
puts "@item_index: #{@item_index}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_channels
|
92
|
+
puts 'Updating channels ... '.green
|
93
|
+
@rss.update_channels
|
94
|
+
puts "Updated #{Time.now}".green
|
95
|
+
end
|
96
|
+
|
97
|
+
def run
|
98
|
+
loop do
|
99
|
+
line = Readline.readline('> ', true)
|
100
|
+
break if line.nil? || line == 'quit'
|
101
|
+
|
102
|
+
line = line.strip
|
103
|
+
case line
|
104
|
+
when 'quit', 'exit'
|
105
|
+
exit 0
|
106
|
+
when 'h', 'help'
|
107
|
+
puts HELP_DOC
|
108
|
+
when /(\d+)/
|
109
|
+
index = ::Regexp.last_match(1)
|
110
|
+
index = index.strip.to_i
|
111
|
+
if @channel_index >= 0
|
112
|
+
@item_index = index if index >= 0 && index < @rss.channels[@channel_index].items.length
|
113
|
+
elsif index >= 0 && index < @rss.channels.length
|
114
|
+
@channel_index = index
|
115
|
+
end
|
116
|
+
print_index
|
117
|
+
render
|
118
|
+
when 'c', 'clear'
|
119
|
+
puts 'clear channel'
|
120
|
+
system('clear')
|
121
|
+
print_index
|
122
|
+
render
|
123
|
+
when 'r', 'reset'
|
124
|
+
puts 'reset channel'
|
125
|
+
reset_index
|
126
|
+
print_index
|
127
|
+
render
|
128
|
+
when 'q'
|
129
|
+
back_index
|
130
|
+
print_index
|
131
|
+
system('clear')
|
132
|
+
render
|
133
|
+
when 'update'
|
134
|
+
update_channels
|
135
|
+
print_index
|
136
|
+
render
|
137
|
+
else
|
138
|
+
# do nothing
|
139
|
+
print_index
|
140
|
+
puts 'nothing happened...'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/core.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './patch'
|
4
|
+
require_relative './utils'
|
5
|
+
require 'rss'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'async'
|
8
|
+
|
9
|
+
# RSS Channel class
|
10
|
+
class RSSChannel
|
11
|
+
attr_accessor :feed
|
12
|
+
|
13
|
+
def initialize(channel_url)
|
14
|
+
@channel_url = channel_url
|
15
|
+
@feed = nil
|
16
|
+
|
17
|
+
fetch_channel
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_channel
|
21
|
+
URI.open(@channel_url) do |rss|
|
22
|
+
@feed = RSS::Parser.parse(rss)
|
23
|
+
# Utils.display_by_reader(@feed)
|
24
|
+
end
|
25
|
+
rescue OpenURI::HTTPError
|
26
|
+
puts "[Request Error] #{@channel_url}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def items
|
30
|
+
items = []
|
31
|
+
@feed.items.each_with_index do |item, index|
|
32
|
+
items.push "#{"[#{index}]".green} #{Utils.url_text(item.title.to_s.dark_green, item.link)}"
|
33
|
+
end
|
34
|
+
items
|
35
|
+
end
|
36
|
+
|
37
|
+
def open_url_by_index(index)
|
38
|
+
item = @feed.items[index]
|
39
|
+
Utils.open_url(item.link)
|
40
|
+
end
|
41
|
+
|
42
|
+
def description_by_index(index)
|
43
|
+
item = @feed.items[index]
|
44
|
+
item.description
|
45
|
+
end
|
46
|
+
|
47
|
+
def content_by_index(index)
|
48
|
+
Utils.html_to_text(description_by_index(index))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# RSS Manager: Core Channel Manager Class
|
53
|
+
class RSSManager
|
54
|
+
attr_accessor :channels, :current_channel_index
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
config_file = File.join(ENV['HOME'], 'subscribe-rss.list')
|
58
|
+
unless File.exist?(config_file)
|
59
|
+
File.open(config_file, 'w') do |f|
|
60
|
+
f << 'https://www.ruby-lang.org/en/feeds/news.rss'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@channel_config_path = config_file
|
64
|
+
@channel_urls = []
|
65
|
+
@channels = []
|
66
|
+
|
67
|
+
init_pipeline
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_channel_urls
|
71
|
+
channel_lines = File.open(@channel_config_path).readlines.map(&:strip)
|
72
|
+
@channel_urls = channel_lines.filter do |x|
|
73
|
+
x != '' && x.index('#') != 0
|
74
|
+
end
|
75
|
+
@channel_urls
|
76
|
+
end
|
77
|
+
|
78
|
+
def init_channels
|
79
|
+
@channel_urls.each do |channel_url|
|
80
|
+
@channels.push(RSSChannel.new(channel_url))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_channels
|
85
|
+
time_start = Time.now
|
86
|
+
async_fetch
|
87
|
+
time_end = Time.now
|
88
|
+
puts "use: #{time_end - time_start}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def sync_fetch
|
92
|
+
# use: 6.022412
|
93
|
+
@channels.each(&:fetch_channel)
|
94
|
+
end
|
95
|
+
|
96
|
+
def async_fetch
|
97
|
+
# use: 2.854694
|
98
|
+
Async do |task|
|
99
|
+
@channels.each do |ch|
|
100
|
+
task.async do
|
101
|
+
ch.fetch_channel
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def init_pipeline
|
108
|
+
load_channel_urls
|
109
|
+
init_channels
|
110
|
+
update_channels
|
111
|
+
end
|
112
|
+
|
113
|
+
def channel_titles
|
114
|
+
titles = []
|
115
|
+
@channels.each_with_index do |x, index|
|
116
|
+
titles.push "#{"[#{index}]".green} #{x.feed.channel.title.to_s.blue} (#{x.feed.channel.items.length}) \n"
|
117
|
+
end
|
118
|
+
titles
|
119
|
+
end
|
120
|
+
|
121
|
+
def channel_title_by_index(channel_index)
|
122
|
+
channel = @channels[channel_index]
|
123
|
+
channel.feed.channel.title
|
124
|
+
end
|
125
|
+
|
126
|
+
def channel_by_index(channel_index)
|
127
|
+
@channels[channel_index]
|
128
|
+
end
|
129
|
+
|
130
|
+
def channel_items(channel_index = 0)
|
131
|
+
@channels[channel_index].items
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# rss_atom = RSSChannel.new('https://www.ruby-lang.org/en/feeds/news.rss')
|
136
|
+
# rss_atom.open_url_by_index(1)
|
137
|
+
# puts rss_atom.content_by_index(1)
|
138
|
+
#
|
139
|
+
# m = RSSManager.new
|
140
|
+
# puts m.channel_titles
|
141
|
+
# first = m.channel_by_index(1)
|
142
|
+
# puts first.items
|
data/lib/patch.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def red = colorize(self, "\e[1m\e[31m")
|
5
|
+
def green = colorize(self, "\e[1m\e[32m")
|
6
|
+
def dark_green = colorize(self, "\e[32m")
|
7
|
+
def yellow = colorize(self, "\e[1m\e[33m")
|
8
|
+
def blue = colorize(self, "\e[1m\e[34m")
|
9
|
+
def dark_blue = colorize(self, "\e[34m")
|
10
|
+
def purple = colorize(self, "\e[35m")
|
11
|
+
def dark_purple = colorize(self, "\e[1;35m")
|
12
|
+
def cyan = colorize(self, "\e[1;36m")
|
13
|
+
def dark_cyan = colorize(self, "\e[36m")
|
14
|
+
def pure = colorize(self, "\e[0m\e[28m")
|
15
|
+
def bold = colorize(self, "\e[1m")
|
16
|
+
def colorize(text, color_code) = "#{color_code}#{text}\e[0m"
|
17
|
+
end
|
18
|
+
|
19
|
+
class String
|
20
|
+
def mv_up(n = 1)
|
21
|
+
cursor(self, "\033[#{n}A")
|
22
|
+
end
|
23
|
+
|
24
|
+
def mv_down(n = 1)
|
25
|
+
cursor(self, "\033[#{n}B")
|
26
|
+
end
|
27
|
+
|
28
|
+
def mv_fw(n = 1)
|
29
|
+
cursor(self, "\033[#{n}C")
|
30
|
+
end
|
31
|
+
|
32
|
+
def mv_bw(n = 1)
|
33
|
+
cursor(self, "\033[#{n}D")
|
34
|
+
end
|
35
|
+
|
36
|
+
def cls_upline
|
37
|
+
cursor(self, "\e[K")
|
38
|
+
end
|
39
|
+
|
40
|
+
def cls
|
41
|
+
# cursor(self, "\033[2J")
|
42
|
+
cursor(self, "\e[H\e[2J")
|
43
|
+
end
|
44
|
+
|
45
|
+
def save_position
|
46
|
+
cursor(self, "\033[s")
|
47
|
+
end
|
48
|
+
|
49
|
+
def restore_position
|
50
|
+
cursor(self, "\033[u")
|
51
|
+
end
|
52
|
+
|
53
|
+
def cursor(text, position)
|
54
|
+
"\r#{position}#{text}"
|
55
|
+
end
|
56
|
+
end
|
data/lib/utils.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'tty-link'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Utils
|
5
|
+
def self.url_text(text, url)
|
6
|
+
TTY::Link.link_to(text, url)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.html_to_text(html_text)
|
10
|
+
Nokogiri::HTML(html_text).text
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.display_by_reader(content)
|
14
|
+
system("echo '#{content}' | less")
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.open_url(url)
|
18
|
+
cmd = case RbConfig::CONFIG['host_os']
|
19
|
+
when /mswin|mingw|cygwin/ then 'start '
|
20
|
+
when /darwin/ then 'open '
|
21
|
+
when /linux|bsd/ then 'xdg-open '
|
22
|
+
else raise 'No OS detected'
|
23
|
+
end
|
24
|
+
|
25
|
+
system cmd + url
|
26
|
+
end
|
27
|
+
end
|
data/rss-reader.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'rss-reader'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.summary = 'RSS Reader!'
|
5
|
+
s.description = 'A simple cli RSS Reader'
|
6
|
+
s.authors = ['Mark24']
|
7
|
+
s.email = 'mark.zhangyoung@gmail.com'
|
8
|
+
s.files = ['lib/*.rb']
|
9
|
+
s.homepage =
|
10
|
+
'https://github.com/Mark24Code/rss-reader'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = Dir.chdir(__dir__) do
|
14
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
15
|
+
(File.expand_path(f) == __FILE__) ||
|
16
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
s.bindir = 'exe'
|
20
|
+
s.executables = s.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
s.add_dependency 'async', '~> 2.6'
|
23
|
+
s.add_dependency 'colorize', '~> 1.1.0'
|
24
|
+
s.add_dependency 'nokogiri', '~> 1.15.4'
|
25
|
+
s.add_dependency 'rss', '~> 0.3.0'
|
26
|
+
s.add_dependency 'tty-link', '~> 0.1.1'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rss-reader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark24
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: async
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.15.4
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.15.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rss
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tty-link
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.1.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.1.1
|
83
|
+
description: A simple cli RSS Reader
|
84
|
+
email: mark.zhangyoung@gmail.com
|
85
|
+
executables:
|
86
|
+
- rss-reader
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- README.md
|
91
|
+
- exe/rss-reader
|
92
|
+
- lib/client.rb
|
93
|
+
- lib/core.rb
|
94
|
+
- lib/patch.rb
|
95
|
+
- lib/utils.rb
|
96
|
+
- rss-reader.gemspec
|
97
|
+
homepage: https://github.com/Mark24Code/rss-reader
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.4.19
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: RSS Reader!
|
120
|
+
test_files: []
|