mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 19:33:12 +00:00
* feat(roster): add Bearer PUT/DELETE roster API Identity hire needs to write Slack IDs onto roster rows without a stale daily 3CX sync clearing them, using the existing HTTP client contract. * fix(roster): strip Secrets Manager token whitespace A file:// secret commonly includes a trailing newline, so compare_digest must strip the cached value the same way it strips the Bearer header.
133 lines
5.4 KiB
Markdown
133 lines
5.4 KiB
Markdown
# After-Hours Shift Manager — Setup Guide
|
||
|
||
## 1. Create a Slack App
|
||
|
||
1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create New App** → **From scratch**
|
||
2. Name: `After-Hours Shift Manager`, pick your workspace
|
||
3. Under **OAuth & Permissions**, add these **Bot Token Scopes**:
|
||
- `chat:write` — post messages to channels
|
||
- `commands` — register slash commands
|
||
- `chat:write.public` — post to channels the bot isn't in
|
||
4. Under **Slash Commands**, create a new command:
|
||
- Command: `/oncall`
|
||
- Request URL: `(fill in after deploy — see step 4)`
|
||
- Short Description: `Manage after-hours on-call shifts`
|
||
- Usage Hint: `[schedule | pick <date> | drop <date> | swap <date> @person | register <ext> | roster | help]`
|
||
5. Under **Interactivity & Shortcuts**, toggle **ON**:
|
||
- Request URL: same URL as the slash command (the `/slack/events` endpoint)
|
||
6. **Install to Workspace** — approve the permissions
|
||
7. Copy the **Bot User OAuth Token** (`xoxb-...`) and **Signing Secret** (under Basic Information)
|
||
|
||
## 2. Store Secrets in AWS Secrets Manager
|
||
|
||
The stack reads these from Secrets Manager (the IAM roles grant
|
||
`secretsmanager:GetSecretValue` on `afterhours-shift-manager/*`):
|
||
|
||
```bash
|
||
# Slack
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/slack-bot-token \
|
||
--secret-string "xoxb-YOUR-BOT-TOKEN"
|
||
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/slack-signing-secret \
|
||
--secret-string "YOUR-SIGNING-SECRET"
|
||
|
||
# 3CX Queue XAPI (used by roster-sync and ring-scheduler)
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/3cx-domain \
|
||
--secret-string "yourcompany.3cx.us"
|
||
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/3cx-client-id \
|
||
--secret-string "YOUR-3CX-CLIENT-ID"
|
||
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/3cx-client-secret \
|
||
--secret-string "YOUR-3CX-CLIENT-SECRET"
|
||
|
||
# Roster HTTP API bearer token (plain string). Duplicate the same value into
|
||
# seahaven-prod as paychex-integrations/afterhours-roster-token. Generate the
|
||
# token into a temp file, pass --secret-string file://..., then delete the file.
|
||
# Never paste the value into chat, Terraform, or a PR.
|
||
aws secretsmanager create-secret \
|
||
--name afterhours-shift-manager/roster-api-token \
|
||
--secret-string file://./roster-api-token.tmp
|
||
```
|
||
|
||
Create `afterhours-shift-manager/roster-api-token` **before** the first deploy that
|
||
includes `afterhours-roster-api`, or live PUT/DELETE calls return 503.
|
||
|
||
Rotation is coordinated: write the new value to both the mgmt secret and the
|
||
prod copy, then recycle `afterhours-roster-api` so cached execution environments
|
||
pick it up. Updating only one copy causes 401s. The identity processor
|
||
`AFTERHOURS_BASE_URL` is the stack output `AfterhoursApiBaseUrl` (origin only,
|
||
no `/mgmt` or `/roster` suffix). Leave that URL empty until the API is live
|
||
and smoke-tested.
|
||
|
||
Daily roster-sync still removes DynamoDB rows that are not in the 3CX `DEFAULT`
|
||
group. Hire stays safe because 3CX create lands the extension in that group
|
||
before the identity processor PUTs `/roster`.
|
||
|
||
> The Slack **channel ID** is not a secret — it's passed as the `ShiftChannel`
|
||
> deploy parameter in step 3, not stored in Secrets Manager or SSM.
|
||
|
||
## 3. Deploy the Stack
|
||
|
||
```bash
|
||
# Build and deploy. ShiftChannel is the Slack channel ID for schedule posts
|
||
# (right-click the channel in Slack → Copy link → the ID is the last segment).
|
||
sam build
|
||
sam deploy --guided \
|
||
--stack-name afterhours-shift-manager \
|
||
--region us-east-1 \
|
||
--parameter-overrides ShiftChannel=C0XXXXXXX QueueNumber=801
|
||
|
||
# Note SlackBotApiUrl (Slack Request URL) and AfterhoursApiBaseUrl
|
||
# (paychex AFTERHOURS_BASE_URL origin).
|
||
```
|
||
|
||
## 4. Set the Slack Request URL
|
||
|
||
After deploy, copy the `SlackBotApiUrl` from the SAM output. Go back to your Slack app settings:
|
||
|
||
- **Slash Commands** → edit `/oncall` → set **Request URL** to the output URL
|
||
- **Interactivity & Shortcuts** → set **Request URL** to the same URL
|
||
|
||
## 5. Seed the Schedule
|
||
|
||
```bash
|
||
python scripts/seed_schedule.py
|
||
```
|
||
|
||
This populates the DynamoDB table with the employee roster and default weekly schedule.
|
||
|
||
## 6. Invite the Bot & Register Users
|
||
|
||
1. Create a channel (e.g. `#after-hours-shifts`) and invite the bot: `/invite @After-Hours Shift Manager`
|
||
2. Each employee links their Slack account by running:
|
||
```
|
||
/oncall register 114
|
||
```
|
||
(using their own extension number)
|
||
|
||
## 7. Update the 3CX Scheduler (optional)
|
||
|
||
To have the 3CX scheduler read overrides from DynamoDB (so Slack-driven changes apply to future dates automatically), deploy the updated 3CX scheduler from the `feature/dynamodb-shift-integration` branch. See that branch's changes for details.
|
||
|
||
Without this step, the Slack bot still works — it invokes the 3CX scheduler Lambda directly for same-day changes. Future-date overrides would only take effect if the scheduler reads DynamoDB.
|
||
|
||
## Commands Reference
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `/oncall` | Show this week's schedule |
|
||
| `/oncall next` | Show next week's schedule |
|
||
| `/oncall pick <date>` | Pick up a shift |
|
||
| `/oncall drop <date>` | Drop your shift (marks it open) |
|
||
| `/oncall swap <date> @person` | Hand your shift to someone else |
|
||
| `/oncall register <ext>` | Link your Slack to your extension |
|
||
| `/oncall roster` | Show all employees and their link status |
|
||
| `/oncall help` | Show help |
|
||
|
||
Dates can be: `today`, `tomorrow`, `monday`–`sunday`, `4/5`, `2026-04-05`
|