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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/messages-tool +7 -0
  3. data/lib/messages_tool.rb +419 -0
  4. 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,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+ require 'messages_tool'
6
+
7
+ MessagesTool.run(ARGV)
@@ -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: []