git_game_show 0.1.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.
@@ -0,0 +1,537 @@
1
+ require 'uri'
2
+
3
+ module GitGameShow
4
+ class CLI < Thor
5
+ map %w[--version -v] => :version
6
+
7
+ desc 'version', 'Display Git Game Show version'
8
+ def version
9
+ puts "Git Game Show version #{GitGameShow::VERSION}"
10
+ end
11
+
12
+ desc '', 'Show welcome screen'
13
+ def welcome
14
+ display_welcome_screen
15
+
16
+ prompt = TTY::Prompt.new
17
+ choice = prompt.select("What would you like to do?", [
18
+ {name: "Host a new game", value: :host},
19
+ {name: "Join a game", value: :join},
20
+ {name: "Exit", value: :exit}
21
+ ])
22
+
23
+ case choice
24
+ when :host
25
+ prompt_for_host_options
26
+ when :join
27
+ prompt_for_join_options
28
+ when :exit
29
+ puts "Thanks for playing Git Game Show!"
30
+ exit(0)
31
+ end
32
+ end
33
+
34
+ desc 'host [OPTIONS]', 'Host a new game session'
35
+ method_option :port, type: :numeric, default: GitGameShow::DEFAULT_CONFIG[:port],
36
+ desc: 'Port to run the server on'
37
+ method_option :password, type: :string,
38
+ desc: 'Optional password for players to join (auto-generated if not provided)'
39
+ method_option :rounds, type: :numeric, default: GitGameShow::DEFAULT_CONFIG[:rounds],
40
+ desc: 'Number of rounds to play'
41
+ method_option :repo_path, type: :string, default: '.',
42
+ desc: 'Path to git repository'
43
+ def host
44
+ begin
45
+ # Validate git repository
46
+ repo = Git.open(options[:repo_path])
47
+
48
+ # Generate a random password if not provided
49
+ password = options[:password] || generate_random_password
50
+
51
+ # Start the game server
52
+ server = GameServer.new(
53
+ port: options[:port],
54
+ password: password,
55
+ rounds: options[:rounds],
56
+ repo: repo
57
+ )
58
+
59
+ # Get IP addresses before clearing screen
60
+ # Get the local IP address for players to connect to
61
+ local_ip = `hostname -I 2>/dev/null || ipconfig getifaddr en0 2>/dev/null`.strip
62
+
63
+ # Get external IP address using a public service
64
+ puts "Detecting your external IP address... (this will only take a second)"
65
+ begin
66
+ # Try multiple services in case one is down
67
+ external_ip = `curl -s --connect-timeout 3 https://api.ipify.org || curl -s --connect-timeout 3 https://ifconfig.me || curl -s --connect-timeout 3 https://icanhazip.com`.strip
68
+ external_ip = nil if external_ip.empty? || external_ip.length > 45 # Sanity check
69
+ rescue
70
+ external_ip = nil
71
+ end
72
+
73
+ # Clear the screen
74
+ clear_screen
75
+
76
+ # Ask user which IP to use
77
+ prompt = TTY::Prompt.new
78
+ ip_choices = []
79
+ ip_choices << {name: "Local network only (#{local_ip})", value: {:type => :local, :ip => local_ip}} if !local_ip.empty?
80
+ ip_choices << {name: "Internet - External IP (#{external_ip}) - requires port forwarding", value: {:type => :external, :ip => external_ip}} if external_ip
81
+ ip_choices << {name: "Internet - Automatic tunneling with ngrok (requires free account)", value: {:type => :tunnel}}
82
+ ip_choices << {name: "Custom IP or hostname", value: {:type => :custom}}
83
+
84
+ # Format question with explanation
85
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
86
+ puts "┃ NETWORK SETUP ┃"
87
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
88
+ puts "┃ • Local IP: Only for players on the same network ┃"
89
+ puts "┃ • External IP: For internet players (requires router port forwarding) ┃"
90
+ puts "┃ • Automatic tunneling: Uses ngrok (requires free account & authorization) ┃"
91
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
92
+ puts ""
93
+
94
+ ip_choice = prompt.select("How should players connect to your game?", ip_choices)
95
+
96
+ # Handle different connection options
97
+ case ip_choice[:type]
98
+ when :local, :external
99
+ ip = ip_choice[:ip]
100
+ when :custom
101
+ ip = prompt.ask("Enter your IP address or hostname:", required: true)
102
+ when :tunnel
103
+ # Clear the screen and show informative message about ngrok
104
+ clear_screen
105
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
106
+ puts "┃ NGROK TUNNEL SETUP ┃"
107
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
108
+ puts "┃ The automatic tunneling option uses ngrok, a secure tunneling service. ┃"
109
+ puts "┃ This will allow players to connect from anywhere without port forwarding. ┃"
110
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
111
+ puts ""
112
+
113
+ # Check if ngrok is available
114
+ begin
115
+ # First, try to find if ngrok command is available
116
+ ngrok_available = system("which ngrok > /dev/null 2>&1") || system("where ngrok > /dev/null 2>&1")
117
+
118
+ unless ngrok_available
119
+ # Offer to install ngrok automatically
120
+ prompt = TTY::Prompt.new
121
+ install_ngrok = prompt.yes?("Ngrok is required for tunneling but wasn't found. Would you like to install it now?")
122
+
123
+ if install_ngrok
124
+ puts "Installing ngrok..."
125
+
126
+ # Determine platform and architecture
127
+ os = RbConfig::CONFIG['host_os']
128
+ arch = RbConfig::CONFIG['host_cpu']
129
+
130
+ # Default to 64-bit
131
+ arch_suffix = arch =~ /64|amd64/ ? '64' : '32'
132
+
133
+ # Determine the download URL based on OS
134
+ download_url = if os =~ /darwin/i
135
+ "https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-darwin-#{arch =~ /arm|aarch64/ ? 'arm64' : 'amd64'}.zip"
136
+ elsif os =~ /linux/i
137
+ "https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-#{arch =~ /arm|aarch64/ ? 'arm64' : 'amd64'}.zip"
138
+ elsif os =~ /mswin|mingw|cygwin/i
139
+ "https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-windows-#{arch_suffix}.zip"
140
+ else
141
+ nil
142
+ end
143
+
144
+ if download_url.nil?
145
+ puts "Could not determine your system type. Please install ngrok manually from https://ngrok.com/download"
146
+ exit(1)
147
+ end
148
+
149
+ # Determine installation directory in user's home folder
150
+ user_home = ENV['HOME'] || ENV['USERPROFILE']
151
+ install_dir = File.join(user_home, '.git_game_show')
152
+ FileUtils.mkdir_p(install_dir) unless Dir.exist?(install_dir)
153
+
154
+ # Download ngrok
155
+ puts "Downloading ngrok from #{download_url}..."
156
+ require 'open-uri'
157
+ require 'tempfile'
158
+
159
+ temp_zip = Tempfile.new(['ngrok', '.zip'])
160
+ temp_zip.binmode
161
+
162
+ begin
163
+ URI.open(download_url) do |remote_file|
164
+ temp_zip.write(remote_file.read)
165
+ end
166
+ temp_zip.close
167
+
168
+ # Extract zip
169
+ require 'zip'
170
+ puts "Extracting ngrok..."
171
+
172
+ Zip::File.open(temp_zip.path) do |zip_file|
173
+ zip_file.each do |entry|
174
+ entry_path = File.join(install_dir, entry.name)
175
+ entry.extract(entry_path) { true } # Overwrite if exists
176
+ FileUtils.chmod(0755, entry_path) if entry.name == 'ngrok' || entry.name == 'ngrok.exe'
177
+ end
178
+ end
179
+
180
+ # Add to PATH for the current process
181
+ ENV['PATH'] = "#{install_dir}:#{ENV['PATH']}"
182
+
183
+ # Check if installation was successful
184
+ ngrok_path = File.join(install_dir, os =~ /mswin|mingw|cygwin/i ? 'ngrok.exe' : 'ngrok')
185
+ if File.exist?(ngrok_path)
186
+ puts "Ngrok installed successfully to #{install_dir}"
187
+ ngrok_available = true
188
+
189
+ # Add a hint about adding to PATH permanently
190
+ puts "\nTIP: To use ngrok in other terminal sessions, add this to your shell profile:"
191
+ puts "export PATH=\"#{install_dir}:$PATH\""
192
+ puts "\nPress Enter to continue..."
193
+ gets
194
+ else
195
+ puts "Failed to install ngrok. Please install manually from https://ngrok.com/download"
196
+ exit(1)
197
+ end
198
+ rescue => e
199
+ puts "Error installing ngrok: #{e.message}"
200
+ puts "Please install manually from https://ngrok.com/download"
201
+ exit(1)
202
+ ensure
203
+ temp_zip.unlink
204
+ end
205
+ else
206
+ # User opted not to install
207
+ puts "Ngrok installation declined. Please choose a different connection option."
208
+ exit(1)
209
+ end
210
+ end
211
+
212
+ puts "Starting tunnel service... (this may take a few moments)"
213
+
214
+ # Start ngrok in non-blocking mode for the specified port
215
+ require 'open3'
216
+ require 'json'
217
+
218
+ # Kill any existing ngrok processes
219
+ system("pkill -f ngrok > /dev/null 2>&1 || taskkill /F /IM ngrok.exe > /dev/null 2>&1")
220
+
221
+ # Check for ngrok api availability first (might be a previous instance running)
222
+ puts "Checking for existing ngrok sessions..."
223
+ api_available = system("curl -s http://localhost:4040/api/tunnels > /dev/null 2>&1")
224
+
225
+ if api_available
226
+ puts "Found existing ngrok session. Attempting to use it or restart if needed..."
227
+ # Try to kill it to start fresh
228
+ system("pkill -f ngrok > /dev/null 2>&1 || taskkill /F /IM ngrok.exe > /dev/null 2>&1")
229
+ # Give it a moment to shut down
230
+ sleep(1)
231
+ end
232
+
233
+ # Check for ngrok auth status
234
+ puts "Checking ngrok authentication status..."
235
+
236
+ # Check if the user has authenticated with ngrok
237
+ auth_check = `ngrok config check 2>&1`
238
+ auth_needed = auth_check.include?("auth") || auth_check.include?("authtoken") || auth_check.include?("ERR") || auth_check.include?("error")
239
+
240
+ if auth_needed
241
+ clear_screen
242
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
243
+ puts "┃ NGROK AUTHORIZATION REQUIRED ┃"
244
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
245
+ puts "┃ Starting with ngrok v3, you need to create a free account and authorize ┃"
246
+ puts "┃ to use TCP tunnels. This is a one-time setup. ┃"
247
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
248
+ puts ""
249
+ puts "Steps to authorize ngrok:"
250
+ puts " 1. Create a free account at https://ngrok.com/signup"
251
+ puts " 2. Get your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken"
252
+ puts " 3. Enter your authtoken below"
253
+ puts ""
254
+
255
+ prompt = TTY::Prompt.new
256
+ token = prompt.mask("Enter your ngrok authtoken:")
257
+
258
+ if token && !token.empty?
259
+ puts "Setting up ngrok authentication..."
260
+ auth_result = system("ngrok config add-authtoken #{token}")
261
+
262
+ if !auth_result
263
+ puts "Failed to set ngrok authtoken. Please try again manually with:"
264
+ puts " ngrok config add-authtoken YOUR_TOKEN"
265
+ puts ""
266
+ puts "Press Enter to continue with local IP instead..."
267
+ gets
268
+ ip = local_ip
269
+ return
270
+ else
271
+ puts "Successfully authenticated with ngrok!"
272
+ end
273
+ else
274
+ puts "No token provided. Falling back to local IP."
275
+ puts "Press Enter to continue..."
276
+ gets
277
+ ip = local_ip
278
+ return
279
+ end
280
+ end
281
+
282
+ # Start ngrok with enhanced options
283
+ puts "Starting ngrok tunnel for port #{options[:port]}..."
284
+ stdin, stdout, stderr, wait_thr = Open3.popen3("ngrok tcp #{options[:port]} --log=stdout")
285
+
286
+ # Capture the stderr from ngrok to check for common errors
287
+ err_thread = Thread.new do
288
+ while (error_line = stderr.gets)
289
+ if error_line.include?("ERR") || error_line.include?("error")
290
+ if error_line.include?("auth") || error_line.include?("authtoken")
291
+ puts "\nAuthentication error detected: #{error_line.strip}"
292
+ elsif error_line.include?("connection")
293
+ puts "\nConnection error detected: #{error_line.strip}"
294
+ elsif error_line.include?("bind") || error_line.include?("address already in use")
295
+ puts "\nPort binding error detected: #{error_line.strip}"
296
+ else
297
+ puts "\nngrok error: #{error_line.strip}"
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ # Wait for ngrok to start and get the URL
304
+ puts "Waiting for tunnel to be established (this may take up to 30 seconds)..."
305
+ tunnel_url = nil
306
+ 30.times do |attempt|
307
+ # Visual feedback for long wait times
308
+ print "." if attempt > 0 && attempt % 5 == 0
309
+
310
+ # Check if we can query the ngrok API
311
+ status = Open3.capture2("curl -s http://localhost:4040/api/tunnels")
312
+ if status[1].success? # If API is available
313
+ tunnels = JSON.parse(status[0])['tunnels']
314
+ if tunnels && !tunnels.empty?
315
+ tunnel = tunnels.first
316
+ # Get the tunnel URL - it should be a tcp URL with format tcp://x.x.x.x:port
317
+ public_url = tunnel['public_url']
318
+ if public_url && public_url.start_with?('tcp://')
319
+ # Extract the host and port
320
+ public_url = public_url.sub('tcp://', '')
321
+ host, port = public_url.split(':')
322
+
323
+ # Use the host with the ngrok-assigned port
324
+ ip = host
325
+ # Create a new port variable instead of modifying the frozen options hash
326
+ ngrok_port = port.to_i
327
+ # Log the port change
328
+ puts "Ngrok assigned port: #{ngrok_port} (original port: #{options[:port]})"
329
+
330
+ tunnel_url = public_url
331
+
332
+ # Save the process ID for later cleanup
333
+ at_exit do
334
+ system("pkill -f ngrok > /dev/null 2>&1 || taskkill /F /IM ngrok.exe > /dev/null 2>&1")
335
+ end
336
+
337
+ clear_screen
338
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
339
+ puts "┃ TUNNEL ESTABLISHED SUCCESSFULLY! ┃"
340
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
341
+ puts "┃ • Your game is now accessible over the internet ┃"
342
+ puts "┃ • The ngrok tunnel is running in the background ┃"
343
+ puts "┃ • DO NOT close the terminal window until your game is finished ┃"
344
+ puts "┃ • The tunnel will automatically close when you exit the game ┃"
345
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
346
+ puts ""
347
+ puts "Public URL: #{public_url}"
348
+ puts ""
349
+ puts "Press Enter to continue..."
350
+ gets
351
+
352
+ break
353
+ end
354
+ end
355
+ end
356
+ sleep 1
357
+ end
358
+
359
+ unless tunnel_url
360
+ clear_screen
361
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
362
+ puts "┃ TUNNEL SETUP FAILED ┃"
363
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
364
+ puts "┃ • ngrok tunnel could not be established ┃"
365
+ puts "┃ • Most common reason: Missing or invalid ngrok authentication token ┃"
366
+ puts "┃ • Falling back to local IP (players will only be able to join locally) ┃"
367
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
368
+ puts ""
369
+ puts "Common solutions:"
370
+ puts " 1. Create a free account at https://ngrok.com/signup"
371
+ puts " 2. Get your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken"
372
+ puts " 3. Run this command: ngrok config add-authtoken <YOUR_TOKEN>"
373
+ puts " 4. Then restart the game and try tunneling again"
374
+ puts ""
375
+ puts "Press Enter to continue with local IP..."
376
+ gets
377
+
378
+ ip = local_ip
379
+ end
380
+ rescue => e
381
+ clear_screen
382
+ puts "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
383
+ puts "┃ ERROR SETTING UP NGROK TUNNEL ┃"
384
+ puts "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
385
+ puts "┃ • An error occurred while trying to set up the ngrok tunnel ┃"
386
+ puts "┃ • This is likely an authentication issue with ngrok ┃"
387
+ puts "┃ • Falling back to local IP (players will only be able to join locally) ┃"
388
+ puts "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
389
+ puts ""
390
+ puts "Error details: #{e.message}"
391
+ puts ""
392
+ puts "Press Enter to continue with local IP..."
393
+ gets
394
+
395
+ ip = local_ip
396
+ end
397
+ end
398
+
399
+ # Generate a secure join link with embedded password
400
+ # If we have a ngrok tunnel, use the ngrok port, otherwise use the original port
401
+ port_to_use = defined?(ngrok_port) ? ngrok_port : options[:port]
402
+ secure_link = "gitgame://#{ip}:#{port_to_use}/#{URI.encode_www_form_component(password)}"
403
+
404
+ # Start the server with the improved UI and pass the join link
405
+ server.start_with_ui(secure_link)
406
+
407
+ rescue Git::GitExecuteError
408
+ puts "Error: Not a valid Git repository at #{options[:repo_path]}".colorize(:red)
409
+ rescue => e
410
+ puts "Error: #{e.message}".colorize(:red)
411
+ end
412
+ end
413
+
414
+ desc 'join SECURE_LINK', 'Join an existing game session using a secure link'
415
+ method_option :name, type: :string, desc: 'Your player name'
416
+ def join(secure_link)
417
+ begin
418
+ # Check if we need to prompt for a name
419
+ name = options[:name]
420
+
421
+ # Parse the secure link
422
+ if secure_link.start_with?('gitgame://')
423
+ uri = URI.parse(secure_link.sub('gitgame://', 'http://'))
424
+ host = uri.host
425
+ port = uri.port || GitGameShow::DEFAULT_CONFIG[:port]
426
+ password = URI.decode_www_form_component(uri.path.sub('/', ''))
427
+ else
428
+ # Legacy format - assume it's host:port
429
+ host, port = secure_link.split(':')
430
+ port ||= GitGameShow::DEFAULT_CONFIG[:port]
431
+ password = options[:password]
432
+
433
+ # If no password provided in legacy format, ask for it
434
+ unless password
435
+ prompt = TTY::Prompt.new
436
+ password = prompt.mask("Enter the game password:")
437
+ end
438
+ end
439
+
440
+ # If no name provided, ask for it
441
+ unless name
442
+ prompt = TTY::Prompt.new
443
+ name = prompt.ask("Enter your name:") do |q|
444
+ q.required true
445
+ end
446
+ end
447
+
448
+ # Create player client
449
+ client = PlayerClient.new(
450
+ host: host,
451
+ port: port.to_i,
452
+ password: password,
453
+ name: name
454
+ )
455
+
456
+ puts "=== Git Game Show Client ===".colorize(:green)
457
+ puts "Connecting to game at #{host}:#{port}".colorize(:light_blue)
458
+
459
+ # Connect to the game
460
+ client.connect
461
+
462
+ rescue => e
463
+ puts "Error: #{e.message}".colorize(:red)
464
+ end
465
+ end
466
+
467
+ default_task :welcome
468
+
469
+ private
470
+
471
+ def display_welcome_screen
472
+ clear_screen
473
+
474
+ puts " ██████╗ ██╗████████╗".colorize(:red) + " ██████╗ █████╗ ███╗ ███╗███████╗".colorize(:green)
475
+ puts "██╔════╝ ██║╚══██╔══╝".colorize(:red) + " ██╔════╝ ██╔══██╗████╗ ████║██╔════╝".colorize(:green)
476
+ puts "██║ ███╗██║ ██║ ".colorize(:red) + " ██║ ███╗███████║██╔████╔██║█████╗ ".colorize(:green)
477
+ puts "██║ ██║██║ ██║ ".colorize(:red) + " ██║ ██║██╔══██║██║╚██╔╝██║██╔══╝ ".colorize(:green)
478
+ puts "╚██████╔╝██║ ██║ ".colorize(:red) + " ╚██████╔╝██║ ██║██║ ╚═╝ ██║███████╗".colorize(:green)
479
+ puts " ╚═════╝ ╚═╝ ╚═╝ ".colorize(:red) + " ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝".colorize(:green)
480
+
481
+ puts " █████╗ ██╗ ██╗ ██████╗ ██╗ ██╗".colorize(:blue)
482
+ puts "██╔═══╝ ██║ ██║██╔═══██╗██║ ██║".colorize(:blue)
483
+ puts "███████╗███████║██║ ██║██║ █╗ ██║".colorize(:blue)
484
+ puts "╚════██║██╔══██║██║ ██║██║███╗██║".colorize(:blue)
485
+ puts "██████╔╝██║ ██║╚██████╔╝╚███╔███╔╝".colorize(:blue)
486
+ puts "╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ".colorize(:blue)
487
+
488
+ puts "\nWelcome to Git Game Show version #{GitGameShow::VERSION}!".colorize(:light_blue)
489
+ puts "Test your team's Git knowledge with fun trivia games.\n\n"
490
+ end
491
+
492
+ def prompt_for_host_options
493
+ prompt = TTY::Prompt.new
494
+
495
+ repo_path = prompt.ask("Enter the path to the Git repository (leave empty for current directory):", default: '.')
496
+ rounds = prompt.ask("How many rounds would you like to play? (1-10)",
497
+ convert: :int,
498
+ default: GitGameShow::DEFAULT_CONFIG[:rounds]) do |q|
499
+ q.validate(/^([1-9]|10)$/, "Please enter a number between 1 and 10")
500
+ end
501
+ port = prompt.ask("Which port would you like to use?",
502
+ convert: :int,
503
+ default: GitGameShow::DEFAULT_CONFIG[:port])
504
+
505
+ # Call the host method with the provided options (password will be auto-generated)
506
+ invoke :host, [], {
507
+ repo_path: repo_path,
508
+ rounds: rounds,
509
+ port: port
510
+ }
511
+ end
512
+
513
+ def prompt_for_join_options
514
+ prompt = TTY::Prompt.new
515
+
516
+ secure_link = prompt.ask("Paste the join link provided by the host:")
517
+ name = prompt.ask("Enter your name:")
518
+
519
+ # Call the join method with the provided options
520
+ invoke :join, [secure_link], {
521
+ name: name
522
+ }
523
+ end
524
+
525
+ def clear_screen
526
+ system('clear') || system('cls')
527
+ end
528
+
529
+ def generate_random_password
530
+ # Generate a simple but memorable password: adjective-noun-number
531
+ adjectives = %w[happy quick brave clever funny orange purple blue green golden shiny lucky awesome mighty rapid swift]
532
+ nouns = %w[dog cat fox tiger panda bear whale shark lion wolf dragon eagle falcon rocket ship star planet moon river mountain]
533
+
534
+ "#{adjectives.sample}-#{nouns.sample}-#{rand(100..999)}"
535
+ end
536
+ end
537
+ end