# Access model
- **You**: run `admin.js` on the VPS. You mint the Caller ID AND every client token.
- **Caller** (e.g. Sam): logs in with the code you gave him, taps "Locked"/"Unlocked" to arm it, taps a button to signal. Can run 1-4 independent buttons at once, each with its own editable label. Hands client tokens to whoever's joining.
- **Client** (desktop exe): on launch, prompts for server URL, token, display name, and which button (1-4) to listen for. Expiry starts counting the moment that token is entered and accepted. To listen to a different button, exit and relaunch — it doesn't switch channels while running.
- Only one live login per caller code at a time — if Sam gives his code to Joe, they can't both be "caller" simultaneously. A second login attempt is rejected while the first is connected, with a clear on-screen message.
- **You can still get in hands-on**: an admin override key lets you join an caller's live session for troubleshooting without kicking them off.
- **Callers can set their own display name** in the app; it's saved server-side, so `node admin.js list` always shows their current name, however they last set it (by you or by them).

## 1. VPS
```
npm install ws
ADMIN_OVERRIDE_KEY=some-private-string node serverclick.js   # PORT env var optional, default 8080
```
Keep `ADMIN_OVERRIDE_KEY` private — it's only for you. If you don't set it, override login is simply disabled.
Put nginx/caddy in front for `wss://` in production.

## 2. Admin commands (`node admin.js ...`)

**Callers**
```
node admin.js create-caller sam                  # normal caller, named "sam"
node admin.js create-caller boss --admin         # admin caller: sees/triggers ALL clients, all callers
node admin.js rename-caller C001 new-name  # rename anytime
node admin.js reset-password C001                # generate a new password, printed once
node admin.js revoke-caller C001
node admin.js delete-caller C001
```
`create-caller` auto-generates a password and prints it once — give both the Caller ID and password to the caller together via Discord PM. It can't be retrieved later, only reset. Login now requires both; a wrong Caller ID and a wrong password both show the same generic error, so an attacker can't tell which part failed. Callers can also change their own password from the app (in "My Client Tokens & Exe File") — they must correctly enter their current password to do so.

**Tokens**
```
node admin.js create-token C001 24h              # single 24h token, tied to that caller
node admin.js create-token - 30m                       # unassigned, assign later
node admin.js create-token C001 never             # never-expiring (e.g. admin/test)
node admin.js create-token C001 never --token=SAM-VIP-PASS   # pick the exact string yourself

node admin.js mint-batch C001 30m 5               # 5 tokens, 30 min each
node admin.js mint-batch C001 24h 6               # 6 tokens, 24h each
node admin.js mint-batch C001 1mo 2               # 2 tokens, 1 month each
# run mint-batch once per duration you need — any mix of quantity/duration this way

node admin.js assign-token TOKEN-XXXX C001
node admin.js revoke-token TOKEN-XXXX
node admin.js delete-token TOKEN-XXXX
```
Duration accepts `30m`, `24h`, `2d`, `1mo`, or `never`.

