"""Token file reader. Format: ``discord_id:password`` on a single line. Whitespace tolerated. The Discord ID is the URL path segment under cloud.tm.center// that restic-rest-server's --private-repos enforces against the basic-auth username. The password is the bcrypt'd entry's plaintext — it covers HTTP basic auth only (restic repos use --insecure-no-password). The Timemachine Network control plane provisions the credential at /register time. """ from __future__ import annotations from pathlib import Path class CredentialsError(Exception): """Raised when the token file is missing or malformed.""" def read_credentials(token_file: Path) -> tuple[str, str]: if not token_file.exists(): raise CredentialsError( f"instance-sync token not found at {token_file}. " f"After /register in Discord you should have received credentials; " f"paste them into this file as 'discord_id:password' on one line." ) raw = token_file.read_text(encoding="utf-8").strip() if ":" not in raw: raise CredentialsError( f"instance-sync token at {token_file} malformed " f"(expected 'discord_id:password' on one line)" ) discord_id, password = raw.split(":", 1) discord_id = discord_id.strip() password = password.strip() if not discord_id or not password: raise CredentialsError( f"instance-sync token at {token_file} malformed " f"(empty discord_id or password)" ) return discord_id, password