messages-tool 1.6.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 +7 -0
- data/bin/messages-tool +7 -0
- data/lib/messages_tool.rb +419 -0
- metadata +47 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d48c12680504abaccf7bac25df1d39eb38c4721ae9593d18e1697ffcfca1942e
|
|
4
|
+
data.tar.gz: f30c932ac4d44046e977c692829b833d1f9155f3590088713887094afc241c87
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 02e0d6881de21564d013a560b570c1e1bb8a6114ded3a6b01b79fa67e0efb7ea8bdd08232ebe14a24f8ec272142d913e4e29d9648a2fe284b48c42a4ed633129
|
|
7
|
+
data.tar.gz: 737a13d80054fb914615d4c3d3f47d4263dad99abe35580751f56e57b87795eee492da30d68e7079a8777888c845eec04f5eb19305070487261295c277858efe
|
data/bin/messages-tool
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'io/console'
|
|
8
|
+
|
|
9
|
+
module MessagesTool
|
|
10
|
+
VERSION = '1.6.0'
|
|
11
|
+
ENDPOINT = 'https://special-system-987z.vercel.app/api/messages'
|
|
12
|
+
AUTH_FILE = File.expand_path('../.auth_session.json', __dir__)
|
|
13
|
+
LOCK_FILE = File.expand_path('../pin.lock', __dir__)
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def load_auth
|
|
17
|
+
return {} unless File.exist?(AUTH_FILE)
|
|
18
|
+
|
|
19
|
+
JSON.parse(File.read(AUTH_FILE))
|
|
20
|
+
rescue StandardError
|
|
21
|
+
{}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def save_auth(data)
|
|
25
|
+
File.write(AUTH_FILE, JSON.pretty_generate(data))
|
|
26
|
+
sync_lock_file(data)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def sync_lock_file(data)
|
|
30
|
+
if data['logged_in']
|
|
31
|
+
lock_data = {
|
|
32
|
+
'status' => 'locked',
|
|
33
|
+
'registered' => data['registered'],
|
|
34
|
+
'use_application_default' => !!data['use_application_default'],
|
|
35
|
+
'timestamp' => Time.now.to_i
|
|
36
|
+
}
|
|
37
|
+
File.write(LOCK_FILE, JSON.pretty_generate(lock_data))
|
|
38
|
+
else
|
|
39
|
+
FileUtils.rm_f(LOCK_FILE)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def prompt_pin(label = 'Enter PIN: ')
|
|
44
|
+
auth = load_auth
|
|
45
|
+
if auth['use_application_default'] && auth['pin']
|
|
46
|
+
puts "#{label}[Application Default Active]"
|
|
47
|
+
return auth['pin']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
print label
|
|
51
|
+
$stdin.noecho(&:gets)&.chomp.tap { puts }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def require_auth
|
|
55
|
+
auth = load_auth
|
|
56
|
+
unless auth['registered']
|
|
57
|
+
puts '[-] No PIN registered. Run `messages-tool auth register` or `account register` first.'
|
|
58
|
+
exit 1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
unless auth['logged_in'] && File.exist?(LOCK_FILE)
|
|
62
|
+
puts '[-] Not logged in. Please log in first.'
|
|
63
|
+
exit 1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
input_pin = prompt_pin('Enter PIN to continue: ')
|
|
67
|
+
unless input_pin == auth['pin']
|
|
68
|
+
puts '[-] Invalid PIN.'
|
|
69
|
+
exit 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
auth
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def http_request(method, url_str, payload = nil)
|
|
76
|
+
uri = URI.parse(url_str)
|
|
77
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
78
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
79
|
+
|
|
80
|
+
request = case method
|
|
81
|
+
when :get then Net::HTTP::Get.new(uri)
|
|
82
|
+
when :post then Net::HTTP::Post.new(uri)
|
|
83
|
+
when :delete then Net::HTTP::Delete.new(uri)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
request['Content-Type'] = 'application/json'
|
|
87
|
+
request.body = payload.to_json if payload
|
|
88
|
+
|
|
89
|
+
response = http.request(request)
|
|
90
|
+
JSON.parse(response.body)
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
{ 'error' => e.message }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def show_auth_help
|
|
96
|
+
puts <<~HELP
|
|
97
|
+
=========================================="
|
|
98
|
+
AUTH / ACCOUNT HELP "
|
|
99
|
+
=========================================="
|
|
100
|
+
Commands Configuration:
|
|
101
|
+
auth / account register Set initial PIN (prompts: Enter new PIN, Confirm new PIN)
|
|
102
|
+
auth / account login Set active session & write pin.lock (prompts: Enter PIN to login)
|
|
103
|
+
auth / account logout End session & remove pin.lock (prompts: Enter PIN to logout)
|
|
104
|
+
auth / account status Display session state (prompts: Enter PIN for status)
|
|
105
|
+
auth / account reset Change existing PIN (prompts: Enter PIN to continue, Enter new PIN to reset, Confirm new PIN)
|
|
106
|
+
auth / account remove Clear authentication file & pin.lock (prompts: Enter PIN to remove)
|
|
107
|
+
auth / account list Display session settings JSON (prompts: Enter PIN to list auth data)
|
|
108
|
+
auth / account help Display this settings configuration menu
|
|
109
|
+
|
|
110
|
+
Application-Default Commands:
|
|
111
|
+
auth / account application-default help Display application-default configuration menu
|
|
112
|
+
auth / account application-default register Register default application credentials
|
|
113
|
+
auth / account application-default login Login using default application credentials
|
|
114
|
+
auth / account application-default logout Logout default session
|
|
115
|
+
auth / account application-default status Check default application status
|
|
116
|
+
auth / account application-default reset Reset default application PIN
|
|
117
|
+
auth / account application-default remove Remove application default configuration
|
|
118
|
+
auth / account application-default list List default auth settings JSON
|
|
119
|
+
|
|
120
|
+
PIN & Lock Settings:
|
|
121
|
+
- Masked input enabled via console hidden input.
|
|
122
|
+
- Session state synchronized across `.auth_session.json` and `pin.lock`.
|
|
123
|
+
=========================================="
|
|
124
|
+
HELP
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def show_app_default_help
|
|
128
|
+
puts <<~HELP
|
|
129
|
+
=========================================="
|
|
130
|
+
APPLICATION DEFAULT AUTHENTICATION "
|
|
131
|
+
=========================================="
|
|
132
|
+
Commands Configuration:
|
|
133
|
+
application-default register Register application default PIN
|
|
134
|
+
application-default login Authenticate session using default PIN
|
|
135
|
+
application-default logout Log out of default session
|
|
136
|
+
application-default status Show default status & lock settings
|
|
137
|
+
application-default reset Reset application default PIN
|
|
138
|
+
application-default remove Remove stored default credentials
|
|
139
|
+
application-default list Print JSON of default credentials configuration
|
|
140
|
+
application-default help Show this application-default help menu
|
|
141
|
+
=========================================="
|
|
142
|
+
HELP
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def show_main_help
|
|
146
|
+
puts <<~USAGE
|
|
147
|
+
Usage: messages-tool <category> <command> [options]
|
|
148
|
+
|
|
149
|
+
Global Options:
|
|
150
|
+
--help, -h Show main CLI options/help
|
|
151
|
+
--version, -v Show script version
|
|
152
|
+
--json Output raw API results formatted as JSON
|
|
153
|
+
|
|
154
|
+
Auth / Account Commands:
|
|
155
|
+
auth / account help Display auth settings and manual
|
|
156
|
+
auth / account register Register PIN (prompts: Enter new PIN, Confirm new PIN)
|
|
157
|
+
auth / account login Login (prompts: Enter PIN to login)
|
|
158
|
+
auth / account logout Logout (prompts: Enter PIN to logout)
|
|
159
|
+
auth / account status Check status (prompts: Enter PIN for status)
|
|
160
|
+
auth / account reset Reset PIN (prompts: Enter PIN to continue, Enter new PIN to reset, Confirm new PIN)
|
|
161
|
+
auth / account remove Remove auth data and lock file (prompts: Enter PIN to remove)
|
|
162
|
+
auth / account list List auth session JSON (prompts: Enter PIN to list auth data)
|
|
163
|
+
auth / account application-default <cmd> Manage application-default credentials
|
|
164
|
+
|
|
165
|
+
Messages Commands:
|
|
166
|
+
messages send --number <phone> --text <text>
|
|
167
|
+
message send --number <phone> --text <text>
|
|
168
|
+
send --number <phone> --text <text>
|
|
169
|
+
|
|
170
|
+
messages list / message list / list
|
|
171
|
+
messages delete [--id <id>] / message delete [--id <id>] / delete [--id <id>]
|
|
172
|
+
USAGE
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def handle_auth(subcommand, args)
|
|
176
|
+
auth = load_auth
|
|
177
|
+
|
|
178
|
+
if subcommand == 'application-default'
|
|
179
|
+
app_sub = args.shift || 'help'
|
|
180
|
+
handle_app_default(app_sub, args)
|
|
181
|
+
return
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
case subcommand
|
|
185
|
+
when 'help'
|
|
186
|
+
show_auth_help
|
|
187
|
+
when 'register'
|
|
188
|
+
if auth['registered']
|
|
189
|
+
puts '[-] Account already registered. Use `auth reset` or `account reset` to change PIN.'
|
|
190
|
+
return
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
pin = prompt_pin('Enter new PIN: ')
|
|
194
|
+
confirm = prompt_pin('Confirm new PIN: ')
|
|
195
|
+
|
|
196
|
+
if pin.empty? || pin != confirm
|
|
197
|
+
puts '[-] PINs do not match or cannot be empty.'
|
|
198
|
+
return
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
save_auth({ 'registered' => true, 'logged_in' => true, 'pin' => pin, 'use_application_default' => false })
|
|
202
|
+
puts '[+] Registered, logged in, and updated pin.lock successfully.'
|
|
203
|
+
when 'login'
|
|
204
|
+
unless auth['registered']
|
|
205
|
+
puts '[-] No account registered.'
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
pin = prompt_pin('Enter PIN to login: ')
|
|
210
|
+
if pin == auth['pin']
|
|
211
|
+
auth['logged_in'] = true
|
|
212
|
+
save_auth(auth)
|
|
213
|
+
puts '[+] Logged in and pin.lock acquired successfully.'
|
|
214
|
+
else
|
|
215
|
+
puts '[-] Invalid PIN.'
|
|
216
|
+
end
|
|
217
|
+
when 'logout'
|
|
218
|
+
prompt_pin('Enter PIN to logout: ')
|
|
219
|
+
auth['logged_in'] = false
|
|
220
|
+
save_auth(auth)
|
|
221
|
+
puts '[+] Logged out and released pin.lock successfully.'
|
|
222
|
+
when 'status'
|
|
223
|
+
prompt_pin('Enter PIN for status: ')
|
|
224
|
+
status = auth['logged_in'] && File.exist?(LOCK_FILE) ? 'Logged In (Active Lock)' : 'Logged Out'
|
|
225
|
+
app_def = auth['use_application_default'] ? 'Enabled' : 'Disabled'
|
|
226
|
+
puts '=========================================='
|
|
227
|
+
puts ' AUTH STATUS '
|
|
228
|
+
puts '=========================================='
|
|
229
|
+
puts " Registered : #{auth['registered'] ? 'Yes' : 'No'}"
|
|
230
|
+
puts " Session : #{status}"
|
|
231
|
+
puts " Lock File (pin.lock): #{File.exist?(LOCK_FILE) ? 'Present' : 'Absent'}"
|
|
232
|
+
puts " Application Default : #{app_def}"
|
|
233
|
+
puts '=========================================='
|
|
234
|
+
when 'reset'
|
|
235
|
+
require_auth
|
|
236
|
+
pin = prompt_pin('Enter new PIN to reset: ')
|
|
237
|
+
confirm = prompt_pin('Confirm new PIN: ')
|
|
238
|
+
|
|
239
|
+
if pin.empty? || pin != confirm
|
|
240
|
+
puts '[-] PINs do not match or cannot be empty.'
|
|
241
|
+
return
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
auth['pin'] = pin
|
|
245
|
+
save_auth(auth)
|
|
246
|
+
puts '[+] PIN reset and session state updated.'
|
|
247
|
+
when 'remove'
|
|
248
|
+
prompt_pin('Enter PIN to remove: ')
|
|
249
|
+
FileUtils.rm_f(AUTH_FILE)
|
|
250
|
+
FileUtils.rm_f(LOCK_FILE)
|
|
251
|
+
puts '[+] Auth session, PIN, and pin.lock removed successfully.'
|
|
252
|
+
when 'list'
|
|
253
|
+
prompt_pin('Enter PIN to list auth data: ')
|
|
254
|
+
puts JSON.pretty_generate(auth)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def handle_app_default(subcommand, _args)
|
|
259
|
+
auth = load_auth
|
|
260
|
+
|
|
261
|
+
case subcommand
|
|
262
|
+
when 'help'
|
|
263
|
+
show_app_default_help
|
|
264
|
+
when 'register'
|
|
265
|
+
pin = prompt_pin('Enter new PIN: ')
|
|
266
|
+
confirm = prompt_pin('Confirm new PIN: ')
|
|
267
|
+
|
|
268
|
+
if pin.empty? || pin != confirm
|
|
269
|
+
puts '[-] PINs do not match or cannot be empty.'
|
|
270
|
+
return
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
save_auth({ 'registered' => true, 'logged_in' => true, 'pin' => pin, 'use_application_default' => true })
|
|
274
|
+
puts '[+] Application-default credentials registered, logged in, and pin.lock written.'
|
|
275
|
+
when 'login'
|
|
276
|
+
unless auth['registered']
|
|
277
|
+
puts '[-] No account registered.'
|
|
278
|
+
return
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
auth['use_application_default'] = true
|
|
282
|
+
auth['logged_in'] = true
|
|
283
|
+
save_auth(auth)
|
|
284
|
+
puts '[+] Application-default session logged in (pin.lock updated).'
|
|
285
|
+
when 'logout'
|
|
286
|
+
auth['logged_in'] = false
|
|
287
|
+
save_auth(auth)
|
|
288
|
+
puts '[+] Application-default session logged out (pin.lock removed).'
|
|
289
|
+
when 'status'
|
|
290
|
+
status = auth['logged_in'] && File.exist?(LOCK_FILE) ? 'Logged In (Active Lock)' : 'Logged Out'
|
|
291
|
+
app_def = auth['use_application_default'] ? 'Enabled' : 'Disabled'
|
|
292
|
+
puts '=========================================='
|
|
293
|
+
puts ' APPLICATION DEFAULT AUTH STATUS '
|
|
294
|
+
puts '=========================================='
|
|
295
|
+
puts " Registered : #{auth['registered'] ? 'Yes' : 'No'}"
|
|
296
|
+
puts " Session : #{status}"
|
|
297
|
+
puts " Lock File (pin.lock): #{File.exist?(LOCK_FILE) ? 'Present' : 'Absent'}"
|
|
298
|
+
puts " Application Default : #{app_def}"
|
|
299
|
+
puts '=========================================='
|
|
300
|
+
when 'reset'
|
|
301
|
+
require_auth
|
|
302
|
+
pin = prompt_pin('Enter new PIN to reset: ')
|
|
303
|
+
confirm = prompt_pin('Confirm new PIN: ')
|
|
304
|
+
|
|
305
|
+
if pin.empty? || pin != confirm
|
|
306
|
+
puts '[-] PINs do not match or cannot be empty.'
|
|
307
|
+
return
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
auth['pin'] = pin
|
|
311
|
+
auth['use_application_default'] = true
|
|
312
|
+
save_auth(auth)
|
|
313
|
+
puts '[+] Application-default PIN reset successfully.'
|
|
314
|
+
when 'remove'
|
|
315
|
+
auth['use_application_default'] = false
|
|
316
|
+
save_auth(auth)
|
|
317
|
+
puts '[+] Application-default credentials mode disabled.'
|
|
318
|
+
when 'list'
|
|
319
|
+
puts JSON.pretty_generate(auth)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def handle_messages(subcommand, args, raw_json: false)
|
|
324
|
+
require_auth
|
|
325
|
+
|
|
326
|
+
case subcommand
|
|
327
|
+
when 'send'
|
|
328
|
+
number_idx = args.index('--number')
|
|
329
|
+
text_idx = args.index('--text')
|
|
330
|
+
|
|
331
|
+
number = number_idx ? args[number_idx + 1] : nil
|
|
332
|
+
text = text_idx ? args[text_idx + 1] : nil
|
|
333
|
+
|
|
334
|
+
if number.nil? || text.nil?
|
|
335
|
+
puts '[-] Usage: messages send --number <PHONE> --text <MESSAGE>'
|
|
336
|
+
return
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
res = http_request(:post, ENDPOINT, { phone: number, text: text })
|
|
340
|
+
|
|
341
|
+
if raw_json
|
|
342
|
+
puts JSON.pretty_generate(res)
|
|
343
|
+
elsif res['success']
|
|
344
|
+
msg = res['message']
|
|
345
|
+
puts '=========================================='
|
|
346
|
+
puts ' MESSAGE SENT '
|
|
347
|
+
puts '=========================================='
|
|
348
|
+
puts ' Status : Success (201)'
|
|
349
|
+
puts " ID : #{msg['id']}"
|
|
350
|
+
puts " Phone : #{msg['phone']}"
|
|
351
|
+
puts " Text : #{msg['text']}"
|
|
352
|
+
puts '=========================================='
|
|
353
|
+
else
|
|
354
|
+
puts '=========================================='
|
|
355
|
+
puts ' FAILED TO SEND MESSAGE '
|
|
356
|
+
puts '=========================================='
|
|
357
|
+
puts " Error : #{res['error'] || 'Failed to send'}"
|
|
358
|
+
puts '=========================================='
|
|
359
|
+
end
|
|
360
|
+
when 'list'
|
|
361
|
+
res = http_request(:get, ENDPOINT)
|
|
362
|
+
if raw_json
|
|
363
|
+
puts JSON.pretty_generate(res)
|
|
364
|
+
else
|
|
365
|
+
puts '=========================================='
|
|
366
|
+
puts ' MESSAGE LIST '
|
|
367
|
+
puts '=========================================='
|
|
368
|
+
puts " Total Count : #{res['totalMessages'] || 0}"
|
|
369
|
+
puts " Data : #{res.to_json}"
|
|
370
|
+
puts '=========================================='
|
|
371
|
+
end
|
|
372
|
+
when 'delete'
|
|
373
|
+
id_idx = args.index('--id')
|
|
374
|
+
id = id_idx ? args[id_idx + 1] : nil
|
|
375
|
+
|
|
376
|
+
url = id ? "#{ENDPOINT}?id=#{id}" : ENDPOINT
|
|
377
|
+
res = http_request(:delete, url)
|
|
378
|
+
|
|
379
|
+
if raw_json
|
|
380
|
+
puts JSON.pretty_generate(res)
|
|
381
|
+
else
|
|
382
|
+
puts '=========================================='
|
|
383
|
+
puts ' DELETE MESSAGES '
|
|
384
|
+
puts '=========================================='
|
|
385
|
+
puts " Response : #{res.to_json}"
|
|
386
|
+
puts '=========================================='
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def run(argv)
|
|
392
|
+
raw_json_flag = argv.delete('--json') != nil
|
|
393
|
+
|
|
394
|
+
if argv.empty? || argv.include?('--help') || argv.include?('-h')
|
|
395
|
+
show_main_help
|
|
396
|
+
exit 0
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
if argv.include?('--version') || argv.include?('-v')
|
|
400
|
+
puts "messages-tool v#{VERSION}"
|
|
401
|
+
exit 0
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
group = argv.shift
|
|
405
|
+
subcmd = argv.shift
|
|
406
|
+
|
|
407
|
+
case group
|
|
408
|
+
when 'auth', 'account'
|
|
409
|
+
handle_auth(subcmd, argv)
|
|
410
|
+
when 'messages', 'message'
|
|
411
|
+
handle_messages(subcmd, argv, raw_json: raw_json_flag)
|
|
412
|
+
when 'send', 'list', 'delete'
|
|
413
|
+
handle_messages(group, [subcmd] + argv, raw_json: raw_json_flag)
|
|
414
|
+
else
|
|
415
|
+
show_main_help
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: messages-tool
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.6.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- hb044082-alt
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-08-31 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: A CLI utility providing authentication, session lock management, and
|
|
13
|
+
REST endpoint messaging commands.
|
|
14
|
+
email:
|
|
15
|
+
- dodi66412@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- messages-tool
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/messages-tool
|
|
22
|
+
- lib/messages_tool.rb
|
|
23
|
+
homepage: https://github.com/hb044082-alt/messages-tool
|
|
24
|
+
licenses:
|
|
25
|
+
- MIT
|
|
26
|
+
metadata:
|
|
27
|
+
homepage_uri: https://github.com/hb044082-alt/messages-tool
|
|
28
|
+
source_code_uri: https://github.com/hb044082-alt/messages-tool.git
|
|
29
|
+
bug_tracker_uri: https://github.com/hb044082-alt/messages-tool/issues
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: 2.6.0
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
requirements: []
|
|
44
|
+
rubygems_version: 3.6.2
|
|
45
|
+
specification_version: 4
|
|
46
|
+
summary: CLI tool for authentication and message sending operations.
|
|
47
|
+
test_files: []
|