Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ AGENT_SKILLS=
# NTFY_URL=
# NTFY_TOKEN= # 可选,用于需要 Bearer Token 的 topic 或自建 ntfy server
#
# 【方式六扩展】Gotify 配置
# GOTIFY_URL 是 Gotify server base URL,不包含 /message;系统会拼接 /message 并用 X-Gotify-Key Header 发送 token。
# GOTIFY_URL=
# GOTIFY_TOKEN= # Gotify application token
#
# 【方式七】PushPlus 配置(国内推送服务,推荐)
# 注册PushPlus账号并获取Token https://www.pushplus.plus
# PUSHPLUS_TOKEN=
Expand Down Expand Up @@ -486,7 +491,7 @@ AGENT_SKILLS=
#
# 【通知路由策略】(Issue #1200 P3)
# 默认留空:该类型通知发送到所有已配置渠道。填写后仅发送到列出的已配置渠道。
# 允许值:wechat,feishu,telegram,email,pushover,ntfy,pushplus,serverchan3,custom,discord,slack,astrbot
# 允许值:wechat,feishu,telegram,email,pushover,ntfy,gotify,pushplus,serverchan3,custom,discord,slack,astrbot
# NOTIFICATION_REPORT_CHANNELS=
# NOTIFICATION_ALERT_CHANNELS=
# NOTIFICATION_SYSTEM_ERROR_CHANNELS=
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/daily_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ jobs:
# 方式五扩展:ntfy
NTFY_URL: ${{ secrets.NTFY_URL }}
NTFY_TOKEN: ${{ secrets.NTFY_TOKEN }}

# 方式五扩展:Gotify
GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}