Every minted token gets a simple name like `A71CF6-30m` (random / duration) — just enough to recognize the expiry at a glance, no caller name baked in (kept simple to avoid any mismatch with an caller's display name).

**Dashboard**
```
node admin.js list
```
Shows caller codes with their current name and admin tag, then tokens grouped into `unassigned` / `unused` / `active` / `expired` / `revoked`, each with expiry.

## 3. Caller (phone)
- Before hosting `caller.html`, edit two constants near the top of the `<script>`:
  - `SERVER_URL` — your real `wss://...` relay address.
  - `EXE_DOWNLOAD_URL` — wherever you host `QuadClicker.exe` (see setup below).
  Callers never see or enter either of these — one less thing to confuse them.
- Host the file somewhere reachable, open it in the browser (Safari on iPhone, Chrome on Android), then use "Add to Home Screen" from the browser menu on either platform.
- **`presets.js` must be uploaded to the same folder as `caller.html`** — it holds 5 named themes (Theme A-E), each with 10 preset button-label options, shown in "Edit Buttons" mode via a theme picker. Edit the theme names or any of the 10 strings under them anytime; no other changes needed, just re-upload it.
- Login is a single field: just the Caller ID (his real credential, not a name), tap Connect.
- An "Instructions" link on the login screen (and a "?" button once logged in) shows the step-by-step explainer for callers.
- **Main screen**: a `1 2 3 4` toggle picks how many independent buttons are active (stacked vertically, not a grid). Each button has an editable label — edit it and it updates everywhere instantly, for you and every connected client. Below the buttons, a single **"Locked"/"Unlocked"** control (tap to toggle) arms all of them — it re-locks automatically right after any tap fires, so a second tap needs a deliberate unlock again. Two nav buttons below that: **"Request tokens →"** and **"My client tokens →"** — everything else lives one tap away to keep this screen simple.
- **"My client tokens"** screen: shows his caller ID in gray, read-only text (not editable — a name/label change is a request to you, via `rename-caller`, not something he can do himself in the app). Below it, "Copy download link" for the client exe, then every token tied to the account with status/expiry and a **copy** button per token (no share button).
- **"Request tokens"** screen: pick quantities for the two tiers (15 minutes / $2, 5 hours / $10), see an estimated total, then **Copy Request, then Paste to Discord PM** to you. Nothing auto-mints — you mint manually after payment.
- If someone else is already logged in with the same code, they'll see: "this Caller ID is already in use on another device."

## 4. You, troubleshooting hands-on
- Since the server URL is no longer a visible field, trigger the override by opening the page with `?admin_key=YOUR_KEY` in the URL, e.g. `https://yourhost/caller.html?admin_key=YOUR_KEY`, then log in with the caller's own Caller ID as normal — you don't need their password, the override bypasses it (leave the password field blank).
- This joins their live session (same code, same view) without disconnecting them — you'll both be connected at once.
- An admin caller (created with `--admin`) is a different thing: a permanent cross-caller account that always sees/triggers every client from every caller, no override key needed.

## 5. Client (desktop)
```
pip install websocket-client pyautogui
python desktop_client.py
```
Prompts for server URL, token, display name, and which button (1-4) to listen for — entering the token is what starts its expiry clock (unless it's a `never`-expiring token). Whenever the caller edits any button's label, the client reprints all 4 as a reminder, with the just-edited one highlighted in red. To listen to a different button, close it and run it again — no live switching mid-session.

Build to .exe:
```
pip install pyinstaller
pyinstaller --onefile --name QuadClicker desktop_client.py
```
(No `--noconsole` — the console window is needed for the token prompt.)

If the token is invalid/expired/revoked/unassigned, the client prints why and stops (no retry loop).

Expiry is enforced continuously, not just at connect time — a client already mid-session gets a triple-beep warning at 10 and 5 minutes before their token expires, and gets force-disconnected the moment it actually does. Staying connected doesn't let someone outlast their token.

### Keeping clients on the current version
`desktop_client.py` has a `CLIENT_VERSION` string and its own `EXE_DOWNLOAD_URL` (same value as the one in `caller.html`) baked in. Every time you make a real change and rebuild:
1. Bump `CLIENT_VERSION` in `desktop_client.py` (e.g. `"1.0.0"` → `"1.1.0"`) before rebuilding.
2. On the VPS: `node admin.js set-exe-version 1.1.0` — takes effect immediately, no server restart needed (read fresh from `db.json` on each connection).
3. Overwrite `QuadClicker.exe` at whatever URL you're hosting it — the original download link keeps working forever, since it's a fixed URL pointing at a fixed filename.

Already-running older clients will see a hard-to-miss red notice with the download link the next time they connect (or reconnect). New downloads always get the current build automatically. Every client also always prints its own download link on startup (Note I), regardless of version, so it's never lost even without an update notice.

### Distributing the exe to callers
Callers now have a "Copy download link" button right on their phone (in "My client tokens") — but it only works once you've set `EXE_DOWNLOAD_URL` in `caller.html` to a real, working link. Pick one setup:

**Option A — cloud drive link (least setup, good to start with)**
1. Build the exe once (see below), upload `QuadClicker.exe` to Google Drive/Dropbox/etc.
2. Set link sharing to "anyone with the link."
3. Paste that share link into `EXE_DOWNLOAD_URL` in `caller.html`.
Done — no server config, but you're depending on that drive service staying up.

**Option B — static file on the VPS (everything in one place you control)**
1. Keep the exe in its own subfolder, separate from the code files — e.g. `mkdir -p /root/clicker/downloads && cp QuadClicker.exe /root/clicker/downloads/`. Keeping it separate means re-uploading `serverclick.js`/`admin.js` never risks touching the exe, and vice versa.
2. Serve that folder (if you're already running the file-host under `pm2` per the setup below, point it at `downloads/` instead of the code folder):
   ```
   pm2 start "python3 -m http.server 8081" --name clicker-files --cwd /root/clicker/downloads
   ```
   That makes it reachable at `http://your-vps:8081/QuadClicker.exe`. For `https://`, put it behind the same nginx/caddy you're using for the relay, as a static file location pointing at `/root/clicker/downloads`.
3. Set `EXE_DOWNLOAD_URL` in `caller.html` (and the matching constant in `desktop_client.py`) to that final URL. It stays the same URL forever — overwriting the file in that folder is all a future update needs.

Either way: callers tap "Copy download link" on their phone, send that link plus a token to each client — no separate build needed per client, no need for you to touch it again once it's set up.

`db.json` (created automatically next to `serverclick.js`) stores everything — back it up.

