meshtastic 0.0.42 → 0.0.43
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/lib/meshtastic/mqtt.rb +78 -107
- data/lib/meshtastic/version.rb +1 -1
- data/lib/meshtastic.rb +187 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 105d223bd94e02d4473ac5fd66ed0b8dc7eef73d44578eb427fc3d55eb51ae21
|
|
4
|
+
data.tar.gz: 216a3957a439408579d1d7b48dd552d394885bbc09b8dc3410d776e4c1eb37d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1381a13cff943403ef269c5a349529ab9c2f12e3a3af5b3e88c589f7fd427a6e45b23543251449e1d520471fdf5f6c8a11c55c56bfc24553dc2b02db7ace28d9
|
|
7
|
+
data.tar.gz: 01d01863635a22c2d3c42ace2a34076c4094ece8ef9629f08ae0e8da7c0a98bbc5815db26e2e4063fa462619caf710938628a8cd086f62411d1cd885017665a4
|
data/lib/meshtastic/mqtt.rb
CHANGED
|
@@ -64,120 +64,91 @@ module Meshtastic
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Supported Method Parameters::
|
|
67
|
-
# Meshtastic::MQQT.
|
|
68
|
-
#
|
|
67
|
+
# Meshtastic::MQQT.decode_payload(
|
|
68
|
+
# payload: 'required - payload to recursively decode',
|
|
69
69
|
# msg_type: 'required - message type (e.g. :TEXT_MESSAGE_APP)',
|
|
70
|
-
# gps_metadata: 'optional - include GPS metadata in output (default: false)'
|
|
70
|
+
# gps_metadata: 'optional - include GPS metadata in output (default: false)',
|
|
71
71
|
# )
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
public_class_method def self.decode_payload(opts = {})
|
|
74
|
+
payload = opts[:payload]
|
|
75
75
|
msg_type = opts[:msg_type]
|
|
76
76
|
gps_metadata = opts[:gps_metadata]
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
when :TEXT_MESSAGE_APP
|
|
141
|
-
# Unsure if this is the correct protobuf object
|
|
142
|
-
# decoder = Meshtastic::MqttClientProxyMessage
|
|
143
|
-
decoder = Meshtastic::Data
|
|
144
|
-
when :TELEMETRY_APP
|
|
145
|
-
decoder = Meshtastic::Telemetry
|
|
146
|
-
when :TRACEROUTE_APP
|
|
147
|
-
decoder = Meshtastic::RouteDiscovery
|
|
148
|
-
when :UNKNOWN_APP
|
|
149
|
-
decoder = Meshtastic::Data.decode
|
|
150
|
-
when :WAYPOINT_APP
|
|
151
|
-
decoder = Meshtastic::Waypoint
|
|
152
|
-
# when :ZPS_APP
|
|
153
|
-
# decoder = Meshtastic::Zps
|
|
154
|
-
else
|
|
155
|
-
puts "WARNING: Unknown message type: #{msg_type}"
|
|
156
|
-
decoder = Meshtastic::Data.decode
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# begin
|
|
160
|
-
# # If the value is base64 encoded, base64 decode and then decode
|
|
161
|
-
# base64_decoded_value = Base64.strict_decode64(value)
|
|
162
|
-
# object[key] = decoder.decode(base64_decoded_value).to_h
|
|
163
|
-
# rescue ArgumentError => e
|
|
164
|
-
# # Otherwise, just decode the value
|
|
165
|
-
object[key] = decoder.decode(value).to_h
|
|
166
|
-
# end
|
|
167
|
-
|
|
78
|
+
case msg_type
|
|
79
|
+
when :ADMIN_APP
|
|
80
|
+
decoder = Meshtastic::AdminMessage
|
|
81
|
+
when :ATAK_FORWARDER, :ATAK_PLUGIN
|
|
82
|
+
decoder = Meshtastic::TAKPacket
|
|
83
|
+
# when :AUDIO_APP
|
|
84
|
+
# decoder = Meshtastic::Audio
|
|
85
|
+
when :DETECTION_SENSOR_APP
|
|
86
|
+
decoder = Meshtastic::DeviceState
|
|
87
|
+
# when :IP_TUNNEL_APP
|
|
88
|
+
# decoder = Meshtastic::IpTunnel
|
|
89
|
+
when :MAP_REPORT_APP
|
|
90
|
+
decoder = Meshtastic::MapReport
|
|
91
|
+
# when :MAX
|
|
92
|
+
# decoder = Meshtastic::Max
|
|
93
|
+
when :NEIGHBORINFO_APP
|
|
94
|
+
decoder = Meshtastic::NeighborInfo
|
|
95
|
+
when :NODEINFO_APP
|
|
96
|
+
decoder = Meshtastic::User
|
|
97
|
+
when :PAXCOUNTER_APP
|
|
98
|
+
decoder = Meshtastic::Paxcount
|
|
99
|
+
when :POSITION_APP
|
|
100
|
+
decoder = Meshtastic::Position
|
|
101
|
+
# when :PRIVATE_APP
|
|
102
|
+
# decoder = Meshtastic::Private
|
|
103
|
+
when :RANGE_TEST_APP
|
|
104
|
+
# Unsure if this is the correct protobuf object
|
|
105
|
+
decoder = Meshtastic::FromRadio
|
|
106
|
+
when :REMOTE_HARDWARE_APP
|
|
107
|
+
decoder = Meshtastic::HardwareMessage
|
|
108
|
+
# when :REPLY_APP
|
|
109
|
+
# decoder = Meshtastic::Reply
|
|
110
|
+
when :ROUTING_APP
|
|
111
|
+
decoder = Meshtastic::Routing
|
|
112
|
+
when :SERIAL_APP
|
|
113
|
+
decoder = Meshtastic::SerialConnectionStatus
|
|
114
|
+
when :SIMULATOR_APP
|
|
115
|
+
decoder = Meshtastic::Compressed
|
|
116
|
+
when :STORE_FORWARD_APP
|
|
117
|
+
decoder = Meshtastic::StoreAndForward
|
|
118
|
+
when :TEXT_MESSAGE_APP
|
|
119
|
+
# Unsure if this is the correct protobuf object
|
|
120
|
+
# decoder = Meshtastic::MqttClientProxyMessage
|
|
121
|
+
decoder = Meshtastic::Data
|
|
122
|
+
when :TELEMETRY_APP
|
|
123
|
+
decoder = Meshtastic::Telemetry
|
|
124
|
+
when :TRACEROUTE_APP
|
|
125
|
+
decoder = Meshtastic::RouteDiscovery
|
|
126
|
+
when :WAYPOINT_APP
|
|
127
|
+
decoder = Meshtastic::Waypoint
|
|
128
|
+
# when :ZPS_APP
|
|
129
|
+
# decoder = Meshtastic::Zps
|
|
130
|
+
end
|
|
131
|
+
payload = decoder.decode(payload).to_h
|
|
132
|
+
if payload.keys.include?(:latitude_i) && payload.keys.include?(:longitude_i)
|
|
133
|
+
lat = payload[:latitude_i] * 0.0000001
|
|
134
|
+
lon = payload[:longitude_i] * 0.0000001
|
|
135
|
+
payload[:latitude] = lat
|
|
136
|
+
payload[:longitude] = lon
|
|
137
|
+
if gps_metadata
|
|
138
|
+
gps_search_resp = Meshtastic.gps_search(lat: lat, lon: lon)
|
|
139
|
+
payload[:gps_metadata] = gps_search_resp
|
|
168
140
|
end
|
|
141
|
+
end
|
|
169
142
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
lon: object[:longitude]
|
|
175
|
-
).first.data
|
|
143
|
+
if payload.keys.include?(:macaddr)
|
|
144
|
+
mac_hex_arr = payload[:macaddr].bytes.map { |byte| byte.to_s(16).rjust(2, '0') }
|
|
145
|
+
mac_str = mac_hex_arr.join(':')
|
|
146
|
+
payload[:macaddr] = mac_str
|
|
176
147
|
end
|
|
177
148
|
|
|
178
|
-
|
|
149
|
+
payload
|
|
179
150
|
rescue Google::Protobuf::ParseError
|
|
180
|
-
|
|
151
|
+
payload
|
|
181
152
|
rescue StandardError => e
|
|
182
153
|
raise e
|
|
183
154
|
end
|
|
@@ -256,8 +227,8 @@ module Meshtastic
|
|
|
256
227
|
nonce = "#{nonce_packet_id}#{nonce_from_node}".b
|
|
257
228
|
|
|
258
229
|
psk = psks[:LongFast]
|
|
259
|
-
|
|
260
|
-
psk = psks[
|
|
230
|
+
target_channel = decoded_payload_hash[:channel_id].to_s.to_sym
|
|
231
|
+
psk = psks[target_channel] if psks.keys.include?(target_channel)
|
|
261
232
|
dec_psk = Base64.strict_decode64(psk)
|
|
262
233
|
|
|
263
234
|
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
|
@@ -275,8 +246,8 @@ module Meshtastic
|
|
|
275
246
|
# payload = Meshtastic::Data.decode(message[:decoded][:payload]).to_h
|
|
276
247
|
payload = message[:decoded][:payload]
|
|
277
248
|
msg_type = message[:decoded][:portnum]
|
|
278
|
-
message[:decoded][:payload] =
|
|
279
|
-
|
|
249
|
+
message[:decoded][:payload] = decode_payload(
|
|
250
|
+
payload: payload,
|
|
280
251
|
msg_type: msg_type,
|
|
281
252
|
gps_metadata: gps_metadata
|
|
282
253
|
)
|
data/lib/meshtastic/version.rb
CHANGED
data/lib/meshtastic.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Plugin used to interact with Meshtastic nodes
|
|
4
4
|
module Meshtastic
|
|
5
|
+
require 'base64'
|
|
5
6
|
# Protocol Buffers for Meshtastic
|
|
6
7
|
require 'meshtastic/admin_pb'
|
|
7
8
|
require 'nanopb_pb'
|
|
@@ -25,6 +26,7 @@ module Meshtastic
|
|
|
25
26
|
require 'meshtastic/telemetry_pb'
|
|
26
27
|
require 'meshtastic/version'
|
|
27
28
|
require 'meshtastic/xmodem_pb'
|
|
29
|
+
require 'openssl'
|
|
28
30
|
|
|
29
31
|
autoload :Admin, 'meshtastic/admin'
|
|
30
32
|
autoload :Apponly, 'meshtastic/apponly'
|
|
@@ -47,6 +49,191 @@ module Meshtastic
|
|
|
47
49
|
autoload :Telemetry, 'meshtastic/telemetry'
|
|
48
50
|
autoload :Xmodem, 'meshtastic/xmodem'
|
|
49
51
|
|
|
52
|
+
# Supported Method Parameters::
|
|
53
|
+
# Meshtastic.send_text(
|
|
54
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
|
55
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
|
56
|
+
# text: 'optional - Text Message (Default: SYN)',
|
|
57
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
|
58
|
+
# want_response: 'optional - Want Response (Default: false)',
|
|
59
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
|
60
|
+
# on_response: 'optional - Callback on Response',
|
|
61
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
|
62
|
+
# )
|
|
63
|
+
public_class_method def self.send_text(opts = {})
|
|
64
|
+
# Send a text message to a node
|
|
65
|
+
from_id = opts[:from_id].to_i
|
|
66
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
|
67
|
+
text = opts[:text] ||= 'SYN'
|
|
68
|
+
want_ack = opts[:want_ack] ||= false
|
|
69
|
+
want_response = opts[:want_response] ||= false
|
|
70
|
+
hop_limit = opts[:hop_limit] ||= 3
|
|
71
|
+
on_response = opts[:on_response]
|
|
72
|
+
psk = opts[:psk]
|
|
73
|
+
|
|
74
|
+
# TODO: verify text length validity
|
|
75
|
+
max_txt_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
|
|
76
|
+
raise "ERROR: Text Length > #{max_txt_len} Bytes" if text.length > max_txt_len
|
|
77
|
+
|
|
78
|
+
port_num = Meshtastic::PortNum::TEXT_MESSAGE_APP
|
|
79
|
+
|
|
80
|
+
data = Meshtastic::Data.new
|
|
81
|
+
data.payload = text
|
|
82
|
+
data.portnum = port_num
|
|
83
|
+
data.want_response = want_response
|
|
84
|
+
puts data.to_h
|
|
85
|
+
|
|
86
|
+
send_data(
|
|
87
|
+
from_id: from_id,
|
|
88
|
+
dest_id: dest_id,
|
|
89
|
+
data: data,
|
|
90
|
+
want_ack: want_ack,
|
|
91
|
+
want_response: want_response,
|
|
92
|
+
hop_limit: hop_limit,
|
|
93
|
+
port_num: port_num,
|
|
94
|
+
on_response: on_response,
|
|
95
|
+
psk: psk
|
|
96
|
+
)
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
raise e
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Supported Method Parameters::
|
|
102
|
+
# Meshtastic.send_data(
|
|
103
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
|
104
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
|
105
|
+
# data: 'required - Data to Send',
|
|
106
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
|
107
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
|
108
|
+
# port_num: 'optional - (Default: Meshtastic::PortNum::PRIVATE_APP)',
|
|
109
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
|
110
|
+
# )
|
|
111
|
+
public_class_method def self.send_data(opts = {})
|
|
112
|
+
# Send a text message to a node
|
|
113
|
+
from_id = opts[:from_id].to_i
|
|
114
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
|
115
|
+
data = opts[:data]
|
|
116
|
+
want_ack = opts[:want_ack] ||= false
|
|
117
|
+
hop_limit = opts[:hop_limit] ||= 3
|
|
118
|
+
port_num = opts[:port_num] ||= Meshtastic::PortNum::PRIVATE_APP
|
|
119
|
+
max_port_num = Meshtastic::PortNum::MAX
|
|
120
|
+
raise "ERROR: Invalid port_num" unless port_num.positive? && port_num < max_port_num
|
|
121
|
+
|
|
122
|
+
psk = opts[:psk]
|
|
123
|
+
|
|
124
|
+
data_len = data.payload.length
|
|
125
|
+
max_len = Meshtastic::Constants::DATA_PAYLOAD_LEN
|
|
126
|
+
raise "ERROR: Data Length > #{max_len} Bytes" if data_len > max_len
|
|
127
|
+
|
|
128
|
+
mesh_packet = Meshtastic::MeshPacket.new
|
|
129
|
+
mesh_packet.decoded = data
|
|
130
|
+
|
|
131
|
+
send_packet(
|
|
132
|
+
mesh_packet: mesh_packet,
|
|
133
|
+
from_id: from_id,
|
|
134
|
+
dest_id: dest_id,
|
|
135
|
+
want_ack: want_ack,
|
|
136
|
+
hop_limit: hop_limit,
|
|
137
|
+
psk: psk
|
|
138
|
+
)
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
raise e
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Supported Method Parameters::
|
|
144
|
+
# Meshtastic.send_packet(
|
|
145
|
+
# mesh_packet: 'required - Mesh Packet to Send',
|
|
146
|
+
# from_id: 'optional - Source ID (Default: 0)',
|
|
147
|
+
# dest_id: 'optional - Destination ID (Default: 0xFFFFFFFF)',
|
|
148
|
+
# want_ack: 'optional - Want Acknowledgement (Default: false)',
|
|
149
|
+
# hop_limit: 'optional - Hop Limit (Default: 3)',
|
|
150
|
+
# psk: 'optional - Pre-Shared Key if Encrypted (Default: nil)'
|
|
151
|
+
# )
|
|
152
|
+
public_class_method def self.send_packet(opts = {})
|
|
153
|
+
mesh_packet = opts[:mesh_packet]
|
|
154
|
+
from_id = opts[:from_id] ||= 0
|
|
155
|
+
dest_id = opts[:dest_id] ||= 0xFFFFFFFF
|
|
156
|
+
want_ack = opts[:want_ack] ||= false
|
|
157
|
+
hop_limit = opts[:hop_limit] ||= 3
|
|
158
|
+
psk = opts[:psk]
|
|
159
|
+
|
|
160
|
+
# my_info = Meshtastic::FromRadio.my_info
|
|
161
|
+
# wait_connected if dest_id != my_info.my_node_num && my_info.is_a(Meshtastic::Deviceonly::MyInfo)
|
|
162
|
+
|
|
163
|
+
node_num = dest_id
|
|
164
|
+
node_num_hex = dest_id.bytes.map { |b| b.to_s(16).rjust(2, '0') }.join if dest_id.is_a?(String)
|
|
165
|
+
node_num = node_num_hex.to_i(16) if node_num_hex
|
|
166
|
+
|
|
167
|
+
mesh_packet.from = from_id
|
|
168
|
+
mesh_packet.to = node_num
|
|
169
|
+
mesh_packet.want_ack = want_ack
|
|
170
|
+
mesh_packet.hop_limit = hop_limit
|
|
171
|
+
|
|
172
|
+
mesh_packet.id = generate_packet_id if mesh_packet.id.zero?
|
|
173
|
+
|
|
174
|
+
if psk
|
|
175
|
+
nonce_packet_id = [mesh_packet.id].pack('V').ljust(8, "\x00")
|
|
176
|
+
nonce_from_node = [from_id].pack('V').ljust(8, "\x00")
|
|
177
|
+
nonce = "#{nonce_packet_id}#{nonce_from_node}".b
|
|
178
|
+
|
|
179
|
+
dec_psk = Base64.strict_decode64(psk)
|
|
180
|
+
cipher = OpenSSL::Cipher.new('AES-128-CTR')
|
|
181
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR') if dec_psk.length == 32
|
|
182
|
+
cipher.encrypt
|
|
183
|
+
cipher.key = dec_psk
|
|
184
|
+
cipher.iv = nonce
|
|
185
|
+
|
|
186
|
+
decrypted_payload = mesh_packet.decoded.to_s
|
|
187
|
+
encrypted_payload = cipher.update(decrypted_payload) + cipher.final
|
|
188
|
+
|
|
189
|
+
mesh_packet.encrypted = encrypted_payload
|
|
190
|
+
end
|
|
191
|
+
# puts mesh_packet.to_h
|
|
192
|
+
|
|
193
|
+
# to_radio = Meshtastic::ToRadio.new
|
|
194
|
+
# to_radio.packet = mesh_packet
|
|
195
|
+
# send_to_radio(to_radio: to_radio)
|
|
196
|
+
|
|
197
|
+
mesh_packet
|
|
198
|
+
rescue StandardError => e
|
|
199
|
+
raise e
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Supported Method Parameters::
|
|
203
|
+
# packet_id = Meshtastic.generate_packet_id(
|
|
204
|
+
# last_packet_id: 'optional - Last Packet ID (Default: 0)'
|
|
205
|
+
# )
|
|
206
|
+
public_class_method def self.generate_packet_id(opts = {})
|
|
207
|
+
last_packet_id = opts[:last_packet_id] ||= 0
|
|
208
|
+
|
|
209
|
+
packet_id = last_packet_id + 1 if last_packet_id.positive?
|
|
210
|
+
packet_id = rand(2**32) if last_packet_id.zero?
|
|
211
|
+
|
|
212
|
+
packet_id
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Supported Method Parameters::
|
|
216
|
+
# Meshtastic.send_to_radio(
|
|
217
|
+
# to_radio: 'required - ToRadio Message to Send'
|
|
218
|
+
# )
|
|
219
|
+
public_class_method def self.send_to_radio(opts = {})
|
|
220
|
+
to_radio = opts[:to_radio]
|
|
221
|
+
|
|
222
|
+
raise 'ERROR: Invalid ToRadio Message' unless to_radio.is_a?(Meshtastic::ToRadio)
|
|
223
|
+
|
|
224
|
+
to_radio.to_proto
|
|
225
|
+
rescue StandardError => e
|
|
226
|
+
raise e
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Author(s):: 0day Inc. <support@0dayinc.com>
|
|
230
|
+
|
|
231
|
+
public_class_method def self.authors
|
|
232
|
+
"AUTHOR(S):
|
|
233
|
+
0day Inc. <support@0dayinc.com>
|
|
234
|
+
"
|
|
235
|
+
end
|
|
236
|
+
|
|
50
237
|
# Display a List of Every Meshtastic Module
|
|
51
238
|
|
|
52
239
|
public_class_method def self.help
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: meshtastic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.43
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-05-
|
|
11
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|