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:
@@ -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. |
|
||||
|
||||
Reference in New Issue
Block a user