ZoomCheck is a Windows-oriented Zoom attendance tool for classes and training sessions.
It loads a roster from Excel, accepts Zoom join/leave events, classifies attendance match confidence, and gives the operator a simple dashboard for reviewing only the ambiguous cases.
- Imports roster Excel files
- Tracks Zoom participant join/leave events
- Computes live attendance status per meeting
- Classifies matches into:
VerifiedAliasVerifiedNameOnlyPossibleUnmatched
- Shows a review queue for weaker matches
- Saves operator-confirmed aliases for future meetings
- Exports attendance as CSV
src/ZoomCheck.Core— domain models and matching logicsrc/ZoomCheck.Infrastructure— Excel parsing and SQLite persistencesrc/ZoomCheck.Backend— ASP.NET Core API for roster import, Zoom webhooks, board projection, and exportsrc/ZoomCheck.App— Avalonia desktop app for the operator dashboard
The current implementation is event-driven on the backend and refresh-based on the desktop UI.
- Zoom webhook events can be received immediately by the backend
- Those events are stored in SQLite as participant event logs
- The desktop dashboard reads
GET /api/meetings/{meetingId}/board - The screen updates when the operator presses Refresh
So today:
- event collection can be real-time
- screen refresh is currently manual, not push-based
- Use the packaged Windows installer
- No separate .NET installation is needed when using the self-contained Windows package
- .NET 8 SDK
- A Zoom Server-to-Server OAuth app for real Zoom integration
- Zoom webhook configuration for live participant events
- Windows is the intended operator runtime, but the backend also runs on Linux
The intended beginner flow is:
- Run
ZoomCheck-Setup-x64.exe - Finish the installer wizard
- Double-click the ZoomCheck shortcut
- ZoomCheck starts its local service automatically
- Open or join the Zoom meeting
- Enter the meeting ID, load the roster, refresh during class, review flagged people, and export at the end
The installer assets for this flow live under
deploy/windows/.
On a Windows build machine:
- Install .NET 8 SDK
- Install Inno Setup 6
- Run:
powershell -ExecutionPolicy Bypass -File .\deploy\windows\build-installer.ps1This produces:
- self-contained app publish output
- self-contained backend publish output
- packaged install folder
dist\installer\ZoomCheck-Setup-x64.exe
You can also build the installer from GitHub Actions on a Windows runner through:
.github/workflows/build-windows-installer.yml
git clone https://github.com/ianlyoo/zoomcheck.git
cd zoomcheckdotnet run --project src/ZoomCheck.Backend/ZoomCheck.Backend.csproj --urls http://0.0.0.0:5078Useful test endpoints:
http://localhost:5078/healthhttp://localhost:5078/api/zoom/settings-status
dotnet run --project src/ZoomCheck.App/ZoomCheck.App.csprojThe desktop app currently targets:
http://127.0.0.1:5078/
When running from the packaged Windows build, the desktop app starts the backend automatically.
- Start the backend
- Start the desktop app
- Enter the Zoom meeting ID
- Load the roster Excel file once
- Press Refresh during class
- Check only the Review queue
- Save aliases for nickname/device-name cases
- Export the final attendance CSV
GET /healthGET /api/zoom/settings-status
POST /api/roster/importGET /api/rosterPOST /api/roster/alias
POST /api/zoom/webhooks/eventsPOST /api/zoom/webhooks/participant-eventPOST /api/zoom/webhooks/raw
GET /api/zoom/recovery/lastPOST /api/zoom/recovery/run
GET /api/meetings/{meetingId}/boardPOST /api/meetings/{meetingId}/seed-demoGET /api/meetings/{meetingId}/export
curl -X POST http://127.0.0.1:5078/api/roster/import \
-H "Content-Type: application/json" \
-d '{"filePath":"/absolute/path/to/roster.xlsx"}'curl -X POST http://127.0.0.1:5078/api/meetings/demo-meeting/seed-democurl http://127.0.0.1:5078/api/meetings/demo-meeting/boardcurl http://127.0.0.1:5078/api/meetings/demo-meeting/exportSet these values in src/ZoomCheck.Backend/appsettings.json or with environment variables:
"Zoom": {
"WebhookSecretToken": "your-webhook-secret-token",
"ClientId": "your-s2s-client-id",
"ClientSecret": "your-s2s-client-secret",
"AccountId": "your-zoom-account-id",
"RequestTimestampToleranceSeconds": 300
}What this enables:
- Zoom webhook signature validation
- Zoom
endpoint.url_validationchallenge handling - Zoom S2S OAuth token acquisition and caching
- Basic configuration status checking through
/api/zoom/settings-status
See:
docs/windows-operator-guide.md
Verified— strongest match, usually email-basedAliasVerified— operator confirmed this Zoom name beforeNameOnly— name matches, but should still be checked quicklyPossible— weak match candidate, review recommendedUnmatched— no safe roster match found
The goal is to let the operator ignore most attendees and focus only on the few people that need review.
The backend currently stores data in SQLite.
Default database path:
data/zoomcheck.db
Stored data includes:
- imported roster entries
- manual alias mappings
- participant event logs
If the backend starts after a meeting has already begun, Zoom does not resend previous participant events. The new recovery path helps avoid that gap.
-
Configure
ZoomRecoveryinappsettings.json(example included):"ZoomRecovery": { "Enabled": true, "StartupDelaySeconds": 8, "PeriodicScanIntervalSeconds": 0, "UsersPageSize": 100, "MeetingsPageSize": 300, "ParticipantsPageSize": 300, "EnableAccountWideUserDiscovery": false, "HostUserIds": [], "IncludeFallbackMeUser": true }
Enabled: turns automatic startup recovery on/off (defaulttrue)StartupDelaySeconds: delay before first recovery runPeriodicScanIntervalSeconds: optional polling interval;0disables periodic scansHostUserIds: explicit host IDs to query live meetings forEnableAccountWideUserDiscovery: include all active users (requires broader account scope)IncludeFallbackMeUser: addmeas final fallback user
-
Recovery uses Zoom
GET /users/{userId}/meetings?type=livefor discovery. -
For each live meeting it calls
GET /metrics/meetings/{meetingId}/participants?type=liveand inserts missingjoinedevents withsource = zoom-live-recovery. -
Endpoint
POST /api/zoom/recovery/runtriggers recovery on demand. -
Endpoint
GET /api/zoom/recovery/lastreturns the last completed run summary. -
Dedupe rule: recovery skips participants that already have a currently active event for the same normalized name+email.
-
Permissions/scope mismatch on live participant metrics is surfaced as warnings in
POST /api/zoom/recovery/runresponse.
- Desktop UI still uses manual refresh instead of auto-refresh or push updates
- Real Zoom reconciliation after meeting end is not implemented yet
- Packaged installer generation is prepared, but should be built on Windows for the final
setup.exe
- add automatic dashboard refresh
- add post-meeting reconciliation with Zoom participant/report APIs
- add a backend URL/settings screen in the desktop app
- package the Avalonia app for Windows deployment
No license has been added yet.