hackercli 0.0.2 → 0.0.3
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/bin/hackercli.rb +4 -0
- data/bin/hackman.rb +731 -432
- data/lib/hackercli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c4b0da9c2d3080b4e089e3e6cd067932ec532b2
|
4
|
+
data.tar.gz: 3eed3f9cb25b8d34123d2f5f3b243ca28b6d0c20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba88a33dbf30d7a59a7b68a4edc88e8497bea305e96dc70d6f0faec143ba3b90407d7c26af92e2d6bce8937da7602343973c9dae963f08e9fa851ec02f466bea
|
7
|
+
data.tar.gz: 846fef7529f021b205d55165014eb75efb3e36ed50d30f43d90dd9b4d9fce8945ffcc16d24f364ea3c2f2bb5ee8f47ac6c683752588d227a1c23098beb8fd19c
|
data/bin/hackercli.rb
CHANGED
@@ -42,6 +42,7 @@ class Bigrss
|
|
42
42
|
|
43
43
|
resp = []
|
44
44
|
filename = @options[:url]
|
45
|
+
ymlpath = @options[:ymlpath]
|
45
46
|
page[:page_url] = filename
|
46
47
|
now = Time.now
|
47
48
|
page[:create_time_seconds] = now.to_i
|
@@ -55,6 +56,8 @@ class Bigrss
|
|
55
56
|
content.gsub!(''',"'")
|
56
57
|
content.gsub!('4','"')
|
57
58
|
content = CGI.unescapeHTML(content)
|
59
|
+
# next line dirties current dir, does not respect path of yml
|
60
|
+
outfile = File.join(ymlpath, outfile) if ymlpath
|
58
61
|
File.open("#{outfile}.rss","w") {|ff| ff.write(content) }
|
59
62
|
items = content.scan(/<item>(.*?)<\/item>/)
|
60
63
|
items.each_with_index do |e,i|
|
@@ -183,6 +186,7 @@ Usage: #{$0} [options]
|
|
183
186
|
end
|
184
187
|
opts.on("-y yml path", String,"--yml-path", "save as YML file") do |v|
|
185
188
|
ymlfile = v
|
189
|
+
options[:ymlpath] = File.dirname(v)
|
186
190
|
end
|
187
191
|
#opts.on("-s SUBREDDIT", String,"--subreddit", "Get articles from subreddit named SUBREDDIT") do |v|
|
188
192
|
#options[:subreddit] = v
|
data/bin/hackman.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Author: j kepler http://github.com/mare-imbrium/canis/
|
6
6
|
# Date: 2014-08-09 - 10:12
|
7
7
|
# License: MIT
|
8
|
-
# Last update: 2014-08
|
8
|
+
# Last update: 2014-09-08 19:45
|
9
9
|
# ----------------------------------------------------------------------------- #
|
10
10
|
# hackman.rb Copyright (C) 2012-2014 j kepler
|
11
11
|
# encoding: utf-8
|
@@ -13,401 +13,606 @@ require 'canis/core/util/app'
|
|
13
13
|
require 'canis/core/util/rcommandwindow'
|
14
14
|
require 'fileutils'
|
15
15
|
require 'pathname'
|
16
|
+
require 'open3'
|
16
17
|
require 'canis/core/include/defaultfilerenderer'
|
17
18
|
require 'canis/core/include/appmethods'
|
18
19
|
|
19
20
|
# TODO :
|
20
|
-
# -
|
21
|
+
# - Make this a standard for other similar command line ncurses apps, like rigel. we can use
|
22
|
+
# this as a template for others.
|
23
|
+
# - make the menu a little more like the mc usermenu
|
24
|
+
# x change location of data cache, hacker-cli to also use the same for rss file saving
|
25
|
+
# x make color schemes into a hash 'name' => scheme, and each scheme should have named colors such as
|
26
|
+
# header-bg, body-bg body-fg, status etc, so it is clear. load from config file so user can change.
|
27
|
+
# x maybe instead of diong each binding we should be ablde to pass a hash to form
|
28
|
+
# or give it a method which it will invoke to handle keys. Form or window may have a keyhandler ??
|
29
|
+
# TODO
|
30
|
+
# x create a class and put stuff in there, these methods are going into global, and can conflict
|
31
|
+
# x put one url per lne of detail, put comments with comment url, and time with other.
|
32
|
+
# x fix update help_text
|
33
|
+
# x need to trap the output of hackercli in case of error.
|
34
|
+
# x some case URL not showing in Open menu - maybe too large the window. check max size
|
35
|
+
# x add to forum list, remove, save
|
21
36
|
# - specify gui browser and text browser, and on commandline use same keys as corvus
|
22
|
-
# - create a class and put stuff in there, these methods are going into global, and can conflict
|
23
|
-
# - we should have same mechanism for key bindings as corvus, something that can even be loaded?
|
24
37
|
#
|
25
|
-
#
|
26
|
-
#
|
38
|
+
# ? Use ' as bookmark etc just as in cetus and corvus, keep to same keys
|
39
|
+
# ? multibuffers so user can backspace and do M-n etc
|
27
40
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
[
|
33
|
-
|
34
|
-
]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def choose_forum
|
40
|
-
# scrollable filterable list
|
41
|
-
str = display_list $forumlist, :title => "Select a forum"
|
42
|
-
return unless str
|
43
|
-
return if str == ""
|
44
|
-
$current_forum = str
|
45
|
-
forum = str
|
46
|
-
get_data forum if forum
|
47
|
-
end
|
48
|
-
def next_forum
|
49
|
-
index = $forumlist.index($current_forum)
|
50
|
-
index = index >= $forumlist.count - 1 ? 0 : index + 1
|
51
|
-
get_data $forumlist[index]
|
52
|
-
end
|
53
|
-
def prev_forum
|
54
|
-
index = $forumlist.index($current_forum)
|
55
|
-
index = index == 0? $forumlist.count - 1 : index - 1
|
56
|
-
get_data $forumlist[index]
|
57
|
-
end
|
58
|
-
# if components have some commands, can we find a way of passing the command to them
|
59
|
-
# method_missing gave a stack overflow.
|
60
|
-
def execute_this(meth, *args)
|
61
|
-
alert " #{meth} not found ! "
|
62
|
-
$log.debug "app email got #{meth} " if $log.debug?
|
63
|
-
cc = @form.get_current_field
|
64
|
-
[cc].each do |c|
|
65
|
-
if c.respond_to?(meth, true)
|
66
|
-
c.send(meth, *args)
|
67
|
-
return true
|
68
|
-
end
|
69
|
-
end
|
70
|
-
false
|
71
|
-
end
|
72
|
-
def open_url url
|
73
|
-
shell_out "elinks #{url}"
|
74
|
-
#Window.refresh_all
|
75
|
-
end
|
76
|
-
|
77
|
-
##
|
78
|
-
# Menu creator which displays a menu and executes methods based on keys.
|
79
|
-
# In some cases, we call this and then do a case statement on either key or binding.
|
80
|
-
# @param String title
|
81
|
-
# @param hash of keys and methods to call
|
82
|
-
# @return key pressed, and binding (if found, and responded)
|
41
|
+
module HackerCli
|
42
|
+
VERSION="0.0.3"
|
43
|
+
CONFIG_FILE="~/.hackman.yml"
|
44
|
+
# in grey version, cannot see the other links.
|
45
|
+
OLDCOLOR_SCHEMES=[
|
46
|
+
[20,19,17, 18, :white, :green], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status, 4 fg color body, detail color (url and comment count)
|
47
|
+
[17,19,18, 20, :white, :green], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
|
48
|
+
[236,236,0, 232,:white, :green], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
|
49
|
+
[236,236,244, 250, :black, :green] # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
|
50
|
+
]
|
51
|
+
# put all methods and data into this class, so we don't pollute global space. Or get mixed into App's space.
|
83
52
|
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
53
|
+
class Hackman
|
54
|
+
def initialize app, options
|
55
|
+
@app = app
|
56
|
+
@options = options
|
57
|
+
@form = app.form
|
58
|
+
@hash = nil
|
59
|
+
@cache_path = "."
|
60
|
+
@toggle_titles_only = false
|
61
|
+
@toggle_offline = false
|
62
|
+
@logger = @app.logger
|
63
|
+
|
64
|
+
@fg = :white
|
65
|
+
@_forumlist = %w{ hacker ruby programming scifi science haskell java scala cpp c_programming d_language golang vim emacs unix linux bash zsh commandline vimplugins python ars slashdot }
|
66
|
+
@browser_mode = options[:browser_mode] || 'text'
|
67
|
+
@browser_text = options[:browser_text] || 'elinks'
|
68
|
+
@browser_gui = options[:browser_gui] || 'open'
|
69
|
+
@cache_path = options[:cache_path] || "."
|
70
|
+
config_file = options[:config_file]
|
71
|
+
config_read config_file
|
72
|
+
@binding ||= default_bindings
|
73
|
+
@color_schemes ||= default_color_schemes
|
74
|
+
# we should actually pick the fist, since the name could have changed
|
75
|
+
@color_scheme = @color_schemes.values.first
|
76
|
+
@forumlist ||= (options[:list] || @_forumlist)
|
77
|
+
handle_keys @binding
|
78
|
+
#form_bind @binding
|
79
|
+
@cache_path = File.expand_path(@cache_path)
|
80
|
+
end
|
81
|
+
def config_read config_file=nil
|
82
|
+
config_file ||= CONFIG_FILE
|
83
|
+
config_file = File.expand_path(config_file)
|
84
|
+
if config_file
|
85
|
+
if File.exists? config_file
|
86
|
+
#eval(File.open(File.expand_path(config_file)).read)
|
87
|
+
obj = YAML::load( File.open( config_file ) )
|
88
|
+
#%w{ :binding :forumlist :cache_path}.each do |e|
|
89
|
+
#if obj[e]
|
90
|
+
obj.keys.each do |e|
|
91
|
+
instance_variable_set("@#{e}", obj[e])
|
92
|
+
end
|
93
|
+
else
|
94
|
+
alert "NOT EXISTS config file #{config_file} "
|
95
|
+
end
|
104
96
|
end
|
105
97
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# trying to pass populists block to listbox
|
157
|
-
lb = Canis::TextPad.new form, listconfig, &block
|
158
|
-
if fmt == :none
|
159
|
-
lb.text(list)
|
160
|
-
else
|
161
|
-
lb.text(list, fmt)
|
98
|
+
# save current config to a yml file, so user can modify it
|
99
|
+
# This is included since its a bit difficult to create this file if you don't remember YML format.
|
100
|
+
def save_config filename=nil
|
101
|
+
unless filename
|
102
|
+
filename = get_string "Enter filename to save configuration to:"
|
103
|
+
return unless filename
|
104
|
+
end
|
105
|
+
xx = {}
|
106
|
+
[:binding, :forumlist, :browser_gui, :browser_text, :cache_path, :color_schemes, :color_scheme].each do |e|
|
107
|
+
xx[e] = instance_variable_get "@#{e}"
|
108
|
+
end
|
109
|
+
File.open(filename, 'w' ) do |f|
|
110
|
+
f << YAML::dump(xx)
|
111
|
+
end
|
112
|
+
@app.message "Config saved to #{filename} in YML format"
|
113
|
+
end
|
114
|
+
def default_color_schemes
|
115
|
+
@color_schemes={}
|
116
|
+
@color_schemes['deep blue'] = { :header_bg => 20, :menu_bg => 19, :body_bg => 17, :status_bg => 18, :body_fg => :white,
|
117
|
+
:body_detail => :green }
|
118
|
+
@color_schemes['medium blue'] = { :header_bg => 17, :menu_bg => 19, :body_bg => 18, :status_bg => 20, :body_fg => :white,
|
119
|
+
:body_detail => :green }
|
120
|
+
@color_schemes['black body'] = { :header_bg => 236, :menu_bg => 236, :body_bg => 0, :status_bg => 232, :body_fg => :white,
|
121
|
+
:body_detail => :green }
|
122
|
+
@color_schemes['grey body'] = { :header_bg => 236, :menu_bg => 236, :body_bg => 244, :status_bg => 250, :body_fg => :black,
|
123
|
+
:body_detail => :green }
|
124
|
+
return @color_schemes
|
125
|
+
end
|
126
|
+
def articles
|
127
|
+
@hash[:articles]
|
128
|
+
end
|
129
|
+
# return current color scheme
|
130
|
+
def color_scheme
|
131
|
+
@color_scheme
|
132
|
+
end
|
133
|
+
def forumlist
|
134
|
+
@forumlist
|
135
|
+
end
|
136
|
+
def default_bindings
|
137
|
+
@binding = {
|
138
|
+
"`" => "main_menu",
|
139
|
+
"=" => "toggle_menu",
|
140
|
+
">" => "next_forum",
|
141
|
+
"<" => "prev_forum",
|
142
|
+
"z" => "goto_article",
|
143
|
+
"o" => "display_links",
|
144
|
+
"<CR>" => "display_links",
|
145
|
+
"<C-f>" => "display_links",
|
146
|
+
"<F2>" => "choose_forum"
|
147
|
+
}
|
162
148
|
end
|
149
|
+
|
150
|
+
|
151
|
+
# prompt user to select a forum, and fetch data for it.
|
152
|
+
def choose_forum
|
153
|
+
# scrollable filterable list
|
154
|
+
str = display_list @forumlist, :title => "Select a forum"
|
155
|
+
return unless str
|
156
|
+
return if str == ""
|
157
|
+
@current_forum = str
|
158
|
+
forum = str
|
159
|
+
get_data forum if forum
|
160
|
+
end
|
161
|
+
# add a forum at runtime, by default this will be a reddit subforum
|
162
|
+
def add_forum forum=nil
|
163
|
+
unless forum
|
164
|
+
forum = get_string "Add a reddit subforum: "
|
165
|
+
return if forum.nil? or forum == ""
|
166
|
+
end
|
167
|
+
@forumlist << forum
|
168
|
+
get_data forum
|
169
|
+
end
|
170
|
+
def remove_forum forum=nil
|
171
|
+
unless forum
|
172
|
+
forum = display_list @forumlist, :title => "Select a forum"
|
173
|
+
return if forum.nil? or forum == ""
|
174
|
+
end
|
175
|
+
@forumlist.delete forum
|
176
|
+
end
|
177
|
+
def next_forum
|
178
|
+
index = @forumlist.index(@current_forum)
|
179
|
+
index = index >= @forumlist.count - 1 ? 0 : index + 1
|
180
|
+
get_data @forumlist[index]
|
181
|
+
end
|
182
|
+
def prev_forum
|
183
|
+
index = @forumlist.index(@current_forum)
|
184
|
+
index = index == 0? @forumlist.count - 1 : index - 1
|
185
|
+
get_data @forumlist[index]
|
186
|
+
end
|
187
|
+
# if components have some commands, can we find a way of passing the command to them
|
188
|
+
# method_missing gave a stack overflow.
|
189
|
+
def execute_this(meth, *args)
|
190
|
+
alert " #{meth} not found ! "
|
191
|
+
$log.debug "app email got #{meth} " if $log.debug?
|
192
|
+
cc = @form.get_current_field
|
193
|
+
[cc].each do |c|
|
194
|
+
if c.respond_to?(meth, true)
|
195
|
+
c.send(meth, *args)
|
196
|
+
return true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
false
|
200
|
+
end
|
201
|
+
def open_url url, app
|
202
|
+
#shell_out "elinks #{url}"
|
203
|
+
shell_out "#{app} #{url}"
|
204
|
+
#Window.refresh_all
|
205
|
+
end
|
206
|
+
|
207
|
+
##
|
208
|
+
# Menu creator which displays a menu and executes methods based on keys.
|
209
|
+
# In some cases, we call this and then do a case statement on either key or binding.
|
210
|
+
# @param String title
|
211
|
+
# @param hash of keys and methods to call
|
212
|
+
# @return key pressed, and binding (if found, and responded). Can return NIL nil if esc pressed
|
163
213
|
#
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
214
|
+
def menu title, hash, config={}, &block
|
215
|
+
raise ArgumentError, "Nil hash received by menu" unless hash
|
216
|
+
list = []
|
217
|
+
list << config[:subtitle] if config[:subtitle]
|
218
|
+
config.delete(:subtitle)
|
219
|
+
hash.each_pair { |k, v| list << " #[fg=yellow, bold] #{k} #[/end] #[fg=green] #{v} #[/end]" }
|
220
|
+
# s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
|
221
|
+
config[:title] = title
|
222
|
+
config[:width] = hash.values.max_by(&:length).length + 13
|
223
|
+
# need to have a proper check, which takes +left+ / column into account
|
224
|
+
config[:width] = FFI::NCurses.COLS - 10 if config[:width] > FFI::NCurses.COLS
|
225
|
+
ch = padpopup list, config, &block
|
226
|
+
return unless ch
|
227
|
+
if ch.size > 1
|
228
|
+
# could be a string due to pressing enter
|
229
|
+
# but what if we format into multiple columns
|
230
|
+
ch = ch.strip[0]
|
231
|
+
end
|
232
|
+
|
233
|
+
binding = hash[ch]
|
234
|
+
binding = hash[ch.to_sym] unless binding
|
235
|
+
if binding
|
236
|
+
if respond_to?(binding, true)
|
237
|
+
send(binding)
|
182
238
|
end
|
239
|
+
end
|
240
|
+
return ch, binding
|
241
|
+
end
|
242
|
+
# pops up a list, taking a single key and returning if it is in range of 33 and 126
|
243
|
+
# Called by menu, print_help, show_marks etc
|
244
|
+
# You may pass valid chars or ints so it only returns on pressing those.
|
245
|
+
#
|
246
|
+
# @param Array of lines to print which may be formatted using :tmux format
|
247
|
+
# @return character pressed (ch.chr)
|
248
|
+
# @return nil if escape or C-q pressed
|
249
|
+
#
|
250
|
+
def padpopup list, config={}, &block
|
251
|
+
max_visible_items = config[:max_visible_items]
|
252
|
+
row = config[:row] || 1
|
253
|
+
col = config[:col] || 1
|
254
|
+
# format options are :ansi :tmux :none
|
255
|
+
fmt = config[:format] || :tmux
|
256
|
+
config.delete :format
|
257
|
+
relative_to = config[:relative_to]
|
258
|
+
if relative_to
|
259
|
+
layout = relative_to.form.window.layout
|
260
|
+
row += layout[:top]
|
261
|
+
col += layout[:left]
|
262
|
+
end
|
263
|
+
config.delete :relative_to
|
264
|
+
# still has the formatting in the string so length is wrong.
|
265
|
+
#longest = list.max_by(&:length)
|
266
|
+
width = config[:width] || 60
|
267
|
+
if config[:title]
|
268
|
+
width = config[:title].size + 2 if width < config[:title].size
|
269
|
+
end
|
270
|
+
height = config[:height]
|
271
|
+
height ||= [max_visible_items || 25, list.length+2].min
|
272
|
+
#layout(1+height, width+4, row, col)
|
273
|
+
layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
|
274
|
+
window = Canis::Window.new(layout)
|
275
|
+
form = Canis::Form.new window
|
183
276
|
|
184
|
-
|
185
|
-
|
186
|
-
|
277
|
+
## added 2013-03-13 - 18:07 so caller can be more specific on what is to be returned
|
278
|
+
valid_keys_int = config.delete :valid_keys_int
|
279
|
+
valid_keys_char = config.delete :valid_keys_char
|
187
280
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
281
|
+
listconfig = config[:listconfig] || {}
|
282
|
+
#listconfig[:list] = list
|
283
|
+
listconfig[:width] = width
|
284
|
+
listconfig[:height] = height
|
285
|
+
# pass this in config so less dependences
|
286
|
+
listconfig[:bgcolor] = @color_scheme[:menu_bg]
|
287
|
+
#listconfig[:selection_mode] ||= :single
|
288
|
+
listconfig.merge!(config)
|
289
|
+
listconfig.delete(:row);
|
290
|
+
listconfig.delete(:col);
|
291
|
+
#listconfig[:row] = 1
|
292
|
+
#listconfig[:col] = 1
|
293
|
+
# trying to pass populists block to listbox
|
294
|
+
lb = Canis::TextPad.new form, listconfig, &block
|
295
|
+
if fmt == :none
|
296
|
+
lb.text(list)
|
297
|
+
else
|
298
|
+
lb.text(list, fmt)
|
299
|
+
end
|
300
|
+
#
|
301
|
+
#window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
|
302
|
+
form.repaint
|
303
|
+
Ncurses::Panel.update_panels
|
304
|
+
if valid_keys_int.nil? && valid_keys_char.nil?
|
305
|
+
# changed 32 to 33 so space can scroll list
|
306
|
+
valid_keys_int = (33..126)
|
307
|
+
end
|
308
|
+
|
309
|
+
begin
|
310
|
+
while((ch = window.getchar()) != 999 )
|
311
|
+
|
312
|
+
# if a char range or array has been sent, check if the key is in it and send back
|
313
|
+
# else just stay here
|
314
|
+
if valid_keys_char
|
315
|
+
if ch > 32 && ch < 127
|
316
|
+
chr = ch.chr
|
317
|
+
return chr if valid_keys_char.include? chr
|
318
|
+
end
|
195
319
|
end
|
196
|
-
|
197
|
-
if
|
198
|
-
|
320
|
+
|
321
|
+
# if the user specified an array or range of ints check against that
|
322
|
+
# therwise use the range of 33 .. 126
|
323
|
+
return ch.chr if valid_keys_int.include? ch
|
324
|
+
|
325
|
+
case ch
|
326
|
+
when ?\C-q.getbyte(0)
|
327
|
+
break
|
328
|
+
else
|
329
|
+
if ch == 13 || ch == 10
|
330
|
+
s = lb.current_value.to_s # .strip #if lb.selection_mode != :multiple
|
331
|
+
return s
|
332
|
+
end
|
333
|
+
# close if escape or double escape
|
334
|
+
if ch == 27 || ch == 2727
|
335
|
+
return nil
|
336
|
+
end
|
337
|
+
lb.handle_key ch
|
338
|
+
form.repaint
|
199
339
|
end
|
200
|
-
lb.handle_key ch
|
201
|
-
form.repaint
|
202
340
|
end
|
341
|
+
ensure
|
342
|
+
window.destroy
|
203
343
|
end
|
204
|
-
|
205
|
-
window.destroy
|
344
|
+
return nil
|
206
345
|
end
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
def main_menu
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
h = {
|
224
|
-
"t" => :toggle_titles_only,
|
225
|
-
:x => :extras
|
226
|
-
}
|
227
|
-
ch, binding = menu "Main Menu", h
|
228
|
-
#alert "Menu got #{ch}, #{binding}" if ch
|
229
|
-
end
|
230
|
-
def color_scheme_select ch=nil
|
231
|
-
unless ch
|
232
|
-
h = {
|
233
|
-
"0" => 'dark blue body',
|
234
|
-
"1" => 'medium blue body',
|
235
|
-
"2" => 'black body',
|
236
|
-
"3" => 'grey body',
|
237
|
-
"b" => 'change body color',
|
238
|
-
"f" => 'change body fg color',
|
239
|
-
"c" => 'cycle body color'
|
240
|
-
}
|
241
|
-
ch, binding = menu "Color Menu", h
|
242
|
-
end
|
243
|
-
case ch
|
244
|
-
when "1", "2", "0", "3"
|
245
|
-
$color_scheme = COLOR_SCHEMES[ch.to_i] || COLOR_SCHEMES.first
|
246
|
-
$fg = $color_scheme[4]
|
247
|
-
when "b"
|
248
|
-
n = get_string "Enter a number for background color (0..255): "
|
249
|
-
n = n.to_i
|
250
|
-
$color_scheme[2] = n
|
251
|
-
when "4", "f"
|
252
|
-
n = get_string "Enter a number for fg color (0..255) : "
|
253
|
-
$fg = n.to_i
|
254
|
-
when "c"
|
255
|
-
# increment bg color
|
256
|
-
n = $color_scheme[2]
|
257
|
-
n += 1
|
258
|
-
n = 0 if n > 255
|
259
|
-
$color_scheme[2] = n
|
260
|
-
when "C"
|
261
|
-
# decrement bg color
|
262
|
-
n = $color_scheme[2]
|
263
|
-
n -= 1
|
264
|
-
n = 255 if n < 0
|
265
|
-
$color_scheme[2] = n
|
266
|
-
end
|
267
|
-
|
268
|
-
h = @form.by_name["header"]
|
269
|
-
tv = @form.by_name["tv"]
|
270
|
-
sl = @form.by_name["sl"]
|
271
|
-
tv.bgcolor = $color_scheme[2]
|
272
|
-
#tv.color = 255
|
273
|
-
tv.color = $fg
|
274
|
-
sl.color = $color_scheme[3]
|
275
|
-
h.bgcolor = $color_scheme[0]
|
276
|
-
message "bgcolor is #{$color_scheme[2]}. :: #{$color_scheme.join(",")}, CP:#{tv.color_pair}=#{tv.color} / #{tv.bgcolor} "
|
277
|
-
refresh
|
278
|
-
end
|
279
|
-
def refresh
|
280
|
-
show $current_file
|
281
|
-
end
|
282
|
-
|
283
|
-
def toggle_titles_only
|
284
|
-
$toggle_titles_only = !$toggle_titles_only
|
285
|
-
show $current_file
|
286
|
-
end
|
287
|
-
App.new do
|
288
|
-
@startdir ||= File.expand_path("..")
|
289
|
-
@hash = nil
|
290
|
-
def get_item_for_line line
|
291
|
-
index = (line - @hash[:first]) / @hash[:diff]
|
292
|
-
@hash[:articles][index]
|
293
|
-
end
|
294
|
-
def title_right text
|
295
|
-
w = @form.by_name["header"]
|
296
|
-
w.text_right text
|
297
|
-
end
|
298
|
-
def title text
|
299
|
-
w = @form.by_name["header"]
|
300
|
-
w.text_center text
|
301
|
-
end
|
302
|
-
def color_line(fg,bg,attr,text)
|
303
|
-
a = "#["
|
304
|
-
a = []
|
305
|
-
a << "fg=#{fg}" if fg
|
306
|
-
a << "bg=#{bg}" if bg
|
307
|
-
a << "#{attr}" if attr
|
308
|
-
str = "#[" + a.join(",") + "]#{text}#[end]"
|
309
|
-
end
|
310
|
-
def goto_article n=$multiplier
|
311
|
-
i = ((n-1) * @hash[:diff]) + @hash[:first]
|
312
|
-
w = @form.by_name["tv"]
|
313
|
-
w.goto_line i
|
314
|
-
end
|
315
|
-
|
316
|
-
def OLDshow file
|
317
|
-
w = @form.by_name["tv"]
|
318
|
-
if File.directory? file
|
319
|
-
lines = Dir.entries(file)
|
320
|
-
w.text lines
|
321
|
-
w.title "[ #{file} ]"
|
322
|
-
elsif File.exists? file
|
323
|
-
lines = File.open(file,'r').readlines
|
324
|
-
w.text lines
|
325
|
-
w.title "[ #{file} ]"
|
346
|
+
# main options, invokable on backtick.
|
347
|
+
# TODO add selection of browser
|
348
|
+
# r for reload
|
349
|
+
# 1,2 a c view article, comments
|
350
|
+
def main_menu
|
351
|
+
h = {
|
352
|
+
:f => :choose_forum,
|
353
|
+
:c => :color_scheme_select,
|
354
|
+
#:s => :sort_menu,
|
355
|
+
#:F => :filter_menu,
|
356
|
+
:a => :add_forum,
|
357
|
+
:d => :remove_forum,
|
358
|
+
:x => :extras
|
359
|
+
}
|
360
|
+
ch, binding = menu "Main Menu", h
|
361
|
+
#alert "Menu got #{ch}, #{binding}" if ch
|
326
362
|
end
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
else
|
346
|
-
alert "Host not known: #{url} "
|
347
|
-
end
|
348
|
-
articles = obj[:articles]
|
349
|
-
count = articles.count
|
350
|
-
#lines << color_line(:red,COLOR_SCHEME[1],nil,"#{file} #{obj[:page_url]} | #{count} articles | fetched #{obj[:create_time]}")
|
351
|
-
#lines << ("-" * lines.last.size )
|
352
|
-
@hash = Hash.new
|
353
|
-
@hash[:first] = lines.size
|
354
|
-
@hash[:articles] = articles
|
355
|
-
|
356
|
-
articles.each_with_index do |a, i|
|
357
|
-
bg = i
|
358
|
-
bg = 0 if i > 255
|
359
|
-
line = "%3s %s " % [i+1 , a[:title] ]
|
360
|
-
#lines << color_line($fg, bg, nil, line)
|
361
|
-
lines << line
|
362
|
-
if !$toggle_titles_only
|
363
|
-
url = a[:article_url] || a[:url]
|
364
|
-
l = " %s | %s" % [url, a[:comments_url] ]
|
365
|
-
l = "#[fg=green, underline]" + l + "#[end]"
|
366
|
-
lines << l
|
367
|
-
detail = []
|
368
|
-
if a.key? :comment_count
|
369
|
-
detail << a[:comment_count]
|
363
|
+
# TODO uses text browser t, use gui browser g
|
364
|
+
# l - long list (what is currently t)
|
365
|
+
def toggle_menu
|
366
|
+
h = {
|
367
|
+
"t" => :toggle_titles_only,
|
368
|
+
"O" => :toggle_offline
|
369
|
+
#:x => :extras
|
370
|
+
}
|
371
|
+
ch, binding = menu "Main Menu", h
|
372
|
+
#alert "Menu got #{ch}, #{binding}" if ch
|
373
|
+
end
|
374
|
+
def color_scheme_select ch=nil
|
375
|
+
unless ch
|
376
|
+
h = {}
|
377
|
+
ctr = 0
|
378
|
+
@color_schemes.each_pair do |k,v|
|
379
|
+
ctr += 1
|
380
|
+
h[ctr.to_s] = k
|
370
381
|
end
|
371
|
-
|
372
|
-
|
382
|
+
|
383
|
+
h = h.merge({
|
384
|
+
#"0" => 'dark blue body',
|
385
|
+
#"1" => 'medium blue body',
|
386
|
+
#"2" => 'black body',
|
387
|
+
#"3" => 'grey body',
|
388
|
+
"b" => 'change body color',
|
389
|
+
"f" => 'change body fg color',
|
390
|
+
"d" => 'change body detail color',
|
391
|
+
"c" => 'cycle body color'
|
392
|
+
})
|
393
|
+
ch, binding = menu "Color Menu", h
|
394
|
+
end
|
395
|
+
case ch
|
396
|
+
when "1", "2", "0", "3","4","5","6"
|
397
|
+
@color_scheme = @color_schemes[binding]
|
398
|
+
@fg = @color_scheme[:body_fg]
|
399
|
+
when "b"
|
400
|
+
n = get_string "Enter a number for background color (0..255): "
|
401
|
+
unless n =~ /^\d+$/
|
402
|
+
n = Canis::ColorMap.colors.index(n.to_sym)
|
403
|
+
return unless n
|
373
404
|
end
|
374
|
-
|
375
|
-
|
376
|
-
|
405
|
+
n = n.to_i
|
406
|
+
@color_scheme[:body_bg] = n
|
407
|
+
when "f"
|
408
|
+
n = get_string "Enter a number for fg color (0..255) : "
|
409
|
+
unless n =~ /^\d+$/
|
410
|
+
n = Canis::ColorMap.colors.index(n.to_sym)
|
411
|
+
return unless n
|
377
412
|
end
|
413
|
+
@fg = n.to_i
|
414
|
+
@color_scheme[:body_fg] = n.to_i
|
415
|
+
when "d"
|
416
|
+
n = get_string "Enter a number for detail line color (0..255): "
|
417
|
+
unless n =~ /^\d+$/
|
418
|
+
n = Canis::ColorMap.colors.index(n.to_sym)
|
419
|
+
return unless n
|
420
|
+
end
|
421
|
+
n = n.to_i
|
422
|
+
@color_scheme[:body_detail] = n
|
423
|
+
when "c"
|
424
|
+
# increment bg color
|
425
|
+
n = @color_scheme[:body_bg]
|
426
|
+
n += 1
|
427
|
+
n = 0 if n > 255
|
428
|
+
@color_scheme[:body_bg] = n
|
429
|
+
when "C"
|
430
|
+
# decrement bg color
|
431
|
+
n = @color_scheme[:body_bg]
|
432
|
+
n -= 1
|
433
|
+
n = 255 if n < 0
|
434
|
+
@color_scheme[:body_bg] = n
|
378
435
|
end
|
379
|
-
|
436
|
+
|
437
|
+
h = @form.by_name["header"]
|
438
|
+
tv = @form.by_name["tv"]
|
439
|
+
sl = @form.by_name["sl"]
|
440
|
+
tv.bgcolor = @color_scheme[:body_bg]
|
441
|
+
#tv.color = 255
|
442
|
+
tv.color = @fg
|
443
|
+
sl.color = @color_scheme[:status_bg]
|
444
|
+
h.bgcolor = @color_scheme[:header_bg]
|
445
|
+
#@app.message "bgcolor is #{@color_scheme[:body_bg]}. :: #{@color_scheme.join(",")}, CP:#{tv.color_pair}=#{tv.color} / #{tv.bgcolor} "
|
446
|
+
refresh
|
447
|
+
end
|
448
|
+
def extras
|
449
|
+
h = {
|
450
|
+
"s" => :save_config
|
451
|
+
}
|
452
|
+
ch, binding = menu "Extras ", h
|
453
|
+
end
|
454
|
+
def refresh
|
455
|
+
display_yml @current_file
|
380
456
|
end
|
381
|
-
w.text(lines, :content_type => :tmux)
|
382
|
-
w.title "[ #{file} ]"
|
383
457
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
458
|
+
def toggle_titles_only
|
459
|
+
@toggle_titles_only = !@toggle_titles_only
|
460
|
+
show @current_file
|
461
|
+
end
|
462
|
+
def toggle_offline
|
463
|
+
@toggle_offline = !@toggle_offline
|
464
|
+
end
|
465
|
+
# moved from inside App
|
466
|
+
#
|
467
|
+
def get_item_for_line line
|
468
|
+
index = (line - @hash[:first]) / @hash[:diff]
|
469
|
+
@hash[:articles][index]
|
470
|
+
end
|
471
|
+
def title_right text
|
472
|
+
w = @form.by_name["header"]
|
473
|
+
w.text_right text
|
474
|
+
end
|
475
|
+
def title text
|
476
|
+
w = @form.by_name["header"]
|
477
|
+
w.text_center text
|
478
|
+
end
|
479
|
+
def color_line(fg,bg,attr,text)
|
480
|
+
a = "#["
|
481
|
+
a = []
|
482
|
+
a << "fg=#{fg}" if fg
|
483
|
+
a << "bg=#{bg}" if bg
|
484
|
+
a << "#{attr}" if attr
|
485
|
+
str = "#[" + a.join(",") + "]#{text}#[end]"
|
486
|
+
end
|
487
|
+
def goto_article n=$multiplier
|
488
|
+
i = ((n-1) * @hash[:diff]) + @hash[:first]
|
489
|
+
w = @form.by_name["tv"]
|
490
|
+
w.goto_line i
|
491
|
+
end
|
492
|
+
def display_links
|
493
|
+
# if multiplier is 0, use current line
|
494
|
+
art = self.articles[$multiplier - 1]
|
495
|
+
if $multiplier == 0
|
496
|
+
tv = @form.by_name["tv"]
|
497
|
+
index = tv.current_index
|
498
|
+
art = get_item_for_line index
|
400
499
|
end
|
500
|
+
show_links art
|
401
501
|
end
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
502
|
+
|
503
|
+
# display the given yml file.
|
504
|
+
# Converts the yml object to an array for textpad
|
505
|
+
def display_yml file
|
506
|
+
w = @form.by_name["tv"]
|
507
|
+
|
508
|
+
obj = YAML::load( File.open( file ) )
|
509
|
+
lines = Array.new
|
510
|
+
url = obj[:page_url]
|
511
|
+
host = nil
|
512
|
+
if url.index("reddit")
|
513
|
+
host = "reddit"
|
514
|
+
elsif url.index("ycombinator")
|
515
|
+
host = "hacker"
|
516
|
+
elsif url.index("ars")
|
517
|
+
host = "ars"
|
518
|
+
elsif url.index("slashdot")
|
519
|
+
host = "slashdot"
|
520
|
+
else
|
521
|
+
alert "Host not known: #{url} "
|
522
|
+
end
|
523
|
+
articles = obj[:articles]
|
524
|
+
count = articles.count
|
525
|
+
#lines << color_line(:red,COLOR_SCHEME[1],nil,"#{file} #{obj[:page_url]} | #{count} articles | fetched #{obj[:create_time]}")
|
526
|
+
#lines << ("-" * lines.last.size )
|
527
|
+
@hash = Hash.new
|
528
|
+
@hash[:first] = lines.size
|
529
|
+
@hash[:articles] = articles
|
530
|
+
dc = @color_scheme[:body_detail]
|
531
|
+
|
532
|
+
articles.each_with_index do |a, i|
|
533
|
+
bg = i
|
534
|
+
bg = 0 if i > 255
|
535
|
+
line = "%3s %s " % [i+1 , a[:title] ]
|
536
|
+
#lines << color_line(@fg, bg, nil, line)
|
537
|
+
lines << line
|
538
|
+
if !@toggle_titles_only
|
539
|
+
line1 = []
|
540
|
+
line2 = []
|
541
|
+
url = a[:article_url] || a[:url]
|
542
|
+
line1 << url
|
543
|
+
#l = " %s | %s" % [url, a[:comments_url] ]
|
544
|
+
line2 << a[:comments_url] if a[:comments_url]
|
545
|
+
#l = "#[fg=#{dc}, underline]" + l + "#[end]"
|
546
|
+
#lines << l
|
547
|
+
#detail = []
|
548
|
+
if a.key? :comment_count
|
549
|
+
#detail << a[:comment_count]
|
550
|
+
line1 << a[:comment_count]
|
551
|
+
end
|
552
|
+
if a.key? :pubdate
|
553
|
+
#detail << a[:pubdate]
|
554
|
+
line2 << a[:pubdate]
|
555
|
+
end
|
556
|
+
#unless detail.empty?
|
557
|
+
l = "#[fg=#{dc}]" + " " + line1.join(" | ") + "#[end]"
|
558
|
+
lines << l
|
559
|
+
l = "#[fg=#{dc}]" + " " + line2.join(" | ") + "#[end]"
|
560
|
+
lines << l
|
561
|
+
#end
|
562
|
+
end
|
563
|
+
@hash[:diff] ||= lines.size - @hash[:first]
|
564
|
+
end
|
565
|
+
w.text(lines, :content_type => :tmux)
|
566
|
+
w.title "[ #{file} ]"
|
567
|
+
|
568
|
+
i = @hash[:first] || 1
|
569
|
+
w.goto_line i
|
570
|
+
@current_file = file
|
571
|
+
#@current_forum = file_to_forum file
|
572
|
+
title "#{@current_forum} (#{count} articles) "
|
573
|
+
title_right obj[:create_time]
|
574
|
+
end
|
575
|
+
def file_to_forum filename
|
576
|
+
forum = File.basename(filename).sub(File.extname(filename),"").sub("__","/")
|
577
|
+
end
|
578
|
+
def forum_to_file forum
|
579
|
+
file = "#{forum}.yml".sub("/","__")
|
580
|
+
file = "#{@cache_path}/#{file}"
|
581
|
+
end
|
582
|
+
alias :show :display_yml
|
583
|
+
def get_data forum
|
584
|
+
#file = forum + ".yml"
|
585
|
+
file = forum_to_file forum
|
586
|
+
if File.exists? file and fresh?(file)
|
587
|
+
else
|
588
|
+
progress_dialog :color_pair => $reversecolor do |sw|
|
589
|
+
sw.print "Fetching #{forum} ..."
|
590
|
+
#system("hackercli.rb -y #{file} #{forum}")
|
591
|
+
o,e,s = Open3.capture3("hackercli.rb -y #{file} #{forum}")
|
592
|
+
unless s.success?
|
593
|
+
$log.debug " error from capture3 #{e}"
|
594
|
+
alert e
|
595
|
+
return
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|
599
|
+
if File.exists? file
|
600
|
+
@current_forum = forum
|
601
|
+
display_yml file
|
602
|
+
else
|
603
|
+
alert "#{file} not created. Check externally. run hackercli.rb -y #{file} #{forum} externally"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
# return true if younger than one hour
|
607
|
+
def fresh? file
|
608
|
+
return true if @toggle_offline
|
609
|
+
|
610
|
+
f = File.stat(file)
|
611
|
+
now = Time.now
|
612
|
+
return (( now - f.mtime) < 7200)
|
613
|
+
end
|
614
|
+
def show_links art
|
615
|
+
return unless art
|
411
616
|
links = {}
|
412
617
|
keys = %w{a b c d e f}
|
413
618
|
i = 0
|
@@ -417,61 +622,154 @@ App.new do
|
|
417
622
|
i += 1
|
418
623
|
end
|
419
624
|
end
|
420
|
-
ch, binding = menu "
|
625
|
+
ch, binding = menu "Select a link", links, :subtitle => " Enter Upper case letter to open in gui"
|
421
626
|
#alert "is #{index}: #{art[:title]} #{ch}:#{binding} "
|
627
|
+
app = @browser_text || "elinks"
|
628
|
+
unless binding
|
629
|
+
return unless ch
|
630
|
+
# it must be an upper case for GUI
|
631
|
+
return unless ch == ch.upcase
|
632
|
+
ch = ch.downcase
|
633
|
+
return unless keys.include? ch
|
634
|
+
binding = links[ch]
|
635
|
+
app = @browser_gui || "open"
|
636
|
+
end
|
422
637
|
if binding
|
423
|
-
open_url binding
|
638
|
+
open_url binding, app
|
424
639
|
end
|
640
|
+
end
|
641
|
+
# since this does not happen inside form's loop, therefore form is unable to repaint, repaint
|
642
|
+
# happens only after a keystroke
|
643
|
+
# This allows us to pass in a hash with string names for methods. This hash can be easily updated,
|
644
|
+
# or even read in from a config file/yml file. It is assumed here that all the string names
|
645
|
+
# correspond to names of methods withing this class, so no class references are required.
|
646
|
+
# TODO split the command if there are spaces.
|
647
|
+
def handle_keys hash
|
648
|
+
@app.keypress do |str|
|
649
|
+
binding = hash[str]
|
650
|
+
if binding
|
651
|
+
binding = binding.to_sym
|
652
|
+
if respond_to?(binding, true)
|
653
|
+
send(binding)
|
654
|
+
else
|
655
|
+
#alert "unresponded to #{str}"
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
# Should work on this as a means of binding each element of a hash into forms keymap.
|
662
|
+
# FIXME works except that multiplier not working ??
|
663
|
+
def form_bind hash
|
664
|
+
hash.each_pair do |k, v|
|
665
|
+
nk = key_to_i(k)
|
666
|
+
desc = "??"
|
667
|
+
desc = v if v.is_a? String or v.is_a? Symbol
|
668
|
+
@form.bind_key(nk, desc) { self.send(v) }
|
669
|
+
end
|
670
|
+
end
|
671
|
+
# convert a key in the format to an int so it can be mapped using bind_key
|
672
|
+
# "[a-zA-Z"] etc a single cahr
|
673
|
+
# C-a to C-z
|
674
|
+
# M-a to M-z
|
675
|
+
# F1 .. F10
|
676
|
+
# This does not take complex cases yet. It is a simplistic conversion.
|
677
|
+
def key_to_i k
|
678
|
+
if k.size == 1
|
679
|
+
return k.getbyte(0)
|
680
|
+
end
|
681
|
+
if k =~ /^<M-/
|
682
|
+
ch = k[3]
|
683
|
+
return 128 + ch.ord
|
684
|
+
elsif k == "<CR>"
|
685
|
+
return 13
|
686
|
+
elsif k =~ /^<[Cc]/
|
687
|
+
ch = k[3]
|
688
|
+
x = ch.ord - "a".ord + 1
|
689
|
+
elsif k[0,2] == "<F"
|
690
|
+
ch = k[2..-2]
|
691
|
+
return 264 + ch.to_i
|
692
|
+
else
|
693
|
+
alert "not able to bind #{k}"
|
694
|
+
end
|
695
|
+
|
696
|
+
end
|
697
|
+
end # class
|
698
|
+
end # module HackerCli
|
699
|
+
include HackerCli
|
700
|
+
|
701
|
+
# http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
|
702
|
+
require 'optparse'
|
703
|
+
options = {}
|
704
|
+
app = File.basename $0
|
705
|
+
OptionParser.new do |opts|
|
706
|
+
opts.banner = %Q{
|
707
|
+
#{app} version #{VERSION} (YML version)
|
708
|
+
Usage: #{app} [options]
|
709
|
+
}
|
710
|
+
|
711
|
+
#opts.on("-m MODE", String,"--mode", "Use 'text' or 'gui' browser") do |v|
|
712
|
+
#options[:browser_mode] = v
|
713
|
+
#end
|
714
|
+
opts.on("-t browser", String,"--text", "browser for text mode, default elinks") do |v|
|
715
|
+
options[:browser_text] = v
|
716
|
+
end
|
717
|
+
opts.on("-g browser", String,"--gui", "browser for gui mode, default open") do |v|
|
718
|
+
options[:browser_gui] = v
|
425
719
|
end
|
426
|
-
|
427
|
-
|
720
|
+
opts.on("-c cache dir", String,"--cache-dir", "location to store yml files, default .") do |v|
|
721
|
+
options[:cache_path] = File.expand_path(v)
|
722
|
+
end
|
723
|
+
opts.on("-u config_file", String,"--config-file", "path to load config info from") do |v|
|
724
|
+
options[:config_file] = v
|
725
|
+
end
|
726
|
+
opts.on("--list x,y,z", Array, "Example 'list' of forums: hacker,ruby,programming...") do |list|
|
727
|
+
options[:list] = list
|
728
|
+
end
|
729
|
+
# file age in hours
|
730
|
+
# offline mode
|
731
|
+
# config file path
|
732
|
+
end.parse!
|
733
|
+
App.new do
|
734
|
+
def logger; return @log; end
|
735
|
+
@log = create_logger "hacker.log"
|
736
|
+
@h = Hackman.new self, options
|
737
|
+
@color_scheme = @h.color_scheme
|
428
738
|
@header = app_header "hackman #{VERSION}", :text_center => "RSS Reader", :name => "header",
|
429
|
-
:text_right =>"
|
739
|
+
:text_right =>"Menu `", :color => :white, :bgcolor => @color_scheme[:header_bg]
|
430
740
|
message "Press F10 (or qq) to exit, F1 Help, ` for Menu "
|
431
741
|
|
432
742
|
|
743
|
+
|
433
744
|
|
434
745
|
# commands that can be mapped to or executed using M-x
|
435
746
|
# however, commands of components aren't yet accessible.
|
436
747
|
def get_commands
|
437
748
|
%w{ choose_forum next_forum prev_forum }
|
438
749
|
end
|
750
|
+
# help text for F1, but this needs to be kept consistent with @bindings,
|
751
|
+
# if that is changed, then how does this show the change, considering that
|
752
|
+
# the config file will be read in Hackman, not here.
|
439
753
|
def help_text
|
440
754
|
<<-eos
|
441
|
-
|
442
|
-
|
443
|
-
These are some features for either getting filenames from user
|
444
|
-
at the bottom of the window like vim and others do, or filtering
|
445
|
-
from a list (like ControlP plugin). Or seeing a file at bottom
|
446
|
-
of screen for a quick preview.
|
755
|
+
Hackman Help
|
447
756
|
|
448
|
-
|
757
|
+
F2 - forum selection (interface like Ctrl-P, very minimal)
|
449
758
|
F1 - Help
|
450
759
|
F10 - Quit application
|
451
760
|
qq - Quit application
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
Press <ENTER> to select, arrow keys to traverse,
|
465
|
-
and characters to filter list.
|
466
|
-
testdisplaytext - display text at bottom (current file contents)
|
467
|
-
Press <ENTER> when done.
|
468
|
-
|
469
|
-
The file/dir selection options are very minimally functional. Improvements
|
470
|
-
and thorough testing are required. I've only tested them out gingerly.
|
471
|
-
|
472
|
-
testchoosedir and file were earlier like Emacs/memacs with TAB completion
|
473
|
-
but have now moved to the much faster and friendlier ControlP plugin like
|
474
|
-
'filter as you type' format.
|
761
|
+
|
762
|
+
` (backtick) - Main Menu (add, remove, change forum)
|
763
|
+
= (Equal) - Toggle Menu (titles only)
|
764
|
+
|
765
|
+
o - open url menu for current article (under cursor)
|
766
|
+
<n>o - open url menu for <n>th article
|
767
|
+
<n>z - goto <n>th article
|
768
|
+
|
769
|
+
"<" - previous forum in list
|
770
|
+
">" - next forum in list
|
771
|
+
|
772
|
+
"/" - search within the page (case-sensitive). Append "/i" to ignore case.
|
475
773
|
|
476
774
|
-----------------------------------------------------------------------
|
477
775
|
:n or Alt-n for general help.
|
@@ -481,51 +779,52 @@ App.new do
|
|
481
779
|
#install_help_text help_text
|
482
780
|
|
483
781
|
def app_menu
|
484
|
-
|
485
|
-
Dir.chdir(@curdir) if Dir.pwd != @curdir
|
782
|
+
# TODO update and fix this
|
486
783
|
require 'canis/core/util/promptmenu'
|
487
784
|
menu = PromptMenu.new self do
|
488
785
|
item :f, :choose_forum
|
489
|
-
item :
|
786
|
+
item :n, :next_forum
|
787
|
+
item :p, :prev_forum
|
788
|
+
item :a, :add_forum
|
789
|
+
item :d, :remove_forum
|
490
790
|
end
|
491
791
|
menu.display_new :title => "Menu"
|
492
792
|
end
|
493
793
|
# BINDING SECTION
|
494
|
-
|
495
|
-
|
496
|
-
@form.bind_key(
|
497
|
-
@form.bind_key(FFI::NCurses::
|
498
|
-
@form.bind_key(FFI::NCurses::
|
499
|
-
@form.bind_key(
|
794
|
+
if false
|
795
|
+
#@form.bind_key(?:, "App Menu") { app_menu; }
|
796
|
+
@form.bind_key(?`, "Main Menu") { @h.main_menu; }
|
797
|
+
@form.bind_key(FFI::NCurses::KEY_F2, "Main Menu") { @h.choose_forum; }
|
798
|
+
@form.bind_key(FFI::NCurses::KEY_F3, "Cycle bgcolor") { @h.color_scheme_select "c"; }
|
799
|
+
@form.bind_key(FFI::NCurses::KEY_F4, "Cycle bgcolor") { @h.color_scheme_select "C"; }
|
800
|
+
@form.bind_key($kh_int["S-F3"], "Cycle bgcolor") { @h.color_scheme_select "C"; }
|
500
801
|
@form.bind_key(?=, "Toggle Menu") {
|
501
|
-
toggle_menu;
|
802
|
+
@h.toggle_menu;
|
502
803
|
}
|
503
|
-
@form.bind_key(?<, "Previous Forum") { prev_forum; }
|
504
|
-
@form.bind_key(?>, "Next Forum") { next_forum; }
|
804
|
+
@form.bind_key(?<, "Previous Forum") { @h.prev_forum; }
|
805
|
+
@form.bind_key(?>, "Next Forum") { @h.next_forum; }
|
806
|
+
end
|
505
807
|
|
808
|
+
@form.help_manager.help_text = help_text
|
809
|
+
|
810
|
+
begin
|
506
811
|
stack :margin_top => 1, :margin_left => 0, :width => :expand , :height => FFI::NCurses.LINES-2 do
|
507
812
|
tv = textpad :height_pc => 100, :width_pc => 100, :name => "tv", :suppress_borders => true,
|
508
|
-
:bgcolor =>
|
813
|
+
:bgcolor => @color_scheme[:body_bg], :color => 255, :attr => NORMAL
|
509
814
|
#tv.renderer ruby_renderer
|
510
|
-
tv.bind(:PRESS) {|ev|
|
511
|
-
index = ev.current_index
|
512
|
-
art = get_item_for_line index
|
513
|
-
show_links art
|
514
|
-
}
|
515
|
-
tv.bind_key(?z) { goto_article }
|
516
|
-
tv.bind_key(?o) {
|
517
|
-
# if multiplier is 0, use current line
|
518
|
-
art = @hash[:articles][$multiplier - 1]
|
519
|
-
if $multiplier == 0
|
520
|
-
index = tv.current_index
|
521
|
-
art = get_item_for_line index
|
522
|
-
end
|
523
|
-
show_links art
|
524
|
-
}
|
815
|
+
#tv.bind(:PRESS) {|ev| display_links }
|
525
816
|
tv.text_patterns[:articles] = Regexp.new(/^ *\d+ /)
|
526
817
|
tv.bind_key(KEY_TAB, "goto article") { tv.next_regex(:articles) }
|
527
818
|
end # stack
|
528
819
|
|
529
|
-
sl = status_line :row => Ncurses.LINES-1, :bgcolor => :yellow, :color =>
|
530
|
-
choose_forum
|
820
|
+
sl = status_line :row => Ncurses.LINES-1, :bgcolor => :yellow, :color => @color_scheme[:status_bg]
|
821
|
+
@h.choose_forum
|
822
|
+
rescue => ex
|
823
|
+
textdialog ["Error in hackman: #{ex} ", *ex.backtrace], :title => "Exception"
|
824
|
+
$log.debug( ex) if ex
|
825
|
+
$log.debug(ex.backtrace.join("\n")) if ex
|
826
|
+
ensure
|
827
|
+
p ex if ex
|
828
|
+
p(ex.backtrace.join("\n")) if ex
|
829
|
+
end
|
531
830
|
end # app
|
data/lib/hackercli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hackercli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kepler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08
|
11
|
+
date: 2014-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|