Streaming Message Drafts (stream)
This plugin lets you stream long text messages to Telegram. Any iterator of string snippets can be streamed right into any private chat.
For example, you can make LLM output appear gradually while generating the response.
Quickstart
The plugin installs three methods on the context object:
ctx: plain message streaming.reply With Stream ctx: markdown streaming (recommended).reply With Markdown Stream ctx: HTML streaming.reply With Html Stream
Plain text streaming (first option) sends regular text messages. The other two methods use Telegram’s rich messages and are recommended for most cases.
Streaming messages performs many API calls very rapidly. It is strongly recommended to use the auto
-retry plugin alongside the stream plugin.
import { Bot, type Context } from "grammy";
import { autoRetry } from "@grammyjs/auto-retry";
import { stream, type StreamFlavor } from "@grammyjs/stream";
type MyContext = StreamFlavor<Context>;
const bot = new Bot<MyContext>("");
bot.api.config.use(autoRetry()); // strongly recommended!
bot.use(stream());
async function* slowText() {
// emulate slow text generation
yield "This is som";
await new Promise((r) => setTimeout(r, 2000));
yield "e slowly gen";
await new Promise((r) => setTimeout(r, 2000));
yield "erated text";
}
// Telegram only supports streaming in private chats.
bot.chatType("private")
.command("stream", async (ctx) => {
// Stream the message!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const { Bot } = require("grammy");
const { autoRetry } = require("@grammyjs/auto-retry");
const { stream } = require("@grammyjs/stream");
const bot = new Bot("");
bot.api.config.use(autoRetry()); // strongly recommended!
bot.use(stream());
async function* slowText() {
// emulate slow text generation
yield "This is som";
await new Promise((r) => setTimeout(r, 2000));
yield "e slowly gen";
await new Promise((r) => setTimeout(r, 2000));
yield "erated text";
}
// Telegram only supports streaming in private chats.
bot.chatType("private")
.command("stream", async (ctx) => {
// Stream the message!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Bot, type Context } from "https://deno.land/x/grammy@v1.44.0/mod.ts";
import { autoRetry } from "https://deno.land/x/grammy_auto_retry@v2.0.2/mod.ts";
import {
stream,
type StreamFlavor,
} from "https://deno.land/x/grammy_stream@v1.1.0/mod.ts";
type MyContext = StreamFlavor<Context>;
const bot = new Bot<MyContext>("");
bot.api.config.use(autoRetry()); // strongly recommended!
bot.use(stream());
async function* slowText() {
// emulate slow text generation
yield "This is som";
await new Promise((r) => setTimeout(r, 2000));
yield "e slowly gen";
await new Promise((r) => setTimeout(r, 2000));
yield "erated text";
}
// Telegram only supports streaming in private chats.
bot.chatType("private")
.command("stream", async (ctx) => {
// Stream the message!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
That’s it!
LLM Integration
Most LLM integrations let you stream the output while it is being generated. You can use this plugin to make the LLM output appear gradually in any private chat.
For example, if you use the AI SDK, your setup could look like this:
import { streamText } from "ai";
import { google } from "@ai-sdk/google";
bot.chatType("private")
.command("credits", async (ctx) => {
// Send prompt to LLM:
const { textStream } = streamText({
model: google("gemini-2.5-flash"),
prompt: "How cool are grammY bots?",
});
// Automatically stream response with grammY:
await ctx.replyWithMarkdownStream(textStream);
});2
3
4
5
6
7
8
9
10
11
12
13
14
import { streamText } from "npm:ai";
import { google } from "npm:@ai-sdk/google";
bot.chatType("private")
.command("credits", async (ctx) => {
// Send prompt to LLM:
const { textStream } = streamText({
model: google("gemini-2.5-flash"),
prompt: "How cool are grammY bots?",
});
// Automatically stream response with grammY:
await ctx.replyWithMarkdownStream(textStream);
});2
3
4
5
6
7
8
9
10
11
12
13
14
Make sure to replace gemini by whatever the latest model is.