arTTY 0.9.48
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/arTTY +486 -0
- data/lib/arTTY.rb +78 -0
- data/lib/arTTY/art.rb +111 -0
- data/lib/arTTY/cache.rb +117 -0
- data/lib/arTTY/error.rb +9 -0
- data/lib/arTTY/error/art_not_found.rb +6 -0
- data/lib/arTTY/error/failed_to_download.rb +5 -0
- data/lib/arTTY/error/image_magick_not_found.rb +5 -0
- data/lib/arTTY/error/image_named_improperly.rb +6 -0
- data/lib/arTTY/error/image_not_found.rb +6 -0
- data/lib/arTTY/error/no_pixel_data_found.rb +5 -0
- data/lib/arTTY/generator.rb +156 -0
- data/lib/arTTY/system_info.rb +212 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: df17d6bc87a48aafd84c5371ff5bad973bc9c5e6
|
4
|
+
data.tar.gz: abcb987b6e79acfe71b1da42b520bba0d11ee556
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 866089b6a04d29a56c1a42fbbd303615eed3b3d72257dae6103f05c7f75c406bfdba251017ff4f194964298d7876e55fa1ea1e8ceaed0ccf6cc99d36386639de
|
7
|
+
data.tar.gz: 88e6e76cc7678f2c52fef9bbb67b63ee4ea0bfd31ad384df5dc6c801cdede9f1692f14de2ed23fb9c47757a9e69968bb34798746df77bc79d038ae0f72388954
|
data/bin/arTTY
ADDED
@@ -0,0 +1,486 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "arTTY"
|
4
|
+
require "hilighter"
|
5
|
+
require "io/wait"
|
6
|
+
require "jsoncfg"
|
7
|
+
require "optparse"
|
8
|
+
require "scoobydoo"
|
9
|
+
require "typhoeus"
|
10
|
+
|
11
|
+
class ArTTYConfig < JSONConfig
|
12
|
+
extend JSONConfig::Keys
|
13
|
+
|
14
|
+
add_key("art")
|
15
|
+
add_bool_key("clear_screen")
|
16
|
+
add_bool_key("devexcuse")
|
17
|
+
add_key("exclude")
|
18
|
+
add_key("fields")
|
19
|
+
add_bool_key("fit")
|
20
|
+
add_bool_key("fortune")
|
21
|
+
add_key("match")
|
22
|
+
add_bool_key("random")
|
23
|
+
add_bool_key("sysinfo")
|
24
|
+
|
25
|
+
def initialize(file = nil)
|
26
|
+
file ||= "~/.config/arTTY/rc"
|
27
|
+
@defaults = {
|
28
|
+
"art" => "",
|
29
|
+
"clear_screen" => true,
|
30
|
+
"devexcuse" => false,
|
31
|
+
"exclude" => "",
|
32
|
+
"fields" => Array.new,
|
33
|
+
"fit" => true,
|
34
|
+
"fortune" => false,
|
35
|
+
"match" => "",
|
36
|
+
"random" => true,
|
37
|
+
"sysinfo" => true
|
38
|
+
}
|
39
|
+
super(file, false)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ArTTYExit
|
44
|
+
GOOD = 0
|
45
|
+
INVALID_OPTION = 1
|
46
|
+
INVALID_ARGUMENT = 2
|
47
|
+
MISSING_ARGUMENT = 3
|
48
|
+
EXTRA_ARGUMENTS = 4
|
49
|
+
EXCEPTION = 5
|
50
|
+
AMBIGUOUS_ARGUMENT = 6
|
51
|
+
UNSUPPORTED_ART = 7
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_devexcuse
|
55
|
+
begin
|
56
|
+
Typhoeus::Request.get(
|
57
|
+
"http://developerexcuses.com"
|
58
|
+
).body.match(/<a href.+>(.+)<\/a>/) do |m|
|
59
|
+
return m[1]
|
60
|
+
end
|
61
|
+
rescue
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse(args)
|
67
|
+
options = Hash.new
|
68
|
+
options["action"] = "draw"
|
69
|
+
options["all"] = false
|
70
|
+
options["art"] = ""
|
71
|
+
options["clear"] = false
|
72
|
+
options["conf"] = ArTTYConfig.new
|
73
|
+
options["devexcuse"] = false
|
74
|
+
options["exclude"] = ""
|
75
|
+
options["fields"] = Array.new
|
76
|
+
options["fit"] = false
|
77
|
+
options["fortune"] = false
|
78
|
+
options["generate"] = nil
|
79
|
+
options["match"] = ""
|
80
|
+
options["plain"] = false
|
81
|
+
options["random"] = false
|
82
|
+
options["sysinfo"] = false
|
83
|
+
options["verbose"] = false
|
84
|
+
|
85
|
+
info = "Art for your TTY."
|
86
|
+
|
87
|
+
parser = OptionParser.new do |opts|
|
88
|
+
opts.summary_width = 25
|
89
|
+
|
90
|
+
opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [art]"
|
91
|
+
|
92
|
+
opts.on("", "DESCRIPTION")
|
93
|
+
|
94
|
+
info.scan(/\S.{0,76}\S(?=\s|$)|\S+/).each do |line|
|
95
|
+
opts.on(" #{line}")
|
96
|
+
end
|
97
|
+
|
98
|
+
opts.on("", "OPTIONS")
|
99
|
+
|
100
|
+
opts.on("-a", "--all", "Ignore previous filtering") do
|
101
|
+
options["all"] = true
|
102
|
+
end
|
103
|
+
|
104
|
+
opts.on("--cache", "Refresh the cache") do
|
105
|
+
case options["action"]
|
106
|
+
when "draw"
|
107
|
+
options["action"] = "cache"
|
108
|
+
else
|
109
|
+
puts(opts)
|
110
|
+
exit ArTTYExit::INVALID_OPTION
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
opts.on("-c", "--clear", "Clear screen first") do
|
115
|
+
options["clear"] = true
|
116
|
+
end
|
117
|
+
|
118
|
+
opts.on("--demo", "Demo art matching filters") do
|
119
|
+
case options["action"]
|
120
|
+
when "draw"
|
121
|
+
options["action"] = "demo"
|
122
|
+
else
|
123
|
+
puts(opts)
|
124
|
+
exit ArTTYExit::INVALID_OPTION
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
opts.on("-d", "--devexcuse", "Display a developer excuse") do
|
129
|
+
options["devexcuse"] = true
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.on(
|
133
|
+
"-e",
|
134
|
+
"--exclude=PATTERN",
|
135
|
+
"Exclude art matching pattern"
|
136
|
+
) do |pattern|
|
137
|
+
options["exclude"] = pattern
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on(
|
141
|
+
"--fields=FIELDS",
|
142
|
+
"Specify order of sysinfo (comma-separated, see",
|
143
|
+
"FIELDS)"
|
144
|
+
) do |fields|
|
145
|
+
options["fields"] = fields.split(",")
|
146
|
+
options["sysinfo"] = true
|
147
|
+
end
|
148
|
+
|
149
|
+
opts.on(
|
150
|
+
"--fit",
|
151
|
+
"Only use art that fits in the current window"
|
152
|
+
) do |f|
|
153
|
+
options["fit"] = true
|
154
|
+
end
|
155
|
+
|
156
|
+
opts.on(
|
157
|
+
"-f",
|
158
|
+
"--fortune",
|
159
|
+
"Display a fortune (if installed)"
|
160
|
+
) do
|
161
|
+
options["fortune"] = true
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.on("-h", "--help", "Display this help message") do
|
165
|
+
puts(opts)
|
166
|
+
exit ArTTYExit::GOOD
|
167
|
+
end
|
168
|
+
|
169
|
+
opts.on(
|
170
|
+
"-g",
|
171
|
+
"--generate=IMAGE",
|
172
|
+
"Generate ArTTY art from image (NAME_WxH.png)"
|
173
|
+
) do |image|
|
174
|
+
case options["action"]
|
175
|
+
when "draw"
|
176
|
+
options["action"] = "generate"
|
177
|
+
else
|
178
|
+
puts(opts)
|
179
|
+
exit ArTTYExit::INVALID_OPTION
|
180
|
+
end
|
181
|
+
options["generate"] = image
|
182
|
+
end
|
183
|
+
|
184
|
+
opts.on("--ls", "List art matching filters") do
|
185
|
+
case options["action"]
|
186
|
+
when "draw"
|
187
|
+
options["action"] = "list"
|
188
|
+
else
|
189
|
+
puts(opts)
|
190
|
+
exit ArTTYExit::INVALID_OPTION
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
opts.on(
|
195
|
+
"-m",
|
196
|
+
"--match=PATTERN",
|
197
|
+
"Only use art matching pattern"
|
198
|
+
) do |pattern|
|
199
|
+
options["match"] = pattern
|
200
|
+
end
|
201
|
+
|
202
|
+
opts.on("--[no-]color", "Disable colorized output") do |c|
|
203
|
+
Hilighter.disable if (!c)
|
204
|
+
end
|
205
|
+
|
206
|
+
opts.on(
|
207
|
+
"-p",
|
208
|
+
"--plain",
|
209
|
+
"Ignore previous flags and filtering (useful for",
|
210
|
+
"tab-completion with --ls)"
|
211
|
+
) do
|
212
|
+
options["plain"] = true
|
213
|
+
end
|
214
|
+
|
215
|
+
opts.on(
|
216
|
+
"-r",
|
217
|
+
"--random",
|
218
|
+
"Display random art matching filters"
|
219
|
+
) do
|
220
|
+
options["random"] = true
|
221
|
+
end
|
222
|
+
|
223
|
+
opts.on("--save", "Save specified options as default") do
|
224
|
+
case options["action"]
|
225
|
+
when "draw"
|
226
|
+
options["action"] = "save"
|
227
|
+
else
|
228
|
+
puts(opts)
|
229
|
+
exit ArTTYExit::INVALID_OPTION
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
opts.on(
|
234
|
+
"--show-config",
|
235
|
+
"Show the current config with any other options applied",
|
236
|
+
) do
|
237
|
+
case options["action"]
|
238
|
+
when "draw"
|
239
|
+
options["action"] = "show"
|
240
|
+
else
|
241
|
+
puts(opts)
|
242
|
+
exit ArTTYExit::INVALID_OPTION
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
opts.on("-s", "--sysinfo", "Display system info") do
|
247
|
+
options["sysinfo"] = true
|
248
|
+
end
|
249
|
+
|
250
|
+
opts.on(
|
251
|
+
"-u",
|
252
|
+
"--update",
|
253
|
+
"Download new art and refresh the cache"
|
254
|
+
) do
|
255
|
+
case options["action"]
|
256
|
+
when "draw"
|
257
|
+
options["action"] = "update"
|
258
|
+
else
|
259
|
+
puts(opts)
|
260
|
+
exit ArTTYExit::INVALID_OPTION
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
opts.on(
|
265
|
+
"-v",
|
266
|
+
"--verbose",
|
267
|
+
"Show backtrace when error occurs"
|
268
|
+
) do
|
269
|
+
options["verbose"] = true
|
270
|
+
end
|
271
|
+
|
272
|
+
opts.on("-V", "--version", "Show version") do
|
273
|
+
__FILE__.match(/arTTY-(\d+\.\d+\.\d+)/) do |m|
|
274
|
+
puts("arTTY version #{m[1]}")
|
275
|
+
end
|
276
|
+
exit ArTTYExit::GOOD
|
277
|
+
end
|
278
|
+
|
279
|
+
opts.on(
|
280
|
+
"",
|
281
|
+
"FIELDS",
|
282
|
+
" colors Show terminal colors",
|
283
|
+
" cpu Show cpu info",
|
284
|
+
" fs Show filesystem usage",
|
285
|
+
" host, hostname Show hostname",
|
286
|
+
" ip Show IPv4 and IPv6 addresses",
|
287
|
+
" ipv4 Show only IPv4 addresses",
|
288
|
+
" ipv6 Show only IPv6 addresses",
|
289
|
+
" kernel Show kernel info",
|
290
|
+
" os Show operating system info",
|
291
|
+
" ram Show RAM usage",
|
292
|
+
" shell Show current shell",
|
293
|
+
" tty Show TTY info",
|
294
|
+
" uptime Show uptime"
|
295
|
+
)
|
296
|
+
end
|
297
|
+
|
298
|
+
begin
|
299
|
+
parser.parse!
|
300
|
+
rescue OptionParser::InvalidOption => e
|
301
|
+
puts(e.message)
|
302
|
+
puts(parser)
|
303
|
+
exit ArTTYExit::INVALID_OPTION
|
304
|
+
rescue OptionParser::InvalidArgument => e
|
305
|
+
puts(e.message)
|
306
|
+
puts(parser)
|
307
|
+
exit ArTTYExit::INVALID_ARGUMENT
|
308
|
+
rescue OptionParser::MissingArgument => e
|
309
|
+
puts(e.message)
|
310
|
+
puts(parser)
|
311
|
+
exit ArTTYExit::MISSING_ARGUMENT
|
312
|
+
rescue OptionParser::AmbiguousOption => e
|
313
|
+
puts(e.message)
|
314
|
+
puts(parser)
|
315
|
+
exit ArTTYExit::AMBIGUOUS_ARGUMENT
|
316
|
+
end
|
317
|
+
|
318
|
+
if (args.length == 1)
|
319
|
+
options["random"] = false
|
320
|
+
options["art"] = args[0]
|
321
|
+
elsif (args.length > 1)
|
322
|
+
puts(parser)
|
323
|
+
exit ArTTYExit::EXTRA_ARGUMENTS
|
324
|
+
end
|
325
|
+
|
326
|
+
return options
|
327
|
+
end
|
328
|
+
|
329
|
+
def validate(cfg, opts)
|
330
|
+
if opts["all"]
|
331
|
+
cfg.no_fit
|
332
|
+
cfg.set_exclude("")
|
333
|
+
cfg.set_match("")
|
334
|
+
elsif opts["plain"]
|
335
|
+
cfg.default
|
336
|
+
cfg.no_clear_screen
|
337
|
+
cfg.no_fit
|
338
|
+
cfg.no_random
|
339
|
+
cfg.no_sysinfo
|
340
|
+
end
|
341
|
+
|
342
|
+
cfg.set_art(opts["art"])
|
343
|
+
cfg.clear_screen if (opts["clear"])
|
344
|
+
cfg.devexcuse if (opts["devexcuse"])
|
345
|
+
cfg.set_exclude(opts["exclude"]) if (!opts["exclude"].empty?)
|
346
|
+
cfg.set_fields(opts["fields"]) if (!opts["fields"].empty?)
|
347
|
+
cfg.fit if (opts["fit"])
|
348
|
+
cfg.fortune if (opts["fortune"])
|
349
|
+
cfg.set_match(opts["match"]) if (!opts["match"].empty?)
|
350
|
+
cfg.random if (opts["random"])
|
351
|
+
cfg.sysinfo if (opts["sysinfo"])
|
352
|
+
end
|
353
|
+
|
354
|
+
begin
|
355
|
+
options = parse(ARGV)
|
356
|
+
rescue Interrupt
|
357
|
+
# Exit gracefully on ^C
|
358
|
+
exit ArTTYExit::GOOD
|
359
|
+
end
|
360
|
+
|
361
|
+
begin
|
362
|
+
config = options["conf"]
|
363
|
+
validate(config, options)
|
364
|
+
|
365
|
+
arTTY = ArTTY.new
|
366
|
+
arTTY.exclude(config.get_exclude) if (config.exclude?)
|
367
|
+
arTTY.match(config.get_match) if (config.match?)
|
368
|
+
|
369
|
+
devexcuse = nil
|
370
|
+
if (config.devexcuse?)
|
371
|
+
devexcuse = get_devexcuse
|
372
|
+
end
|
373
|
+
|
374
|
+
fortune = nil
|
375
|
+
if (config.fortune? && ScoobyDoo.where_are_you("fortune"))
|
376
|
+
fortune = %x(fortune -s)
|
377
|
+
end
|
378
|
+
|
379
|
+
info = nil
|
380
|
+
if (config.sysinfo?)
|
381
|
+
info = ArTTY::SystemInfo.new(config.get_fields)
|
382
|
+
end
|
383
|
+
|
384
|
+
# Remove all that do not fit
|
385
|
+
if (config.fit?)
|
386
|
+
height = %x(tput lines).to_i - 4 # Leave some space for prompt
|
387
|
+
width = %x(tput cols).to_i - 1
|
388
|
+
if (config.sysinfo?)
|
389
|
+
width -= (info.width + 2)
|
390
|
+
info = nil if ((height <= info.height) || (width <= 0))
|
391
|
+
end
|
392
|
+
if (devexcuse)
|
393
|
+
eheight = devexcuse.split("\n").length
|
394
|
+
ewidth = devexcuse.split("\n").map(&:length).max
|
395
|
+
devexcuse = nil if (height <= eheight || ewidth > width)
|
396
|
+
height -= eheight if (devexcuse)
|
397
|
+
end
|
398
|
+
if (fortune)
|
399
|
+
fheight = fortune.split("\n").length
|
400
|
+
fwidth = fortune.split("\n").map(&:length).max
|
401
|
+
fortune = nil if (height <= fheight || fwidth > width)
|
402
|
+
height -= fheight if (fortune)
|
403
|
+
end
|
404
|
+
arTTY.fits(width, height)
|
405
|
+
end
|
406
|
+
|
407
|
+
case options["action"]
|
408
|
+
when "cache"
|
409
|
+
arTTY.cache
|
410
|
+
when "demo"
|
411
|
+
arTTY.art.each do |name|
|
412
|
+
puts("### #{name} ###".white)
|
413
|
+
art = arTTY.get(name, info).draw
|
414
|
+
puts("\n#{art}\n") if (!art.empty?)
|
415
|
+
end
|
416
|
+
when "draw"
|
417
|
+
# Randomize
|
418
|
+
if (!config.art? && config.random?)
|
419
|
+
config.set_art(arTTY.random)
|
420
|
+
end
|
421
|
+
|
422
|
+
# Ensure art is not nil
|
423
|
+
config.set_art("none") if (!config.art?)
|
424
|
+
|
425
|
+
# Clear screen if requested
|
426
|
+
system("clear") if (config.clear_screen?)
|
427
|
+
|
428
|
+
art = arTTY.get(config.get_art, info).draw
|
429
|
+
puts("\n#{art}\n") if (!art.empty?)
|
430
|
+
puts(devexcuse) if (devexcuse)
|
431
|
+
puts(fortune) if (fortune)
|
432
|
+
when "generate"
|
433
|
+
puts(
|
434
|
+
ArTTY::Generator.new.generate(
|
435
|
+
options["generate"],
|
436
|
+
options["conf"].getdiff("art")
|
437
|
+
)
|
438
|
+
)
|
439
|
+
when "list"
|
440
|
+
puts(arTTY.art)
|
441
|
+
when "save"
|
442
|
+
config.save
|
443
|
+
when "show"
|
444
|
+
puts(config)
|
445
|
+
when "update"
|
446
|
+
arTTY.cache(true)
|
447
|
+
end
|
448
|
+
rescue ArTTY::Error => e
|
449
|
+
$stderr.puts(e.message.white.on_red)
|
450
|
+
if (options["verbose"])
|
451
|
+
e.backtrace.each do |line|
|
452
|
+
$stderr.puts(line.yellow)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
exit ArTTYExit::EXCEPTION
|
456
|
+
rescue Interrupt
|
457
|
+
# Exit gracefully on ^C
|
458
|
+
rescue Errno::EPIPE
|
459
|
+
# Do nothing. This can happen if piping to another program such as
|
460
|
+
# less. Usually if less is closed before we're done with STDOUT.
|
461
|
+
rescue Exception => e
|
462
|
+
$stderr.puts(
|
463
|
+
[
|
464
|
+
"Oops! Looks like an error has occured! If the error",
|
465
|
+
"persists, file a bug at:"
|
466
|
+
].join(" ").wrap
|
467
|
+
)
|
468
|
+
$stderr.puts
|
469
|
+
$stderr.puts(" https://gitlab.com/mjwhitta/arTTY/issues")
|
470
|
+
$stderr.puts
|
471
|
+
$stderr.puts(
|
472
|
+
[
|
473
|
+
"Maybe the message below will help. If not, you can use",
|
474
|
+
"the --verbose flag to get a backtrace."
|
475
|
+
].join(" ").wrap
|
476
|
+
)
|
477
|
+
|
478
|
+
$stderr.puts(e.message.white.on_red)
|
479
|
+
if (options["verbose"])
|
480
|
+
e.backtrace.each do |line|
|
481
|
+
$stderr.puts(line.light_yellow)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
exit ArTTYExit::EXCEPTION
|
485
|
+
end
|
486
|
+
exit ArTTYExit::GOOD
|