@coffer-org/plugin-telegram 2.1.0 → 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.
- package/dist/runtime/bot.js +43 -2
- package/dist/runtime/chain-store.d.ts +1 -0
- package/dist/runtime/chain-store.js +9 -2
- package/dist/schema.js +23 -30
- package/package.json +4 -4
package/dist/runtime/bot.js
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
@@ -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({
|
|
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/dist/schema.js
CHANGED
|
@@ -4391,11 +4391,11 @@ function resolveUnits(u) {
|
|
|
4391
4391
|
//#endregion
|
|
4392
4392
|
//#region ../sdk/src/currencies.ts
|
|
4393
4393
|
/**
|
|
4394
|
-
*
|
|
4395
|
-
*
|
|
4394
|
+
* Currencies from built-in `Intl` (ISO 4217), with NO hand list or npm dependency.
|
|
4395
|
+
* Codes come from `Intl.supportedValuesOf('currency')`; symbols/names are localized through
|
|
4396
4396
|
* `Intl.NumberFormat`/`Intl.DisplayNames`.
|
|
4397
4397
|
*/
|
|
4398
|
-
/**
|
|
4398
|
+
/** All ISO 4217 codes. Fallback: several major codes for older runtimes. */
|
|
4399
4399
|
var CURRENCY_CODES = (() => {
|
|
4400
4400
|
try {
|
|
4401
4401
|
return Intl.supportedValuesOf("currency");
|
|
@@ -4410,26 +4410,26 @@ var CURRENCY_CODES = (() => {
|
|
|
4410
4410
|
}
|
|
4411
4411
|
})();
|
|
4412
4412
|
var CODE_SET = new Set(CURRENCY_CODES);
|
|
4413
|
-
/**
|
|
4413
|
+
/** Whether an ISO 4217 code is valid. */
|
|
4414
4414
|
var isCurrencyCode = (c) => CODE_SET.has(c);
|
|
4415
4415
|
//#endregion
|
|
4416
4416
|
//#region ../sdk/src/fields/validation.ts
|
|
4417
|
-
/**
|
|
4417
|
+
/** Structured message for zod: JSON {code, params}. Decoded by mutate.ts. */
|
|
4418
4418
|
function vmsg(code, params) {
|
|
4419
4419
|
return JSON.stringify(params ? {
|
|
4420
4420
|
code,
|
|
4421
4421
|
params
|
|
4422
4422
|
} : { code });
|
|
4423
4423
|
}
|
|
4424
|
-
/** v4 error-map:
|
|
4424
|
+
/** v4 error-map: message for a missing value (formerly required_error). */
|
|
4425
4425
|
function reqErr(code = "required") {
|
|
4426
4426
|
return { error: (iss) => iss.input === void 0 ? vmsg(code) : void 0 };
|
|
4427
4427
|
}
|
|
4428
|
-
/** v4 error-map:
|
|
4428
|
+
/** v4 error-map: message for an invalid type (formerly invalid_type_error). */
|
|
4429
4429
|
function typeErr(code = "invalid_type") {
|
|
4430
4430
|
return { error: (iss) => iss.code === "invalid_type" ? vmsg(code) : void 0 };
|
|
4431
4431
|
}
|
|
4432
|
-
/** v4 error-map: required + invalid_type
|
|
4432
|
+
/** v4 error-map: required + invalid_type together (formerly required_error + invalid_type_error). */
|
|
4433
4433
|
function reqTypeErr() {
|
|
4434
4434
|
return { error: (iss) => iss.code === "invalid_type" ? iss.input === void 0 ? vmsg("required") : vmsg("invalid_type") : void 0 };
|
|
4435
4435
|
}
|
|
@@ -4447,10 +4447,10 @@ function jsonValue(raw) {
|
|
|
4447
4447
|
return raw;
|
|
4448
4448
|
}
|
|
4449
4449
|
/**
|
|
4450
|
-
*
|
|
4451
|
-
*
|
|
4452
|
-
* Single
|
|
4453
|
-
*
|
|
4450
|
+
* Factory for fields that store JSON and validate it with a nested zod schema.
|
|
4451
|
+
* Accepts a native object/array (native form state) OR a JSON string (legacy).
|
|
4452
|
+
* Single parse through jsonValue → inner.safeParse → issue `code`; an unparseable
|
|
4453
|
+
* string remains a string → code 'json'.
|
|
4454
4454
|
*/
|
|
4455
4455
|
function jsonRefined(inner, code) {
|
|
4456
4456
|
return unknown().superRefine((raw, ctx) => {
|
|
@@ -4522,16 +4522,16 @@ function normalizeOpts(rawIn) {
|
|
|
4522
4522
|
//#endregion
|
|
4523
4523
|
//#region ../sdk/src/field-presets.ts
|
|
4524
4524
|
/**
|
|
4525
|
-
*
|
|
4525
|
+
* Field presets are thin wrappers around the primitives in fields.ts.
|
|
4526
4526
|
*
|
|
4527
|
-
*
|
|
4528
|
-
*
|
|
4527
|
+
* Each preset = one kind (one widget). Presets do not accept `format`;
|
|
4528
|
+
* they are semantic types themselves. min/max/step go through `config`.
|
|
4529
4529
|
*
|
|
4530
|
-
*
|
|
4531
|
-
*
|
|
4530
|
+
* Presets are added to `f` through `composeF`, which guarantees no preset
|
|
4531
|
+
* overrides a primitive.
|
|
4532
4532
|
*
|
|
4533
|
-
*
|
|
4534
|
-
*
|
|
4533
|
+
* The cyclic import from fields.ts is safe: factories/helpers are hoisted declarations,
|
|
4534
|
+
* and presets call them only inside their function bodies.
|
|
4535
4535
|
*/
|
|
4536
4536
|
function email(raw) {
|
|
4537
4537
|
const o = normalizeOpts(raw);
|
|
@@ -4705,9 +4705,9 @@ function link(raw) {
|
|
|
4705
4705
|
}, o.multiple ?? false));
|
|
4706
4706
|
}
|
|
4707
4707
|
var TEL_RE = /^\+?[\d\s()-]{4,}$/;
|
|
4708
|
-
/** Loose URL:
|
|
4708
|
+
/** Loose URL: any scheme:// OR dotted host (optional port/path). No spaces. */
|
|
4709
4709
|
var LINK_RE = /^([a-z][a-z0-9+.-]*:\/\/\S+|[\w-]+(\.[\w-]+)+(:\d+)?(\/\S*)?)$/i;
|
|
4710
|
-
/** CSS named colors (CSS Color Module L4)
|
|
4710
|
+
/** CSS named colors (CSS Color Module L4) for f.colorname. */
|
|
4711
4711
|
var CSS_COLOR_NAMES = /* @__PURE__ */ new Set([
|
|
4712
4712
|
"aliceblue",
|
|
4713
4713
|
"antiquewhite",
|
|
@@ -4966,7 +4966,7 @@ function reminder(raw) {
|
|
|
4966
4966
|
}
|
|
4967
4967
|
var _real = real;
|
|
4968
4968
|
var _int = int;
|
|
4969
|
-
/**
|
|
4969
|
+
/** Percentage 0..100 — real with rules:{min:0,max:100}. */
|
|
4970
4970
|
function percent(o) {
|
|
4971
4971
|
return _real({
|
|
4972
4972
|
...o,
|
|
@@ -4977,7 +4977,7 @@ function percent(o) {
|
|
|
4977
4977
|
}
|
|
4978
4978
|
});
|
|
4979
4979
|
}
|
|
4980
|
-
/**
|
|
4980
|
+
/** Year — int with rules:{min:1900,max:2100}; bounds can be overridden via rules.min/max. */
|
|
4981
4981
|
function year(o) {
|
|
4982
4982
|
return _int({
|
|
4983
4983
|
...o,
|
|
@@ -6027,13 +6027,6 @@ function wrapKey(opts, meta) {
|
|
|
6027
6027
|
role: opts.role
|
|
6028
6028
|
}
|
|
6029
6029
|
};
|
|
6030
|
-
if (opts.pinned) m = {
|
|
6031
|
-
...m,
|
|
6032
|
-
hints: {
|
|
6033
|
-
...m.hints,
|
|
6034
|
-
pinned: true
|
|
6035
|
-
}
|
|
6036
|
-
};
|
|
6037
6030
|
if (opts.default !== void 0) m = {
|
|
6038
6031
|
...m,
|
|
6039
6032
|
default: opts.default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffer-org/plugin-telegram",
|
|
3
|
-
"version": "2.
|
|
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.
|
|
29
|
-
"@coffer-org/sdk": "^2.1.
|
|
30
|
-
"@coffer-org/server": "^2.
|
|
28
|
+
"@coffer-org/plugin-orchestrator": "^2.2.0",
|
|
29
|
+
"@coffer-org/sdk": "^2.1.1",
|
|
30
|
+
"@coffer-org/server": "^2.3.0",
|
|
31
31
|
"input": "^1.0.1",
|
|
32
32
|
"teleproto": "^1.228.1"
|
|
33
33
|
},
|