raygatherer 0.2.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 +27 -0
- data/lib/raygatherer/api_client.rb +71 -23
- data/lib/raygatherer/cli.rb +27 -18
- data/lib/raygatherer/commands/alerts.rb +2 -8
- data/lib/raygatherer/commands/analysis/report.rb +2 -6
- data/lib/raygatherer/commands/analysis/run.rb +2 -6
- 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 +1 -4
- data/lib/raygatherer/commands/gps/set.rb +69 -0
- data/lib/raygatherer/commands/gps/show.rb +54 -0
- data/lib/raygatherer/commands/log.rb +1 -4
- data/lib/raygatherer/commands/recording/delete.rb +2 -5
- data/lib/raygatherer/commands/recording/download.rb +1 -4
- data/lib/raygatherer/commands/recording/list.rb +1 -9
- data/lib/raygatherer/commands/recording/start.rb +1 -4
- data/lib/raygatherer/commands/recording/stop.rb +1 -4
- data/lib/raygatherer/commands/stats.rb +4 -9
- data/lib/raygatherer/commands/time/show.rb +1 -9
- data/lib/raygatherer/commands/time/sync.rb +1 -4
- 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/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/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 +13 -0
- metadata +29 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19099779bda496591260a273c3d6db20c9203f1c046393c2ce6b7c6b90b77db5
|
|
4
|
+
data.tar.gz: 7105312abb9d3d27aafbd452e386f87343b8ebce08308e30e4b7330128d260aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68524f1ebfc17554e8fed101331eca036e25217f23c8ada0922e0da5c9f257257314126bebf671a37bb7a8a50ebffca355d2df24cd7dbf8d677951414d865288
|
|
7
|
+
data.tar.gz: d5453cb08bbe54cc9d07fbd1a32d05bce51f9e25ba866ced69bd657fcd3d067c3fe5210aec77f6e193c7a3421867fb0cc4cdfc26eefb61a9f1b8792f4e124c39
|
data/README.md
CHANGED
|
@@ -17,6 +17,9 @@ Currently implemented:
|
|
|
17
17
|
- system stats and raw log output
|
|
18
18
|
- device clock show and sync
|
|
19
19
|
- config show/set/test-notification
|
|
20
|
+
- rayhunter update status, with a dedicated exit code for scripting
|
|
21
|
+
- GPS position show/set (requires the device's gps_mode config to be set to Api)
|
|
22
|
+
- wifi client status and network scan
|
|
20
23
|
- JSON output mode for scriptable commands
|
|
21
24
|
- optional basic auth and config file support
|
|
22
25
|
- debug utilities (display-state)
|
|
@@ -31,6 +34,10 @@ gem install raygatherer
|
|
|
31
34
|
|
|
32
35
|
Requires Ruby >= 3.2.
|
|
33
36
|
|
|
37
|
+
## Compatibility
|
|
38
|
+
|
|
39
|
+
Tested against [rayhunter](https://github.com/EFForg/rayhunter/) v0.12.0 (last synced 2026-09-07). Older rayhunter versions may be missing some fields or endpoints raygatherer expects — commands that already have the connected daemon's version on hand (`stats`, `update status`) print a one-line warning to stderr when it's older than that, without blocking anything.
|
|
40
|
+
|
|
34
41
|
### From source
|
|
35
42
|
|
|
36
43
|
```bash
|
|
@@ -161,6 +168,11 @@ Main commands:
|
|
|
161
168
|
- `config test-notification`
|
|
162
169
|
- `stats`
|
|
163
170
|
- `log`
|
|
171
|
+
- `update status`
|
|
172
|
+
- `gps show`
|
|
173
|
+
- `gps set LAT LON` (requires gps_mode=Api on the device)
|
|
174
|
+
- `wifi show`
|
|
175
|
+
- `wifi scan`
|
|
164
176
|
- `debug display-state <recording|paused|warning> [--severity low|medium|high]`
|
|
165
177
|
|
|
166
178
|
For command-specific help:
|
|
@@ -195,6 +207,21 @@ code=$?
|
|
|
195
207
|
[ "$code" -ge 11 ] && echo "medium or high alert"
|
|
196
208
|
```
|
|
197
209
|
|
|
210
|
+
## Update Status Exit Codes
|
|
211
|
+
|
|
212
|
+
`update status` returns a dedicated code so a cron job can react without parsing output:
|
|
213
|
+
|
|
214
|
+
- `0`: up to date (or `auto_check_updates` is disabled on the device)
|
|
215
|
+
- `1`: error
|
|
216
|
+
- `20`: update available
|
|
217
|
+
|
|
218
|
+
Example:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
raygatherer --host http://192.168.1.1 update status
|
|
222
|
+
[ $? -eq 20 ] && echo "rayhunter update available"
|
|
223
|
+
```
|
|
224
|
+
|
|
198
225
|
## JSON Output
|
|
199
226
|
|
|
200
227
|
Commands that support `--json` return machine-readable output to `stdout`. This is intended for `jq` and/or scripts.
|
|
@@ -79,6 +79,32 @@ module Raygatherer
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
def fetch_gps
|
|
83
|
+
get_optional("/api/gps") { |body| parse_json(body) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def set_gps(latitude, longitude)
|
|
87
|
+
post("/api/gps", expected_code: "200",
|
|
88
|
+
body: ::JSON.generate({latitude: latitude, longitude: longitude}),
|
|
89
|
+
content_type: "application/json")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def fetch_wifi_status
|
|
93
|
+
get("/api/wifi-status") do |body|
|
|
94
|
+
parse_json(body)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def scan_wifi
|
|
99
|
+
parse_json(post("/api/wifi-scan", expected_code: "200"))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def fetch_update_status
|
|
103
|
+
get("/api/update-status") do |body|
|
|
104
|
+
parse_json(body)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
82
108
|
def set_time_offset(offset_seconds)
|
|
83
109
|
post("/api/time-offset", expected_code: "200",
|
|
84
110
|
body: ::JSON.generate({offset_seconds: offset_seconds}),
|
|
@@ -151,6 +177,26 @@ module Raygatherer
|
|
|
151
177
|
raise
|
|
152
178
|
end
|
|
153
179
|
|
|
180
|
+
def get_optional(path, not_found_code: "404")
|
|
181
|
+
response, body = request(:get, path, ok_code: "200", ok_status_text: "OK")
|
|
182
|
+
|
|
183
|
+
return nil if response.code == not_found_code
|
|
184
|
+
|
|
185
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
186
|
+
raise ApiError, server_error_message(response, body)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
yield body
|
|
190
|
+
rescue ParseError => e
|
|
191
|
+
log_verbose "Parse failed: #{e.message}"
|
|
192
|
+
raise
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# expected_code defaults to "202" (Accepted), matching the daemon's behavior for most POST
|
|
196
|
+
# actions (recordings, analysis, config). A handful of endpoints (gps, wifi-scan, time-offset,
|
|
197
|
+
# test-notification, display-state) respond "200" instead — pass expected_code: "200" for
|
|
198
|
+
# those. This isn't documented by rayhunter anywhere; it's observed per-endpoint behavior, so
|
|
199
|
+
# verify against the real device if you're adding a new one and unsure which applies.
|
|
154
200
|
def post(path, expected_code: "202", body: nil, content_type: nil)
|
|
155
201
|
ok_status_text = (expected_code == "202") ? "Accepted" : "OK"
|
|
156
202
|
response, resp_body = request(:post, path, ok_code: expected_code,
|
|
@@ -164,16 +210,20 @@ module Raygatherer
|
|
|
164
210
|
end
|
|
165
211
|
|
|
166
212
|
def request(method, path, ok_code:, ok_status_text:, body: nil, content_type: nil)
|
|
167
|
-
|
|
168
|
-
|
|
213
|
+
with_connection_error_handling do
|
|
214
|
+
url = "#{@host}#{path}"
|
|
215
|
+
uri = URI.parse(url)
|
|
169
216
|
|
|
170
|
-
|
|
171
|
-
|
|
217
|
+
log_verbose "HTTP #{method.to_s.upcase} #{url}"
|
|
218
|
+
log_verbose "Basic Auth: user=#{@username}" if @username
|
|
172
219
|
|
|
173
|
-
|
|
174
|
-
|
|
220
|
+
req = build_request(method, uri, body: body, content_type: content_type)
|
|
221
|
+
execute_request(uri, req, ok_code: ok_code, ok_status_text: ok_status_text)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
175
224
|
|
|
176
|
-
|
|
225
|
+
def with_connection_error_handling
|
|
226
|
+
yield
|
|
177
227
|
rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
|
|
178
228
|
log_verbose "Connection error: #{e.class} - #{e.message}"
|
|
179
229
|
raise ConnectionError, "Failed to connect to #{@host}: #{e.message}"
|
|
@@ -220,28 +270,26 @@ module Raygatherer
|
|
|
220
270
|
start_time = Time.now
|
|
221
271
|
log_verbose "Request started at: #{start_time.utc}"
|
|
222
272
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
273
|
+
with_connection_error_handling do
|
|
274
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
|
|
275
|
+
req = build_request(:get, uri)
|
|
226
276
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
277
|
+
http.request(req) do |response|
|
|
278
|
+
elapsed = Time.now - start_time
|
|
279
|
+
status_text = (response.code == "200") ? "OK" : response.message.to_s
|
|
280
|
+
log_verbose "Response received: #{response.code} #{status_text} (#{format("%.3f", elapsed)}s)"
|
|
231
281
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
282
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
283
|
+
error_body = response.read_body
|
|
284
|
+
raise ApiError, server_error_message(response, error_body)
|
|
285
|
+
end
|
|
236
286
|
|
|
237
|
-
|
|
238
|
-
|
|
287
|
+
response.read_body do |chunk|
|
|
288
|
+
io.write(chunk)
|
|
289
|
+
end
|
|
239
290
|
end
|
|
240
291
|
end
|
|
241
292
|
end
|
|
242
|
-
rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
|
|
243
|
-
log_verbose "Connection error: #{e.class} - #{e.message}"
|
|
244
|
-
raise ConnectionError, "Failed to connect to #{@host}: #{e.message}"
|
|
245
293
|
end
|
|
246
294
|
|
|
247
295
|
def server_error_message(response, body)
|
data/lib/raygatherer/cli.rb
CHANGED
|
@@ -16,23 +16,28 @@ module Raygatherer
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
ROUTES = {
|
|
19
|
-
["log", nil] => {
|
|
20
|
-
["stats", nil] => {
|
|
21
|
-
["alerts", nil] => {
|
|
22
|
-
["recording", "list"] => {
|
|
23
|
-
["recording", "download"] => {
|
|
24
|
-
["recording", "delete"] => {
|
|
25
|
-
["recording", "stop"] => {
|
|
26
|
-
["recording", "start"] => {
|
|
27
|
-
["analysis", "status"] => {
|
|
28
|
-
["analysis", "run"] => {
|
|
29
|
-
["analysis", "report"] => {
|
|
30
|
-
["config", "show"] => {
|
|
31
|
-
["config", "set"] => {
|
|
32
|
-
["config", "test-notification"] => {
|
|
33
|
-
["debug", "display-state"] => {
|
|
34
|
-
["time", "show"] => {
|
|
35
|
-
["time", "sync"] => {
|
|
19
|
+
["log", nil] => {klass: "Commands::Log", json: false},
|
|
20
|
+
["stats", nil] => {klass: "Commands::Stats", json: true},
|
|
21
|
+
["alerts", nil] => {klass: "Commands::Alerts", json: true},
|
|
22
|
+
["recording", "list"] => {klass: "Commands::Recording::List", json: true},
|
|
23
|
+
["recording", "download"] => {klass: "Commands::Recording::Download", json: false},
|
|
24
|
+
["recording", "delete"] => {klass: "Commands::Recording::Delete", json: false},
|
|
25
|
+
["recording", "stop"] => {klass: "Commands::Recording::Stop", json: false},
|
|
26
|
+
["recording", "start"] => {klass: "Commands::Recording::Start", json: false},
|
|
27
|
+
["analysis", "status"] => {klass: "Commands::Analysis::Status", json: true},
|
|
28
|
+
["analysis", "run"] => {klass: "Commands::Analysis::Run", json: true},
|
|
29
|
+
["analysis", "report"] => {klass: "Commands::Analysis::Report", json: true},
|
|
30
|
+
["config", "show"] => {klass: "Commands::Config::Show", json: true},
|
|
31
|
+
["config", "set"] => {klass: "Commands::Config::Set", json: false},
|
|
32
|
+
["config", "test-notification"] => {klass: "Commands::Config::TestNotification", json: false},
|
|
33
|
+
["debug", "display-state"] => {klass: "Commands::Debug::DisplayState", json: false},
|
|
34
|
+
["time", "show"] => {klass: "Commands::Time::Show", json: true},
|
|
35
|
+
["time", "sync"] => {klass: "Commands::Time::Sync", json: false},
|
|
36
|
+
["update", "status"] => {klass: "Commands::Update::Status", json: true},
|
|
37
|
+
["gps", "show"] => {klass: "Commands::Gps::Show", json: true},
|
|
38
|
+
["gps", "set"] => {klass: "Commands::Gps::Set", json: false},
|
|
39
|
+
["wifi", "show"] => {klass: "Commands::Wifi::Show", json: true},
|
|
40
|
+
["wifi", "scan"] => {klass: "Commands::Wifi::Scan", json: true}
|
|
36
41
|
}.freeze
|
|
37
42
|
|
|
38
43
|
def self.run(argv, stdout: $stdout, stderr: $stderr, config: Config.new)
|
|
@@ -91,7 +96,6 @@ module Raygatherer
|
|
|
91
96
|
return Commands::EXIT_CODE_ERROR
|
|
92
97
|
end
|
|
93
98
|
|
|
94
|
-
require_relative route[:file]
|
|
95
99
|
return Commands::EXIT_CODE_ERROR unless require_host!
|
|
96
100
|
|
|
97
101
|
kwargs = {stdout: @stdout, stderr: @stderr, api_client: build_api_client}
|
|
@@ -189,6 +193,11 @@ module Raygatherer
|
|
|
189
193
|
output.puts " debug display-state STATE Set device display state (recording, paused, warning)"
|
|
190
194
|
output.puts " time show Show device time (system, adjusted, offset)"
|
|
191
195
|
output.puts " time sync Sync device clock to this machine's time"
|
|
196
|
+
output.puts " update status Show rayhunter daemon update status"
|
|
197
|
+
output.puts " gps show Show last known GPS position"
|
|
198
|
+
output.puts " gps set LAT LON Submit a GPS position (requires gps_mode=Api on device)"
|
|
199
|
+
output.puts " wifi show Show wifi client connection status"
|
|
200
|
+
output.puts " wifi scan Scan for available wifi networks"
|
|
192
201
|
output.puts " log Download the device log"
|
|
193
202
|
output.puts " stats Show device system stats"
|
|
194
203
|
output.puts ""
|
|
@@ -21,8 +21,7 @@ module Raygatherer
|
|
|
21
21
|
EXIT_CODE_HIGH_SEVERITY = 12
|
|
22
22
|
|
|
23
23
|
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
24
|
-
super
|
|
25
|
-
@json = json
|
|
24
|
+
super
|
|
26
25
|
@latest = false
|
|
27
26
|
@after = nil
|
|
28
27
|
@recording = nil
|
|
@@ -41,11 +40,9 @@ module Raygatherer
|
|
|
41
40
|
alerts = filter_after(alerts) if @after
|
|
42
41
|
alerts = filter_latest(alerts, data[:rows]) if @latest
|
|
43
42
|
|
|
44
|
-
# Select formatter based on --json flag
|
|
45
43
|
formatter = @json ? Formatters::AlertsJSON.new : Formatters::AlertsHuman.new
|
|
46
44
|
@stdout.puts formatter.format(alerts)
|
|
47
45
|
|
|
48
|
-
# Return severity-based exit code
|
|
49
46
|
severity_exit_code(alerts)
|
|
50
47
|
end
|
|
51
48
|
end
|
|
@@ -70,10 +67,7 @@ module Raygatherer
|
|
|
70
67
|
@after = parse_timestamp(ts)
|
|
71
68
|
end
|
|
72
69
|
|
|
73
|
-
opts
|
|
74
|
-
show_help
|
|
75
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
76
|
-
end
|
|
70
|
+
add_help_option(opts)
|
|
77
71
|
end.parse!(@argv)
|
|
78
72
|
end
|
|
79
73
|
|
|
@@ -10,8 +10,7 @@ module Raygatherer
|
|
|
10
10
|
module Analysis
|
|
11
11
|
class Report < Base
|
|
12
12
|
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super
|
|
14
|
-
@json = json
|
|
13
|
+
super
|
|
15
14
|
@live = false
|
|
16
15
|
end
|
|
17
16
|
|
|
@@ -52,10 +51,7 @@ module Raygatherer
|
|
|
52
51
|
@live = true
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
opts
|
|
56
|
-
show_help
|
|
57
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
58
|
-
end
|
|
54
|
+
add_help_option(opts)
|
|
59
55
|
end.parse!(@argv)
|
|
60
56
|
end
|
|
61
57
|
|
|
@@ -10,8 +10,7 @@ module Raygatherer
|
|
|
10
10
|
module Analysis
|
|
11
11
|
class Run < Base
|
|
12
12
|
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super
|
|
14
|
-
@json = json
|
|
13
|
+
super
|
|
15
14
|
@all = false
|
|
16
15
|
end
|
|
17
16
|
|
|
@@ -59,10 +58,7 @@ module Raygatherer
|
|
|
59
58
|
@all = true
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
opts
|
|
63
|
-
show_help
|
|
64
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
65
|
-
end
|
|
61
|
+
add_help_option(opts)
|
|
66
62
|
end.parse!(@argv)
|
|
67
63
|
end
|
|
68
64
|
|
|
@@ -9,11 +9,6 @@ module Raygatherer
|
|
|
9
9
|
module Commands
|
|
10
10
|
module Analysis
|
|
11
11
|
class Status < Base
|
|
12
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
|
|
14
|
-
@json = json
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
def run
|
|
18
13
|
with_error_handling do
|
|
19
14
|
parse_options
|
|
@@ -35,10 +30,7 @@ module Raygatherer
|
|
|
35
30
|
opts.separator ""
|
|
36
31
|
opts.separator "Options:"
|
|
37
32
|
|
|
38
|
-
opts
|
|
39
|
-
show_help
|
|
40
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
41
|
-
end
|
|
33
|
+
add_help_option(opts)
|
|
42
34
|
end.parse!(@argv)
|
|
43
35
|
end
|
|
44
36
|
|
|
@@ -10,15 +10,23 @@ module Raygatherer
|
|
|
10
10
|
new(argv, **kwargs).run
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, **_kwargs)
|
|
13
|
+
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false, **_kwargs)
|
|
14
14
|
@argv = argv
|
|
15
15
|
@stdout = stdout
|
|
16
16
|
@stderr = stderr
|
|
17
17
|
@api_client = api_client
|
|
18
|
+
@json = json
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
private
|
|
21
22
|
|
|
23
|
+
def add_help_option(opts)
|
|
24
|
+
opts.on("-h", "--help", "Show this help message") do
|
|
25
|
+
show_help
|
|
26
|
+
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
def print_global_options(output, json: false)
|
|
23
31
|
output.puts "Global options (see 'raygatherer --help'):"
|
|
24
32
|
if json
|
|
@@ -36,6 +44,17 @@ module Raygatherer
|
|
|
36
44
|
@stderr.puts "Error: #{e.message}"
|
|
37
45
|
EXIT_CODE_ERROR
|
|
38
46
|
end
|
|
47
|
+
|
|
48
|
+
def warn_if_rayhunter_outdated(rayhunter_version)
|
|
49
|
+
actual = Gem::Version.new(rayhunter_version)
|
|
50
|
+
tested = Gem::Version.new(Raygatherer::TESTED_RAYHUNTER_VERSION)
|
|
51
|
+
return unless actual < tested
|
|
52
|
+
|
|
53
|
+
@stderr.puts "Warning: connected rayhunter is v#{rayhunter_version}; raygatherer was last " \
|
|
54
|
+
"tested against v#{Raygatherer::TESTED_RAYHUNTER_VERSION} — some fields or commands may not be supported."
|
|
55
|
+
rescue ArgumentError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
39
58
|
end
|
|
40
59
|
end
|
|
41
60
|
end
|
|
@@ -9,11 +9,6 @@ module Raygatherer
|
|
|
9
9
|
module Commands
|
|
10
10
|
module Config
|
|
11
11
|
class Show < Base
|
|
12
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
|
|
14
|
-
@json = json
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
def run
|
|
18
13
|
with_error_handling do
|
|
19
14
|
parse_options
|
|
@@ -35,10 +30,7 @@ module Raygatherer
|
|
|
35
30
|
opts.separator ""
|
|
36
31
|
opts.separator "Options:"
|
|
37
32
|
|
|
38
|
-
opts
|
|
39
|
-
show_help
|
|
40
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
41
|
-
end
|
|
33
|
+
add_help_option(opts)
|
|
42
34
|
end.parse!(@argv)
|
|
43
35
|
end
|
|
44
36
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../base"
|
|
4
|
+
|
|
5
|
+
module Raygatherer
|
|
6
|
+
module Commands
|
|
7
|
+
module Gps
|
|
8
|
+
class Set < Base
|
|
9
|
+
def run
|
|
10
|
+
with_error_handling do
|
|
11
|
+
parse_options
|
|
12
|
+
|
|
13
|
+
unless @argv.length == 2
|
|
14
|
+
@stderr.puts "Error: latitude and longitude are required (usage: gps set LAT LON)"
|
|
15
|
+
return EXIT_CODE_ERROR
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
lat_str, lon_str = @argv
|
|
19
|
+
|
|
20
|
+
latitude = parse_coordinate(lat_str, "latitude")
|
|
21
|
+
return EXIT_CODE_ERROR unless latitude
|
|
22
|
+
|
|
23
|
+
longitude = parse_coordinate(lon_str, "longitude")
|
|
24
|
+
return EXIT_CODE_ERROR unless longitude
|
|
25
|
+
|
|
26
|
+
@api_client.set_gps(latitude, longitude)
|
|
27
|
+
@stdout.puts "GPS position set: #{latitude}, #{longitude}"
|
|
28
|
+
|
|
29
|
+
EXIT_CODE_SUCCESS
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def parse_coordinate(str, label)
|
|
36
|
+
Float(str)
|
|
37
|
+
rescue ArgumentError
|
|
38
|
+
@stderr.puts "Error: #{label} must be a number (got #{str.inspect})"
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Deliberately not using OptionParser#parse! here: a negative longitude like "-0.1"
|
|
43
|
+
# looks like an unrecognized flag to it and raises OptionParser::InvalidOption. This
|
|
44
|
+
# command's only "option" is help, so just scan for it directly instead.
|
|
45
|
+
def parse_options
|
|
46
|
+
return unless @argv.include?("-h") || @argv.include?("--help")
|
|
47
|
+
|
|
48
|
+
show_help
|
|
49
|
+
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show_help(output = @stdout)
|
|
53
|
+
output.puts "Usage: raygatherer [global options] gps set [options] LAT LON"
|
|
54
|
+
output.puts ""
|
|
55
|
+
output.puts "Submit a GPS position to the device, to be embedded into the current recording."
|
|
56
|
+
output.puts "Requires the device's gps_mode config to be set to 'Api' (see 'config show'/'config set')."
|
|
57
|
+
output.puts ""
|
|
58
|
+
output.puts "Options:"
|
|
59
|
+
output.puts " -h, --help Show this help message"
|
|
60
|
+
output.puts ""
|
|
61
|
+
print_global_options(output)
|
|
62
|
+
output.puts ""
|
|
63
|
+
output.puts "Examples:"
|
|
64
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 gps set 51.5074 -0.1278"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require_relative "../base"
|
|
5
|
+
require_relative "../../formatters/gps_json"
|
|
6
|
+
require_relative "../../formatters/gps_human"
|
|
7
|
+
|
|
8
|
+
module Raygatherer
|
|
9
|
+
module Commands
|
|
10
|
+
module Gps
|
|
11
|
+
class Show < Base
|
|
12
|
+
def run
|
|
13
|
+
with_error_handling do
|
|
14
|
+
parse_options
|
|
15
|
+
|
|
16
|
+
gps = @api_client.fetch_gps
|
|
17
|
+
|
|
18
|
+
formatter = @json ? Formatters::GpsJSON.new : Formatters::GpsHuman.new
|
|
19
|
+
@stdout.puts formatter.format(gps)
|
|
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 gps 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] gps show [options]"
|
|
39
|
+
output.puts ""
|
|
40
|
+
output.puts "Show the last GPS position submitted to the device (see 'gps set')."
|
|
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 gps show"
|
|
49
|
+
output.puts " raygatherer --host http://192.168.1.100:8080 --json gps show"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -20,7 +20,7 @@ module Raygatherer
|
|
|
20
20
|
|
|
21
21
|
if @all && !@argv.empty?
|
|
22
22
|
@stderr.puts "Error: cannot specify both a recording name and --all"
|
|
23
|
-
|
|
23
|
+
return EXIT_CODE_ERROR
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
if @all
|
|
@@ -72,10 +72,7 @@ module Raygatherer
|
|
|
72
72
|
@force = true
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
opts
|
|
76
|
-
show_help
|
|
77
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
78
|
-
end
|
|
75
|
+
add_help_option(opts)
|
|
79
76
|
end.parse!(@argv)
|
|
80
77
|
end
|
|
81
78
|
|
|
@@ -9,11 +9,6 @@ module Raygatherer
|
|
|
9
9
|
module Commands
|
|
10
10
|
module Recording
|
|
11
11
|
class List < Base
|
|
12
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
|
|
14
|
-
@json = json
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
def run
|
|
18
13
|
with_error_handling do
|
|
19
14
|
parse_options
|
|
@@ -35,10 +30,7 @@ module Raygatherer
|
|
|
35
30
|
opts.separator ""
|
|
36
31
|
opts.separator "Options:"
|
|
37
32
|
|
|
38
|
-
opts
|
|
39
|
-
show_help
|
|
40
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
41
|
-
end
|
|
33
|
+
add_help_option(opts)
|
|
42
34
|
end.parse!(@argv)
|
|
43
35
|
end
|
|
44
36
|
|
|
@@ -8,17 +8,15 @@ require_relative "../formatters/stats_human"
|
|
|
8
8
|
module Raygatherer
|
|
9
9
|
module Commands
|
|
10
10
|
class Stats < Base
|
|
11
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
12
|
-
super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
|
|
13
|
-
@json = json
|
|
14
|
-
end
|
|
15
|
-
|
|
16
11
|
def run
|
|
17
12
|
with_error_handling do
|
|
18
13
|
parse_options
|
|
19
14
|
|
|
20
15
|
stats = @api_client.fetch_system_stats
|
|
21
16
|
|
|
17
|
+
rayhunter_version = stats.dig("runtime_metadata", "rayhunter_version")
|
|
18
|
+
warn_if_rayhunter_outdated(rayhunter_version) if rayhunter_version
|
|
19
|
+
|
|
22
20
|
formatter = @json ? Formatters::StatsJSON.new : Formatters::StatsHuman.new
|
|
23
21
|
@stdout.puts formatter.format(stats)
|
|
24
22
|
|
|
@@ -34,10 +32,7 @@ module Raygatherer
|
|
|
34
32
|
opts.separator ""
|
|
35
33
|
opts.separator "Options:"
|
|
36
34
|
|
|
37
|
-
opts
|
|
38
|
-
show_help
|
|
39
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
40
|
-
end
|
|
35
|
+
add_help_option(opts)
|
|
41
36
|
end.parse!(@argv)
|
|
42
37
|
end
|
|
43
38
|
|
|
@@ -9,11 +9,6 @@ module Raygatherer
|
|
|
9
9
|
module Commands
|
|
10
10
|
module Time
|
|
11
11
|
class Show < Base
|
|
12
|
-
def initialize(argv, stdout: $stdout, stderr: $stderr, api_client: nil, json: false)
|
|
13
|
-
super(argv, stdout: stdout, stderr: stderr, api_client: api_client)
|
|
14
|
-
@json = json
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
def run
|
|
18
13
|
with_error_handling do
|
|
19
14
|
parse_options
|
|
@@ -35,10 +30,7 @@ module Raygatherer
|
|
|
35
30
|
opts.separator ""
|
|
36
31
|
opts.separator "Options:"
|
|
37
32
|
|
|
38
|
-
opts
|
|
39
|
-
show_help
|
|
40
|
-
raise CLI::EarlyExit, EXIT_CODE_SUCCESS
|
|
41
|
-
end
|
|
33
|
+
add_help_option(opts)
|
|
42
34
|
end.parse!(@argv)
|
|
43
35
|
end
|
|
44
36
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
@@ -18,6 +18,14 @@ require_relative "raygatherer/formatters/config_json"
|
|
|
18
18
|
require_relative "raygatherer/formatters/config_human"
|
|
19
19
|
require_relative "raygatherer/formatters/time_json"
|
|
20
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"
|
|
21
29
|
require_relative "raygatherer/spinner"
|
|
22
30
|
require_relative "raygatherer/commands/base"
|
|
23
31
|
require_relative "raygatherer/commands/alerts"
|
|
@@ -37,6 +45,11 @@ require_relative "raygatherer/commands/config/test_notification"
|
|
|
37
45
|
require_relative "raygatherer/commands/debug/display_state"
|
|
38
46
|
require_relative "raygatherer/commands/time/show"
|
|
39
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"
|
|
40
53
|
|
|
41
54
|
module Raygatherer
|
|
42
55
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raygatherer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Stallard
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: bundler-audit
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.9'
|
|
40
54
|
description: 'Ruby CLI tool for interacting with Rayhunter, a cell tower analysis
|
|
41
55
|
device for detecting IMSI catchers and other cellular network anomalies. '
|
|
42
56
|
email:
|
|
@@ -65,6 +79,8 @@ files:
|
|
|
65
79
|
- lib/raygatherer/commands/config/show.rb
|
|
66
80
|
- lib/raygatherer/commands/config/test_notification.rb
|
|
67
81
|
- lib/raygatherer/commands/debug/display_state.rb
|
|
82
|
+
- lib/raygatherer/commands/gps/set.rb
|
|
83
|
+
- lib/raygatherer/commands/gps/show.rb
|
|
68
84
|
- lib/raygatherer/commands/log.rb
|
|
69
85
|
- lib/raygatherer/commands/recording/delete.rb
|
|
70
86
|
- lib/raygatherer/commands/recording/download.rb
|
|
@@ -74,6 +90,9 @@ files:
|
|
|
74
90
|
- lib/raygatherer/commands/stats.rb
|
|
75
91
|
- lib/raygatherer/commands/time/show.rb
|
|
76
92
|
- lib/raygatherer/commands/time/sync.rb
|
|
93
|
+
- lib/raygatherer/commands/update/status.rb
|
|
94
|
+
- lib/raygatherer/commands/wifi/scan.rb
|
|
95
|
+
- lib/raygatherer/commands/wifi/show.rb
|
|
77
96
|
- lib/raygatherer/config.rb
|
|
78
97
|
- lib/raygatherer/format_helpers.rb
|
|
79
98
|
- lib/raygatherer/formatters/alerts_human.rb
|
|
@@ -84,12 +103,20 @@ files:
|
|
|
84
103
|
- lib/raygatherer/formatters/analysis_status_json.rb
|
|
85
104
|
- lib/raygatherer/formatters/config_human.rb
|
|
86
105
|
- lib/raygatherer/formatters/config_json.rb
|
|
106
|
+
- lib/raygatherer/formatters/gps_human.rb
|
|
107
|
+
- lib/raygatherer/formatters/gps_json.rb
|
|
87
108
|
- lib/raygatherer/formatters/recording_list_human.rb
|
|
88
109
|
- lib/raygatherer/formatters/recording_list_json.rb
|
|
89
110
|
- lib/raygatherer/formatters/stats_human.rb
|
|
90
111
|
- lib/raygatherer/formatters/stats_json.rb
|
|
91
112
|
- lib/raygatherer/formatters/time_human.rb
|
|
92
113
|
- lib/raygatherer/formatters/time_json.rb
|
|
114
|
+
- lib/raygatherer/formatters/update_status_human.rb
|
|
115
|
+
- lib/raygatherer/formatters/update_status_json.rb
|
|
116
|
+
- lib/raygatherer/formatters/wifi_scan_human.rb
|
|
117
|
+
- lib/raygatherer/formatters/wifi_scan_json.rb
|
|
118
|
+
- lib/raygatherer/formatters/wifi_status_human.rb
|
|
119
|
+
- lib/raygatherer/formatters/wifi_status_json.rb
|
|
93
120
|
- lib/raygatherer/spinner.rb
|
|
94
121
|
- lib/raygatherer/version.rb
|
|
95
122
|
homepage: https://github.com/mjstallard/raygatherer
|
|
@@ -113,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
113
140
|
- !ruby/object:Gem::Version
|
|
114
141
|
version: '0'
|
|
115
142
|
requirements: []
|
|
116
|
-
rubygems_version:
|
|
143
|
+
rubygems_version: 4.0.9
|
|
117
144
|
specification_version: 4
|
|
118
145
|
summary: CLI for fetching and displaying alerts from Rayhunter
|
|
119
146
|
test_files: []
|