feat: disable keyring in Steam environments and allow prelaunch to continue if OTP is missing

This commit is contained in:
CPTN Cosmo 2026-04-18 15:54:36 +02:00
parent b1a25e20ee
commit 5ea9f38a12

View file

@ -237,18 +237,31 @@ def detect_launch_command():
return commands
def is_steam_env():
"""Detects if we are running under Steam, Steam Deck Gaming Mode, or as a Steam Compat Tool."""
if os.environ.get('XDG_CURRENT_DESKTOP', '').lower() == 'gamescope':
return True
if 'STEAM_COMPAT_DATA_PATH' in os.environ:
return True
if 'STEAM_RUNTIME' in os.environ:
return True
return False
def get_secret(config, config_path, allow_prompt=True):
"""
Retrieves the OTP secret in the following priority:
1. System Keyring (SERVICE_NAME, USERNAME) - if available
2. config.json - if Keyring unavailable or secret found there (and Keyring unavailable for migration)
3. User Input - prompts user and saves to Keyring (if available) or config.json.
1. System Keyring (SERVICE_NAME, USERNAME) - if available and not in Steam environment
2. config.json - if Keyring unavailable/disabled or secret found there
3. User Input - prompts user and saves to Keyring/config (if allowed and not in Steam environment)
"""
SERVICE_NAME = "XIVLauncherWrapper"
USERNAME = "OTP_SECRET"
steam_env = is_steam_env()
use_keyring = HAS_KEYRING and not steam_env
# 1. Try Keyring
if HAS_KEYRING:
if use_keyring:
try:
secret = keyring.get_password(SERVICE_NAME, USERNAME)
if secret:
@ -260,7 +273,7 @@ def get_secret(config, config_path, allow_prompt=True):
if config and "secret" in config:
secret = config["secret"]
if secret and secret != "YOUR_BASE32_SECRET_HERE":
if HAS_KEYRING:
if use_keyring:
print("Migrating secret from config.json to system keyring...")
try:
keyring.set_password(SERVICE_NAME, USERNAME, secret)
@ -279,11 +292,11 @@ def get_secret(config, config_path, allow_prompt=True):
# No keyring, just use config
return secret
if not allow_prompt:
if not allow_prompt or steam_env:
return None
# 3. Prompt User
if HAS_KEYRING:
if use_keyring:
print("\nOTP Secret not found in keyring.")
else:
print("\nOTP Secret not found in config.json and keyring module is missing.")
@ -319,7 +332,7 @@ def get_secret(config, config_path, allow_prompt=True):
secret_input = getpass.getpass("Secret: ").strip()
parsed = parse_secret(secret_input)
if HAS_KEYRING:
if use_keyring:
try:
keyring.set_password(SERVICE_NAME, USERNAME, parsed)
print("Secret saved to system keyring.")
@ -388,15 +401,27 @@ def main():
secret = get_secret(config, config_path, allow_prompt=not (args.prelaunch or args.inject_only))
if not secret:
if args.prelaunch or args.inject_only:
error_msg = "Error: OTP secret not configured. Please run 'xivlauncher-wrapper' manually in Desktop Mode to configure it."
print(error_msg)
# Log the error so user can see it if they check logs
error_log = os.path.join(config_dir, 'error.log')
try:
with open(error_log, 'a') as f:
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Error: OTP secret not configured.\n")
f.write("Please run 'xivlauncher-wrapper' manually from your terminal or application menu to configure your secret before launching via Steam.\n\n")
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {error_msg}\n")
except Exception:
pass
print("Error: No secret available.")
if args.prelaunch:
print("Continuing prelaunch without OTP injection so the game can still start.")
sys.exit(0)
elif args.inject_only:
sys.exit(1)
else:
# If we are acting as a wrapper for %command%, continue without OTP.
if unknown_args:
print("Continuing command launch without OTP injection.")
else:
sys.exit(1)
if args.inject_only:
@ -514,9 +539,12 @@ def main():
sys.exit(1)
# Start the OTP injector in a background thread
if secret:
injector_thread = threading.Thread(target=send_otp, args=(secret,))
injector_thread.daemon = True
injector_thread.start()
else:
print("OTP injection skipped.")
try:
# Wait for the launcher to exit