rss-reader 1.0.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 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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/client'
5
+ RssClient.new.run
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
@@ -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: []