@coffer-org/plugin-telegram 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ import { NewMessage } from 'teleproto/events/index.js';
7
7
  import { handleIncoming, loadGatePolicy, makeLiveChannel, chunk } from '@coffer-org/plugin-orchestrator/runtime';
8
8
  import { recordUser, recordAssistant, buildChain } from "./chain-store.js";
9
9
  import { loadBotConfig, hasCredentials, loadReasoningDisplay } from "./config.js";
10
+ import { saveUploadBytes } from '@coffer-org/server/uploads';
10
11
  import { getLogger } from '@coffer-org/sdk/logger';
11
12
  const log = getLogger('telegram');
12
13
  const FALLBACK_REPLY_WINDOW_MS = 1_800_000;
@@ -77,6 +78,25 @@ export function makeReplyFactory(t, display) {
77
78
  return display === 'separate' ? ch : { ...ch, segment() { } };
78
79
  };
79
80
  }
81
+ function mediaInfo(message) {
82
+ const media = message.media;
83
+ if (!media)
84
+ return null;
85
+ const documentName = media.document?.attributes?.find((a) => typeof a.fileName === 'string')?.fileName;
86
+ if (media.document)
87
+ return { mime: media.document.mimeType, label: documentName ? path.basename(documentName) : 'attachment' };
88
+ if (media.photo)
89
+ return { mime: 'image/jpeg', label: 'photo.jpg' };
90
+ return null;
91
+ }
92
+ async function downloadAttachment(tg, message, info) {
93
+ const downloaded = await tg.downloadMedia(message, {});
94
+ if (!downloaded || typeof downloaded === 'string')
95
+ throw new Error('Telegram returned no in-memory media bytes');
96
+ const bytes = Buffer.isBuffer(downloaded) ? downloaded : Buffer.from(downloaded);
97
+ const stored = await saveUploadBytes(bytes, { originalName: info.label, mime: info.mime });
98
+ return { name: stored.name, ...(stored.mime ? { mime: stored.mime } : {}), size: stored.size, label: info.label };
99
+ }
80
100
  function loadSessionString(file) {
81
101
  try {
82
102
  return fs.readFileSync(file, 'utf-8');
@@ -182,7 +202,8 @@ export async function startBot(opts = {}) {
182
202
  if (message.senderId && meId && message.senderId.toString() === meId)
183
203
  return;
184
204
  const text = (message.text || message.message || '').trim();
185
- if (!text)
205
+ const attachmentInfo = mediaInfo(message);
206
+ if (!text && !attachmentInfo)
186
207
  return;
187
208
  let chatLabel = String(event.chatId ?? '?');
188
209
  let peer = event.chatId;
@@ -194,7 +215,7 @@ export async function startBot(opts = {}) {
194
215
  }
195
216
  catch {
196
217
  }
197
- log.info(`msg [${chatLabel}]: ${text.slice(0, 80)}`);
218
+ log.info(`msg [${chatLabel}]: ${(text || `[${attachmentInfo?.label ?? 'attachment'}]`).slice(0, 80)}`);
198
219
  const stopTyping = startTypingPeer(peer);
199
220
  try {
200
221
  const chatId = String(event.chatId ?? '?');
@@ -235,6 +256,25 @@ export async function startBot(opts = {}) {
235
256
  }
236
257
  };
237
258
  const messages = await buildChain(incomingMsgId, { chatId, fetch, botId: meId ?? '' });
259
+ let preparedMessages;
260
+ const prepareAttachments = attachmentInfo
261
+ ? async () => {
262
+ if (preparedMessages)
263
+ return preparedMessages;
264
+ const attachment = await downloadAttachment(tg, message, attachmentInfo);
265
+ await recordUser({
266
+ chatId,
267
+ msgId: incomingMsgId,
268
+ sender: displayName,
269
+ text,
270
+ attachments: [attachment],
271
+ ts: message.date ?? 0,
272
+ replyToId,
273
+ });
274
+ preparedMessages = messages.map((m, i) => i === messages.length - 1 ? { ...m, attachments: [attachment] } : m);
275
+ return preparedMessages;
276
+ }
277
+ : undefined;
238
278
  const reasoningDisplay = await loadReasoningDisplay();
239
279
  const connector = {
240
280
  id: 'telegram',
@@ -259,6 +299,7 @@ export async function startBot(opts = {}) {
259
299
  channelSystem: TELEGRAM_FORMAT,
260
300
  sender: { id: userId, displayName },
261
301
  messages,
302
+ ...(prepareAttachments ? { prepareAttachments } : {}),
262
303
  });
263
304
  }
264
305
  finally {
@@ -10,6 +10,7 @@ export declare function recordUser(m: {
10
10
  msgId: string;
11
11
  sender?: string | null;
12
12
  text: string;
13
+ attachments?: NonNullable<ConvMessage['attachments']>;
13
14
  ts: number;
14
15
  replyToId: string | null;
15
16
  }): Promise<void>;
@@ -4,7 +4,7 @@ const log = getLogger('telegram');
4
4
  const CONNECTOR = 'telegram';
5
5
  const DEFAULT_MAX_DEPTH = 30;
6
6
  export async function recordUser(m) {
7
- await putThreadMessage({ connector: CONNECTOR, chatId: m.chatId, msgId: m.msgId, role: 'user', sender: m.sender ?? null, text: m.text, ts: m.ts, replyToId: m.replyToId });
7
+ await putThreadMessage({ connector: CONNECTOR, chatId: m.chatId, msgId: m.msgId, role: 'user', sender: m.sender ?? null, attachments: m.attachments, text: m.text, ts: m.ts, replyToId: m.replyToId });
8
8
  }
9
9
  export async function recordAssistant(m) {
10
10
  if (m.botMsgId == null)
@@ -36,7 +36,14 @@ export async function buildChain(headMsgId, opts) {
36
36
  stored = { msgId: cur, role, sender: null, text: fetched.text, ts: fetched.date, replyToId: fetched.replyToId };
37
37
  }
38
38
  if (stored.role !== 'reasoning') {
39
- acc.push({ role: stored.role, content: stored.text, sender: stored.sender, msgId: stored.msgId, ts: stored.ts });
39
+ acc.push({
40
+ role: stored.role,
41
+ content: stored.text,
42
+ sender: stored.sender,
43
+ ...(stored.attachments ? { attachments: stored.attachments } : {}),
44
+ msgId: stored.msgId,
45
+ ts: stored.ts,
46
+ });
40
47
  }
41
48
  cur = stored.replyToId;
42
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/plugin-telegram",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -25,9 +25,9 @@
25
25
  "test": "node --import tsx --test \"src/runtime/*.test.ts\""
26
26
  },
27
27
  "dependencies": {
28
- "@coffer-org/plugin-orchestrator": "^2.1.1",
28
+ "@coffer-org/plugin-orchestrator": "^2.2.0",
29
29
  "@coffer-org/sdk": "^2.1.1",
30
- "@coffer-org/server": "^2.2.2",
30
+ "@coffer-org/server": "^2.3.0",
31
31
  "input": "^1.0.1",
32
32
  "teleproto": "^1.228.1"
33
33
  },