hackercli 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|