docs: add project documentation and update README

- Add `agent.md`: Complete maintenance reference covering project structure, API interactions, data flow, configuration, security, and operational procedures.
- Add `architecture.md`: High-level architecture overview including component map, data flow, design decisions, and security model.
- Update `README.md`: Add Apprise support documentation, manual deployment instructions, updated installation steps (bot admin requirement), and expanded payload mapping and troubleshooting sections.
- Improve `lib/Controller/WebhookController.php`: Enhance `saveBotPassword` validation to check for missing fields and validate the password content.
- Improve `lib/Service/TalkService.php`: Add `validateBotPassword` method for round-trip encryption testing and integrate it into `saveConfig`.
- Update `css/adminSettings.css`: Add styles for the new `.nc-app-frame` wrapper.
- Update `templates/adminSettings.php`: Refactor UI structure to use `.nc-app-frame` and improve layout of settings sections.
This commit is contained in:
kyle
2026-06-12 19:44:42 -07:00
parent 19241c842d
commit 18084d0f69
7 changed files with 1853 additions and 112 deletions
+183 -74
View File
@@ -1,8 +1,26 @@
# NCbotwebhooks
Discord webhook bridge for Nextcloud Talk with image attachment support.
Webhook bridge that forwards Discord webhook-style and Apprise payloads into Nextcloud Talk rooms, with image attachment support and per-room authentication tokens.
Accepts Discord webhook-style JSON payloads and posts them into Nextcloud Talk rooms, preserving images and embed formatting.
Accepts Discord webhook-compatible JSON payloads (embeds, fields, images) and Apprise notification payloads, maps them to Nextcloud Talk Chat API v1 format, and posts them as the `talk-bot` user.
## Table of Contents
- [Installation](#installation)
- [Manual Deployment](#manual-deployment)
- [Configuration](#configuration)
- [Webhook URLs](#webhook-urls)
- [Payload Formats](#payload-formats)
- [Discord Webhook Format](#discord-webhook-format)
- [Apprise Format](#apprise-format)
- [Payload Mapping](#payload-mapping)
- [Image Management](#image-management)
- [Security](#security)
- [Debug Endpoint](#debug-endpoint)
- [Logging](#logging)
- [Troubleshooting](#troubleshooting)
---
## Installation
@@ -14,39 +32,105 @@ cp -r /path/to/nc_bot_webhooks .
php occ app:enable nc_bot_webhooks
```
Or upload via the Nextcloud web UI as a ZIP (only the `nc_bot_webhooks` directory, not its parent).
### 2. Create the bot user
```bash
php occ user:add --password-from-env --display-name="Webhook Bot" talk-bot
```
### 3. Generate an app password for the bot
### 3. Make the bot user an admin
1. Log in as `talk-bot` (or set a password as admin)
2. Go to **Settings → Security → Devices & sessions → Add device**
3. Copy the app password
The bot must have admin privileges to list all Talk rooms. Grant admin access:
### 4. Configure the app
1. Go to **Settings → Users**
2. Find **talk-bot** and click it
3. Check **Admin** under "Settings"
4. Save
### 4. Generate an app password for the bot
1. Log in to Nextcloud as **admin** (or set a password as admin, then log in as talk-bot)
2. Go to **Settings → talk-bot → Devices & sessions**
3. Click **Add device** and give it a name (e.g., "NCbotwebhooks")
4. Copy the generated device password — you'll need it in the next step
### 5. Configure the app
Go to **Settings → Admin → NCbotwebhooks**:
1. **Bot Configuration** — paste the app password from step 3
2. **Image Retention** — set how long to keep uploaded images (default: 90 days)
3. **Room Management** — click "Fetch Rooms" to see available Talk rooms, select the ones you want, and generate auth tokens
1. **Bot App Password** — paste the app password from step 4
2. **Default Sender Name** — set the name that appears as the message sender (default: "Webhook Bot")
3. **Image Retention** — set how many days to keep uploaded images (default: 90)
4. **Room Selection** — click **Fetch Rooms** to list available Talk rooms
5. Check the rooms you want to accept webhooks for
6. For each room, click **+ Generate Auth Token** to create a webhook URL
7. Click **Save Configuration**
### 5. Point your services at the webhook URLs
---
Each configured room gets **two** webhook URLs (Discord and Apprise formats):
## Manual Deployment
```
https://your-server.com/apps/nc_bot_webhooks/discord-webhook/<room-token>/<auth-token>
> **Note:** The paths below are specific to a TrueNAS Scale Nextcloud Docker install. Adjust them to match your deployment.
This section covers updating the app on a running Nextcloud instance using the synced-file workflow.
**Workflow:** Keep your local development copy synced to your server via the Nextcloud desktop client. Edit files locally, then deploy with the script below.
### Update script
```bash
cd /var/www/html
php occ app:disable nc_bot_webhooks
rm -rf /var/www/html/custom_apps/nc_bot_webhooks/*
mkdir -p /var/www/html/custom_apps/nc_bot_webhooks
# Update from your synced Nextcloud files directory — adjust this path to match your setup.
cp -r "/var/www/html/data/<username>/files/TrueNAS configs/Nextcloud Hooker/nc_bot_webhooks/"* /var/www/html/custom_apps/nc_bot_webhooks/
chown -R www-data:www-data /var/www/html/custom_apps/nc_bot_webhooks
chmod -R u+rX,go+rX /var/www/html/custom_apps/nc_bot_webhooks
cd /var/www/html
php occ app:enable nc_bot_webhooks
php occ config:app:delete nc_bot_webhooks routes 2>/dev/null || true
php occ maintenance:repair
clear
```
Copy the auth token from the app settings for each room.
> **Note:** The path `"/var/www/html/data/<username>/files/TrueNAS configs/Nextcloud Hooker/nc_bot_webhooks/"` is where your Nextcloud user's synced files land on the server (TrueNAS Docker path). Replace `<username>` with your Nextcloud username, and adjust `TrueNAS configs/Nextcloud Hooker/nc_bot_webhooks/` to match the directory you are syncing to.
## API
---
### Accepts (Discord webhook format)
## Webhook URLs
Each configured room gets two webhook URLs:
### Discord format
```
https://your-nextcloud-server/apps/nc_bot_webhooks/discord-webhook/<room-token>/<auth-token>
```
### Apprise format
```
https://your-nextcloud-server/apps/nc_bot_webhooks/apprise-webhook/<room-token>/notify/<auth-token>
```
> **Note:** The `notify` segment is required — Apprise's `apprises://` URL scheme inserts it in the path automatically.
### Multiple auth tokens per room
Each room can have multiple auth tokens — useful for rotating keys or sharing the webhook across multiple services. Generate additional tokens in the admin settings UI.
---
## Payload Formats
### Discord Webhook Format
```json
{
@@ -57,6 +141,7 @@ Copy the auth token from the app settings for each room.
"description": "Successfully deployed to production",
"color": 3066993,
"image": { "url": "https://example.com/screenshot.png" },
"thumbnail": { "url": "https://example.com/thumb.png" },
"fields": [
{ "name": "Duration", "value": "2m 34s" },
{ "name": "Environment", "value": "Production" }
@@ -68,85 +153,95 @@ Copy the auth token from the app settings for each room.
}
```
### Sends to Nextcloud Talk
### Apprise Format
- Formatted text message (content + embeds + fields)
- Each image from `embeds[].image` or `embeds[].thumbnail` uploaded and shared inline
- `username` shown as the sender display name
Apprise sends a JSON wrapper containing a `notifications` array:
### Payload mapping
```json
{
"version": 0,
"subject": "Build #1234",
"title": "Deployment",
"type": "info",
"notifications": [
{
"subject": "Build #1234",
"title": "Deployment",
"body": "Successfully deployed to production",
"type": "info",
"attachments": [
{ "path": "https://example.com/screenshot.png" }
]
}
]
}
```
| Discord field | Nextcloud action |
Apprise also supports form-encoded payloads. The app auto-detects the content type (JSON, `multipart/form-data`, or form-encoded).
---
## Payload Mapping
| Field | Nextcloud Talk action |
|---|---|
| `content` | Sent as text message |
| `embeds[].title` | Included as bold line |
| `embeds[].description` | Included in message body |
| `embeds[].image.url` | Downloaded, uploaded to NC, shared inline |
| `embeds[].thumbnail.url` | Downloaded, uploaded to NC, shared inline |
| `embeds[].fields` | Formatted as `name: value` lines |
| `username` | Sender display name |
| `embeds[].fields[].name` + `value` | Formatted as `name: value` lines |
| `username` | Sender display name (prepended to message) |
| `avatar_url` | Ignored (NCTalk doesn't support per-message avatars) |
| `subject` / `title` (Apprise) | Used as bold title line |
| `body` (Apprise) | Sent as message body |
| `attachments[].path` (Apprise) | Downloaded, uploaded to NC, shared inline |
## Room routing
### Sender name resolution
Each room gets its own webhook URL with a unique auth token:
When posting to Talk, the app prepends a bold sender name line to the message (since Talk doesn't support per-message avatars). The sender name is resolved in this order:
```
/discord-webhook/<room-token>/<auth-token>
```
1. `username` field from Discord payload
2. `subject` / `title` from Apprise payload
3. Configured default sender name (Settings → Default Sender Name)
- **Room token** — identifies which Talk room to post to (from `occ talk:room:list`)
- **Auth token** — secret key for this webhook endpoint (generated in app settings)
---
Multiple auth tokens can be created per room — useful if you need to rotate keys or share the webhook across multiple services.
## Image Management
### Apprise webhook
- Images from webhooks are downloaded via HTTP and uploaded to the bot user's files
- Stored at `/nc_bot_webhooks-images/<room-token>/` under the bot user's personal storage
- A public link share is created for each image so Talk can resolve the rich object and display the inline attachment
- **Cron job** (`ImageCleanup`) purges images older than the configured retention period (default: 90 days)
- Images count toward the bot user's storage quota
- Purge operates on the bot user's personal storage directory only — it does not scan other users' files
For Apprise integrations, each room also gets an Apprise webhook URL:
```
https://your-server.com/apps/nc_bot_webhooks/apprise-webhook/<room-token>/notify/<auth-token>
```
Note: the `notify` segment is required — Apprise's `apprises://` URL scheme inserts it in the path.
Apprise sends a different JSON format. Supported fields:
```json
{
"title": "Build #1234",
"body": "Successfully deployed to production",
"type": "info",
"attachments": [
{ "url": "https://example.com/screenshot.png" }
]
}
```
The apprise webhook maps `title`, `body`, and `attachments` to the same Talk message format as the Discord endpoint, so images and formatting work the same way.
## Image management
- Images are uploaded to the bot user's files at `/nc_bot_webhooks-images/<room-token>/`
- Cron job purges images older than the configured retention period (default: 90 days)
- Images are stored in the bot user's storage — they count toward the bot user's quota
---
## Security
- Auth token in the URL is the primary auth mechanism — keep it secret
- Bot password is encrypted at rest using Nextcloud's crypto
- Image download uses Nextcloud's HTTP client with local address blocking
- Rate limiting should be handled at the web server or reverse proxy level
- **Debug endpoint disabled by default** — see below
| Mechanism | Description |
|---|---|
| **Auth tokens** | Each webhook URL contains a unique secret token; the endpoint validates it before processing |
| **Bot password** | Encrypted at rest using Nextcloud's crypto layer (`ICrypto`) |
| **No CSRF** | Webhook endpoints are marked `#[PublicPage]` + `#[NoCSRFRequired]` — auth token is the sole access control |
| **Local address blocking** | Image downloads use Nextcloud's HTTP client with `allow_local_address: true` (blocks private ranges by default) |
| **Path traversal protection** | Uploaded filenames use `basename()` to prevent directory traversal |
| **Rate limiting** | Not built in — handle at the web server or reverse proxy level |
### Debug endpoint
### Known limitations
The `/apps/nc_bot_webhooks/debug` endpoint exposes internal configuration,
database schema, and bot credentials. It is **disabled by default** and must
be explicitly enabled via the CLI:
- Auth tokens generated from the settings UI use client-side generation; for higher security, regenerate them via the server API
- The webhook endpoint has no rate limiting — consider placing it behind a reverse proxy rate limiter if exposing to untrusted sources
---
## Debug Endpoint
The `/apps/nc_bot_webhooks/debug` endpoint exposes internal configuration, database schema, and bot credentials. It is **disabled by default**.
```bash
# Check current status
# Check status
php occ nc_bot_webhooks:debug:status
# Enable (WARNING: exposes sensitive data)
@@ -159,13 +254,14 @@ php occ nc_bot_webhooks:debug:disable
php occ nc_bot_webhooks:debug:toggle
```
Never leave the debug endpoint enabled in production. After troubleshooting,
disable it immediately:
After troubleshooting, disable it immediately:
```bash
php occ nc_bot_webhooks:debug:disable
```
---
## Logging
Responses include a `X-Webhook-Status` header:
@@ -177,3 +273,16 @@ Responses include a `X-Webhook-Status` header:
| `bad_request` | Invalid JSON payload |
| `no_content` | No message content in payload |
| `error` | Check server logs for details |
---
## Troubleshooting
| Problem | Solution |
|---|---|
| **"talk-bot user not found"** | The `talk-bot` user doesn't exist. Re-run Step 2 of installation. |
| **"Bot password not configured"** | You haven't entered the bot password in admin settings, or it was cleared. Re-enter it. |
| **Messages not appearing** | Check the Nextcloud log (`data/nextcloud.log` or Settings → Admin → Logging) for errors from the `nc_bot_webhooks` app. |
| **Image upload fails** | Verify the bot user has file storage quota and the `nc_bot_webhooks-images` folder can be created. |
| **Apprise 500 error** | Likely config data not migrated from the old app ID. Re-enter settings or run: `UPDATE oc_appconfig SET appid = 'nc_bot_webhooks' WHERE appid = 'ncdiscordhook';` |
| **Bot not listed in room participants** | The `ensureBotParticipants()` method runs automatically on save, but you may need to re-check the room in admin settings to trigger it. |