Skip to content

Commit 3228729

Browse files
committed
feat: implement rendering functions for tables and Vega-Lite charts, and integrate Playwright for image generation
1 parent 0317093 commit 3228729

4 files changed

Lines changed: 542 additions & 4 deletions

File tree

index.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,21 @@ import {
88
type IAdminForth,
99
} from "adminforth";
1010
import { AdapterOptions } from "./types.js";
11+
import { getFinalMessageStreamPreview, renderFinalMessageImages } from "./renderers.js";
1112
import { randomInt } from "node:crypto";
1213
export type { AdapterOptions, TelegramStreamingMode } from "./types.js";
14+
export {
15+
getFinalMessageStreamPreview,
16+
renderFinalMessageImages,
17+
renderHtmlBlockToPng,
18+
renderTablePng,
19+
renderVegaLitePng,
20+
type RenderedMessage,
21+
type RenderedMessageImage,
22+
type RenderTableColumn,
23+
type RenderTablePngInput,
24+
type VegaLiteSpec,
25+
} from "./renderers.js";
1326

1427
type TelegramUpdate = {
1528
message?: {
@@ -165,16 +178,18 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
165178
};
166179

167180
const flushDraft = async () => {
181+
const draftPreviewText = getFinalMessageStreamPreview(text);
182+
168183
if (
169184
closed ||
170185
done ||
171186
streamingMode !== "draft" ||
172-
!text
187+
!draftPreviewText
173188
) {
174189
return;
175190
}
176191

177-
const draftText = truncateTelegramDraft(text);
192+
const draftText = truncateTelegramDraft(draftPreviewText);
178193

179194
if (draftText === lastDraftText) {
180195
return;
@@ -225,7 +240,7 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
225240
stopTyping();
226241
clearDraftTimer();
227242

228-
await this.sendMessage(
243+
await this.sendFinalMessage(
229244
chatId,
230245
text || event.text,
231246
);
@@ -301,6 +316,34 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
301316
}
302317
}
303318

319+
private async sendFinalMessage(chatId: string, text: string) {
320+
const renderedMessage = await renderFinalMessageImages(text);
321+
322+
await this.sendMessage(chatId, renderedMessage.text);
323+
324+
for (const image of renderedMessage.images) {
325+
await this.sendPhoto(chatId, image.buffer, image.filename);
326+
}
327+
}
328+
329+
private async sendPhoto(chatId: string, png: Buffer, filename: string) {
330+
const photoBytes = new Uint8Array(png);
331+
const formData = new FormData();
332+
formData.append("chat_id", chatId);
333+
formData.append("photo", new Blob([photoBytes], {
334+
type: "image/png",
335+
}), filename);
336+
337+
const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendPhoto`, {
338+
method: "POST",
339+
body: formData,
340+
});
341+
342+
if (!response.ok) {
343+
throw new Error(`Telegram sendPhoto failed: ${response.status} ${await response.text()}`);
344+
}
345+
}
346+
304347
private async sendChatAction(chatId: string, action: "typing") {
305348
const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendChatAction`, {
306349
method: "POST",
@@ -343,4 +386,4 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
343386
}
344387
}
345388

346-
export default TelegramChatSurfaceAdapter;
389+
export default TelegramChatSurfaceAdapter;

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
"semantic-release-slack-bot": "^4.0.2",
3030
"typescript": "^5.9.3"
3131
},
32+
"dependencies": {
33+
"playwright": "^1.57.0"
34+
},
3235
"peerDependencies": {
3336
"@adminforth/agent": ">=1.0.2",
3437
"adminforth": ">=2.59.0"

pnpm-lock.yaml

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)