mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Merge commit from fork
This commit is contained in:
@@ -340,7 +340,7 @@ Details: [Security guide](https://docs.openclaw.ai/gateway/security) · [Docker
|
|||||||
### [Telegram](https://docs.openclaw.ai/channels/telegram)
|
### [Telegram](https://docs.openclaw.ai/channels/telegram)
|
||||||
|
|
||||||
- Set `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken` (env wins).
|
- Set `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken` (env wins).
|
||||||
- Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` as needed.
|
- Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` as needed.
|
||||||
|
|
||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ title: grammY
|
|||||||
- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default.
|
- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default.
|
||||||
- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`.
|
- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`.
|
||||||
- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
|
- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
|
||||||
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` is set (otherwise it long-polls).
|
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` are set (otherwise it long-polls).
|
||||||
- **Sessions:** direct chats collapse into the agent main session (`agent:<agentId>:<mainKey>`); groups use `agent:<agentId>:telegram:group:<chatId>`; replies route back to the same channel.
|
- **Sessions:** direct chats collapse into the agent main session (`agent:<agentId>:<mainKey>`); groups use `agent:<agentId>:telegram:group:<chatId>`; replies route back to the same channel.
|
||||||
- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`.
|
- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`.
|
||||||
- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming.
|
- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming.
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ Most users want: `groupPolicy: "allowlist"` + `groupAllowFrom` + specific groups
|
|||||||
## Long-polling vs webhook
|
## Long-polling vs webhook
|
||||||
|
|
||||||
- Default: long-polling (no public URL required).
|
- Default: long-polling (no public URL required).
|
||||||
- Webhook mode: set `channels.telegram.webhookUrl` (optionally `channels.telegram.webhookSecret` + `channels.telegram.webhookPath`).
|
- Webhook mode: set `channels.telegram.webhookUrl` and `channels.telegram.webhookSecret` (optionally `channels.telegram.webhookPath`).
|
||||||
- The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
- The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
||||||
- If your public URL is different, use a reverse proxy and point `channels.telegram.webhookUrl` at the public endpoint.
|
- If your public URL is different, use a reverse proxy and point `channels.telegram.webhookUrl` at the public endpoint.
|
||||||
|
|
||||||
@@ -732,8 +732,8 @@ Provider options:
|
|||||||
- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
||||||
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts.
|
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts.
|
||||||
- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
|
- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
|
||||||
- `channels.telegram.webhookUrl`: enable webhook mode.
|
- `channels.telegram.webhookUrl`: enable webhook mode (requires `channels.telegram.webhookSecret`).
|
||||||
- `channels.telegram.webhookSecret`: webhook secret (optional).
|
- `channels.telegram.webhookSecret`: webhook secret (required when webhookUrl is set).
|
||||||
- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
|
- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
|
||||||
- `channels.telegram.actions.reactions`: gate Telegram tool reactions.
|
- `channels.telegram.actions.reactions`: gate Telegram tool reactions.
|
||||||
- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
|
- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
|
||||||
|
|||||||
@@ -1091,7 +1091,7 @@ Set `channels.telegram.configWrites: false` to block Telegram-initiated config w
|
|||||||
autoSelectFamily: false,
|
autoSelectFamily: false,
|
||||||
},
|
},
|
||||||
proxy: "socks5://localhost:9050",
|
proxy: "socks5://localhost:9050",
|
||||||
webhookUrl: "https://example.com/telegram-webhook",
|
webhookUrl: "https://example.com/telegram-webhook", // requires webhookSecret
|
||||||
webhookSecret: "secret",
|
webhookSecret: "secret",
|
||||||
webhookPath: "/telegram-webhook",
|
webhookPath: "/telegram-webhook",
|
||||||
},
|
},
|
||||||
|
|||||||
65
src/config/telegram-webhook-secret.test.ts
Normal file
65
src/config/telegram-webhook-secret.test.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { validateConfigObject } from "./config.js";
|
||||||
|
|
||||||
|
describe("Telegram webhook config", () => {
|
||||||
|
it("accepts webhookUrl when webhookSecret is configured", () => {
|
||||||
|
const res = validateConfigObject({
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
webhookUrl: "https://example.com/telegram-webhook",
|
||||||
|
webhookSecret: "secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects webhookUrl without webhookSecret", () => {
|
||||||
|
const res = validateConfigObject({
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
webhookUrl: "https://example.com/telegram-webhook",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(false);
|
||||||
|
if (!res.ok) {
|
||||||
|
expect(res.issues[0]?.path).toBe("channels.telegram.webhookSecret");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts account webhookUrl when base webhookSecret is configured", () => {
|
||||||
|
const res = validateConfigObject({
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
webhookSecret: "secret",
|
||||||
|
accounts: {
|
||||||
|
ops: {
|
||||||
|
webhookUrl: "https://example.com/telegram-webhook",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects account webhookUrl without webhookSecret", () => {
|
||||||
|
const res = validateConfigObject({
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
accounts: {
|
||||||
|
ops: {
|
||||||
|
webhookUrl: "https://example.com/telegram-webhook",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(false);
|
||||||
|
if (!res.ok) {
|
||||||
|
expect(res.issues[0]?.path).toBe("channels.telegram.accounts.ops.webhookSecret");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -164,6 +164,43 @@ export const TelegramConfigSchema = TelegramAccountSchemaBase.extend({
|
|||||||
'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"',
|
'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"',
|
||||||
});
|
});
|
||||||
validateTelegramCustomCommands(value, ctx);
|
validateTelegramCustomCommands(value, ctx);
|
||||||
|
|
||||||
|
const baseWebhookUrl = typeof value.webhookUrl === "string" ? value.webhookUrl.trim() : "";
|
||||||
|
const baseWebhookSecret =
|
||||||
|
typeof value.webhookSecret === "string" ? value.webhookSecret.trim() : "";
|
||||||
|
if (baseWebhookUrl && !baseWebhookSecret) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "channels.telegram.webhookUrl requires channels.telegram.webhookSecret",
|
||||||
|
path: ["webhookSecret"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!value.accounts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const [accountId, account] of Object.entries(value.accounts)) {
|
||||||
|
if (!account) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (account.enabled === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const accountWebhookUrl =
|
||||||
|
typeof account.webhookUrl === "string" ? account.webhookUrl.trim() : "";
|
||||||
|
if (!accountWebhookUrl) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const accountSecret =
|
||||||
|
typeof account.webhookSecret === "string" ? account.webhookSecret.trim() : "";
|
||||||
|
if (!accountSecret && !baseWebhookSecret) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message:
|
||||||
|
"channels.telegram.accounts.*.webhookUrl requires channels.telegram.webhookSecret or channels.telegram.accounts.*.webhookSecret",
|
||||||
|
path: ["accounts", accountId, "webhookSecret"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DiscordDmSchema = z
|
export const DiscordDmSchema = z
|
||||||
|
|||||||
Reference in New Issue
Block a user