- C# 100%
| docs | ||
| src/SmsNotification.App | ||
| .gitignore | ||
| README.md | ||
| SMS_NOTIFICATION.sln | ||
SMS_NOTIFICATION
C# WPF desktop application for reviewing absence-report CSV files before preparing parent SMS notifications.
This is a WPF/MVVM rebuild. The previous web scaffold was removed from the working tree.
Project type
- C#
- WPF desktop application
- .NET 8 Windows target:
net8.0-windows - MVVM pattern
MVVM layout
src/SmsNotification.App/
App.xaml
App.xaml.cs
Views/MainWindow.xaml
Views/MainWindow.xaml.cs
Views/TemplateWindow.xaml
Views/TemplateWindow.xaml.cs
ViewModels/MainWindowViewModel.cs
ViewModels/TemplateEditorViewModel.cs
ViewModels/AbsenceRecordViewModel.cs
ViewModels/ViewModelBase.cs
Models/AbsenceRecord.cs
Models/SmsTemplate.cs
Models/TemplatePlaceholder.cs
Models/RingCentralSettings.cs
Models/RingCentralJwtCredentials.cs
Models/RingCentralSenderNumber.cs
Models/RingCentralConnectionResult.cs
Services/CsvAbsenceReportService.cs
Services/JsonMessageTemplateService.cs
Services/TemplateRenderer.cs
Services/RingCentralOAuthService.cs
Services/RingCentralSettingsService.cs
Services/FileDialogService.cs
Commands/RelayCommand.cs
Commands/AsyncRelayCommand.cs
Code-behind is limited to view construction and assigning the root view model. UI behavior lives in view models, commands, and services.
Current UI behavior
- Configure RingCentral connection at application startup/opening.
- Supports OAuth authorization-code with PKCE for current-user login.
- Supports
.rc-creds-style JWT credential JSON for a service/attendance sender. - Store RingCentral UI settings locally under
%APPDATA%\SMS_NOTIFICATION\ringcentral-oauth.json; no access tokens are stored. - For OAuth, open the RingCentral login page in the user's browser, receive the local loopback OAuth callback, and authenticate the current RingCentral user/extension.
- For JWT, read a local JSON credential file containing server, clientId, clientSecret, and jwt, then authenticate the RingCentral extension represented by that JWT.
- Read the authenticated extension's phone numbers with
GET /restapi/v1.0/account/~/extension/~/phone-number. - Filter and display available from numbers with
SmsSenderorA2PSmsSender. - Read sender SMS campaign configuration when available and display brand status, campaign status, registration tier, and use cases.
- Let the user select the from number when multiple SMS-capable sender numbers are available.
- Load an absence report in CSV format.
- Supports the WACOG Unexpected Absence Grid report shape:
Program TermAgencySiteClassChild NameAttendance StatusAbsence ReasonFamily IDPrimary AdultPrimary EmailPrimary Adult PhoneSecondary AdultSecondary EmailSecondary Adult Phone
- Expands each source report row into one SMS review row per adult that has both an adult name and a phone number.
- Ignores report rows with no child name and no adult name. These are usually continuation/extra-number rows and are intentionally excluded.
- Keeps support for the older simple CSV columns:
sms_number,parent_name,child_name,absence_reason. - Show checkbox per row to include/remove from the send batch.
- Show checkbox per row to mark a phone number opted out.
- Gray opted-out rows and disable their send checkbox.
- Show legend explaining gray opted-out rows.
- Show report columns in the review grid, including adult role, email, attendance status, family ID, program term, agency, site, class, and source row.
- Show summary counts for total, ready, manually removed, and opted out.
- Include filtering/search to support review.
- Manage saved SMS templates from the Message Templates window.
- Store templates as JSON under the current Windows user's
%APPDATA%\SMS_NOTIFICATION\templates.json. - Render selected-record preview messages through the selected saved template.
- Send selected live SMS notices through RingCentral only after a final confirmation dialog.
- Show a modeless send-progress window while selected messages are dispatched asynchronously and RingCentral message status is checked in the background.
- Store the returned RingCentral message id/status on each review row for the current app session.
- Automatically write a UTF-8 CSV batch audit report after each completed live send under
%LOCALAPPDATA%\SmsNotification\AuditLogs\. - Show a user-facing Batch audit logs panel where staff can refresh previous batch logs, preview a selected CSV, open the audit folder, or download/export a selected report copy.
RingCentral OAuth setup
The desktop app uses OAuth authorization-code with PKCE. This is the supported current-user desktop flow and avoids storing a client secret in the app.
RingCentral Developer Console requirements:
App type/auth: OAuth authorization-code with PKCE / user login
Redirect URI: http://127.0.0.1:8979/oauth2callback/
Permissions: enough to read account/extension phone numbers and SMS configuration; SMS send permissions will be needed only when live sending is added.
App startup fields:
Server URL: https://platform.ringcentral.com
Client ID: from the RingCentral developer app
Redirect URI: http://127.0.0.1:8979/oauth2callback/
If the Client ID field is blank, OAuth cannot construct the authorization URL, so the app will not open the browser. Click Connect OAuth with the field blank to get an in-app setup message, paste the RingCentral Developer Console Client ID to use browser login, or use Connect JWT with the service credential JSON for the attendance sender.
Flow:
Connect RingCentral
↓
Browser opens RingCentral login
↓
App receives local loopback OAuth callback
↓
App lists SMS-capable sender numbers
↓
User selects the from number for preview/future send batches
The app can send selected live SMS notices after preview and a final confirmation dialog. It dispatches selected messages asynchronously to POST /restapi/v1.0/account/~/extension/~/sms, then polls GET /restapi/v1.0/account/~/extension/~/message-store/{messageId} for the latest RingCentral status in the background. The progress window is modeless so the main app remains usable while results arrive.
JWT credential JSON mode
For the attendance/service sender, use a .rc-creds-style JSON file instead of interactive OAuth. The JSON file is not committed and should live on the Windows workstation under a protected user profile path, for example:
%APPDATA%\SMS_NOTIFICATION\ringcentral-jwt-creds.json
Expected shape, with secret values omitted here:
{
"server": "https://platform.ringcentral.com",
"clientId": "...",
"clientSecret": "...",
"jwt": {
"Demo_App": "..."
}
}
The JWT is tied to the RingCentral extension/user it was generated for. The app key identifies the API application; the JWT authenticates a specific RingCentral extension; the selected from.phoneNumber must belong to, or be SMS-assigned to, that authenticated extension.
Current verified service credential facts from the development environment, with number masked:
JWT auth: verified
SMS-capable sender count: 1
Sender: +*******7149
Feature: SmsSender/MmsSender
Brand: Verified
Campaign: Confirmed
Tier: LowVolume
Template placeholders
Templates support the original placeholders plus report-data placeholders:
[parentname]
[parentemail]
[adultrole]
[childname]
[smsnumber]
[attendancestatus]
[absencereason]
[familyid]
[programterm]
[agency]
[site]
[class]
[sourcerow]
[today]
The renderer also exposes normalized tokens for any source CSV header by removing spaces/punctuation and lowercasing the header. For example, Primary Adult Phone is available as [primaryadultphone].
Build
On Windows with the .NET 8 SDK installed:
dotnet build SMS_NOTIFICATION.sln -c Release
From this Linux Hermes runtime, the project can be compiled for Windows using cross-targeting:
export DOTNET_ROOT=/mnt/campaigndata/code_monkey_data/.dotnet
export PATH="$DOTNET_ROOT:$PATH"
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
dotnet build SMS_NOTIFICATION.sln -c Release
Run
Run from Visual Studio on Windows or from a Windows shell:
dotnet run --project src/SmsNotification.App/SmsNotification.App.csproj
Safety boundary
This build now includes live SMS sending, but only after:
- RingCentral is connected.
- An SMS-capable from number is selected.
- A saved message template is selected.
- Staff reviews selected rows and opt-out flags.
- Staff previews the rendered messages.
- Staff confirms the final live-send warning dialog.
The app dispatches selected notices asynchronously with a bounded in-flight limit instead of blocking on one recipient at a time. For each selected row it waits for the RingCentral send response and polls the message store for the latest status. A successful API response means RingCentral accepted/queued/sent the message; final carrier delivery can still update later in RingCentral.
Still pending before broad production use: opt-out source synchronization, dry-run audit reports, and capacity/rate-limit controls.