# QMAX QMAX is a private Android messenger client backed by a personal bridge server. The Android app talks to your QMAX server, and the server keeps a browser session for `https://web.max.ru`. Current shape: - `server/QMax.Api` - ASP.NET Core API, SQLite cache, JWT pairing auth, SignalR hub, attachment storage, APK update catalog. - `worker` - Node/Playwright worker with a persistent MAX Web browser profile. - `android` - Kotlin + Jetpack Compose Android client pointed at `https://qmax.kusoft.xyz`. - `deploy` - Docker Compose + Caddy for Raspberry Pi 5. ## Local Checks ```powershell dotnet test QMax.slnx cd android .\gradlew.bat :app:assembleDebug :app:assembleRelease --console=plain --no-daemon cd ..\worker npm.cmd install node --check src/server.js ``` ## Raspberry Pi Deployment 1. Point DNS `qmax.kusoft.xyz` to the Raspberry Pi public IP. 2. Install Docker Engine and the Docker Compose plugin on the Pi. 3. Copy the repository or the archive produced by `scripts/deploy-rpi.ps1`. 4. On the Pi: ```bash cd ~/qmax/deploy cp .env.example .env nano .env docker compose up -d --build ``` This compose publishes QMAX API on `127.0.0.1:18080`. The existing system Caddy should contain: ```caddy qmax.kusoft.xyz { encode zstd gzip reverse_proxy 127.0.0.1:18080 } ``` Set strong values in `.env`: - `QMAX_JWT_SECRET` - at least 32 random characters. - `QMAX_PAIRING_CODE` - one-time-ish pairing password for your Android client and `/admin/max`. - `QMAX_MAX_PHONE_NUMBER` - the phone number used for MAX Web login. Secrets must stay in `.env`, not in git. ## MAX Login Open: ```text https://qmax.kusoft.xyz/admin/max?pairingCode=YOUR_PAIRING_CODE ``` Use **Start phone login**. If MAX shows a robot check, solve it manually on this page using the screenshot plus click/type forms. When MAX sends the confirmation code, submit it on the same page or from the Android app MAX panel. The worker stores browser state in the `qmax-max-profile` Docker volume, so the MAX session should survive restarts. ## Android Pairing Build or install: ```powershell cd android .\gradlew.bat :app:assembleRelease --console=plain --no-daemon ``` On first launch: - Server: `https://qmax.kusoft.xyz` - Code: `QMAX_PAIRING_CODE` from `.env` The first screen after pairing is the chat list. ## APK Update Catalog Create a release package: ```powershell .\scripts\package-android-release.ps1 -Version 0.1.4 -VersionCode 5 ``` The compose file mounts `deploy/releases` as `/data/releases`, so files generated by the packaging script are served automatically after `docker compose up -d`. The server exposes: ```text GET /api/app-updates/android/latest GET /api/app-updates/android/download/{fileName} ``` Use one stable release key for production APKs. Do not install a new APK signed by a different key over an already installed QMAX app. ## Push Notifications The server has an FCM HTTP v1 dispatcher. To enable real push delivery on the Raspberry Pi, put a Firebase service account JSON under `deploy/secrets/` and set: ```dotenv QMAX_FIREBASE_PROJECT_ID=qmax-29df6 QMAX_FIREBASE_SERVICE_ACCOUNT_PATH=/run/secrets/qmax/firebase-service-account.json ``` The Android app registers its FCM token after pairing. It uses the Google Services Gradle plugin with `android/app/google-services.json` for the package `xyz.kusoft.qmax`. `FirebaseBootstrap` still supports asset-based `firebase.android.json` or `google-services.json` as a fallback for custom local builds. ## Argus Publication QMAX is published in Argus with: - base URL: `https://argus.kusoft.xyz` - slug: `qmax` - platform: `android` - channel: `stable` Manifest: ```text https://argus.kusoft.xyz/api/apps/qmax/manifest?platform=android&channel=stable ``` Latest APK: ```text https://argus.kusoft.xyz/api/apps/qmax/download/latest?platform=android&channel=stable ``` The Android app checks this manifest, compares semantic versions, downloads the APK to a temporary file, verifies SHA-256, and only then opens Android's package installer. ## Current MAX Mapping Status The deployed worker is authorized in MAX Web and currently maps: - chat list extraction from the left MAX Web dialog list; - visible chat history extraction when Android opens a dialog; - text sending through the MAX Web composer; - attachment upload through the MAX Web file chooser; - image, video, file and voice attachment projection from MAX Web; - Android image attachment caching with `.part` downloads before a file is shown from local storage; - manual browser inspection endpoints for future selector changes. The bridge uses DOM selectors in `worker/src/server.js`, so MAX Web UI changes may require a worker redeploy without changing the Android app contract.