refactor(nc_bot_webhooks): rename app display name and logger tags to nc_bot_webhooks and update docs

- appinfo/info.xml: Update app `<name>` to `nc_bot_web_hooks`.
- lib/NavigationProvider.php: Update navigation entry name to `nc_bot_web_hooks`.
- lib/Controller/WebhookController.php: Update all logger tags from `NCbotwebhooks:` to `nc_bot_web_hooks:`.
- lib/Cron/ImageCleanup.php: Update logger tag to `nc_bot_web_hooks:`.
- lib/Service/TalkService.php: Update all logger tags from `NCbotwebhooks:` to `nc_bot_web_hooks:`.
- agent.md: Update references to app name and logger tags.
- architecture.md: Update references to app name.
- INSTALL.md: Update references to app name and example strings.
- README.md: Restructure "Installation" section (split into Initial installation, Updating using git, Local update), add "Debugging" section with diagnostic grep examples, update app name references.
This commit is contained in:
kyle
2026-06-14 15:06:15 -07:00
parent 1180ab14f7
commit b18b996468
9 changed files with 318 additions and 144 deletions
+4 -4
View File
@@ -1,4 +1,4 @@
# NCbotwebhooks — Installation Guide # nc_bot_webhooks — Installation Guide
## Prerequisites ## Prerequisites
@@ -33,7 +33,7 @@ The bot must have admin privileges to list all Talk rooms. Grant admin access:
1. Log in to Nextcloud as **admin** 1. Log in to Nextcloud as **admin**
2. Go to **Settings → talk-bot → Devices & sessions** 2. Go to **Settings → talk-bot → Devices & sessions**
3. Click **Add device** and give it a name (e.g., "NCbotwebhooks") 3. Click **Add device** and give it a name (e.g., "nc_bot_webhooks")
4. Copy the generated device password — you'll need this in Step 3 4. Copy the generated device password — you'll need this in Step 3
## Step 2: Install the app ## Step 2: Install the app
@@ -72,7 +72,7 @@ php occ app:enable nc_bot_webhooks
## Step 3: Configure the app ## Step 3: Configure the app
1. Log in to Nextcloud as **admin** 1. Log in to Nextcloud as **admin**
2. Go to **Settings → Admin → NCbotwebhooks** 2. Go to **Settings → Admin → nc_bot_webhooks**
3. **Bot App Password** — Paste the device password you generated in Step 1 3. **Bot App Password** — Paste the device password you generated in Step 1
4. **Default Sender Name** — Set the name that appears as the message sender (default: "Webhook Bot") 4. **Default Sender Name** — Set the name that appears as the message sender (default: "Webhook Bot")
5. **Image Retention** — Set how many days to keep uploaded images (default: 90) 5. **Image Retention** — Set how many days to keep uploaded images (default: 90)
@@ -102,7 +102,7 @@ Test with curl:
```bash ```bash
curl -X POST \ curl -X POST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"content":"Test message from NCbotwebhooks","username":"CI Bot"}' \ -d '{"content":"Test message from nc_bot_webhooks","username":"CI Bot"}' \
https://your-nextcloud-server/apps/nc_bot_webhooks/discord-webhook/<room-token>/<auth-token> https://your-nextcloud-server/apps/nc_bot_webhooks/discord-webhook/<room-token>/<auth-token>
``` ```
+191 -17
View File
@@ -7,7 +7,9 @@ Accepts Discord webhook-compatible JSON payloads (embeds, fields, images) and Ap
## Table of Contents ## Table of Contents
- [Installation](#installation) - [Installation](#installation)
- [Manual Deployment](#manual-deployment) - [Initial installation using git](#initial-installation-using-git)
- [Updating using git](#updating-using-git)
- [Local update](#local-update)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Webhook URLs](#webhook-urls) - [Webhook URLs](#webhook-urls)
- [Payload Formats](#payload-formats) - [Payload Formats](#payload-formats)
@@ -18,6 +20,7 @@ Accepts Discord webhook-compatible JSON payloads (embeds, fields, images) and Ap
- [Security](#security) - [Security](#security)
- [Debug Endpoint](#debug-endpoint) - [Debug Endpoint](#debug-endpoint)
- [Logging](#logging) - [Logging](#logging)
- [Debugging](#debugging)
- [Integrations](#integrations) - [Integrations](#integrations)
- [Home Assistant](#home-assistant) - [Home Assistant](#home-assistant)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
@@ -26,18 +29,18 @@ Accepts Discord webhook-compatible JSON payloads (embeds, fields, images) and Ap
## Installation ## Installation
### 1. Deploy the app ### Initial installation using git
```bash ```bash
cd /path/to/nextcloud/apps cd /path/to/nextcloud/custom_apps
cp -r /path/to/nc_bot_webhooks . git clone https://github.com/Mr-Newlove/nc_bot_webhooks.git
php occ app:enable 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 ### 2. Create the bot user
Create a user named `talk-bot` with the display name "Webhook Bot". This can be done via the `occ` CLI or the admin user manager GUI.
```bash ```bash
php occ user:add --password-from-env --display-name="Webhook Bot" talk-bot php occ user:add --password-from-env --display-name="Webhook Bot" talk-bot
``` ```
@@ -55,12 +58,12 @@ The bot must have admin privileges to list all Talk rooms. Grant admin access:
1. Log in to Nextcloud as **admin** (or set a password as admin, then log in as talk-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** 2. Go to **Settings → talk-bot → Devices & sessions**
3. Click **Add device** and give it a name (e.g., "NCbotwebhooks") 3. Click **Add device** and give it a name (e.g., "nc_bot_webhooks")
4. Copy the generated device password — you'll need it in the next step 4. Copy the generated device password — you'll need it in the next step
### 5. Configure the app ### 5. Configure the app
Go to **Settings → Admin → NCbotwebhooks**: Go to **Settings → Admin → nc_bot_webhooks**:
1. **Bot App Password** — paste the app password from step 4 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") 2. **Default Sender Name** — set the name that appears as the message sender (default: "Webhook Bot")
@@ -72,15 +75,27 @@ Go to **Settings → Admin → NCbotwebhooks**:
--- ---
## Manual Deployment ### Updating using git
> **Note:** On a standard TrueNAS Scale Nextcloud Docker install, the app is located at `/var/www/html/custom_apps/nc_bot_webhooks/`. Adjust the paths below to match your deployment.
```bash
cd /var/www/html/custom_apps/nc_bot_webhooks
git pull
cd /var/www/html
php occ app:disable nc_bot_webhooks
php occ app:enable nc_bot_webhooks
php occ config:app:delete nc_bot_webhooks routes 2>/dev/null || true
php occ maintenance:repair
```
---
### Local update
> **Note:** The paths below are specific to a TrueNAS Scale Nextcloud Docker install. Adjust them to match your deployment. > **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. This workflow is for when you keep your local development copy synced to your server via the Nextcloud desktop client. Edit files locally, then deploy with the script below.
**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 ```bash
cd /var/www/html cd /var/www/html
@@ -90,7 +105,7 @@ rm -rf /var/www/html/custom_apps/nc_bot_webhooks/*
mkdir -p /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. # 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/ cp -r "/var/www/html/data/<username>/files/<Path on your nextcloud sync>/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 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 chmod -R u+rX,go+rX /var/www/html/custom_apps/nc_bot_webhooks
@@ -99,7 +114,6 @@ cd /var/www/html
php occ app:enable nc_bot_webhooks php occ app:enable nc_bot_webhooks
php occ config:app:delete nc_bot_webhooks routes 2>/dev/null || true php occ config:app:delete nc_bot_webhooks routes 2>/dev/null || true
php occ maintenance:repair php occ maintenance:repair
clear
``` ```
> **Note:** The path `"/var/www/html/data/<username>/files/<Path on your nextcloud sync>/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 `<Path on your nextcloud sync>/nc_bot_webhooks/` to match the directory you are syncing to. > **Note:** The path `"/var/www/html/data/<username>/files/<Path on your nextcloud sync>/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 `<Path on your nextcloud sync>/nc_bot_webhooks/` to match the directory you are syncing to.
@@ -354,7 +368,167 @@ Responses include a `X-Webhook-Status` header:
--- ---
## Troubleshooting ## Debugging
Enable debug logging by setting the log level to `0` (debug) in your Nextcloud `config.php`:
```php
'loglevel' => 0,
```
Or via `occ`:
```bash
php occ config:app:set --value=0 core log_level
```
The default log file on a TrueNAS Scale Nextcloud Docker install is `/var/www/html/data/nextcloud.log`. Adjust the path if your data directory is elsewhere.
### Diagnostic grep examples
Each section below covers a common symptom, what to grep for, and what to look for.
---
#### Webhook endpoint is being hit
Confirm the webhook URL is receiving traffic:
```bash
grep 'nc_bot_webhooks: receiveApprise raw request' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: webhook processed successfully' /var/www/html/data/nextcloud.log
```
**What to look for:** A `receiveApprise raw request` entry followed by a `webhook processed successfully` entry. If neither appears, the webhook isn't reaching the server (check your Discord/Apprise webhook URL, network routing, or reverse proxy).
---
#### Invalid auth token
A webhook is being rejected with `401 unauthorized`:
```bash
grep 'nc_bot_webhooks: invalid auth token for room' /var/www/html/data/nextcloud.log
```
**What to look for:** The room token in the log entry. Compare it against the tokens shown in **Settings → Admin → nc_bot_webhooks**. If the token in the webhook URL doesn't match any configured token for that room, the webhook will be rejected. Regenerate the token in the admin settings and update your external service's webhook URL.
---
#### Invalid JSON / malformed payload
The webhook is being rejected with `400 bad_request`:
```bash
grep 'nc_bot_webhooks: invalid JSON from webhook' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: invalid payload from apprise webhook' /var/www/html/data/nextcloud.log
```
**What to look for:** The parsed payload snippet in the log entry. Verify the JSON structure matches the [Discord Webhook Format](#discord-webhook-format) or [Apprise Format](#apprise-format) documented above.
---
#### Message not appearing in Talk
The webhook succeeded but the message didn't show up in the Talk room:
```bash
grep 'nc_bot_webhooks: failed to post.*message to Talk' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: webhook processed successfully' /var/www/html/data/nextcloud.log
```
**What to look for:**
- If you see `failed to post.*message to Talk`, the app couldn't reach the Talk Chat API. Check that the bot user has an app password configured and that the bot is a participant in the target room.
- If you see `webhook processed successfully` but no error, the message was posted but Talk may have silently dropped it. Verify the bot user is listed as a participant in the Talk room.
---
#### Image download/upload fails
Images from webhooks aren't appearing inline:
```bash
grep 'nc_bot_webhooks: Failed to download image' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: Failed to upload image' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: uploadImage — bot user not found' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: image processing failed' /var/www/html/data/nextcloud.log
```
**What to look for:**
- `Failed to download image` — the source URL is unreachable, blocked by local address filtering, or returned an error. Verify the image URL is publicly accessible.
- `Failed to upload image` — check the error message for details (quota exceeded, filesystem error, etc.).
- `uploadImage — bot user not found` — the `talk-bot` user doesn't exist. Re-run Step 2 of installation.
- `image processing failed` — image download succeeded but upload failed. Check the bot user's storage quota and permissions.
---
#### Bot user not found
Errors referencing the `talk-bot` user:
```bash
grep 'nc_bot_webhooks: uploadImage — bot user not found' /var/www/html/data/nextcloud.log
```
**What to look for:** This confirms the `talk-bot` user doesn't exist in Nextcloud. Create it via `php occ user:add --password-from-env talk-bot` (Step 2 of installation).
---
#### Config save fails
Admin settings not saving:
```bash
grep 'nc_bot_webhooks: saveConfig failed' /var/www/html/data/nextcloud.log
```
**What to look for:** The error message from the crypto layer. Common causes include an invalid bot password (contains characters that break encryption) or a misconfigured Nextcloud crypto backend.
---
#### Room listing empty
The "Fetch Rooms" button returns no rooms:
```bash
grep 'nc_bot_webhooks: getAvailableTalkRooms' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: found.*rooms' /var/www/html/data/nextcloud.log
grep 'nc_bot_webhooks: room listing exception' /var/www/html/data/nextcloud.log
```
**What to look for:**
- `found 0 rooms` — the bot user may not have admin privileges. Re-grant admin access (Step 3 of installation).
- `room listing exception` — check the exception details for the root cause (Talk app not installed, database error, etc.).
---
#### Image cleanup cron not running
Images are not being purged after the retention period:
```bash
grep 'nc_bot_webhooks: ImageCleanup' /var/www/html/data/nextcloud.log
```
**What to look for:** A log entry each time the cron job runs. If nothing appears, ensure system cron is configured to run `php occ cron.php` (not the "Web" cron setting in Admin settings). Check the Nextcloud system cron status:
```bash
php occ system:cron:set --method=cron
```
---
#### Payload mapping inspection
See exactly how the webhook payload was mapped to Talk format (verbose):
```bash
grep 'nc_bot_webhooks: DEBUG mapped' /var/www/html/data/nextcloud.log
```
**What to look for:** The full mapped payload JSON. This is useful when a message appears incorrectly in Talk — compare the mapped payload against the expected Talk Chat API format.
> **Note:** Debug logging is verbose and impacts performance. After troubleshooting, restore your log level to `2` (warning) or higher.
| Problem | Solution | | Problem | Solution |
|---|---| |---|---|
+19 -19
View File
@@ -1,4 +1,4 @@
# NCbotwebhooks — Maintenance Reference # nc_bot_webhooks — Maintenance Reference
Complete maintenance reference for the `nc_bot_webhooks` Nextcloud app. Covers every file, API interaction, data flow, design decision, and operational detail. Complete maintenance reference for the `nc_bot_webhooks` Nextcloud app. Covers every file, API interaction, data flow, design decision, and operational detail.
@@ -61,7 +61,7 @@ nc_bot_webhooks/
**Key paths at runtime:** **Key paths at runtime:**
- Web URL base: `/apps/nc_bot_webhooks/` - Web URL base: `/apps/nc_bot_webhooks/`
- Admin settings path: `Settings → Admin → NCbotwebhooks` - Admin settings path: `Settings → Admin → nc_bot_webhooks`
- Bot user: `talk-bot` (must exist and be admin) - Bot user: `talk-bot` (must exist and be admin)
- Image storage: `/nc_bot_webhooks-images/<room-token>/` under `talk-bot`'s personal files - Image storage: `/nc_bot_webhooks-images/<room-token>/` under `talk-bot`'s personal files
@@ -430,7 +430,7 @@ Admin settings form handler. Implements `ISettings`.
### lib/NavigationProvider.php ### lib/NavigationProvider.php
Implements `INavigationProvider`. Adds "NCbotwebhooks" entry to admin settings navigation. Implements `INavigationProvider`. Adds "nc_bot_webhooks" entry to admin settings navigation.
**Admin-only:** returns empty array if user is not admin. **Admin-only:** returns empty array if user is not admin.
@@ -440,7 +440,7 @@ Implements `INavigationProvider`. Adds "NCbotwebhooks" entry to admin settings n
'id' => 'nc_bot_webhooks', 'id' => 'nc_bot_webhooks',
'app_id' => 'nc_bot_webhooks', 'app_id' => 'nc_bot_webhooks',
'type' => 'settings', 'type' => 'settings',
'name' => 'NCbotwebhooks', 'name' => 'nc_bot_webhooks',
'href' => linkToRoute('settings.AdminSettings#index'), 'href' => linkToRoute('settings.AdminSettings#index'),
'icon' => imagePath('nc_bot_webhooks', 'app.svg'), 'icon' => imagePath('nc_bot_webhooks', 'app.svg'),
'order' => 0, 'order' => 0,
@@ -573,7 +573,7 @@ Route definitions. All webhook routes use `{roomToken}` and `{token}` path param
```xml ```xml
<id>nc_bot_webhooks</id> <id>nc_bot_webhooks</id>
<name>NCbotwebhooks</name> <name>nc_bot_webhooks</name>
<summary>Discord webhook bridge for Nextcloud Talk with image support</summary> <summary>Discord webhook bridge for Nextcloud Talk with image support</summary>
<description>Accepts Discord webhook-style JSON payloads and posts them into Nextcloud Talk rooms, preserving image attachments.</description> <description>Accepts Discord webhook-style JSON payloads and posts them into Nextcloud Talk rooms, preserving image attachments.</description>
<version>1.1.0</version> <version>1.1.0</version>
@@ -974,12 +974,12 @@ cp -r nc_bot_webhooks /path/to/nextcloud/apps/
# 5. Enable app # 5. Enable app
php occ app:enable nc_bot_webhooks php occ app:enable nc_bot_webhooks
# 6. Configure via web UI (Settings → Admin → NCbotwebhooks) # 6. Configure via web UI (Settings → Admin → nc_bot_webhooks)
``` ```
### Configuring Rooms ### Configuring Rooms
1. Go to Settings → Admin → NCbotwebhooks 1. Go to Settings → Admin → nc_bot_webhooks
2. Enter bot app password 2. Enter bot app password
3. Click "Fetch Rooms" to list available Talk rooms 3. Click "Fetch Rooms" to list available Talk rooms
4. Check rooms to enable webhooks for 4. Check rooms to enable webhooks for
@@ -1019,7 +1019,7 @@ UPDATE oc_appconfig SET appid = 'nc_bot_webhooks' WHERE appid = 'ncdiscordhook';
php occ background:cron php occ background:cron
``` ```
**Adjust retention:** Settings → Admin → NCbotwebhooks → Image Retention (1-365 days) **Adjust retention:** Settings → Admin → nc_bot_webhooks → Image Retention (1-365 days)
**Check images directory:** **Check images directory:**
```bash ```bash
@@ -1074,21 +1074,21 @@ These public methods on `TalkService` are available for programmatic debugging:
### Log Tags ### Log Tags
All log entries use: All log entries use:
- Logger tag: `'NCbotwebhooks:'` - Logger tag: `'nc_bot_webhooks:'`
- App tag: `'app' => 'nc_bot_webhooks'` - App tag: `'app' => 'nc_bot_webhooks'`
Example log entries: Example log entries:
``` ```
NCbotwebhooks: webhook processed successfully nc_bot_webhooks: webhook processed successfully
NCbotwebhooks: failed to post webhook message to Talk nc_bot_webhooks: failed to post webhook message to Talk
NCbotwebhooks: message posted to room ABC123 nc_bot_webhooks: message posted to room ABC123
NCbotwebhooks: bot password not configured nc_bot_webhooks: bot password not configured
NCbotwebhooks: bot not enabled for room nc_bot_webhooks: bot not enabled for room
NCbotwebhooks: base URL not configured nc_bot_webhooks: base URL not configured
NCbotwebhooks: getAvailableTalkRooms nc_bot_webhooks: getAvailableTalkRooms
NCbotwebhooks: found 5 rooms nc_bot_webhooks: found 5 rooms
NCbotwebhooks: purgeOldImages nc_bot_webhooks: purgeOldImages
NCbotwebhooks: purged 3 old image files nc_bot_webhooks: purged 3 old image files
``` ```
--- ---
+1 -1
View File
@@ -2,7 +2,7 @@
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>nc_bot_webhooks</id> <id>nc_bot_webhooks</id>
<name>NCbotwebhooks</name> <name>nc_bot_webhooks</name>
<summary>Discord webhook bridge for Nextcloud Talk with image support</summary> <summary>Discord webhook bridge for Nextcloud Talk with image support</summary>
<description>Accepts Discord webhook-style JSON payloads and posts them into Nextcloud Talk rooms, preserving image attachments. Each room gets its own webhook URL with an auth token for security.</description> <description>Accepts Discord webhook-style JSON payloads and posts them into Nextcloud Talk rooms, preserving image attachments. Each room gets its own webhook URL with an auth token for security.</description>
<version>1.1.0</version> <version>1.1.0</version>
+3 -3
View File
@@ -1,6 +1,6 @@
# Architecture # Architecture
Overview of how NCbotwebhooks functions, its components, data flow, design decisions, and security model. Overview of how nc_bot_webhooks functions, its components, data flow, design decisions, and security model.
## Table of Contents ## Table of Contents
@@ -17,7 +17,7 @@ Overview of how NCbotwebhooks functions, its components, data flow, design decis
## High-Level Overview ## High-Level Overview
NCbotwebhooks is a Nextcloud app that acts as a webhook bridge. External services (CI/CD pipelines, monitoring tools, Apprise notification gateways) send webhook payloads to the app, which validates the request, maps the payload to Nextcloud Talk's Chat API format, and posts the message as the `talk-bot` user into the target Talk room. nc_bot_webhooks is a Nextcloud app that acts as a webhook bridge. External services (CI/CD pipelines, monitoring tools, Apprise notification gateways) send webhook payloads to the app, which validates the request, maps the payload to Nextcloud Talk's Chat API format, and posts the message as the `talk-bot` user into the target Talk room.
``` ```
External Service ──► /discord-webhook/{room}/{token} ──┐ External Service ──► /discord-webhook/{room}/{token} ──┐
@@ -66,7 +66,7 @@ Each Talk room gets its own webhook URL with a unique auth token. Multiple token
| Class | Responsibility | | Class | Responsibility |
|---|---| |---|---|
| `Admin` | ISettings implementation — loads template, injects config, declares CSS/JS assets | | `Admin` | ISettings implementation — loads template, injects config, declares CSS/JS assets |
| `NavigationProvider` | INavigationProvider — adds "NCbotwebhooks" entry to admin settings navigation | | `NavigationProvider` | INavigationProvider — adds "nc_bot_webhooks" entry to admin settings navigation |
### Frontend ### Frontend
+16 -16
View File
@@ -54,7 +54,7 @@ class WebhookController extends Controller {
public function receive(string $roomToken, string $token): DataResponse { public function receive(string $roomToken, string $token): DataResponse {
// Validate auth token // Validate auth token
if (!$this->talkService->validateAuthToken($roomToken, $token)) { if (!$this->talkService->validateAuthToken($roomToken, $token)) {
$this->logger->warning('NCbotwebhooks: invalid auth token for room', [ $this->logger->warning('nc_bot_webhooks: invalid auth token for room', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -76,7 +76,7 @@ class WebhookController extends Controller {
} }
} }
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) { if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
$this->logger->warning('NCbotwebhooks: invalid JSON from webhook', [ $this->logger->warning('nc_bot_webhooks: invalid JSON from webhook', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
'body_length' => strlen($body), 'body_length' => strlen($body),
@@ -168,7 +168,7 @@ class WebhookController extends Controller {
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$imageError = $e->getMessage(); $imageError = $e->getMessage();
$this->logger->error('NCbotwebhooks: image processing failed, continuing without images', [ $this->logger->error('nc_bot_webhooks: image processing failed, continuing without images', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
@@ -187,7 +187,7 @@ class WebhookController extends Controller {
for ($i = 0; $i < count($fileNames); $i++) { for ($i = 0; $i < count($fileNames); $i++) {
if ($fileErrors[$i] !== UPLOAD_ERR_OK) { if ($fileErrors[$i] !== UPLOAD_ERR_OK) {
$this->logger->warning('NCbotwebhooks: file upload error for attachment ' . $i, [ $this->logger->warning('nc_bot_webhooks: file upload error for attachment ' . $i, [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'error_code' => $fileErrors[$i], 'error_code' => $fileErrors[$i],
]); ]);
@@ -199,7 +199,7 @@ class WebhookController extends Controller {
$fileData = file_get_contents($fileTmps[$i]); $fileData = file_get_contents($fileTmps[$i]);
if ($fileData === false || strlen($fileData) === 0) { if ($fileData === false || strlen($fileData) === 0) {
$this->logger->warning('NCbotwebhooks: empty or unreadable file attachment ' . $i, [ $this->logger->warning('nc_bot_webhooks: empty or unreadable file attachment ' . $i, [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'tmp_name' => $fileTmps[$i], 'tmp_name' => $fileTmps[$i],
]); ]);
@@ -220,7 +220,7 @@ class WebhookController extends Controller {
$success = $this->talkService->postToRoom($roomToken, $message, $senderName, $richObjects); $success = $this->talkService->postToRoom($roomToken, $message, $senderName, $richObjects);
if ($success) { if ($success) {
$this->logger->info('NCbotwebhooks: webhook processed successfully', [ $this->logger->info('nc_bot_webhooks: webhook processed successfully', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -231,7 +231,7 @@ class WebhookController extends Controller {
); );
} }
$this->logger->error('NCbotwebhooks: failed to post webhook message to Talk', [ $this->logger->error('nc_bot_webhooks: failed to post webhook message to Talk', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -262,7 +262,7 @@ class WebhookController extends Controller {
public function receiveApprise(string $roomToken, string $token): DataResponse { public function receiveApprise(string $roomToken, string $token): DataResponse {
// Validate auth token // Validate auth token
if (!$this->talkService->validateAuthToken($roomToken, $token)) { if (!$this->talkService->validateAuthToken($roomToken, $token)) {
$this->logger->warning('NCbotwebhooks: invalid auth token for room', [ $this->logger->warning('nc_bot_webhooks: invalid auth token for room', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -292,7 +292,7 @@ class WebhookController extends Controller {
$preview = substr($body, 0, 2000); $preview = substr($body, 0, 2000);
// Sanitize binary data from preview to avoid corrupting logs // Sanitize binary data from preview to avoid corrupting logs
$preview = preg_replace('/[^\x20-\x7E\x0A\x0D\x09]/', '.', $preview); $preview = preg_replace('/[^\x20-\x7E\x0A\x0D\x09]/', '.', $preview);
$this->logger->info('NCbotwebhooks: receiveApprise raw request', [ $this->logger->info('nc_bot_webhooks: receiveApprise raw request', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
'content_type' => $contentType, 'content_type' => $contentType,
@@ -356,7 +356,7 @@ class WebhookController extends Controller {
} }
if (empty($data) || !is_array($data)) { if (empty($data) || !is_array($data)) {
$this->logger->warning('NCbotwebhooks: invalid payload from apprise webhook', [ $this->logger->warning('nc_bot_webhooks: invalid payload from apprise webhook', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
'content_type' => $contentType, 'content_type' => $contentType,
@@ -432,7 +432,7 @@ class WebhookController extends Controller {
$mapped = $this->talkService->mapApprisePayload($data, $roomToken); $mapped = $this->talkService->mapApprisePayload($data, $roomToken);
} catch (\Exception $e) { } catch (\Exception $e) {
$imageError = $e->getMessage(); $imageError = $e->getMessage();
$this->logger->error('NCbotwebhooks: image processing failed, continuing without images', [ $this->logger->error('nc_bot_webhooks: image processing failed, continuing without images', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
@@ -450,7 +450,7 @@ class WebhookController extends Controller {
} }
// DEBUG: log mapped values // DEBUG: log mapped values
$this->logger->info('NCbotwebhooks: DEBUG mapped', [ $this->logger->info('nc_bot_webhooks: DEBUG mapped', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'message' => $mapped['message'] ?? 'EMPTY', 'message' => $mapped['message'] ?? 'EMPTY',
'senderName' => $mapped['senderName'] ?? 'EMPTY', 'senderName' => $mapped['senderName'] ?? 'EMPTY',
@@ -480,7 +480,7 @@ class WebhookController extends Controller {
$success = $this->talkService->postToRoom($roomToken, $message, $senderName, $richObjects); $success = $this->talkService->postToRoom($roomToken, $message, $senderName, $richObjects);
if ($success) { if ($success) {
$this->logger->info('NCbotwebhooks: apprise webhook processed successfully', [ $this->logger->info('nc_bot_webhooks: apprise webhook processed successfully', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -491,7 +491,7 @@ class WebhookController extends Controller {
); );
} }
$this->logger->error('NCbotwebhooks: failed to post apprise message to Talk', [ $this->logger->error('nc_bot_webhooks: failed to post apprise message to Talk', [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -567,7 +567,7 @@ class WebhookController extends Controller {
try { try {
$this->talkService->saveConfig($config); $this->talkService->saveConfig($config);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('NCbotwebhooks: saveConfig failed: ' . $e->getMessage(), [ $this->logger->error('nc_bot_webhooks: saveConfig failed: ' . $e->getMessage(), [
'app' => 'nc_bot_webhooks', 'app' => 'nc_bot_webhooks',
'exception' => (string)$e, 'exception' => (string)$e,
]); ]);
@@ -1118,7 +1118,7 @@ class WebhookController extends Controller {
return new DataResponse($result); return new DataResponse($result);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('NCbotwebhooks getRooms failed: ' . $e->getMessage(), ['app' => 'nc_bot_webhooks', 'exception' => (string)$e]); $this->logger->error('nc_bot_webhooks getRooms failed: ' . $e->getMessage(), ['app' => 'nc_bot_webhooks', 'exception' => (string)$e]);
return new DataResponse( return new DataResponse(
['error' => 'Server error: ' . $e->getMessage()], ['error' => 'Server error: ' . $e->getMessage()],
Http::STATUS_INTERNAL_SERVER_ERROR, Http::STATUS_INTERNAL_SERVER_ERROR,
+1 -1
View File
@@ -51,7 +51,7 @@ class ImageCleanup implements IJob {
if ($count > 0) { if ($count > 0) {
$this->logger->info( $this->logger->info(
'NCbotwebhooks: purged ' . $count . ' old image files', 'nc_bot_webhooks: purged ' . $count . ' old image files',
['app' => 'nc_bot_webhooks'], ['app' => 'nc_bot_webhooks'],
); );
} }
+1 -1
View File
@@ -27,7 +27,7 @@ class NavigationProvider implements INavigationProvider {
'id' => 'nc_bot_webhooks', 'id' => 'nc_bot_webhooks',
'app_id' => 'nc_bot_webhooks', 'app_id' => 'nc_bot_webhooks',
'type' => 'settings', 'type' => 'settings',
'name' => $this->l10n->t('NCbotwebhooks'), 'name' => $this->l10n->t('nc_bot_webhooks'),
'href' => $this->urlGenerator->linkToRoute('settings.AdminSettings#index'), 'href' => $this->urlGenerator->linkToRoute('settings.AdminSettings#index'),
'icon' => $this->urlGenerator->imagePath('nc_bot_webhooks', 'app.svg'), 'icon' => $this->urlGenerator->imagePath('nc_bot_webhooks', 'app.svg'),
'order' => 0, 'order' => 0,
+82 -82
View File
@@ -295,7 +295,7 @@ class TalkService {
if ($roomTable === null) { if ($roomTable === null) {
$sysPrefix = $this->config->getSystemValueString('dbtableprefix', ''); $sysPrefix = $this->config->getSystemValueString('dbtableprefix', '');
$talkPrefix = $this->config->getAppValue('spreed', 'databaseprefix', $sysPrefix); $talkPrefix = $this->config->getAppValue('spreed', 'databaseprefix', $sysPrefix);
$this->logger->warning('NCbotwebhooks: Talk tables not found', [ $this->logger->warning('nc_bot_webhooks: Talk tables not found', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'sysPrefix' => $sysPrefix, 'sysPrefix' => $sysPrefix,
'talkPrefix' => $talkPrefix, 'talkPrefix' => $talkPrefix,
@@ -317,7 +317,7 @@ class TalkService {
AND name NOT LIKE \'["%\''; AND name NOT LIKE \'["%\'';
$result = $this->db->executeQuery($sql); $result = $this->db->executeQuery($sql);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: room name query failed, using token fallback', [ $this->logger->warning('nc_bot_webhooks: room name query failed, using token fallback', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
@@ -331,7 +331,7 @@ class TalkService {
$result = $this->db->executeQuery($sql); $result = $this->db->executeQuery($sql);
} }
$this->logger->info('NCbotwebhooks: getAvailableTalkRooms', [ $this->logger->info('nc_bot_webhooks: getAvailableTalkRooms', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_table' => $roomTable, 'room_table' => $roomTable,
]); ]);
@@ -343,14 +343,14 @@ class TalkService {
} }
$result->closeCursor(); $result->closeCursor();
$this->logger->info('NCbotwebhooks: found ' . count($rooms) . ' rooms', [ $this->logger->info('nc_bot_webhooks: found ' . count($rooms) . ' rooms', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'rooms' => array_keys($rooms), 'rooms' => array_keys($rooms),
]); ]);
return $rooms; return $rooms;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('NCbotwebhooks: room listing exception', [ $this->logger->error('nc_bot_webhooks: room listing exception', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
'file' => $e->getFile(), 'file' => $e->getFile(),
@@ -447,7 +447,7 @@ class TalkService {
} }
$result->closeCursor(); $result->closeCursor();
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: information_schema query failed', [ $this->logger->warning('nc_bot_webhooks: information_schema query failed', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
@@ -671,7 +671,7 @@ class TalkService {
* Returns: { message, senderName, richObjects } * Returns: { message, senderName, richObjects }
*/ */
public function mapApprisePayload(array $data, string $roomToken = ''): array { public function mapApprisePayload(array $data, string $roomToken = ''): array {
$this->logger->info('NCbotwebhooks: mapApprisePayload input', [ $this->logger->info('nc_bot_webhooks: mapApprisePayload input', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'type' => $data['type'] ?? 'none', 'type' => $data['type'] ?? 'none',
'has_attachments' => !empty($data['attachments']), 'has_attachments' => !empty($data['attachments']),
@@ -782,7 +782,7 @@ class TalkService {
} }
$filePath = $attachment['path'] ?? ''; $filePath = $attachment['path'] ?? '';
$this->logger->info('NCbotwebhooks: attachment path', [ $this->logger->info('nc_bot_webhooks: attachment path', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => $filePath, 'path' => $filePath,
'url_key' => $attachment['url'] ?? 'none', 'url_key' => $attachment['url'] ?? 'none',
@@ -918,14 +918,14 @@ class TalkService {
$body = $response->getBody(); $body = $response->getBody();
$statusCode = $response->getStatusCode(); $statusCode = $response->getStatusCode();
$this->logger->info('NCbotwebhooks: image download response', [ $this->logger->info('nc_bot_webhooks: image download response', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'url' => $url, 'url' => $url,
'status_code' => $statusCode, 'status_code' => $statusCode,
'body_length' => strlen($body), 'body_length' => strlen($body),
]); ]);
if (strlen($body) === 0) { if (strlen($body) === 0) {
$this->logger->warning('NCbotwebhooks: image download returned empty body', [ $this->logger->warning('nc_bot_webhooks: image download returned empty body', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'url' => $url, 'url' => $url,
'status_code' => $statusCode, 'status_code' => $statusCode,
@@ -935,7 +935,7 @@ class TalkService {
return ['data' => $body, 'mimeType' => $mimeType]; return ['data' => $body, 'mimeType' => $mimeType];
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: Failed to download image: ' . $url . ' — ' . $e->getMessage(), ['app' => self::APP_ID]); $this->logger->warning('nc_bot_webhooks: Failed to download image: ' . $url . ' — ' . $e->getMessage(), ['app' => self::APP_ID]);
return null; return null;
} }
} }
@@ -948,12 +948,12 @@ class TalkService {
public function uploadImage(string $roomToken, string $filename, string $data, string $mimeType): ?string { public function uploadImage(string $roomToken, string $filename, string $data, string $mimeType): ?string {
$bot = $this->userManager->get('talk-bot'); $bot = $this->userManager->get('talk-bot');
if (!$bot) { if (!$bot) {
$this->logger->error('NCbotwebhooks: uploadImage — bot user not found', ['app' => self::APP_ID]); $this->logger->error('nc_bot_webhooks: uploadImage — bot user not found', ['app' => self::APP_ID]);
return null; return null;
} }
try { try {
$this->logger->info('NCbotwebhooks: uploadImage start', [ $this->logger->info('nc_bot_webhooks: uploadImage start', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'roomToken' => $roomToken, 'roomToken' => $roomToken,
'filename' => $filename, 'filename' => $filename,
@@ -961,7 +961,7 @@ class TalkService {
'bot_uid' => $bot->getUID(), 'bot_uid' => $bot->getUID(),
]); ]);
$userFolder = $this->rootFolder->getUserFolder($bot->getUID()); $userFolder = $this->rootFolder->getUserFolder($bot->getUID());
$this->logger->info('NCbotwebhooks: getUserFolder succeeded', [ $this->logger->info('nc_bot_webhooks: getUserFolder succeeded', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'userFolder_type' => get_class($userFolder), 'userFolder_type' => get_class($userFolder),
]); ]);
@@ -970,19 +970,19 @@ class TalkService {
$imagesDir = null; $imagesDir = null;
try { try {
$imagesDir = $userFolder->get(self::IMAGES_DIR); $imagesDir = $userFolder->get(self::IMAGES_DIR);
$this->logger->info('NCbotwebhooks: images dir already exists', [ $this->logger->info('nc_bot_webhooks: images dir already exists', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => self::IMAGES_DIR, 'path' => self::IMAGES_DIR,
]); ]);
} catch (\OCP\Files\NotFoundException $e) { } catch (\OCP\Files\NotFoundException $e) {
$this->logger->info('NCbotwebhooks: creating images dir', [ $this->logger->info('nc_bot_webhooks: creating images dir', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => self::IMAGES_DIR, 'path' => self::IMAGES_DIR,
]); ]);
$userFolder->newFolder(self::IMAGES_DIR); $userFolder->newFolder(self::IMAGES_DIR);
$imagesDir = $userFolder->get(self::IMAGES_DIR); $imagesDir = $userFolder->get(self::IMAGES_DIR);
} catch (\Error $e) { } catch (\Error $e) {
$this->logger->error('NCbotwebhooks: get threw Error on images dir: ' . $e->getMessage(), [ $this->logger->error('nc_bot_webhooks: get threw Error on images dir: ' . $e->getMessage(), [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => self::IMAGES_DIR, 'path' => self::IMAGES_DIR,
'exception' => get_class($e), 'exception' => get_class($e),
@@ -994,12 +994,12 @@ class TalkService {
$roomDir = null; $roomDir = null;
try { try {
$roomDir = $imagesDir->get($roomToken); $roomDir = $imagesDir->get($roomToken);
$this->logger->info('NCbotwebhooks: room dir already exists', [ $this->logger->info('nc_bot_webhooks: room dir already exists', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => $roomToken, 'path' => $roomToken,
]); ]);
} catch (\OCP\Files\NotFoundException $e) { } catch (\OCP\Files\NotFoundException $e) {
$this->logger->info('NCbotwebhooks: creating room dir', [ $this->logger->info('nc_bot_webhooks: creating room dir', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'parent' => self::IMAGES_DIR, 'parent' => self::IMAGES_DIR,
'path' => $roomToken, 'path' => $roomToken,
@@ -1007,7 +1007,7 @@ class TalkService {
$imagesDir->newFolder($roomToken); $imagesDir->newFolder($roomToken);
$roomDir = $imagesDir->get($roomToken); $roomDir = $imagesDir->get($roomToken);
} catch (\Error $e) { } catch (\Error $e) {
$this->logger->error('NCbotwebhooks: get threw Error on room dir: ' . $e->getMessage(), [ $this->logger->error('nc_bot_webhooks: get threw Error on room dir: ' . $e->getMessage(), [
'app' => self::APP_ID, 'app' => self::APP_ID,
'parent' => self::IMAGES_DIR, 'parent' => self::IMAGES_DIR,
'path' => $roomToken, 'path' => $roomToken,
@@ -1023,13 +1023,13 @@ class TalkService {
$timestamp = date('Y-m-d H.i.s') . '.' . sprintf('%06d', (int)(microtime(true) * 1000) % 1000000); $timestamp = date('Y-m-d H.i.s') . '.' . sprintf('%06d', (int)(microtime(true) * 1000) % 1000000);
$relativePath = 'talk-bot ' . $timestamp . '_' . $base . '.' . $ext; $relativePath = 'talk-bot ' . $timestamp . '_' . $base . '.' . $ext;
$this->logger->info('NCbotwebhooks: writing file', [ $this->logger->info('nc_bot_webhooks: writing file', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'original' => $safeFilename, 'original' => $safeFilename,
'path' => $relativePath, 'path' => $relativePath,
]); ]);
$roomDir->newFile($relativePath, $data); $roomDir->newFile($relativePath, $data);
$this->logger->info('NCbotwebhooks: file written successfully', [ $this->logger->info('nc_bot_webhooks: file written successfully', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath, 'path' => self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath,
]); ]);
@@ -1048,21 +1048,21 @@ class TalkService {
$stmtFc->execute([self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath]); $stmtFc->execute([self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath]);
$fcRow = $stmtFc->fetchAll(\PDO::FETCH_ASSOC); $fcRow = $stmtFc->fetchAll(\PDO::FETCH_ASSOC);
$stmtFc->closeCursor(); $stmtFc->closeCursor();
$this->logger->info('NCbotwebhooks: uploadImage filecache check', [ $this->logger->info('nc_bot_webhooks: uploadImage filecache check', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'found' => count($fcRow) > 0, 'found' => count($fcRow) > 0,
'entries' => $fcRow, 'entries' => $fcRow,
]); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('NCbotwebhooks: uploadImage filecache check failed: ' . $e->getMessage(), ['app' => self::APP_ID]); $this->logger->warning('nc_bot_webhooks: uploadImage filecache check failed: ' . $e->getMessage(), ['app' => self::APP_ID]);
} }
return self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath; return self::IMAGES_DIR . '/' . $roomToken . '/' . $relativePath;
} catch (\Error $e) { } catch (\Error $e) {
$this->logger->error('NCbotwebhooks: uploadImage Error: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]); $this->logger->error('nc_bot_webhooks: uploadImage Error: ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]);
return null; return null;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('NCbotwebhooks: Failed to upload image: ' . $e->getMessage(), ['app' => self::APP_ID]); $this->logger->error('nc_bot_webhooks: Failed to upload image: ' . $e->getMessage(), ['app' => self::APP_ID]);
return null; return null;
} }
} }
@@ -1080,25 +1080,25 @@ class TalkService {
* Creates a public link share so Talk can resolve the rich object. * Creates a public link share so Talk can resolve the rich object.
*/ */
public function buildRichObject(string $filePath, string $mimeType, string $roomToken): ?array { public function buildRichObject(string $filePath, string $mimeType, string $roomToken): ?array {
$this->logger->info('NCbotwebhooks: buildRichObject ENTER', ['app' => self::APP_ID, 'filePath' => $filePath, 'roomToken' => $roomToken]); $this->logger->info('nc_bot_webhooks: buildRichObject ENTER', ['app' => self::APP_ID, 'filePath' => $filePath, 'roomToken' => $roomToken]);
$bot = $this->userManager->get('talk-bot'); $bot = $this->userManager->get('talk-bot');
if (!$bot) { if (!$bot) {
$this->logger->info('NCbotwebhooks: buildRichObject EXIT noBot', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: buildRichObject EXIT noBot', ['app' => self::APP_ID]);
return null; return null;
} }
try { try {
$safeFilename = basename($filePath); $safeFilename = basename($filePath);
$this->logger->info('NCbotwebhooks: richObject step=basename', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=basename', ['app' => self::APP_ID]);
// filePath is the full filecache path returned by uploadImage (e.g. nc_bot_webhooks-images/abc123/talk-bot 2026-06-13 01.35.19.123456_filename.png). // filePath is the full filecache path returned by uploadImage (e.g. nc_bot_webhooks-images/abc123/talk-bot 2026-06-13 01.35.19.123456_filename.png).
$fileCachePath = $filePath; $fileCachePath = $filePath;
$this->logger->info('NCbotwebhooks: richObject step=buildPath', ['app' => self::APP_ID, 'path' => $fileCachePath]); $this->logger->info('nc_bot_webhooks: richObject step=buildPath', ['app' => self::APP_ID, 'path' => $fileCachePath]);
// Use PDO directly to avoid any DBConnection method that could trigger lazy init. // Use PDO directly to avoid any DBConnection method that could trigger lazy init.
$this->logger->info('NCbotwebhooks: richObject step=getPdo', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=getPdo', ['app' => self::APP_ID]);
$pdo = $this->db->getInner()->getNativeConnection(); $pdo = $this->db->getInner()->getNativeConnection();
$this->logger->info('NCbotwebhooks: richObject step=pdoOk', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=pdoOk', ['app' => self::APP_ID]);
// Get table prefix from system config (safe — no DB abstraction layer). // Get table prefix from system config (safe — no DB abstraction layer).
$prefix = $this->config->getSystemValueString('dbtableprefix', 'oc_'); $prefix = $this->config->getSystemValueString('dbtableprefix', 'oc_');
@@ -1113,7 +1113,7 @@ class TalkService {
$fileCacheTable = $prefix . 'filecache'; $fileCacheTable = $prefix . 'filecache';
$shareTable = $prefix . 'share'; $shareTable = $prefix . 'share';
$this->logger->info('NCbotwebhooks: richObject step=tables', ['app' => self::APP_ID, 'filecache' => $fileCacheTable, 'share' => $shareTable]); $this->logger->info('nc_bot_webhooks: richObject step=tables', ['app' => self::APP_ID, 'filecache' => $fileCacheTable, 'share' => $shareTable]);
// Resolve the bot user's home storage ID from the storages table. // Resolve the bot user's home storage ID from the storages table.
// Storage IDs are instance-specific; hardcoding '1' breaks on many setups. // Storage IDs are instance-specific; hardcoding '1' breaks on many setups.
@@ -1124,29 +1124,29 @@ class TalkService {
$stmt->execute(['home::' . $bot->getUID()]); $stmt->execute(['home::' . $bot->getUID()]);
$storageId = (int)$stmt->fetch(\PDO::FETCH_COLUMN); $storageId = (int)$stmt->fetch(\PDO::FETCH_COLUMN);
$stmt->closeCursor(); $stmt->closeCursor();
$this->logger->info('NCbotwebhooks: richObject step=storageId', ['app' => self::APP_ID, 'storageId' => $storageId]); $this->logger->info('nc_bot_webhooks: richObject step=storageId', ['app' => self::APP_ID, 'storageId' => $storageId]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('NCbotwebhooks: richObject step=storageIdFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]); $this->logger->warning('nc_bot_webhooks: richObject step=storageIdFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]);
} }
$this->logger->info('NCbotwebhooks: richObject step=prepareSelect', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=prepareSelect', ['app' => self::APP_ID]);
$stmt = $pdo->prepare( $stmt = $pdo->prepare(
'SELECT fileid, path, mimetype, permissions, size, storage' 'SELECT fileid, path, mimetype, permissions, size, storage'
. ' FROM "' . $fileCacheTable . '"' . ' FROM "' . $fileCacheTable . '"'
. ' WHERE "path_hash" = ?', . ' WHERE "path_hash" = ?',
); );
$pathHash = md5($fileCachePath); $pathHash = md5($fileCachePath);
$this->logger->info('NCbotwebhooks: richObject step=executeSelect', ['app' => self::APP_ID, 'pathHash' => $pathHash, 'path' => $fileCachePath]); $this->logger->info('nc_bot_webhooks: richObject step=executeSelect', ['app' => self::APP_ID, 'pathHash' => $pathHash, 'path' => $fileCachePath]);
$stmt->execute([$pathHash]); $stmt->execute([$pathHash]);
$this->logger->info('NCbotwebhooks: richObject step=fetch', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=fetch', ['app' => self::APP_ID]);
$row = $stmt->fetch(\PDO::FETCH_ASSOC); $row = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor(); $stmt->closeCursor();
$this->logger->info('NCbotwebhooks: richObject step=rowFetched', ['app' => self::APP_ID, 'found' => $row !== false, 'columns' => array_keys($row ?: []), 'data' => $row]); $this->logger->info('nc_bot_webhooks: richObject step=rowFetched', ['app' => self::APP_ID, 'found' => $row !== false, 'columns' => array_keys($row ?: []), 'data' => $row]);
// Path-based fallback: if path_hash lookup found nothing, try LIKE match on path column. // Path-based fallback: if path_hash lookup found nothing, try LIKE match on path column.
// This catches cases where the filecache entry exists but path_hash didn't match. // This catches cases where the filecache entry exists but path_hash didn't match.
if (!$row) { if (!$row) {
$this->logger->info('NCbotwebhooks: richObject step=pathFallback', ['app' => self::APP_ID, 'path' => $fileCachePath]); $this->logger->info('nc_bot_webhooks: richObject step=pathFallback', ['app' => self::APP_ID, 'path' => $fileCachePath]);
$stmt2 = $pdo->prepare( $stmt2 = $pdo->prepare(
'SELECT fileid, path, mimetype, permissions, size, storage' 'SELECT fileid, path, mimetype, permissions, size, storage'
. ' FROM "' . $fileCacheTable . '"' . ' FROM "' . $fileCacheTable . '"'
@@ -1155,11 +1155,11 @@ class TalkService {
$stmt2->execute([self::IMAGES_DIR . '/%']); $stmt2->execute([self::IMAGES_DIR . '/%']);
$rows = $stmt2->fetchAll(\PDO::FETCH_ASSOC); $rows = $stmt2->fetchAll(\PDO::FETCH_ASSOC);
$stmt2->closeCursor(); $stmt2->closeCursor();
$this->logger->info('NCbotwebhooks: richObject step=pathFallbackRows', ['app' => self::APP_ID, 'count' => count($rows)]); $this->logger->info('nc_bot_webhooks: richObject step=pathFallbackRows', ['app' => self::APP_ID, 'count' => count($rows)]);
foreach ($rows as $r) { foreach ($rows as $r) {
if ($r['path'] === $fileCachePath) { if ($r['path'] === $fileCachePath) {
$row = $r; $row = $r;
$this->logger->info('NCbotwebhooks: richObject step=pathFallbackMatch', ['app' => self::APP_ID, 'fileId' => $r['fileid']]); $this->logger->info('nc_bot_webhooks: richObject step=pathFallbackMatch', ['app' => self::APP_ID, 'fileId' => $r['fileid']]);
break; break;
} }
} }
@@ -1168,7 +1168,7 @@ class TalkService {
if (!$row) { if (!$row) {
// Manual filecache insertion — the file exists on disk but the entry is missing. // Manual filecache insertion — the file exists on disk but the entry is missing.
// This can happen if the scanner didn't run or the entry was deleted. // This can happen if the scanner didn't run or the entry was deleted.
$this->logger->warning('NCbotwebhooks: richObject step=insertFc', ['app' => self::APP_ID, 'path' => $fileCachePath, 'storageId' => $storageId]); $this->logger->warning('nc_bot_webhooks: richObject step=insertFc', ['app' => self::APP_ID, 'path' => $fileCachePath, 'storageId' => $storageId]);
try { try {
// Look up mimetype ID from the mimetype lookup table. // Look up mimetype ID from the mimetype lookup table.
// In NC33, mimetype is a bigint (ID), not a string. // In NC33, mimetype is a bigint (ID), not a string.
@@ -1228,9 +1228,9 @@ class TalkService {
\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE,
]); ]);
$fileId = (int)$pdo->lastInsertId($prefix . 'filecache_fileid_seq'); $fileId = (int)$pdo->lastInsertId($prefix . 'filecache_fileid_seq');
$this->logger->info('NCbotwebhooks: richObject step=fcInserted', ['app' => self::APP_ID, 'fileId' => $fileId]); $this->logger->info('nc_bot_webhooks: richObject step=fcInserted', ['app' => self::APP_ID, 'fileId' => $fileId]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->error('NCbotwebhooks: richObject step=fcInsertFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]); $this->logger->error('nc_bot_webhooks: richObject step=fcInsertFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]);
return null; return null;
} }
} else { } else {
@@ -1238,9 +1238,9 @@ class TalkService {
} }
$fileMimeType = $row !== false ? ($row['mimetype'] ?? $mimeType) : $mimeType; $fileMimeType = $row !== false ? ($row['mimetype'] ?? $mimeType) : $mimeType;
$this->logger->info('NCbotwebhooks: richObject step=fileFound', ['app' => self::APP_ID, 'fileId' => $fileId]); $this->logger->info('nc_bot_webhooks: richObject step=fileFound', ['app' => self::APP_ID, 'fileId' => $fileId]);
$this->logger->info('NCbotwebhooks: richObject step=prepareInsert', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: richObject step=prepareInsert', ['app' => self::APP_ID]);
// Resolve the actual Node object so createShare() doesn't have to // Resolve the actual Node object so createShare() doesn't have to
// lazily resolve it via getFirstNodeById() which can fail on // lazily resolve it via getFirstNodeById() which can fail on
@@ -1249,9 +1249,9 @@ class TalkService {
try { try {
$userFolder = $this->rootFolder->getUserFolder($bot->getUID()); $userFolder = $this->rootFolder->getUserFolder($bot->getUID());
$node = $userFolder->get($fileCachePath); $node = $userFolder->get($fileCachePath);
$this->logger->info('NCbotwebhooks: richObject step=nodeResolved', ['app' => self::APP_ID, 'nodeType' => get_class($node)]); $this->logger->info('nc_bot_webhooks: richObject step=nodeResolved', ['app' => self::APP_ID, 'nodeType' => get_class($node)]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('NCbotwebhooks: richObject step=nodeResolveFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]); $this->logger->warning('nc_bot_webhooks: richObject step=nodeResolveFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]);
} }
// NOTE: Share creation is deferred to postToRoom() so we can set // NOTE: Share creation is deferred to postToRoom() so we can set
@@ -1279,9 +1279,9 @@ class TalkService {
'UPDATE "' . $fileCacheTable . '" SET "size" = ?, "unencrypted_size" = ?, "etag" = ?, "mtime" = ? WHERE "fileid" = ?', 'UPDATE "' . $fileCacheTable . '" SET "size" = ?, "unencrypted_size" = ?, "etag" = ?, "mtime" = ? WHERE "fileid" = ?',
[$actualSize, $actualSize, $etag, (int)$this->config->getSystemValueInt('phpfileclient_mapping_localtime', time()), $fileId], [$actualSize, $actualSize, $etag, (int)$this->config->getSystemValueInt('phpfileclient_mapping_localtime', time()), $fileId],
); );
$this->logger->info('NCbotwebhooks: richObject step=filecacheUpdated', ['app' => self::APP_ID, 'fileId' => $fileId, 'size' => $actualSize, 'etag' => $etag]); $this->logger->info('nc_bot_webhooks: richObject step=filecacheUpdated', ['app' => self::APP_ID, 'fileId' => $fileId, 'size' => $actualSize, 'etag' => $etag]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('NCbotwebhooks: richObject step=filecacheUpdateFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]); $this->logger->warning('nc_bot_webhooks: richObject step=filecacheUpdateFail', ['app' => self::APP_ID, 'error' => $e->getMessage()]);
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
@@ -1308,13 +1308,13 @@ class TalkService {
]; ];
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('Failed to build rich object (Exception): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]); $this->logger->error('Failed to build rich object (Exception): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]);
$this->logger->info('NCbotwebhooks: buildRichObject EXIT exception: ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ')', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: buildRichObject EXIT exception: ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ')', ['app' => self::APP_ID]);
return null; return null;
} catch (\Error $e) { } catch (\Error $e) {
$this->logger->warning('NCbotwebhooks: buildRichObject ERROR=' . get_class($e) . ' msg=' . $e->getMessage() . ' file=' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]); $this->logger->warning('nc_bot_webhooks: buildRichObject ERROR=' . get_class($e) . ' msg=' . $e->getMessage() . ' file=' . $e->getFile() . ':' . $e->getLine(), ['app' => self::APP_ID]);
return null; return null;
} }
$this->logger->info('NCbotwebhooks: buildRichObject EXIT success', ['app' => self::APP_ID]); $this->logger->info('nc_bot_webhooks: buildRichObject EXIT success', ['app' => self::APP_ID]);
} }
// ── Chat API: post message to Talk room ────────────────────── // ── Chat API: post message to Talk room ──────────────────────
@@ -1340,13 +1340,13 @@ class TalkService {
): bool { ): bool {
$botPassword = $this->getBotPassword(); $botPassword = $this->getBotPassword();
if ($botPassword === null) { if ($botPassword === null) {
$this->logger->error('NCbotwebhooks: bot password not configured', ['app' => self::APP_ID]); $this->logger->error('nc_bot_webhooks: bot password not configured', ['app' => self::APP_ID]);
return false; return false;
} }
// Check bot is enabled for this room (via AppConfig) // Check bot is enabled for this room (via AppConfig)
if (!$this->isBotEnabledForRoom($roomToken)) { if (!$this->isBotEnabledForRoom($roomToken)) {
$this->logger->warning('NCbotwebhooks: bot not enabled for room', [ $this->logger->warning('nc_bot_webhooks: bot not enabled for room', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -1355,7 +1355,7 @@ class TalkService {
$baseUrl = $this->getBaseUrl(); $baseUrl = $this->getBaseUrl();
if ($baseUrl === '') { if ($baseUrl === '') {
$this->logger->error('NCbotwebhooks: base URL not configured', ['app' => self::APP_ID]); $this->logger->error('nc_bot_webhooks: base URL not configured', ['app' => self::APP_ID]);
return false; return false;
} }
@@ -1396,14 +1396,14 @@ class TalkService {
try { try {
$room = $this->talkManager->getRoomByToken($roomToken); $room = $this->talkManager->getRoomByToken($roomToken);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('NCbotwebhooks: failed to get room for token ' . $roomToken, [ $this->logger->error('nc_bot_webhooks: failed to get room for token ' . $roomToken, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
return false; return false;
} }
$this->logger->info('NCbotwebhooks: sending file_shared message to Talk', [ $this->logger->info('nc_bot_webhooks: sending file_shared message to Talk', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_token' => $roomToken, 'room_token' => $roomToken,
'system_message' => $systemMessage, 'system_message' => $systemMessage,
@@ -1449,17 +1449,17 @@ class TalkService {
$textStatus = $textResponse->getStatusCode(); $textStatus = $textResponse->getStatusCode();
if ($textStatus >= 200 && $textStatus < 300) { if ($textStatus >= 200 && $textStatus < 300) {
$this->logger->info('NCbotwebhooks: text message posted to room ' . $roomToken, [ $this->logger->info('nc_bot_webhooks: text message posted to room ' . $roomToken, [
'app' => self::APP_ID, 'app' => self::APP_ID,
]); ]);
} else { } else {
$this->logger->warning('NCbotwebhooks: text message POST returned ' . $textStatus, [ $this->logger->warning('nc_bot_webhooks: text message POST returned ' . $textStatus, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: text message POST failed: ' . $e->getMessage(), [ $this->logger->warning('nc_bot_webhooks: text message POST failed: ' . $e->getMessage(), [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_token' => $roomToken, 'room_token' => $roomToken,
]); ]);
@@ -1477,7 +1477,7 @@ class TalkService {
if ($richObject !== null && $richObject['fileId'] !== null) { if ($richObject !== null && $richObject['fileId'] !== null) {
$bot = $this->userManager->get('talk-bot'); $bot = $this->userManager->get('talk-bot');
if (!$bot) { if (!$bot) {
$this->logger->error('NCbotwebhooks: talk-bot user not found, cannot create share', [ $this->logger->error('nc_bot_webhooks: talk-bot user not found, cannot create share', [
'app' => self::APP_ID, 'app' => self::APP_ID,
]); ]);
$this->clearSessionOverwrite(); $this->clearSessionOverwrite();
@@ -1526,12 +1526,12 @@ class TalkService {
], ],
]); ]);
$this->logger->info('NCbotwebhooks: room share created with session overwrite', [ $this->logger->info('nc_bot_webhooks: room share created with session overwrite', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'shareId' => $actualShareId, 'shareId' => $actualShareId,
]); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->error('NCbotwebhooks: deferred share creation failed: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(), [ $this->logger->error('nc_bot_webhooks: deferred share creation failed: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(), [
'app' => self::APP_ID, 'app' => self::APP_ID,
'fileId' => (int)$richObject['fileId'], 'fileId' => (int)$richObject['fileId'],
'roomToken' => $roomToken, 'roomToken' => $roomToken,
@@ -1579,11 +1579,11 @@ class TalkService {
// new \DateTime('now', new \DateTimeZone('UTC')), // new \DateTime('now', new \DateTimeZone('UTC')),
// true, // sendNotifications // true, // sendNotifications
// ); // );
// $this->logger->info('NCbotwebhooks: system message posted to room ' . $roomToken, [ // $this->logger->info('nc_bot_webhooks: system message posted to room ' . $roomToken, [
// 'app' => self::APP_ID, // 'app' => self::APP_ID,
// ]); // ]);
// // Verify the system message was created with correct fileId/shareId // // Verify the system message was created with correct fileId/shareId
// $this->logger->info('NCbotwebhooks: system message verification', [ // $this->logger->info('nc_bot_webhooks: system message verification', [
// 'app' => self::APP_ID, // 'app' => self::APP_ID,
// 'fileId' => $richObject['fileId'] ?? null, // 'fileId' => $richObject['fileId'] ?? null,
// 'shareId' => $shareId, // 'shareId' => $shareId,
@@ -1592,7 +1592,7 @@ class TalkService {
// 'botUserId' => $bot ? $bot->getUID() : 'null', // 'botUserId' => $bot ? $bot->getUID() : 'null',
// ]); // ]);
// } catch (\Exception $e) { // } catch (\Exception $e) {
// $this->logger->error('NCbotwebhooks: addSystemMessage failed: ' . $e->getMessage(), [ // $this->logger->error('nc_bot_webhooks: addSystemMessage failed: ' . $e->getMessage(), [
// 'app' => self::APP_ID, // 'app' => self::APP_ID,
// 'room_token' => $roomToken, // 'room_token' => $roomToken,
// ]); // ]);
@@ -1638,7 +1638,7 @@ class TalkService {
} catch (DoesNotExistException $e) { } catch (DoesNotExistException $e) {
return 'talk-bot'; return 'talk-bot';
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: failed to get bot display name for room ' . $roomToken, [ $this->logger->warning('nc_bot_webhooks: failed to get bot display name for room ' . $roomToken, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
@@ -1768,7 +1768,7 @@ class TalkService {
// Get the talk-bot user // Get the talk-bot user
$botUser = $this->userManager->get('talk-bot'); $botUser = $this->userManager->get('talk-bot');
if ($botUser === null) { if ($botUser === null) {
$this->logger->warning('NCbotwebhooks: talk-bot user not found', ['app' => self::APP_ID]); $this->logger->warning('nc_bot_webhooks: talk-bot user not found', ['app' => self::APP_ID]);
return; return;
} }
@@ -1780,7 +1780,7 @@ class TalkService {
// Detect Talk rooms table name // Detect Talk rooms table name
$roomTable = $this->detectTalkTableFromCatalog('talk_rooms', 'spreed_room'); $roomTable = $this->detectTalkTableFromCatalog('talk_rooms', 'spreed_room');
if ($roomTable === null) { if ($roomTable === null) {
$this->logger->warning('NCbotwebhooks: Talk rooms table not found, skipping participant setup', [ $this->logger->warning('nc_bot_webhooks: Talk rooms table not found, skipping participant setup', [
'app' => self::APP_ID, 'app' => self::APP_ID,
]); ]);
return; return;
@@ -1797,7 +1797,7 @@ class TalkService {
$stmt->closeCursor(); $stmt->closeCursor();
if ($roomId === 0) { if ($roomId === 0) {
$this->logger->warning('NCbotwebhooks: room token not found in database', [ $this->logger->warning('nc_bot_webhooks: room token not found in database', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'token' => $token, 'token' => $token,
]); ]);
@@ -1827,18 +1827,18 @@ class TalkService {
try { try {
$this->attendeeMapper->insert($newAttendee); $this->attendeeMapper->insert($newAttendee);
$this->logger->info('NCbotwebhooks: added talk-bot as participant in room ' . $token, [ $this->logger->info('nc_bot_webhooks: added talk-bot as participant in room ' . $token, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'room_id' => $roomId, 'room_id' => $roomId,
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: failed to insert attendee for room ' . $token, [ $this->logger->warning('nc_bot_webhooks: failed to insert attendee for room ' . $token, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->warning('NCbotwebhooks: failed to add talk-bot to room ' . $token, [ $this->logger->warning('nc_bot_webhooks: failed to add talk-bot to room ' . $token, [
'app' => self::APP_ID, 'app' => self::APP_ID,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
@@ -1853,13 +1853,13 @@ class TalkService {
private function processImageUrls(array $urls, string $roomToken): array { private function processImageUrls(array $urls, string $roomToken): array {
$richObjects = []; $richObjects = [];
foreach ($urls as $imageUrl) { foreach ($urls as $imageUrl) {
$this->logger->info('NCbotwebhooks: attempting image download', [ $this->logger->info('nc_bot_webhooks: attempting image download', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'url' => $imageUrl, 'url' => $imageUrl,
]); ]);
$imageData = $this->downloadImage($imageUrl); $imageData = $this->downloadImage($imageUrl);
if ($imageData === null) { if ($imageData === null) {
$this->logger->warning('NCbotwebhooks: image download returned null', [ $this->logger->warning('nc_bot_webhooks: image download returned null', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'url' => $imageUrl, 'url' => $imageUrl,
]); ]);
@@ -1867,7 +1867,7 @@ class TalkService {
} }
$fileName = basename(parse_url($imageUrl, PHP_URL_PATH)) ?: 'attachment'; $fileName = basename(parse_url($imageUrl, PHP_URL_PATH)) ?: 'attachment';
$this->logger->info('NCbotwebhooks: uploading image', [ $this->logger->info('nc_bot_webhooks: uploading image', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'roomToken' => $roomToken, 'roomToken' => $roomToken,
'fileName' => $fileName, 'fileName' => $fileName,
@@ -1876,24 +1876,24 @@ class TalkService {
]); ]);
$uploadPath = $this->uploadImage($roomToken, $fileName, $imageData['data'], $imageData['mimeType']); $uploadPath = $this->uploadImage($roomToken, $fileName, $imageData['data'], $imageData['mimeType']);
if ($uploadPath !== null) { if ($uploadPath !== null) {
$this->logger->info('NCbotwebhooks: image uploaded', [ $this->logger->info('nc_bot_webhooks: image uploaded', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => $uploadPath, 'path' => $uploadPath,
]); ]);
$this->logger->info('NCbotwebhooks: building rich object', [ $this->logger->info('nc_bot_webhooks: building rich object', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'path' => $uploadPath, 'path' => $uploadPath,
]); ]);
$richObj = $this->buildRichObject($uploadPath, $imageData['mimeType'], $roomToken); $richObj = $this->buildRichObject($uploadPath, $imageData['mimeType'], $roomToken);
if ($richObj !== null) { if ($richObj !== null) {
$this->logger->info('NCbotwebhooks: rich object built', [ $this->logger->info('nc_bot_webhooks: rich object built', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'richObject' => json_encode($richObj), 'richObject' => json_encode($richObj),
]); ]);
$richObjects[] = $richObj; $richObjects[] = $richObj;
} }
} else { } else {
$this->logger->warning('NCbotwebhooks: image upload returned null', [ $this->logger->warning('nc_bot_webhooks: image upload returned null', [
'app' => self::APP_ID, 'app' => self::APP_ID,
'fileName' => $fileName, 'fileName' => $fileName,
]); ]);