raygatherer 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +71 -18
- data/lib/raygatherer/api_client.rb +101 -27
- data/lib/raygatherer/cli.rb +39 -17
- data/lib/raygatherer/commands/alerts.rb +7 -12
- data/lib/raygatherer/commands/analysis/report.rb +77 -0
- data/lib/raygatherer/commands/analysis/run.rb +10 -7
- data/lib/raygatherer/commands/analysis/status.rb +1 -9
- data/lib/raygatherer/commands/base.rb +20 -1
- data/lib/raygatherer/commands/config/set.rb +1 -4
- data/lib/raygatherer/commands/config/show.rb +1 -9
- data/lib/raygatherer/commands/config/test_notification.rb +1 -4
- data/lib/raygatherer/commands/debug/display_state.rb +95 -0
- data/lib/raygatherer/commands/gps/set.rb +69 -0
- data/lib/raygatherer/commands/gps/show.rb +54 -0
- data/lib/raygatherer/commands/log.rb +38 -0
- data/lib/raygatherer/commands/recording/delete.rb +55 -11
- data/lib/raygatherer/commands/recording/download.rb +6 -9
- data/lib/raygatherer/commands/recording/list.rb +1 -9
- data/lib/raygatherer/commands/recording/start.rb +2 -5
- data/lib/raygatherer/commands/recording/stop.rb +2 -5
- data/lib/raygatherer/commands/stats.rb +4 -9
- data/lib/raygatherer/commands/time/show.rb +52 -0
- data/lib/raygatherer/commands/time/sync.rb +53 -0
- data/lib/raygatherer/commands/update/status.rb +62 -0
- data/lib/raygatherer/commands/wifi/scan.rb +54 -0
- data/lib/raygatherer/commands/wifi/show.rb +55 -0
- data/lib/raygatherer/formatters/{human.rb → alerts_human.rb} +1 -1
- data/lib/raygatherer/formatters/{json.rb → alerts_json.rb} +2 -2
- data/lib/raygatherer/formatters/analysis_report_human.rb +88 -0
- data/lib/raygatherer/formatters/analysis_report_json.rb +13 -0
- data/lib/raygatherer/formatters/config_human.rb +39 -2
- data/lib/raygatherer/formatters/gps_human.rb +13 -0
- data/lib/raygatherer/formatters/gps_json.rb +13 -0
- data/lib/raygatherer/formatters/recording_list_human.rb +6 -1
- data/lib/raygatherer/formatters/time_human.rb +15 -0
- data/lib/raygatherer/formatters/time_json.rb +13 -0
- data/lib/raygatherer/formatters/update_status_human.rb +25 -0
- data/lib/raygatherer/formatters/update_status_json.rb +13 -0
- data/lib/raygatherer/formatters/wifi_scan_human.rb +17 -0
- data/lib/raygatherer/formatters/wifi_scan_json.rb +13 -0
- data/lib/raygatherer/formatters/wifi_status_human.rb +19 -0
- data/lib/raygatherer/formatters/wifi_status_json.rb +13 -0
- data/lib/raygatherer/version.rb +6 -1
- data/lib/raygatherer.rb +24 -2
- metadata +41 -6
- data/CLAUDE.md +0 -102
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "time"
|
|
5
|
+
require_relative "../base"
|
|
6
|
+
|
|
7
|
+
module Raygatherer
|
|
8
|
+
module Commands
|
|
9
|
+
module Time
|
|
10
|
+
class Sync < Base
|
|
11
|
+
def run
|
|
12
|
+
with_error_handling do
|
|
13
|
+
parse_options
|
|
14
|
+
|
|
15
|
+
time_data = @api_client.fetch_time
|
|
16
|
+
device_system_time = ::Time.parse(time_data["system_time"])
|
|
17
|
+
offset = (::Time.now - device_system_time).round
|
|
18
|
+
@api_client.set_time_offset(offset)
|
|
19
|
+
@stdout.puts "Clock synced. Offset: #{offset}s"
|
|
20
|
+
|
|
21
|
+
EXIT_CODE_SUCCESS
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def parse_options
|
|
28
|
+
OptionParser.new do |opts|
|
|
29
|
+
opts.banner = "Usage: raygatherer time sync"
|
|
30
|
+
opts.separator ""
|
|
31
|
+
opts.separator "Options:"
|
|
32
|
+
|
|
33
|
+
add_help_option(opts)
|
|
34
|
+
end.parse!(@argv)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def show_help(output = @stdout)
|
|
38
|
+
output.puts "Usage: raygatherer [global options] time sync"
|
|
39
|
+
output.puts ""
|
|
40
|
+
output.puts "Syncs the device clock to this machine's time."
|
|
41
|
+
output.puts ""
|
|
42
|
+
output.puts "Options:"
|
|
43
|
+
output.puts " -h, --help Show this help message"
|
|
44
|
+
output.puts ""
|
|
45
|
+
print_global_options(output)
|
|
46
|
+
output.puts ""
|
|
47
|
+
output.puts "Examples:"
|
|
48
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 time sync"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../base"
|
|
5
|
+
require_relative "../../formatters/update_status_json"
|
|
6
|
+
require_relative "../../formatters/update_status_human"
|
|
7
|
+
|
|
8
|
+
module Raygatherer
|
|
9
|
+
module Commands
|
|
10
|
+
module Update
|
|
11
|
+
class Status < Base
|
|
12
|
+
EXIT_CODE_UPDATE_AVAILABLE = 20
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
with_error_handling do
|
|
16
|
+
parse_options
|
|
17
|
+
|
|
18
|
+
status = @api_client.fetch_update_status
|
|
19
|
+
|
|
20
|
+
warn_if_rayhunter_outdated(status["current_version"]) if status["current_version"]
|
|
21
|
+
|
|
22
|
+
formatter = @json ? Formatters::UpdateStatusJSON.new : Formatters::UpdateStatusHuman.new
|
|
23
|
+
@stdout.puts formatter.format(status)
|
|
24
|
+
|
|
25
|
+
status["update_available"] ? EXIT_CODE_UPDATE_AVAILABLE : EXIT_CODE_SUCCESS
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def parse_options
|
|
32
|
+
OptionParser.new do |opts|
|
|
33
|
+
opts.banner = "Usage: raygatherer update status [options]"
|
|
34
|
+
opts.separator ""
|
|
35
|
+
opts.separator "Options:"
|
|
36
|
+
|
|
37
|
+
add_help_option(opts)
|
|
38
|
+
end.parse!(@argv)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def show_help(output = @stdout)
|
|
42
|
+
output.puts "Usage: raygatherer [global options] update status [options]"
|
|
43
|
+
output.puts ""
|
|
44
|
+
output.puts "Options:"
|
|
45
|
+
output.puts " -h, --help Show this help message"
|
|
46
|
+
output.puts ""
|
|
47
|
+
print_global_options(output, json: true)
|
|
48
|
+
output.puts ""
|
|
49
|
+
output.puts "Exit Codes:"
|
|
50
|
+
output.puts " #{EXIT_CODE_SUCCESS} Up to date (or auto_check_updates is disabled on the device)"
|
|
51
|
+
output.puts " #{EXIT_CODE_ERROR} Error (connection, parse, missing --host, etc.)"
|
|
52
|
+
output.puts " #{EXIT_CODE_UPDATE_AVAILABLE} Update available"
|
|
53
|
+
output.puts ""
|
|
54
|
+
output.puts "Examples:"
|
|
55
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 update status"
|
|
56
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 --json update status"
|
|
57
|
+
output.puts " raygatherer --host http://rayhunter update status; [ $? -eq #{EXIT_CODE_UPDATE_AVAILABLE} ] && telegram-send 'Rayhunter update available!'"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../base"
|
|
5
|
+
require_relative "../../formatters/wifi_scan_json"
|
|
6
|
+
require_relative "../../formatters/wifi_scan_human"
|
|
7
|
+
|
|
8
|
+
module Raygatherer
|
|
9
|
+
module Commands
|
|
10
|
+
module Wifi
|
|
11
|
+
class Scan < Base
|
|
12
|
+
def run
|
|
13
|
+
with_error_handling do
|
|
14
|
+
parse_options
|
|
15
|
+
|
|
16
|
+
networks = @api_client.scan_wifi
|
|
17
|
+
|
|
18
|
+
formatter = @json ? Formatters::WifiScanJSON.new : Formatters::WifiScanHuman.new
|
|
19
|
+
@stdout.puts formatter.format(networks)
|
|
20
|
+
|
|
21
|
+
EXIT_CODE_SUCCESS
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def parse_options
|
|
28
|
+
OptionParser.new do |opts|
|
|
29
|
+
opts.banner = "Usage: raygatherer wifi scan [options]"
|
|
30
|
+
opts.separator ""
|
|
31
|
+
opts.separator "Options:"
|
|
32
|
+
|
|
33
|
+
add_help_option(opts)
|
|
34
|
+
end.parse!(@argv)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def show_help(output = @stdout)
|
|
38
|
+
output.puts "Usage: raygatherer [global options] wifi scan [options]"
|
|
39
|
+
output.puts ""
|
|
40
|
+
output.puts "Scan for nearby wifi networks the device could connect to as a client."
|
|
41
|
+
output.puts ""
|
|
42
|
+
output.puts "Options:"
|
|
43
|
+
output.puts " -h, --help Show this help message"
|
|
44
|
+
output.puts ""
|
|
45
|
+
print_global_options(output, json: true)
|
|
46
|
+
output.puts ""
|
|
47
|
+
output.puts "Examples:"
|
|
48
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 wifi scan"
|
|
49
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 --json wifi scan"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../base"
|
|
5
|
+
require_relative "../../formatters/wifi_status_json"
|
|
6
|
+
require_relative "../../formatters/wifi_status_human"
|
|
7
|
+
|
|
8
|
+
module Raygatherer
|
|
9
|
+
module Commands
|
|
10
|
+
module Wifi
|
|
11
|
+
class Show < Base
|
|
12
|
+
def run
|
|
13
|
+
with_error_handling do
|
|
14
|
+
parse_options
|
|
15
|
+
|
|
16
|
+
status = @api_client.fetch_wifi_status
|
|
17
|
+
|
|
18
|
+
formatter = @json ? Formatters::WifiStatusJSON.new : Formatters::WifiStatusHuman.new
|
|
19
|
+
@stdout.puts formatter.format(status)
|
|
20
|
+
|
|
21
|
+
EXIT_CODE_SUCCESS
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def parse_options
|
|
28
|
+
OptionParser.new do |opts|
|
|
29
|
+
opts.banner = "Usage: raygatherer wifi show [options]"
|
|
30
|
+
opts.separator ""
|
|
31
|
+
opts.separator "Options:"
|
|
32
|
+
|
|
33
|
+
add_help_option(opts)
|
|
34
|
+
end.parse!(@argv)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def show_help(output = @stdout)
|
|
38
|
+
output.puts "Usage: raygatherer [global options] wifi show [options]"
|
|
39
|
+
output.puts ""
|
|
40
|
+
output.puts "Show the device's wifi client connection status (the daemon's own outbound"
|
|
41
|
+
output.puts "connection to an upstream network, separate from the AP it hosts)."
|
|
42
|
+
output.puts ""
|
|
43
|
+
output.puts "Options:"
|
|
44
|
+
output.puts " -h, --help Show this help message"
|
|
45
|
+
output.puts ""
|
|
46
|
+
print_global_options(output, json: true)
|
|
47
|
+
output.puts ""
|
|
48
|
+
output.puts "Examples:"
|
|
49
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 wifi show"
|
|
50
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 --json wifi show"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -4,7 +4,7 @@ require "json"
|
|
|
4
4
|
|
|
5
5
|
module Raygatherer
|
|
6
6
|
module Formatters
|
|
7
|
-
class
|
|
7
|
+
class AlertsJSON
|
|
8
8
|
def format(alerts)
|
|
9
9
|
output = alerts.map do |alert|
|
|
10
10
|
{
|
|
@@ -15,7 +15,7 @@ module Raygatherer
|
|
|
15
15
|
}
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
JSON.generate(output)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
5
|
+
module Raygatherer
|
|
6
|
+
module Formatters
|
|
7
|
+
class AnalysisReportHuman
|
|
8
|
+
def format(data)
|
|
9
|
+
metadata = data[:metadata] || {}
|
|
10
|
+
rows = data[:rows] || []
|
|
11
|
+
|
|
12
|
+
lines = []
|
|
13
|
+
lines << format_header(metadata)
|
|
14
|
+
lines << format_analyzers(metadata)
|
|
15
|
+
lines << ""
|
|
16
|
+
|
|
17
|
+
rows.each do |row|
|
|
18
|
+
lines.concat(format_row(row, metadata))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
lines.join("\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def format_header(metadata)
|
|
27
|
+
rayhunter = metadata["rayhunter"] || {}
|
|
28
|
+
version = rayhunter["rayhunter_version"]
|
|
29
|
+
os = rayhunter["system_os"]
|
|
30
|
+
arch = rayhunter["arch"]
|
|
31
|
+
report_version = metadata["report_version"]
|
|
32
|
+
|
|
33
|
+
parts = []
|
|
34
|
+
parts << "Rayhunter v#{version}" if version
|
|
35
|
+
os_arch = [os, arch].compact.join(" ")
|
|
36
|
+
parts << os_arch unless os_arch.empty?
|
|
37
|
+
parts << "Report version #{report_version}" if report_version
|
|
38
|
+
|
|
39
|
+
parts.join(" | ")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def format_analyzers(metadata)
|
|
43
|
+
analyzers = metadata["analyzers"] || []
|
|
44
|
+
return "Analyzers: (none)" if analyzers.empty?
|
|
45
|
+
|
|
46
|
+
names = analyzers.filter_map do |a|
|
|
47
|
+
next unless a
|
|
48
|
+
v = a["version"] ? " (v#{a["version"]})" : ""
|
|
49
|
+
"#{a["name"]}#{v}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
"Analyzers: #{names.join(", ")}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def format_row(row, metadata)
|
|
56
|
+
analyzers = metadata["analyzers"] || []
|
|
57
|
+
timestamp = format_timestamp(row["packet_timestamp"])
|
|
58
|
+
|
|
59
|
+
if (reason = row["skipped_message_reason"])
|
|
60
|
+
return ["#{timestamp} Skipped: #{reason}"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
events = row["events"] || []
|
|
64
|
+
present_events = events.each_with_index.reject { |e, _| e.nil? }
|
|
65
|
+
|
|
66
|
+
if present_events.empty?
|
|
67
|
+
return ["#{timestamp} No events"]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
present_events.map do |event, index|
|
|
71
|
+
event_type = event["event_type"] || "Unknown"
|
|
72
|
+
message = event["message"]
|
|
73
|
+
analyzer = analyzers.dig(index, "name")
|
|
74
|
+
analyzer_part = analyzer ? "(#{analyzer}) " : ""
|
|
75
|
+
"#{timestamp} #{event_type.ljust(13)} #{analyzer_part}#{message}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def format_timestamp(ts)
|
|
80
|
+
return "[no timestamp]" unless ts
|
|
81
|
+
time = Time.parse(ts).utc
|
|
82
|
+
"[#{time.strftime("%Y-%m-%d %H:%M:%S UTC")}]"
|
|
83
|
+
rescue ArgumentError
|
|
84
|
+
"[#{ts}]"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -7,12 +7,49 @@ module Raygatherer
|
|
|
7
7
|
lines = []
|
|
8
8
|
|
|
9
9
|
lines << "Port: #{config["port"]}"
|
|
10
|
-
lines << "Readonly port: #{config["readonly_port"]}"
|
|
11
10
|
lines << "QMDL store path: #{config["qmdl_store_path"]}"
|
|
11
|
+
lines << "Device: #{config["device"]}" if config.key?("device")
|
|
12
|
+
lines << "Debug mode: #{config["debug_mode"]}" if config.key?("debug_mode")
|
|
13
|
+
lines << "UI level: #{config["ui_level"]}" if config.key?("ui_level")
|
|
14
|
+
lines << "Colorblind mode: #{config["colorblind_mode"]}" if config.key?("colorblind_mode")
|
|
15
|
+
lines << "Key input mode: #{config["key_input_mode"]}" if config.key?("key_input_mode")
|
|
12
16
|
|
|
13
|
-
notification_url = config["notification_url"]
|
|
17
|
+
notification_url = config["ntfy_url"] || config["notification_url"]
|
|
14
18
|
lines << "Notification URL: #{notification_url || "(not set)"}"
|
|
15
19
|
|
|
20
|
+
if config.key?("enabled_notifications")
|
|
21
|
+
lines << "Notifications enabled: #{config["enabled_notifications"].join(", ")}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if config.key?("min_space_to_start_recording_mb")
|
|
25
|
+
lines << "Min space to start recording: #{config["min_space_to_start_recording_mb"]} MB"
|
|
26
|
+
end
|
|
27
|
+
if config.key?("min_space_to_continue_recording_mb")
|
|
28
|
+
lines << "Min space to continue recording: #{config["min_space_to_continue_recording_mb"]} MB"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
lines << "Auto check updates: #{config["auto_check_updates"]}" if config.key?("auto_check_updates")
|
|
32
|
+
lines << "Clock sync mode: #{config["clock_sync_mode"]}" if config.key?("clock_sync_mode")
|
|
33
|
+
lines << "GPS mode: #{config["gps_mode"]}" if config.key?("gps_mode")
|
|
34
|
+
lines << "GPS fixed latitude: #{config["gps_fixed_latitude"]}" if config["gps_fixed_latitude"]
|
|
35
|
+
lines << "GPS fixed longitude: #{config["gps_fixed_longitude"]}" if config["gps_fixed_longitude"]
|
|
36
|
+
lines << "WiFi enabled: #{config["wifi_enabled"]}" if config.key?("wifi_enabled")
|
|
37
|
+
lines << "WiFi SSID: #{config["wifi_ssid"]}" if config["wifi_ssid"]
|
|
38
|
+
lines << "WiFi security: #{config["wifi_security"]}" if config["wifi_security"]
|
|
39
|
+
lines << "DNS servers: #{config["dns_servers"].join(", ")}" if config["dns_servers"]&.any?
|
|
40
|
+
|
|
41
|
+
webdav = config["webdav"]
|
|
42
|
+
if webdav && !webdav["url"].to_s.empty?
|
|
43
|
+
lines << ""
|
|
44
|
+
lines << "WebDAV:"
|
|
45
|
+
lines << " URL: #{webdav["url"]}"
|
|
46
|
+
lines << " Username: #{webdav["username"]}" if webdav["username"]
|
|
47
|
+
lines << " Upload timeout: #{webdav["upload_timeout_secs"]}s" if webdav.key?("upload_timeout_secs")
|
|
48
|
+
lines << " Poll interval: #{webdav["poll_interval_secs"]}s" if webdav.key?("poll_interval_secs")
|
|
49
|
+
lines << " Min age: #{webdav["min_age_secs"]}s" if webdav.key?("min_age_secs")
|
|
50
|
+
lines << " Delete on upload: #{webdav["delete_on_upload"]}" if webdav.key?("delete_on_upload")
|
|
51
|
+
end
|
|
52
|
+
|
|
16
53
|
analyzers = config["analyzers"]
|
|
17
54
|
if analyzers
|
|
18
55
|
lines << ""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raygatherer
|
|
4
|
+
module Formatters
|
|
5
|
+
class GpsHuman
|
|
6
|
+
def format(gps)
|
|
7
|
+
return "No GPS position recorded yet (nothing has been submitted to /api/gps this session)." if gps.nil?
|
|
8
|
+
|
|
9
|
+
"Latitude: #{gps["latitude"]}\nLongitude: #{gps["longitude"]}"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -59,13 +59,18 @@ module Raygatherer
|
|
|
59
59
|
|
|
60
60
|
lines << " Size: #{format_size(entry["qmdl_size_bytes"])}"
|
|
61
61
|
|
|
62
|
+
if !active && entry["stop_reason"]
|
|
63
|
+
lines << " Stop reason: #{entry["stop_reason"]}"
|
|
64
|
+
end
|
|
65
|
+
|
|
62
66
|
lines.join("\n")
|
|
63
67
|
end
|
|
64
68
|
|
|
65
69
|
def format_time(time_string)
|
|
66
70
|
return "" unless time_string
|
|
67
|
-
|
|
68
71
|
Time.parse(time_string).strftime("%Y-%m-%d %H:%M:%S")
|
|
72
|
+
rescue ArgumentError
|
|
73
|
+
time_string
|
|
69
74
|
end
|
|
70
75
|
end
|
|
71
76
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raygatherer
|
|
4
|
+
module Formatters
|
|
5
|
+
class TimeHuman
|
|
6
|
+
def format(time)
|
|
7
|
+
lines = []
|
|
8
|
+
lines << "System time: #{time["system_time"]}"
|
|
9
|
+
lines << "Adjusted time: #{time["adjusted_time"]}"
|
|
10
|
+
lines << "Offset: #{time["offset_seconds"]}s"
|
|
11
|
+
lines.join("\n")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raygatherer
|
|
4
|
+
module Formatters
|
|
5
|
+
class UpdateStatusHuman
|
|
6
|
+
def format(status)
|
|
7
|
+
lines = []
|
|
8
|
+
|
|
9
|
+
lines << "Current version: #{status["current_version"]}"
|
|
10
|
+
lines << "Update available: #{status["update_available"] ? "yes" : "no"}"
|
|
11
|
+
lines << "Latest version: #{status["latest_version"]}" if status["latest_version"]
|
|
12
|
+
lines << "Release notes: #{status["latest_release_url"]}" if status["latest_release_url"]
|
|
13
|
+
lines << "Last checked: #{status["last_checked"]}" if status["last_checked"]
|
|
14
|
+
lines << "Last error: #{status["last_error"]}" if status["last_error"]
|
|
15
|
+
|
|
16
|
+
if status["latest_version"].nil? && status["last_checked"].nil?
|
|
17
|
+
lines << ""
|
|
18
|
+
lines << "No update check has run yet — check that auto_check_updates is enabled in rayhunter's config."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
lines.join("\n")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raygatherer
|
|
4
|
+
module Formatters
|
|
5
|
+
class WifiScanHuman
|
|
6
|
+
def format(networks)
|
|
7
|
+
return "No networks found" if networks.empty?
|
|
8
|
+
|
|
9
|
+
lines = ["Networks: #{networks.length}", ""]
|
|
10
|
+
networks.each do |network|
|
|
11
|
+
lines << "#{network["ssid"]} (#{network["signal_dbm"]} dBm, #{network["security"]})"
|
|
12
|
+
end
|
|
13
|
+
lines.join("\n")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raygatherer
|
|
4
|
+
module Formatters
|
|
5
|
+
class WifiStatusHuman
|
|
6
|
+
def format(status)
|
|
7
|
+
lines = []
|
|
8
|
+
|
|
9
|
+
lines << "State: #{status["state"]}"
|
|
10
|
+
lines << "SSID: #{status["ssid"]}" if status["ssid"]
|
|
11
|
+
lines << "IP: #{status["ip"]}" if status["ip"]
|
|
12
|
+
lines << "TX packets: #{status["txPackets"]}" if status["txPackets"]
|
|
13
|
+
lines << "Error: #{status["error"]}" if status["error"]
|
|
14
|
+
|
|
15
|
+
lines.join("\n")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/raygatherer/version.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Raygatherer
|
|
4
|
-
VERSION = "0.
|
|
4
|
+
VERSION = "0.3.0"
|
|
5
|
+
|
|
6
|
+
# The rayhunter daemon version this release was last verified against. Used only to warn
|
|
7
|
+
# when talking to an older daemon that may lack fields/endpoints raygatherer expects;
|
|
8
|
+
# never enforced.
|
|
9
|
+
TESTED_RAYHUNTER_VERSION = "0.12.0"
|
|
5
10
|
end
|
data/lib/raygatherer.rb
CHANGED
|
@@ -4,16 +4,28 @@ require_relative "raygatherer/version"
|
|
|
4
4
|
require_relative "raygatherer/config"
|
|
5
5
|
require_relative "raygatherer/cli"
|
|
6
6
|
require_relative "raygatherer/api_client"
|
|
7
|
-
require_relative "raygatherer/formatters/
|
|
8
|
-
require_relative "raygatherer/formatters/
|
|
7
|
+
require_relative "raygatherer/formatters/alerts_human"
|
|
8
|
+
require_relative "raygatherer/formatters/alerts_json"
|
|
9
9
|
require_relative "raygatherer/formatters/recording_list_json"
|
|
10
10
|
require_relative "raygatherer/formatters/recording_list_human"
|
|
11
11
|
require_relative "raygatherer/formatters/stats_json"
|
|
12
12
|
require_relative "raygatherer/formatters/stats_human"
|
|
13
13
|
require_relative "raygatherer/formatters/analysis_status_json"
|
|
14
14
|
require_relative "raygatherer/formatters/analysis_status_human"
|
|
15
|
+
require_relative "raygatherer/formatters/analysis_report_json"
|
|
16
|
+
require_relative "raygatherer/formatters/analysis_report_human"
|
|
15
17
|
require_relative "raygatherer/formatters/config_json"
|
|
16
18
|
require_relative "raygatherer/formatters/config_human"
|
|
19
|
+
require_relative "raygatherer/formatters/time_json"
|
|
20
|
+
require_relative "raygatherer/formatters/time_human"
|
|
21
|
+
require_relative "raygatherer/formatters/update_status_json"
|
|
22
|
+
require_relative "raygatherer/formatters/update_status_human"
|
|
23
|
+
require_relative "raygatherer/formatters/gps_json"
|
|
24
|
+
require_relative "raygatherer/formatters/gps_human"
|
|
25
|
+
require_relative "raygatherer/formatters/wifi_status_json"
|
|
26
|
+
require_relative "raygatherer/formatters/wifi_status_human"
|
|
27
|
+
require_relative "raygatherer/formatters/wifi_scan_json"
|
|
28
|
+
require_relative "raygatherer/formatters/wifi_scan_human"
|
|
17
29
|
require_relative "raygatherer/spinner"
|
|
18
30
|
require_relative "raygatherer/commands/base"
|
|
19
31
|
require_relative "raygatherer/commands/alerts"
|
|
@@ -22,12 +34,22 @@ require_relative "raygatherer/commands/recording/download"
|
|
|
22
34
|
require_relative "raygatherer/commands/recording/delete"
|
|
23
35
|
require_relative "raygatherer/commands/recording/stop"
|
|
24
36
|
require_relative "raygatherer/commands/recording/start"
|
|
37
|
+
require_relative "raygatherer/commands/log"
|
|
25
38
|
require_relative "raygatherer/commands/stats"
|
|
26
39
|
require_relative "raygatherer/commands/analysis/status"
|
|
27
40
|
require_relative "raygatherer/commands/analysis/run"
|
|
41
|
+
require_relative "raygatherer/commands/analysis/report"
|
|
28
42
|
require_relative "raygatherer/commands/config/show"
|
|
29
43
|
require_relative "raygatherer/commands/config/set"
|
|
30
44
|
require_relative "raygatherer/commands/config/test_notification"
|
|
45
|
+
require_relative "raygatherer/commands/debug/display_state"
|
|
46
|
+
require_relative "raygatherer/commands/time/show"
|
|
47
|
+
require_relative "raygatherer/commands/time/sync"
|
|
48
|
+
require_relative "raygatherer/commands/update/status"
|
|
49
|
+
require_relative "raygatherer/commands/gps/show"
|
|
50
|
+
require_relative "raygatherer/commands/gps/set"
|
|
51
|
+
require_relative "raygatherer/commands/wifi/show"
|
|
52
|
+
require_relative "raygatherer/commands/wifi/scan"
|
|
31
53
|
|
|
32
54
|
module Raygatherer
|
|
33
55
|
class Error < StandardError; end
|