# 方式六:PushPlus ⬅️ 新增!
PUSHPLUS_TOKEN: ${{ secrets.PUSHPLUS_TOKEN }}
Expand Down Expand Up @@ -427,6 +431,7 @@ jobs:
echo "【通知渠道】"
echo " PushPlus: $([ -n "$PUSHPLUS_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
echo " ntfy: $([ -n "$NTFY_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
echo " Gotify: $([ -n "$GOTIFY_URL" ] && [ -n "$GOTIFY_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
echo " 企业微信: $([ -n "$WECHAT_WEBHOOK_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
echo " 飞书: $([ -n "$FEISHU_WEBHOOK_URL" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
echo " Telegram: $([ -n "$TELEGRAM_BOT_TOKEN" ] && echo '✅ 已配置' || echo '⚪ 未配置')"
Expand Down
1 change: 1 addition & 0 deletions api/v1/schemas/system_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"email",
"pushover",
"ntfy",
"gotify",
"pushplus",
"serverchan3",
"custom",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const CHANNEL_OPTIONS: Array<{ value: NotificationTestChannel; label: string }>
{ value: 'email', label: '邮件' },
{ value: 'pushover', label: 'Pushover' },
{ value: 'ntfy', label: 'ntfy' },
{ value: 'gotify', label: 'Gotify' },
{ value: 'pushplus', label: 'PushPlus' },
{ value: 'serverchan3', label: 'Server酱3' },
{ value: 'custom', label: '自定义 Webhook' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('NotificationTestPanel', () => {
);

expect(screen.getByRole('option', { name: 'ntfy' })).toBeInTheDocument();
expect(screen.getByRole('option', { name: 'Gotify' })).toBeInTheDocument();
fireEvent.change(screen.getByLabelText('渠道'), { target: { value: 'custom' } });
fireEvent.click(screen.getByRole('button', { name: /发送测试/ }));

Expand Down
34 changes: 34 additions & 0 deletions apps/dsa-web/src/locales/settingsHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ const settingsHelpZhCN: SettingsHelpMap = {
'如果飞书侧配置 IP 白名单,需要确认当前运行环境出口 IP 已加入白名单。',
],
},
'settings.notification.CUSTOM_WEBHOOK_URLS': {
title: '自定义 Webhook',
summary: '配置任意支持 POST JSON 的通知端点,可用于钉钉、Bark、Slack、Discord 或自建服务。',
usage: '多个 URL 使用英文逗号分隔。Bark 可直接填写 https://api.day.app/YOUR_BARK_KEY。',
valueNotes: [
'未配置 CUSTOM_WEBHOOK_BODY_TEMPLATE 时,系统会按 URL 类型自动生成常见 payload。',
'配置全局 Body 模板后,会覆盖 Bark、Slack、Discord 等自动 payload。',
'需要认证的通用 webhook 可搭配 CUSTOM_WEBHOOK_BEARER_TOKEN。',
],
impact: [
'影响 custom 通知渠道;单个 URL 失败不会阻断其他 URL 或主分析流程。',
],
notes: [
'Bark 仍作为 custom webhook 使用,不需要新增 BARK_* 配置。',
'自签名内网端点可评估 WEBHOOK_VERIFY_SSL=false,但不要用于公网链路。',
],
},
'settings.system.WEBUI_HOST': {
title: 'WebUI 监听地址',
summary: '控制 WebUI 服务绑定在哪个网络地址上。',
Expand Down Expand Up @@ -159,6 +176,23 @@ const settingsHelpEnUS: SettingsHelpMap = {
'If IP allowlisting is enabled in Feishu, add the outbound IP of your runtime environment.',
],
},
'settings.notification.CUSTOM_WEBHOOK_URLS': {
title: 'Custom Webhook',
summary: 'Sends notifications to any endpoint that accepts POST JSON, such as DingTalk, Bark, Slack, Discord, or a self-hosted service.',
usage: 'Separate multiple URLs with commas. For Bark, use https://api.day.app/YOUR_BARK_KEY.',
valueNotes: [
'Without CUSTOM_WEBHOOK_BODY_TEMPLATE, the sender auto-builds payloads for known URL types.',
'A global body template overrides auto-detected payloads for Bark, Slack, Discord, and similar URLs.',
'Use CUSTOM_WEBHOOK_BEARER_TOKEN for generic webhook endpoints that require bearer auth.',
],
impact: [
'Affects the custom notification channel only; one URL failure does not block other URLs or the analysis flow.',
],
notes: [
'Bark stays on the custom webhook baseline and does not use BARK_* settings.',
'Only consider WEBHOOK_VERIFY_SSL=false for trusted internal self-signed endpoints, not public routes.',
],
},
'settings.system.WEBUI_HOST': {
title: 'WebUI Host',
summary: 'Controls the network address the WebUI service binds to.',
Expand Down
1 change: 1 addition & 0 deletions apps/dsa-web/src/types/systemConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export type NotificationTestChannel =
| 'email'
| 'pushover'
| 'ntfy'
| 'gotify'
| 'pushplus'
| 'serverchan3'
| 'custom'
Expand Down
10 changes: 10 additions & 0 deletions apps/dsa-web/src/utils/systemConfigI18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ const fieldTitleMap: Record<string, string> = {
FEISHU_APP_SECRET: '飞书应用 App Secret',
DINGTALK_APP_KEY: '钉钉 App Key',
DINGTALK_APP_SECRET: '钉钉 App Secret',
CUSTOM_WEBHOOK_URLS: '自定义 Webhook 地址',
CUSTOM_WEBHOOK_BODY_TEMPLATE: '自定义 Webhook Body 模板',
NTFY_URL: 'ntfy URL',
NTFY_TOKEN: 'ntfy Token',
GOTIFY_URL: 'Gotify URL',
GOTIFY_TOKEN: 'Gotify Token',
PUSHPLUS_TOKEN: 'PushPlus Token',
REPORT_SUMMARY_ONLY: '仅分析结果摘要',
MAX_WORKERS: '最大并发线程数',
Expand Down Expand Up @@ -128,7 +133,12 @@ const fieldDescriptionMap: Record<string, string> = {
FEISHU_APP_SECRET: '仅用于飞书应用机器人 / Stream Bot / 云文档等应用模式,不会直接开启群 Webhook 推送。',
DINGTALK_APP_KEY: '钉钉应用模式 App Key。',
DINGTALK_APP_SECRET: '钉钉应用模式 App Secret。',
CUSTOM_WEBHOOK_URLS: '自定义通知端点,多个用英文逗号分隔;Bark 可直接填写 https://api.day.app/YOUR_BARK_KEY。',
CUSTOM_WEBHOOK_BODY_TEMPLATE: '全局 JSON body 模板,会先于 Bark、Slack、Discord 等自动 payload 生效;推荐使用 $content_json / $title_json,裸 $content / $title 不做 JSON 转义。',
NTFY_URL: 'ntfy 完整 topic endpoint,必须包含 topic path,例如 https://ntfy.sh/my-topic。',
NTFY_TOKEN: 'ntfy Bearer Token;公开 topic 可留空。',
GOTIFY_URL: 'Gotify server base URL,不包含 /message;系统发送时会自动拼接 /message。',
GOTIFY_TOKEN: 'Gotify application token,系统通过 X-Gotify-Key Header 发送,不会放进 URL。',
PUSHPLUS_TOKEN: 'PushPlus 推送令牌。',
REPORT_SUMMARY_ONLY: '仅推送分析结果摘要,不包含个股详情。多股时适合快速浏览。',
MAX_WORKERS: '异步任务队列最大并发数。配置保存后,队列空闲时会自动应用;繁忙时延后生效。',
Expand Down
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
- [新功能] 通知网关新增 ntfy 一等渠道,支持通过 `NTFY_URL` / `NTFY_TOKEN` 推送并接入 Web 测试、路由、Actions 与诊断。
- [新功能] 通知网关新增 Gotify 一等渠道,支持通过 `GOTIFY_URL` / `GOTIFY_TOKEN` 推送 Markdown 文本并接入 Web 测试、路由、Actions 与诊断。
- [修复] 收紧 ntfy 结构化校验,避免 URL 编码空白 topic 被误判为有效通知端点。
- [文档] 补充 Bark custom webhook 示例和 WebPush / Apprise 通知渠道评估,明确本轮不新增运行时依赖或配置入口。
- [修复] 聚合报告通知按静态渠道隔离发送失败,并补充自定义 Webhook 部分成功诊断与脱敏测试。
- [修复] 未配置 Tushare / Longbridge 凭据时不再实例化对应可选 fetcher,避免缺失凭据的数据源进入候选集。
- [修复] Longbridge 遇到连接关闭类异常后会进入冷却期,并在美股/港股实时与日线请求中临时跳过该数据源,避免请求级频繁重连。
Expand Down
34 changes: 32 additions & 2 deletions docs/full-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,16 @@ daily_stock_analysis/
| `ASTRBOT_TOKEN` | AstrBot Bearer Token(可选) | 可选 |
| `NTFY_URL` | ntfy 完整 topic endpoint,必须包含 topic path,例如 `https://ntfy.sh/my-topic` | 可选 |
| `NTFY_TOKEN` | ntfy Bearer Token(可选) | 可选 |
| `GOTIFY_URL` | Gotify server base URL,不包含 `/message`;系统会自动拼接 `/message` | 可选 |
| `GOTIFY_TOKEN` | Gotify application token,通过 `X-Gotify-Key` Header 发送 | 可选 |
| `CUSTOM_WEBHOOK_URLS` | 自定义 Webhook(支持钉钉等,多个用逗号分隔) | 可选 |
| `CUSTOM_WEBHOOK_BEARER_TOKEN` | 自定义 Webhook 的 Bearer Token(用于需要认证的 Webhook) | 可选 |
| `CUSTOM_WEBHOOK_BODY_TEMPLATE` | 自定义 Webhook JSON body 模板,适配 AstrBot、NapCat、自建服务等特殊 payload | 可选 |
| `WEBHOOK_VERIFY_SSL` | 读取该配置的 webhook-style HTTPS 通知请求证书校验(默认 true)。设为 false 可支持自签名证书。警告:关闭有严重安全风险(MITM),仅限可信内网 | 可选 |

> *注:至少配置一个渠道,配置多个则同时推送
>
> 当前默认 `daily_analysis.yml` 只显式映射固定 Secret / Variable 名称,不会自动把 `STOCK_GROUP_1`、`EMAIL_GROUP_1` 这类任意编号变量导入运行环境。所以分组邮箱功能目前不适用于仓库自带默认 GitHub Actions workflow;它适用于本地 `.env`、Docker,或你自行显式扩展过 `env:` 映射的运行环境。Actions 已显式映射 `CUSTOM_WEBHOOK_BODY_TEMPLATE`、`WEBHOOK_VERIFY_SSL`、`FEISHU_WEBHOOK_SECRET`、`FEISHU_WEBHOOK_KEYWORD`、`PUSHPLUS_TOPIC`、`NTFY_URL`、`NTFY_TOKEN`、P3 通知路由键以及 P4 通知降噪键;`MARKDOWN_TO_IMAGE_CHANNELS` 和 `MERGE_EMAIL_NOTIFICATION` 仍作为行为开关不在默认 workflow 中自动映射。
> 当前默认 `daily_analysis.yml` 只显式映射固定 Secret / Variable 名称,不会自动把 `STOCK_GROUP_1`、`EMAIL_GROUP_1` 这类任意编号变量导入运行环境。所以分组邮箱功能目前不适用于仓库自带默认 GitHub Actions workflow;它适用于本地 `.env`、Docker,或你自行显式扩展过 `env:` 映射的运行环境。Actions 已显式映射 `CUSTOM_WEBHOOK_BODY_TEMPLATE`、`WEBHOOK_VERIFY_SSL`、`FEISHU_WEBHOOK_SECRET`、`FEISHU_WEBHOOK_KEYWORD`、`PUSHPLUS_TOPIC`、`NTFY_URL`、`NTFY_TOKEN`、`GOTIFY_URL`、`GOTIFY_TOKEN`、P3 通知路由键以及 P4 通知降噪键;`MARKDOWN_TO_IMAGE_CHANNELS` 和 `MERGE_EMAIL_NOTIFICATION` 仍作为行为开关不在默认 workflow 中自动映射。

#### 推送行为配置

Expand Down Expand Up @@ -264,11 +266,13 @@ daily_stock_analysis/
| `PUSHOVER_API_TOKEN` | Pushover API Token | 可选 |
| `NTFY_URL` | ntfy 完整 topic endpoint,必须包含 topic path,例如 `https://ntfy.sh/my-topic` | 可选 |
| `NTFY_TOKEN` | ntfy Bearer Token(可选) | 可选 |
| `GOTIFY_URL` | Gotify server base URL,不包含 `/message` | 可选 |
| `GOTIFY_TOKEN` | Gotify application token,通过 `X-Gotify-Key` Header 发送 | 可选 |
| `PUSHPLUS_TOKEN` | PushPlus Token(国内推送服务) | 可选 |
| `SERVERCHAN3_SENDKEY` | Server酱³ Sendkey | 可选 |
| `ASTRBOT_URL` | AstrBot Webhook URL | 可选 |
| `ASTRBOT_TOKEN` | AstrBot Bearer Token(可选) | 可选 |
| `NOTIFICATION_REPORT_CHANNELS` | report 路由渠道,逗号分隔;允许值:wechat,feishu,telegram,email,pushover,ntfy,pushplus,serverchan3,custom,discord,slack,astrbot | 可选 |
| `NOTIFICATION_REPORT_CHANNELS` | report 路由渠道,逗号分隔;允许值:wechat,feishu,telegram,email,pushover,ntfy,gotify,pushplus,serverchan3,custom,discord,slack,astrbot | 可选 |
| `NOTIFICATION_ALERT_CHANNELS` | alert 路由渠道,逗号分隔;留空保持全渠道 | 可选 |
| `NOTIFICATION_SYSTEM_ERROR_CHANNELS` | system_error 预留路由渠道,逗号分隔;留空保持全渠道 | 可选 |
| `NOTIFICATION_DEDUP_TTL_SECONDS` | 通知去重 TTL 秒数,`0` 关闭 | 可选 |
Expand Down Expand Up @@ -794,6 +798,32 @@ NapCat / OneBot 示例需按实际 endpoint、`user_id` 或 `group_id` 调整:
CUSTOM_WEBHOOK_BODY_TEMPLATE={"user_id":123456,"message":$content_json}
```

### ntfy / Gotify

ntfy 和 Gotify 都是一等通知渠道,只发送文本 / JSON,不参与 Markdown 转图片。

ntfy 使用完整 topic endpoint,最后一个 path segment 会作为 topic:

```env
NTFY_URL=https://ntfy.sh/my-topic
NTFY_TOKEN=
```

Gotify 使用 server base URL,系统会自动拼接固定 `/message` API,并通过 `X-Gotify-Key` Header 发送 application token。`GOTIFY_URL` 可包含反向代理 path prefix,但不要包含 `/message`:

```env
GOTIFY_URL=https://gotify.example
GOTIFY_TOKEN=app-token
```

```env
# 实际请求会发送到 https://example.com/gotify/message
GOTIFY_URL=https://example.com/gotify
GOTIFY_TOKEN=app-token
```

`NTFY_URL` 与 `GOTIFY_URL` 的语义不同是两个服务 API 设计不同导致的刻意选择:ntfy 由用户 topic 构成 endpoint,Gotify 的 `/message` 是固定服务 API。

### Discord

Discord 支持两种方式推送:
Expand Down
48 changes: 45 additions & 3 deletions docs/full-guide_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ Go to your forked repo → `Settings` → `Secrets and variables` → `Actions`
| `ASTRBOT_TOKEN` | Optional AstrBot Bearer Token | Optional |
| `NTFY_URL` | Full ntfy topic endpoint, must include topic path, e.g. `https://ntfy.sh/my-topic` | Optional |
| `NTFY_TOKEN` | Optional ntfy Bearer Token | Optional |
| `GOTIFY_URL` | Gotify server base URL, without `/message`; the sender appends `/message` | Optional |
| `GOTIFY_TOKEN` | Gotify application token sent with the `X-Gotify-Key` header | Optional |
| `CUSTOM_WEBHOOK_URLS` | Custom Webhook (supports DingTalk, etc., comma-separated) | Optional |
| `CUSTOM_WEBHOOK_BEARER_TOKEN` | Bearer Token for custom webhooks (for authenticated webhooks) | Optional |
| `CUSTOM_WEBHOOK_BODY_TEMPLATE` | Custom Webhook JSON body template for AstrBot, NapCat, or self-hosted services with special payloads | Optional |
| `WEBHOOK_VERIFY_SSL` | HTTPS certificate verification for webhook-style notification requests that read this setting (default true). Set to false for self-signed certs. WARNING: Disabling has serious security risk (MITM), use only on trusted internal networks | Optional |

> *Note: Configure at least one channel; multiple channels will all receive notifications
>
> The default `daily_analysis.yml` in this repository only exports fixed Secret / Variable names. Arbitrary numbered env vars such as `STOCK_GROUP_1` and `EMAIL_GROUP_1` are not auto-injected into the job, so grouped email routing is not available in the stock workflow unless you explicitly extend the workflow's `env:` mapping in your own fork. Actions now maps `CUSTOM_WEBHOOK_BODY_TEMPLATE`, `WEBHOOK_VERIFY_SSL`, `FEISHU_WEBHOOK_SECRET`, `FEISHU_WEBHOOK_KEYWORD`, `PUSHPLUS_TOPIC`, `NTFY_URL`, `NTFY_TOKEN`, the P3 notification route keys, and the P4 notification noise-control keys; `MARKDOWN_TO_IMAGE_CHANNELS` and `MERGE_EMAIL_NOTIFICATION` remain behavior toggles outside the default workflow mapping.
> The default `daily_analysis.yml` in this repository only exports fixed Secret / Variable names. Arbitrary numbered env vars such as `STOCK_GROUP_1` and `EMAIL_GROUP_1` are not auto-injected into the job, so grouped email routing is not available in the stock workflow unless you explicitly extend the workflow's `env:` mapping in your own fork. Actions now maps `CUSTOM_WEBHOOK_BODY_TEMPLATE`, `WEBHOOK_VERIFY_SSL`, `FEISHU_WEBHOOK_SECRET`, `FEISHU_WEBHOOK_KEYWORD`, `PUSHPLUS_TOPIC`, `NTFY_URL`, `NTFY_TOKEN`, `GOTIFY_URL`, `GOTIFY_TOKEN`, the P3 notification route keys, and the P4 notification noise-control keys; `MARKDOWN_TO_IMAGE_CHANNELS` and `MERGE_EMAIL_NOTIFICATION` remain behavior toggles outside the default workflow mapping.

#### Push Behavior Configuration

Expand Down Expand Up @@ -236,11 +238,13 @@ For the P0 notification baseline and diagnostics, see [Notification Baseline](no
| `PUSHOVER_API_TOKEN` | Pushover API Token | Optional |
| `NTFY_URL` | Full ntfy topic endpoint, must include topic path, e.g. `https://ntfy.sh/my-topic` | Optional |
| `NTFY_TOKEN` | Optional ntfy Bearer Token | Optional |
| `GOTIFY_URL` | Gotify server base URL, without `/message` | Optional |
| `GOTIFY_TOKEN` | Gotify application token sent with `X-Gotify-Key` | Optional |
| `PUSHPLUS_TOKEN` | PushPlus Token (Chinese push service) | Optional |
| `SERVERCHAN3_SENDKEY` | ServerChan v3 Sendkey | Optional |
| `ASTRBOT_URL` | AstrBot Webhook URL | Optional |
| `ASTRBOT_TOKEN` | Optional AstrBot Bearer Token | Optional |
| `NOTIFICATION_REPORT_CHANNELS` | Report route channels, comma-separated. Allowed values: wechat,feishu,telegram,email,pushover,ntfy,pushplus,serverchan3,custom,discord,slack,astrbot | Optional |
| `NOTIFICATION_REPORT_CHANNELS` | Report route channels, comma-separated. Allowed values: wechat,feishu,telegram,email,pushover,ntfy,gotify,pushplus,serverchan3,custom,discord,slack,astrbot | Optional |
| `NOTIFICATION_ALERT_CHANNELS` | Alert route channels, comma-separated. Empty keeps all configured channels | Optional |
| `NOTIFICATION_SYSTEM_ERROR_CHANNELS` | Reserved system_error route channels, comma-separated. Empty keeps all configured channels | Optional |
| `NOTIFICATION_DEDUP_TTL_SECONDS` | Dedup TTL in seconds. `0` disables dedup | Optional |
Expand Down Expand Up @@ -683,7 +687,13 @@ Available placeholders: `$content_json`, `$content`, `$title_json`, `$title`.
Raw `$content` / `$title` are not JSON-escaped, so quotes or newlines can make
the template invalid and trigger fallback.

When using Bark with a global template, include the Bark body explicitly:
Bark stays on the custom webhook baseline; no `BARK_*` settings are required.
Set the Bark endpoint in `CUSTOM_WEBHOOK_URLS`. When using Bark with a global
template, include the Bark body explicitly:

```env
CUSTOM_WEBHOOK_URLS=https://api.day.app/YOUR_BARK_KEY
```

```env
CUSTOM_WEBHOOK_BODY_TEMPLATE={"title":$title_json,"body":$content_json,"group":"stock"}
Expand All @@ -696,6 +706,38 @@ or `group_id`:
CUSTOM_WEBHOOK_BODY_TEMPLATE={"user_id":123456,"message":$content_json}
```

### ntfy / Gotify

ntfy and Gotify are first-class notification channels. They send text / JSON
only and do not use Markdown-to-image.

ntfy uses the full topic endpoint; the last path segment is treated as the
topic:

```env
NTFY_URL=https://ntfy.sh/my-topic
NTFY_TOKEN=
```

Gotify uses the server base URL. The sender appends the fixed `/message` API and
sends the application token in the `X-Gotify-Key` header. `GOTIFY_URL` may
include a reverse-proxy path prefix, but must not include `/message`:

```env
GOTIFY_URL=https://gotify.example
GOTIFY_TOKEN=app-token
```

```env
# Actual request URL: https://example.com/gotify/message
GOTIFY_URL=https://example.com/gotify
GOTIFY_TOKEN=app-token
```

`NTFY_URL` and `GOTIFY_URL` intentionally use different URL semantics because
the two services expose different APIs: ntfy topics are part of the endpoint,
while Gotify uses `/message` as a fixed server API.

### Discord

Discord supports two push methods:
Expand Down
Loading
Loading