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
5 changes: 5 additions & 0 deletions .changeset/think-telegram-native-streaming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/think": patch
---

Add a `nativeStreaming` option to `telegramMessenger` and forward it to the adapter (#2397). `@chat-adapter/telegram` 4.38 made native draft streaming opt-in, so private-chat replies always posted and edited, with no way to turn drafts back on. `telegramMessenger` also passes `allowUnverifiedWebhooks` when there's no `secretToken`, so `verifyWebhook: false` or a custom `verifyWebhook` no longer throws "secretToken is required in webhook mode" on those adapter versions. Think still refuses a webhook with neither a secret token nor an explicit `verifyWebhook`.
5 changes: 5 additions & 0 deletions docs/think/messengers.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ https://<your-worker>/messengers/telegram/webhook
custom `verifyWebhook` function or explicitly opt out with
`verifyWebhook: false`.

To stream private-chat replies as native Telegram drafts instead of posting a
message and editing it, pass `nativeStreaming: true`. With
`@chat-adapter/telegram` 4.38 or later it defaults to `false`; earlier versions
always use drafts in private chats. Group chats always post and edit.

If one Think agent owns multiple Telegram bots, give each provider a distinct
Chat SDK adapter name:

Expand Down
25 changes: 23 additions & 2 deletions packages/think/src/messengers/telegram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,29 @@ export const TELEGRAM_FOLLOWUP_CHUNK_LIMIT = 3_500;

const TELEGRAM_DEDUPE_PREFIX = "dedupe:telegram:";

/**
* `@chat-adapter/telegram` 4.38 added `nativeStreaming` and
* `allowUnverifiedWebhooks`. Older adapters in the supported range ignore them.
*/
type TelegramAdapterConfigCompat = TelegramAdapterConfig & {
allowUnverifiedWebhooks?: boolean;
nativeStreaming?: boolean;
};

export interface TelegramMessengerOptions extends Omit<
ChatSdkMessengerOptions,
"adapter" | "provider" | "userName" | "verifyWebhook"
> {
apiBaseUrl?: string;
apiUrl?: string;
mode?: TelegramAdapterConfig["mode"];
/**
* Stream private-chat replies as native Telegram drafts instead of posting
* a message and editing it. Requires `@chat-adapter/telegram` 4.38 or later,
* where it defaults to `false`; earlier versions always use drafts in
* private chats. Group chats always post and edit.
*/
nativeStreaming?: boolean;
secretToken?: string;
token: string;
userName: string;
Expand All @@ -42,14 +58,19 @@ export function telegramMessenger(
);
}

const adapter = createTelegramAdapter({
const adapterConfig: TelegramAdapterConfigCompat = {
// Without a secret token, Think verifies the webhook itself (or the caller
// opted out above), so the adapter must not reject the missing token.
allowUnverifiedWebhooks: !options.secretToken,
apiBaseUrl: options.apiBaseUrl,
apiUrl: options.apiUrl,
botToken: options.token,
mode: options.mode ?? "webhook",
nativeStreaming: options.nativeStreaming,
secretToken: options.secretToken,
userName: options.userName
});
};
const adapter = createTelegramAdapter(adapterConfig);

return chatSdkMessenger({
...options,
Expand Down
59 changes: 59 additions & 0 deletions packages/think/src/tests/telegram-messenger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { createTelegramAdapter } from "@chat-adapter/telegram";
import { describe, expect, it, vi } from "vitest";
import { telegramMessenger } from "../messengers/telegram";

vi.mock("@chat-adapter/telegram", async (importOriginal) => {
const actual =
await importOriginal<typeof import("@chat-adapter/telegram")>();
return {
...actual,
createTelegramAdapter: vi.fn(actual.createTelegramAdapter)
};
});

function lastAdapterConfig(): Record<string, unknown> {
return vi.mocked(createTelegramAdapter).mock.lastCall?.[0] as Record<
string,
unknown
>;
}

describe("telegram messenger adapter config (#2397)", () => {
it("forwards nativeStreaming to the adapter", () => {
telegramMessenger({
nativeStreaming: true,
secretToken: "secret",
token: "token",
userName: "fake_bot"
});
expect(lastAdapterConfig()).toMatchObject({ nativeStreaming: true });
Comment on lines +21 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Adapter behavior remains untested

The lockfile installs Telegram adapter 4.31, which predates both new options. These assertions check forwarding, not native drafts or webhook construction under 4.38+.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional. Bumping the lockfile to chat/@chat-adapter/telegram 4.38+ changes the private Chat.prototype.drainQueue signature that Think's recovery drain (#2312) relies on (see #2397), so that belongs in a separate dependency-update PR. What Think controls here is the config it passes the adapter, which these tests cover; draft streaming and webhook validation are the adapter's own behaviour.


telegramMessenger({
secretToken: "secret",
token: "token",
userName: "fake_bot"
});
expect(lastAdapterConfig().nativeStreaming).toBeUndefined();
});

it("allows unverified adapter webhooks only when Think verifies them", () => {
telegramMessenger({
secretToken: "secret",
token: "token",
userName: "fake_bot"
});
expect(lastAdapterConfig()).toMatchObject({
allowUnverifiedWebhooks: false,
secretToken: "secret"
});

telegramMessenger({
token: "token",
userName: "fake_bot",
verifyWebhook: false
});
expect(lastAdapterConfig()).toMatchObject({
allowUnverifiedWebhooks: true
});
});
});
Loading