@elizaos/plugin-telegram 2.0.0-alpha.9 → 2.0.0-beta.1
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/README.md +1 -1
- package/dist/account-auth-service.d.ts +100 -0
- package/dist/account-auth-service.js +25 -0
- package/dist/account-auth-service.js.map +1 -0
- package/dist/chunk-KFEFF6RI.js +642 -0
- package/dist/chunk-KFEFF6RI.js.map +1 -0
- package/dist/index.d.ts +622 -1
- package/dist/index.js +2686 -339
- package/dist/index.js.map +1 -1
- package/package.json +19 -21
package/dist/index.js
CHANGED
|
@@ -1,3 +1,322 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TelegramAccountAuthSession,
|
|
3
|
+
clearTelegramAccountAuthState,
|
|
4
|
+
clearTelegramAccountSession,
|
|
5
|
+
defaultTelegramAccountDeviceModel,
|
|
6
|
+
defaultTelegramAccountSystemVersion,
|
|
7
|
+
loadTelegramAccountSessionString,
|
|
8
|
+
resolveTelegramAccountSessionFile,
|
|
9
|
+
saveTelegramAccountSessionString,
|
|
10
|
+
telegramAccountAuthStateExists,
|
|
11
|
+
telegramAccountSessionExists
|
|
12
|
+
} from "./chunk-KFEFF6RI.js";
|
|
13
|
+
|
|
14
|
+
// src/index.ts
|
|
15
|
+
import { getConnectorAccountManager, logger as logger7 } from "@elizaos/core";
|
|
16
|
+
|
|
17
|
+
// src/account-setup-routes.ts
|
|
18
|
+
function isConnectorSetupService(service) {
|
|
19
|
+
if (!service || typeof service !== "object") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const candidate = service;
|
|
23
|
+
return typeof candidate.getConfig === "function" && typeof candidate.updateConfig === "function" && typeof candidate.persistConfig === "function";
|
|
24
|
+
}
|
|
25
|
+
function getSetupService(runtime) {
|
|
26
|
+
const service = runtime.getService("connector-setup");
|
|
27
|
+
return isConnectorSetupService(service) ? service : null;
|
|
28
|
+
}
|
|
29
|
+
var telegramAccountAuthSession = null;
|
|
30
|
+
async function stopTelegramAccountAuthSession() {
|
|
31
|
+
if (telegramAccountAuthSession) {
|
|
32
|
+
try {
|
|
33
|
+
await telegramAccountAuthSession.stop();
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
telegramAccountAuthSession = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function readConnectorConfig(config) {
|
|
40
|
+
const connectors = config.connectors;
|
|
41
|
+
const raw = connectors?.telegramAccount;
|
|
42
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
return raw;
|
|
46
|
+
}
|
|
47
|
+
function hasConfiguredTelegramAccount(connConfig) {
|
|
48
|
+
return Boolean(
|
|
49
|
+
typeof connConfig.phone === "string" && connConfig.phone.trim() && (typeof connConfig.appId === "string" || typeof connConfig.appId === "number") && typeof connConfig.appHash === "string" && connConfig.appHash.trim() && typeof connConfig.deviceModel === "string" && connConfig.deviceModel.trim() && typeof connConfig.systemVersion === "string" && connConfig.systemVersion.trim() && connConfig.enabled !== false
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
function resolveConfiguredPhone(runtime, connConfig) {
|
|
53
|
+
if (typeof connConfig.phone === "string" && connConfig.phone.trim().length > 0) {
|
|
54
|
+
return connConfig.phone.trim();
|
|
55
|
+
}
|
|
56
|
+
const setting = runtime.getSetting("TELEGRAM_ACCOUNT_PHONE");
|
|
57
|
+
return typeof setting === "string" && setting.trim().length > 0 ? setting.trim() : null;
|
|
58
|
+
}
|
|
59
|
+
function resolveService(runtime) {
|
|
60
|
+
const service = runtime.getService("telegram-account");
|
|
61
|
+
return service ?? null;
|
|
62
|
+
}
|
|
63
|
+
function isServiceConnected(service) {
|
|
64
|
+
if (!service) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (typeof service.isConnected === "function") {
|
|
68
|
+
return service.isConnected();
|
|
69
|
+
}
|
|
70
|
+
const withFlags = service;
|
|
71
|
+
if (typeof withFlags.isServiceConnected === "function") {
|
|
72
|
+
return withFlags.isServiceConnected();
|
|
73
|
+
}
|
|
74
|
+
return withFlags.connected === true;
|
|
75
|
+
}
|
|
76
|
+
function statusFromState(runtime, config) {
|
|
77
|
+
const connectorConfig = readConnectorConfig(config);
|
|
78
|
+
const configured = hasConfiguredTelegramAccount(connectorConfig);
|
|
79
|
+
const sessExists = telegramAccountSessionExists();
|
|
80
|
+
const authSnapshot = telegramAccountAuthSession?.getSnapshot() ?? null;
|
|
81
|
+
const service = resolveService(runtime);
|
|
82
|
+
const serviceConnected = isServiceConnected(service);
|
|
83
|
+
const serviceAccount = typeof service?.getAccountSummary === "function" ? service.getAccountSummary() : null;
|
|
84
|
+
const fallbackPhone = resolveConfiguredPhone(runtime, connectorConfig);
|
|
85
|
+
let status = authSnapshot?.status ?? (serviceConnected ? "connected" : configured || sessExists ? "configured" : "idle");
|
|
86
|
+
if (serviceConnected && status === "configured") {
|
|
87
|
+
status = "connected";
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
available: true,
|
|
91
|
+
status,
|
|
92
|
+
configured,
|
|
93
|
+
sessionExists: sessExists,
|
|
94
|
+
serviceConnected,
|
|
95
|
+
restartRequired: status === "configured" && !serviceConnected,
|
|
96
|
+
hasAppCredentials: Boolean(
|
|
97
|
+
(typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" && connectorConfig.appHash.trim().length > 0
|
|
98
|
+
),
|
|
99
|
+
phone: authSnapshot?.phone ?? fallbackPhone,
|
|
100
|
+
isCodeViaApp: authSnapshot?.isCodeViaApp ?? false,
|
|
101
|
+
account: authSnapshot?.account ?? serviceAccount ?? null,
|
|
102
|
+
error: authSnapshot?.error ?? null
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function ensureConnectorBlock(config) {
|
|
106
|
+
if (!config.connectors) {
|
|
107
|
+
config.connectors = {};
|
|
108
|
+
}
|
|
109
|
+
const connectors = config.connectors;
|
|
110
|
+
if (!connectors.telegramAccount || typeof connectors.telegramAccount !== "object" || Array.isArray(connectors.telegramAccount)) {
|
|
111
|
+
connectors.telegramAccount = {};
|
|
112
|
+
}
|
|
113
|
+
return connectors.telegramAccount;
|
|
114
|
+
}
|
|
115
|
+
function createSessionOptions(config) {
|
|
116
|
+
const connectorConfig = readConnectorConfig(config);
|
|
117
|
+
return {
|
|
118
|
+
deviceModel: typeof connectorConfig.deviceModel === "string" && connectorConfig.deviceModel.trim().length > 0 ? connectorConfig.deviceModel.trim() : defaultTelegramAccountDeviceModel(),
|
|
119
|
+
systemVersion: typeof connectorConfig.systemVersion === "string" && connectorConfig.systemVersion.trim().length > 0 ? connectorConfig.systemVersion.trim() : defaultTelegramAccountSystemVersion()
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function ensureAuthSession(config) {
|
|
123
|
+
if (telegramAccountAuthSession) {
|
|
124
|
+
return telegramAccountAuthSession;
|
|
125
|
+
}
|
|
126
|
+
if (!telegramAccountAuthStateExists()) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
telegramAccountAuthSession = new TelegramAccountAuthSession(
|
|
130
|
+
createSessionOptions(config)
|
|
131
|
+
);
|
|
132
|
+
return telegramAccountAuthSession;
|
|
133
|
+
}
|
|
134
|
+
async function handleStatus(_req, res, runtime) {
|
|
135
|
+
const setupService = getSetupService(runtime);
|
|
136
|
+
const config = setupService?.getConfig() ?? {};
|
|
137
|
+
ensureAuthSession(config);
|
|
138
|
+
res.status(200).json(statusFromState(runtime, config));
|
|
139
|
+
}
|
|
140
|
+
async function handleAuthStart(req, res, runtime) {
|
|
141
|
+
const body = req.body ?? {};
|
|
142
|
+
const setupService = getSetupService(runtime);
|
|
143
|
+
const config = setupService?.getConfig() ?? {};
|
|
144
|
+
const connectorConfig = readConnectorConfig(config);
|
|
145
|
+
const phone = typeof body.phone === "string" && body.phone.trim() || resolveConfiguredPhone(runtime, connectorConfig);
|
|
146
|
+
if (!phone) {
|
|
147
|
+
res.status(400).json({ error: "telegram phone number is required" });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
await telegramAccountAuthSession?.stop();
|
|
151
|
+
telegramAccountAuthSession = new TelegramAccountAuthSession(
|
|
152
|
+
createSessionOptions(config)
|
|
153
|
+
);
|
|
154
|
+
const credentials = hasConfiguredTelegramAccount(connectorConfig) && (typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" ? {
|
|
155
|
+
apiId: Number(connectorConfig.appId),
|
|
156
|
+
apiHash: connectorConfig.appHash
|
|
157
|
+
} : null;
|
|
158
|
+
try {
|
|
159
|
+
await telegramAccountAuthSession.start({ phone, credentials });
|
|
160
|
+
const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
|
|
161
|
+
if (resolved && setupService) {
|
|
162
|
+
setupService.updateConfig((cfg) => {
|
|
163
|
+
Object.assign(ensureConnectorBlock(cfg), resolved);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const freshConfig = setupService?.getConfig() ?? config;
|
|
167
|
+
res.status(200).json(statusFromState(runtime, freshConfig));
|
|
168
|
+
} catch (error) {
|
|
169
|
+
res.status(500).json({
|
|
170
|
+
error: error instanceof Error ? error.message : String(error)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function handleAuthSubmit(req, res, runtime) {
|
|
175
|
+
const body = req.body ?? {};
|
|
176
|
+
const setupService = getSetupService(runtime);
|
|
177
|
+
const config = setupService?.getConfig() ?? {};
|
|
178
|
+
if (!ensureAuthSession(config)) {
|
|
179
|
+
res.status(400).json({ error: "telegram login session has not been started" });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!telegramAccountAuthSession) {
|
|
183
|
+
res.status(400).json({ error: "telegram login session has not been started" });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
await telegramAccountAuthSession.submit(body);
|
|
188
|
+
const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
|
|
189
|
+
if (resolved && setupService) {
|
|
190
|
+
setupService.updateConfig((cfg) => {
|
|
191
|
+
Object.assign(ensureConnectorBlock(cfg), resolved);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const freshConfig = setupService?.getConfig() ?? config;
|
|
195
|
+
res.status(200).json(statusFromState(runtime, freshConfig));
|
|
196
|
+
} catch (error) {
|
|
197
|
+
res.status(500).json({
|
|
198
|
+
error: error instanceof Error ? error.message : String(error)
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function handleDisconnect(_req, res, runtime) {
|
|
203
|
+
await telegramAccountAuthSession?.stop();
|
|
204
|
+
telegramAccountAuthSession = null;
|
|
205
|
+
clearTelegramAccountAuthState();
|
|
206
|
+
clearTelegramAccountSession();
|
|
207
|
+
const service = resolveService(runtime);
|
|
208
|
+
if (typeof service?.stop === "function") {
|
|
209
|
+
await service.stop();
|
|
210
|
+
}
|
|
211
|
+
const setupService = getSetupService(runtime);
|
|
212
|
+
if (setupService) {
|
|
213
|
+
setupService.updateConfig((cfg) => {
|
|
214
|
+
const connectors = cfg.connectors;
|
|
215
|
+
if (connectors?.telegramAccount) {
|
|
216
|
+
delete connectors.telegramAccount;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const config = setupService?.getConfig() ?? {};
|
|
221
|
+
res.status(200).json({
|
|
222
|
+
ok: true,
|
|
223
|
+
...statusFromState(runtime, config)
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
var telegramAccountRoutes = [
|
|
227
|
+
{
|
|
228
|
+
type: "GET",
|
|
229
|
+
path: "/api/telegram-account/status",
|
|
230
|
+
handler: handleStatus,
|
|
231
|
+
rawPath: true
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
type: "POST",
|
|
235
|
+
path: "/api/telegram-account/auth/start",
|
|
236
|
+
handler: handleAuthStart,
|
|
237
|
+
rawPath: true
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
type: "POST",
|
|
241
|
+
path: "/api/telegram-account/auth/submit",
|
|
242
|
+
handler: handleAuthSubmit,
|
|
243
|
+
rawPath: true
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
type: "POST",
|
|
247
|
+
path: "/api/telegram-account/disconnect",
|
|
248
|
+
handler: handleDisconnect,
|
|
249
|
+
rawPath: true
|
|
250
|
+
}
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
// src/accounts.ts
|
|
254
|
+
var DEFAULT_ACCOUNT_ID = "default";
|
|
255
|
+
function readNonEmptyString(value) {
|
|
256
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
257
|
+
}
|
|
258
|
+
function normalizeTelegramAccountId(accountId) {
|
|
259
|
+
return readNonEmptyString(accountId) ?? DEFAULT_ACCOUNT_ID;
|
|
260
|
+
}
|
|
261
|
+
function getTelegramMultiAccountConfig(runtime) {
|
|
262
|
+
const characterTelegram = runtime.character?.settings?.telegram;
|
|
263
|
+
return {
|
|
264
|
+
enabled: characterTelegram?.enabled,
|
|
265
|
+
botToken: characterTelegram?.botToken,
|
|
266
|
+
apiRoot: characterTelegram?.apiRoot,
|
|
267
|
+
accounts: characterTelegram?.accounts
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function listTelegramAccountIds(runtime) {
|
|
271
|
+
const accounts = getTelegramMultiAccountConfig(runtime).accounts;
|
|
272
|
+
if (!accounts || typeof accounts !== "object") {
|
|
273
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
274
|
+
}
|
|
275
|
+
const ids = Object.keys(accounts).filter(Boolean);
|
|
276
|
+
return ids.length > 0 ? ids.slice().sort((a, b) => a.localeCompare(b)) : [DEFAULT_ACCOUNT_ID];
|
|
277
|
+
}
|
|
278
|
+
function resolveDefaultTelegramAccountId(runtime) {
|
|
279
|
+
const ids = listTelegramAccountIds(runtime);
|
|
280
|
+
return ids.includes(DEFAULT_ACCOUNT_ID) ? DEFAULT_ACCOUNT_ID : ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
281
|
+
}
|
|
282
|
+
function getAccountConfig(runtime, accountId) {
|
|
283
|
+
const accounts = getTelegramMultiAccountConfig(runtime).accounts;
|
|
284
|
+
return accounts && typeof accounts === "object" ? accounts[accountId] : void 0;
|
|
285
|
+
}
|
|
286
|
+
function resolveTelegramBotToken(runtime, accountId, merged) {
|
|
287
|
+
const configToken = readNonEmptyString(merged.botToken);
|
|
288
|
+
if (configToken) {
|
|
289
|
+
return configToken;
|
|
290
|
+
}
|
|
291
|
+
if (accountId !== DEFAULT_ACCOUNT_ID) {
|
|
292
|
+
return void 0;
|
|
293
|
+
}
|
|
294
|
+
return readNonEmptyString(runtime.getSetting("TELEGRAM_BOT_TOKEN")) ?? readNonEmptyString(process.env.TELEGRAM_BOT_TOKEN);
|
|
295
|
+
}
|
|
296
|
+
function resolveTelegramAccount(runtime, accountId) {
|
|
297
|
+
const normalizedAccountId = normalizeTelegramAccountId(accountId);
|
|
298
|
+
const multi = getTelegramMultiAccountConfig(runtime);
|
|
299
|
+
const accountConfig = getAccountConfig(runtime, normalizedAccountId) ?? {};
|
|
300
|
+
const merged = {
|
|
301
|
+
enabled: multi.enabled,
|
|
302
|
+
botToken: multi.botToken,
|
|
303
|
+
apiRoot: multi.apiRoot,
|
|
304
|
+
...accountConfig
|
|
305
|
+
};
|
|
306
|
+
const apiRoot = readNonEmptyString(merged.apiRoot) ?? readNonEmptyString(runtime.getSetting("TELEGRAM_API_ROOT")) ?? readNonEmptyString(process.env.TELEGRAM_API_ROOT) ?? "https://api.telegram.org";
|
|
307
|
+
return {
|
|
308
|
+
accountId: normalizedAccountId,
|
|
309
|
+
enabled: multi.enabled !== false && merged.enabled !== false,
|
|
310
|
+
name: readNonEmptyString(merged.name),
|
|
311
|
+
botToken: resolveTelegramBotToken(runtime, normalizedAccountId, merged),
|
|
312
|
+
apiRoot,
|
|
313
|
+
config: merged
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function listEnabledTelegramAccounts(runtime) {
|
|
317
|
+
return listTelegramAccountIds(runtime).map((accountId) => resolveTelegramAccount(runtime, accountId)).filter((account) => account.enabled && account.botToken);
|
|
318
|
+
}
|
|
319
|
+
|
|
1
320
|
// src/constants.ts
|
|
2
321
|
var MESSAGE_CONSTANTS = {
|
|
3
322
|
MAX_MESSAGES: 50,
|
|
@@ -12,38 +331,97 @@ var MESSAGE_CONSTANTS = {
|
|
|
12
331
|
};
|
|
13
332
|
var TELEGRAM_SERVICE_NAME = "telegram";
|
|
14
333
|
|
|
15
|
-
// src/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
334
|
+
// src/connector-account-provider.ts
|
|
335
|
+
function nowMs() {
|
|
336
|
+
return Date.now();
|
|
337
|
+
}
|
|
338
|
+
function synthesizeAccount(accountId, name, externalId) {
|
|
339
|
+
return {
|
|
340
|
+
id: accountId,
|
|
341
|
+
provider: TELEGRAM_SERVICE_NAME,
|
|
342
|
+
label: name ?? `Telegram (${accountId})`,
|
|
343
|
+
role: "AGENT",
|
|
344
|
+
purpose: ["messaging"],
|
|
345
|
+
accessGate: "open",
|
|
346
|
+
status: "connected",
|
|
347
|
+
externalId,
|
|
348
|
+
displayHandle: name,
|
|
349
|
+
createdAt: nowMs(),
|
|
350
|
+
updatedAt: nowMs(),
|
|
351
|
+
metadata: {
|
|
352
|
+
synthesized: true,
|
|
353
|
+
source: "env"
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function createTelegramConnectorAccountProvider(runtime) {
|
|
358
|
+
return {
|
|
359
|
+
provider: TELEGRAM_SERVICE_NAME,
|
|
360
|
+
label: "Telegram",
|
|
361
|
+
listAccounts: async (manager) => {
|
|
362
|
+
const persisted = await manager.getStorage().listAccounts(TELEGRAM_SERVICE_NAME);
|
|
363
|
+
const persistedById = new Map(persisted.map((a) => [a.id, a]));
|
|
364
|
+
const enabled = listEnabledTelegramAccounts(runtime);
|
|
365
|
+
const synthesized = enabled.filter((account) => !persistedById.has(account.accountId)).map(
|
|
366
|
+
(account) => synthesizeAccount(account.accountId, account.name, void 0)
|
|
367
|
+
);
|
|
368
|
+
if (synthesized.length === 0 && persisted.length === 0) {
|
|
369
|
+
const fallback = resolveTelegramAccount(runtime, DEFAULT_ACCOUNT_ID);
|
|
370
|
+
if (fallback.botToken) {
|
|
371
|
+
synthesized.push(
|
|
372
|
+
synthesizeAccount(DEFAULT_ACCOUNT_ID, fallback.name, void 0)
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return [...persisted, ...synthesized];
|
|
377
|
+
},
|
|
378
|
+
createAccount: async (input) => {
|
|
379
|
+
return {
|
|
380
|
+
...input,
|
|
381
|
+
provider: TELEGRAM_SERVICE_NAME,
|
|
382
|
+
role: input.role ?? "AGENT",
|
|
383
|
+
purpose: input.purpose ?? ["messaging"],
|
|
384
|
+
accessGate: input.accessGate ?? "open",
|
|
385
|
+
status: input.status ?? "connected"
|
|
386
|
+
};
|
|
387
|
+
},
|
|
388
|
+
patchAccount: async (_accountId, patch) => {
|
|
389
|
+
return { ...patch, provider: TELEGRAM_SERVICE_NAME };
|
|
390
|
+
},
|
|
391
|
+
deleteAccount: async () => {
|
|
392
|
+
}
|
|
393
|
+
// Telegram bots use a long-lived bot token; no OAuth flow exists.
|
|
394
|
+
// startOAuth / completeOAuth are intentionally omitted.
|
|
395
|
+
};
|
|
396
|
+
}
|
|
25
397
|
|
|
26
398
|
// src/messageManager.ts
|
|
399
|
+
import fs from "fs";
|
|
27
400
|
import {
|
|
28
401
|
ChannelType,
|
|
402
|
+
createUniqueUuid,
|
|
29
403
|
EventType,
|
|
404
|
+
lifeOpsPassiveConnectorsEnabled,
|
|
405
|
+
logger as logger2,
|
|
30
406
|
ModelType,
|
|
31
|
-
ServiceType
|
|
32
|
-
createUniqueUuid,
|
|
33
|
-
logger as logger2
|
|
407
|
+
ServiceType
|
|
34
408
|
} from "@elizaos/core";
|
|
35
409
|
import { Markup as Markup2 } from "telegraf";
|
|
36
410
|
|
|
37
411
|
// src/utils.ts
|
|
38
|
-
import { Markup } from "telegraf";
|
|
39
412
|
import { logger } from "@elizaos/core";
|
|
413
|
+
import { Markup } from "telegraf";
|
|
40
414
|
var TELEGRAM_RESERVED_REGEX = /([_*[\]()~`>#+\-=|{}.!\\])/g;
|
|
41
415
|
function escapePlainText(text) {
|
|
42
|
-
if (!text)
|
|
416
|
+
if (!text) {
|
|
417
|
+
return "";
|
|
418
|
+
}
|
|
43
419
|
return text.replace(TELEGRAM_RESERVED_REGEX, "\\$1");
|
|
44
420
|
}
|
|
45
421
|
function escapePlainTextPreservingBlockquote(text) {
|
|
46
|
-
if (!text)
|
|
422
|
+
if (!text) {
|
|
423
|
+
return "";
|
|
424
|
+
}
|
|
47
425
|
return text.split("\n").map((line) => {
|
|
48
426
|
const match = line.match(/^(>+\s?)(.*)$/);
|
|
49
427
|
if (match) {
|
|
@@ -53,11 +431,15 @@ function escapePlainTextPreservingBlockquote(text) {
|
|
|
53
431
|
}).join("\n");
|
|
54
432
|
}
|
|
55
433
|
function escapeCode(text) {
|
|
56
|
-
if (!text)
|
|
434
|
+
if (!text) {
|
|
435
|
+
return "";
|
|
436
|
+
}
|
|
57
437
|
return text.replace(/([`\\])/g, "\\$1");
|
|
58
438
|
}
|
|
59
439
|
function escapeUrl(url) {
|
|
60
|
-
if (!url)
|
|
440
|
+
if (!url) {
|
|
441
|
+
return "";
|
|
442
|
+
}
|
|
61
443
|
return url.replace(/([)\\])/g, "\\$1");
|
|
62
444
|
}
|
|
63
445
|
function convertMarkdownToTelegram(markdown) {
|
|
@@ -68,14 +450,18 @@ function convertMarkdownToTelegram(markdown) {
|
|
|
68
450
|
return placeholder;
|
|
69
451
|
}
|
|
70
452
|
let converted = markdown;
|
|
71
|
-
converted = converted.replace(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
453
|
+
converted = converted.replace(
|
|
454
|
+
/```(\w+)?\n([\s\S]*?)```/g,
|
|
455
|
+
(_match, lang, code) => {
|
|
456
|
+
const escapedCode = escapeCode(code);
|
|
457
|
+
const formatted = `\`\`\`${lang || ""}
|
|
458
|
+
${escapedCode}\`\`\``;
|
|
459
|
+
return storeReplacement(formatted);
|
|
460
|
+
}
|
|
461
|
+
);
|
|
76
462
|
converted = converted.replace(/`([^`]+)`/g, (_match, code) => {
|
|
77
463
|
const escapedCode = escapeCode(code);
|
|
78
|
-
const formatted =
|
|
464
|
+
const formatted = `\`${escapedCode}\``;
|
|
79
465
|
return storeReplacement(formatted);
|
|
80
466
|
});
|
|
81
467
|
converted = converted.replace(
|
|
@@ -97,20 +483,26 @@ function convertMarkdownToTelegram(markdown) {
|
|
|
97
483
|
const formatted = `~${formattedContent}~`;
|
|
98
484
|
return storeReplacement(formatted);
|
|
99
485
|
});
|
|
100
|
-
converted = converted.replace(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
486
|
+
converted = converted.replace(
|
|
487
|
+
/(?<!\*)\*([^*\n]+)\*(?!\*)/g,
|
|
488
|
+
(_match, content) => {
|
|
489
|
+
const formattedContent = escapePlainText(content);
|
|
490
|
+
const formatted = `_${formattedContent}_`;
|
|
491
|
+
return storeReplacement(formatted);
|
|
492
|
+
}
|
|
493
|
+
);
|
|
105
494
|
converted = converted.replace(/_([^_\n]+)_/g, (_match, content) => {
|
|
106
495
|
const formattedContent = escapePlainText(content);
|
|
107
496
|
const formatted = `_${formattedContent}_`;
|
|
108
497
|
return storeReplacement(formatted);
|
|
109
498
|
});
|
|
110
|
-
converted = converted.replace(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
499
|
+
converted = converted.replace(
|
|
500
|
+
/^(#{1,6})\s*(.*)$/gm,
|
|
501
|
+
(_match, _hashes, headerContent) => {
|
|
502
|
+
const formatted = `*${escapePlainText(headerContent.trim())}*`;
|
|
503
|
+
return storeReplacement(formatted);
|
|
504
|
+
}
|
|
505
|
+
);
|
|
114
506
|
const NULL_CHAR = String.fromCharCode(0);
|
|
115
507
|
const PLACEHOLDER_PATTERN = new RegExp(`(${NULL_CHAR}\\d+${NULL_CHAR})`, "g");
|
|
116
508
|
const PLACEHOLDER_TEST = new RegExp(`^${NULL_CHAR}\\d+${NULL_CHAR}$`);
|
|
@@ -123,16 +515,21 @@ function convertMarkdownToTelegram(markdown) {
|
|
|
123
515
|
}
|
|
124
516
|
}).join("");
|
|
125
517
|
const finalResult = finalEscaped.replace(PLACEHOLDER_REPLACE, (_, index) => {
|
|
126
|
-
return replacements[parseInt(index)];
|
|
518
|
+
return replacements[Number.parseInt(index, 10)];
|
|
127
519
|
});
|
|
128
520
|
return finalResult;
|
|
129
521
|
}
|
|
130
522
|
function convertToTelegramButtons(buttons) {
|
|
131
|
-
if (!buttons)
|
|
523
|
+
if (!buttons) {
|
|
524
|
+
return [];
|
|
525
|
+
}
|
|
132
526
|
const telegramButtons = [];
|
|
133
527
|
for (const button of buttons) {
|
|
134
|
-
if (!button
|
|
135
|
-
logger.warn(
|
|
528
|
+
if (!button?.text || !button.url) {
|
|
529
|
+
logger.warn(
|
|
530
|
+
{ src: "plugin:telegram", button },
|
|
531
|
+
"Invalid button configuration, skipping"
|
|
532
|
+
);
|
|
136
533
|
continue;
|
|
137
534
|
}
|
|
138
535
|
let telegramButton;
|
|
@@ -144,7 +541,10 @@ function convertToTelegramButtons(buttons) {
|
|
|
144
541
|
telegramButton = Markup.button.url(button.text, button.url);
|
|
145
542
|
break;
|
|
146
543
|
default:
|
|
147
|
-
logger.warn(
|
|
544
|
+
logger.warn(
|
|
545
|
+
{ src: "plugin:telegram", buttonKind: button.kind },
|
|
546
|
+
"Unknown button kind, treating as URL button"
|
|
547
|
+
);
|
|
148
548
|
telegramButton = Markup.button.url(button.text, button.url);
|
|
149
549
|
break;
|
|
150
550
|
}
|
|
@@ -153,15 +553,20 @@ function convertToTelegramButtons(buttons) {
|
|
|
153
553
|
return telegramButtons;
|
|
154
554
|
}
|
|
155
555
|
function cleanText(text) {
|
|
156
|
-
if (!text)
|
|
556
|
+
if (!text) {
|
|
557
|
+
return "";
|
|
558
|
+
}
|
|
157
559
|
return text.split("\0").join("");
|
|
158
560
|
}
|
|
159
561
|
|
|
160
562
|
// src/messageManager.ts
|
|
161
|
-
import fs from "fs";
|
|
162
563
|
var MAX_MESSAGE_LENGTH = 4096;
|
|
564
|
+
function isPdfTextService(service) {
|
|
565
|
+
return typeof service === "object" && service !== null && typeof service.convertPdfToText === "function";
|
|
566
|
+
}
|
|
163
567
|
var getChannelType = (chat) => {
|
|
164
|
-
|
|
568
|
+
const chatType = chat.type;
|
|
569
|
+
switch (chatType) {
|
|
165
570
|
case "private":
|
|
166
571
|
return ChannelType.DM;
|
|
167
572
|
case "group":
|
|
@@ -169,21 +574,26 @@ var getChannelType = (chat) => {
|
|
|
169
574
|
case "channel":
|
|
170
575
|
return ChannelType.GROUP;
|
|
171
576
|
default:
|
|
172
|
-
throw new Error(`Unrecognized Telegram chat type: ${
|
|
577
|
+
throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
|
|
173
578
|
}
|
|
174
579
|
};
|
|
175
580
|
var MessageManager = class {
|
|
176
581
|
bot;
|
|
177
582
|
runtime;
|
|
583
|
+
accountId;
|
|
178
584
|
/**
|
|
179
585
|
* Constructor for creating a new instance of a BotAgent.
|
|
180
586
|
*
|
|
181
587
|
* @param {Telegraf<Context>} bot - The Telegraf instance used for interacting with the bot platform.
|
|
182
588
|
* @param {IAgentRuntime} runtime - The runtime environment for the agent.
|
|
183
589
|
*/
|
|
184
|
-
constructor(bot, runtime) {
|
|
590
|
+
constructor(bot, runtime, accountId = "default") {
|
|
185
591
|
this.bot = bot;
|
|
186
592
|
this.runtime = runtime;
|
|
593
|
+
this.accountId = accountId;
|
|
594
|
+
}
|
|
595
|
+
scopedTelegramKey(key) {
|
|
596
|
+
return this.accountId === "default" ? key : `${this.accountId}:${key}`;
|
|
187
597
|
}
|
|
188
598
|
/**
|
|
189
599
|
* Process an image from a Telegram message to extract the image URL and description.
|
|
@@ -194,13 +604,22 @@ var MessageManager = class {
|
|
|
194
604
|
async processImage(message) {
|
|
195
605
|
try {
|
|
196
606
|
let imageUrl = null;
|
|
197
|
-
logger2.debug(
|
|
607
|
+
logger2.debug(
|
|
608
|
+
{
|
|
609
|
+
src: "plugin:telegram",
|
|
610
|
+
agentId: this.runtime.agentId,
|
|
611
|
+
messageId: message.message_id
|
|
612
|
+
},
|
|
613
|
+
"Processing image from message"
|
|
614
|
+
);
|
|
198
615
|
if ("photo" in message && message.photo?.length > 0) {
|
|
199
616
|
const photo = message.photo[message.photo.length - 1];
|
|
200
617
|
const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
|
|
201
618
|
imageUrl = fileLink.toString();
|
|
202
619
|
} else if ("document" in message && message.document?.mime_type?.startsWith("image/") && !message.document?.mime_type?.startsWith("application/pdf")) {
|
|
203
|
-
const fileLink = await this.bot.telegram.getFileLink(
|
|
620
|
+
const fileLink = await this.bot.telegram.getFileLink(
|
|
621
|
+
message.document.file_id
|
|
622
|
+
);
|
|
204
623
|
imageUrl = fileLink.toString();
|
|
205
624
|
}
|
|
206
625
|
if (imageUrl) {
|
|
@@ -212,7 +631,14 @@ var MessageManager = class {
|
|
|
212
631
|
${description}]` };
|
|
213
632
|
}
|
|
214
633
|
} catch (error) {
|
|
215
|
-
logger2.error(
|
|
634
|
+
logger2.error(
|
|
635
|
+
{
|
|
636
|
+
src: "plugin:telegram",
|
|
637
|
+
agentId: this.runtime.agentId,
|
|
638
|
+
error: error instanceof Error ? error.message : String(error)
|
|
639
|
+
},
|
|
640
|
+
"Error processing image"
|
|
641
|
+
);
|
|
216
642
|
}
|
|
217
643
|
return null;
|
|
218
644
|
}
|
|
@@ -231,7 +657,16 @@ ${description}]` };
|
|
|
231
657
|
const document = message.document;
|
|
232
658
|
const fileLink = await this.bot.telegram.getFileLink(document.file_id);
|
|
233
659
|
const documentUrl = fileLink.toString();
|
|
234
|
-
logger2.debug(
|
|
660
|
+
logger2.debug(
|
|
661
|
+
{
|
|
662
|
+
src: "plugin:telegram",
|
|
663
|
+
agentId: this.runtime.agentId,
|
|
664
|
+
fileName: document.file_name,
|
|
665
|
+
mimeType: document.mime_type,
|
|
666
|
+
fileSize: document.file_size
|
|
667
|
+
},
|
|
668
|
+
"Processing document"
|
|
669
|
+
);
|
|
235
670
|
const documentProcessor = this.getDocumentProcessor(document.mime_type);
|
|
236
671
|
if (documentProcessor) {
|
|
237
672
|
return await documentProcessor(document, documentUrl);
|
|
@@ -247,7 +682,14 @@ Size: ${document.file_size || 0} bytes]`,
|
|
|
247
682
|
fileSize: document.file_size
|
|
248
683
|
};
|
|
249
684
|
} catch (error) {
|
|
250
|
-
logger2.error(
|
|
685
|
+
logger2.error(
|
|
686
|
+
{
|
|
687
|
+
src: "plugin:telegram",
|
|
688
|
+
agentId: this.runtime.agentId,
|
|
689
|
+
error: error instanceof Error ? error.message : String(error)
|
|
690
|
+
},
|
|
691
|
+
"Error processing document"
|
|
692
|
+
);
|
|
251
693
|
return null;
|
|
252
694
|
}
|
|
253
695
|
}
|
|
@@ -255,7 +697,9 @@ Size: ${document.file_size || 0} bytes]`,
|
|
|
255
697
|
* Get the appropriate document processor based on MIME type.
|
|
256
698
|
*/
|
|
257
699
|
getDocumentProcessor(mimeType) {
|
|
258
|
-
if (!mimeType)
|
|
700
|
+
if (!mimeType) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
259
703
|
const processors = {
|
|
260
704
|
"application/pdf": this.processPdfDocument.bind(this),
|
|
261
705
|
"text/": this.processTextDocument.bind(this),
|
|
@@ -274,9 +718,13 @@ Size: ${document.file_size || 0} bytes]`,
|
|
|
274
718
|
*/
|
|
275
719
|
async processPdfDocument(document, documentUrl) {
|
|
276
720
|
try {
|
|
277
|
-
const
|
|
721
|
+
const pdfServiceCandidate = this.runtime.getService(ServiceType.PDF);
|
|
722
|
+
const pdfService = isPdfTextService(pdfServiceCandidate) ? pdfServiceCandidate : null;
|
|
278
723
|
if (!pdfService) {
|
|
279
|
-
logger2.warn(
|
|
724
|
+
logger2.warn(
|
|
725
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
726
|
+
"PDF service not available, using fallback"
|
|
727
|
+
);
|
|
280
728
|
return {
|
|
281
729
|
title: `PDF Document: ${document.file_name || "Unknown Document"}`,
|
|
282
730
|
fullText: "",
|
|
@@ -294,7 +742,15 @@ Unable to extract text content]`,
|
|
|
294
742
|
}
|
|
295
743
|
const pdfBuffer = await response.arrayBuffer();
|
|
296
744
|
const text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));
|
|
297
|
-
logger2.debug(
|
|
745
|
+
logger2.debug(
|
|
746
|
+
{
|
|
747
|
+
src: "plugin:telegram",
|
|
748
|
+
agentId: this.runtime.agentId,
|
|
749
|
+
fileName: document.file_name,
|
|
750
|
+
charactersExtracted: text.length
|
|
751
|
+
},
|
|
752
|
+
"PDF processed successfully"
|
|
753
|
+
);
|
|
298
754
|
return {
|
|
299
755
|
title: document.file_name || "Unknown Document",
|
|
300
756
|
fullText: text,
|
|
@@ -306,7 +762,15 @@ Text extracted successfully: ${text.length} characters]`,
|
|
|
306
762
|
fileSize: document.file_size
|
|
307
763
|
};
|
|
308
764
|
} catch (error) {
|
|
309
|
-
logger2.error(
|
|
765
|
+
logger2.error(
|
|
766
|
+
{
|
|
767
|
+
src: "plugin:telegram",
|
|
768
|
+
agentId: this.runtime.agentId,
|
|
769
|
+
fileName: document.file_name,
|
|
770
|
+
error: error instanceof Error ? error.message : String(error)
|
|
771
|
+
},
|
|
772
|
+
"Error processing PDF document"
|
|
773
|
+
);
|
|
310
774
|
return {
|
|
311
775
|
title: `PDF Document: ${document.file_name || "Unknown Document"}`,
|
|
312
776
|
fullText: "",
|
|
@@ -329,7 +793,15 @@ Error: Unable to extract text content]`,
|
|
|
329
793
|
throw new Error(`Failed to fetch text document: ${response.status}`);
|
|
330
794
|
}
|
|
331
795
|
const text = await response.text();
|
|
332
|
-
logger2.debug(
|
|
796
|
+
logger2.debug(
|
|
797
|
+
{
|
|
798
|
+
src: "plugin:telegram",
|
|
799
|
+
agentId: this.runtime.agentId,
|
|
800
|
+
fileName: document.file_name,
|
|
801
|
+
charactersExtracted: text.length
|
|
802
|
+
},
|
|
803
|
+
"Text document processed successfully"
|
|
804
|
+
);
|
|
333
805
|
return {
|
|
334
806
|
title: document.file_name || "Unknown Document",
|
|
335
807
|
fullText: text,
|
|
@@ -341,7 +813,15 @@ Text extracted successfully: ${text.length} characters]`,
|
|
|
341
813
|
fileSize: document.file_size
|
|
342
814
|
};
|
|
343
815
|
} catch (error) {
|
|
344
|
-
logger2.error(
|
|
816
|
+
logger2.error(
|
|
817
|
+
{
|
|
818
|
+
src: "plugin:telegram",
|
|
819
|
+
agentId: this.runtime.agentId,
|
|
820
|
+
fileName: document.file_name,
|
|
821
|
+
error: error instanceof Error ? error.message : String(error)
|
|
822
|
+
},
|
|
823
|
+
"Error processing text document"
|
|
824
|
+
);
|
|
345
825
|
return {
|
|
346
826
|
title: `Text Document: ${document.file_name || "Unknown Document"}`,
|
|
347
827
|
fullText: "",
|
|
@@ -363,7 +843,7 @@ Error: Unable to read content]`,
|
|
|
363
843
|
*/
|
|
364
844
|
async processMessage(message) {
|
|
365
845
|
let processedContent = "";
|
|
366
|
-
|
|
846
|
+
const attachments = [];
|
|
367
847
|
if ("text" in message && message.text) {
|
|
368
848
|
processedContent = message.text;
|
|
369
849
|
} else if ("caption" in message && message.caption) {
|
|
@@ -374,7 +854,9 @@ Error: Unable to read content]`,
|
|
|
374
854
|
const documentInfo = await this.processDocument(message);
|
|
375
855
|
if (documentInfo) {
|
|
376
856
|
try {
|
|
377
|
-
const fileLink = await this.bot.telegram.getFileLink(
|
|
857
|
+
const fileLink = await this.bot.telegram.getFileLink(
|
|
858
|
+
document.file_id
|
|
859
|
+
);
|
|
378
860
|
const title = documentInfo.title;
|
|
379
861
|
const fullText = documentInfo.fullText;
|
|
380
862
|
if (fullText) {
|
|
@@ -398,9 +880,24 @@ ${fullText}
|
|
|
398
880
|
description: documentInfo.formattedDescription,
|
|
399
881
|
text: fullText
|
|
400
882
|
});
|
|
401
|
-
logger2.debug(
|
|
883
|
+
logger2.debug(
|
|
884
|
+
{
|
|
885
|
+
src: "plugin:telegram",
|
|
886
|
+
agentId: this.runtime.agentId,
|
|
887
|
+
fileName: documentInfo.fileName
|
|
888
|
+
},
|
|
889
|
+
"Document processed successfully"
|
|
890
|
+
);
|
|
402
891
|
} catch (error) {
|
|
403
|
-
logger2.error(
|
|
892
|
+
logger2.error(
|
|
893
|
+
{
|
|
894
|
+
src: "plugin:telegram",
|
|
895
|
+
agentId: this.runtime.agentId,
|
|
896
|
+
fileName: documentInfo.fileName,
|
|
897
|
+
error: error instanceof Error ? error.message : String(error)
|
|
898
|
+
},
|
|
899
|
+
"Error processing document"
|
|
900
|
+
);
|
|
404
901
|
attachments.push({
|
|
405
902
|
id: document.file_id,
|
|
406
903
|
url: "",
|
|
@@ -440,7 +937,15 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
440
937
|
});
|
|
441
938
|
}
|
|
442
939
|
}
|
|
443
|
-
logger2.debug(
|
|
940
|
+
logger2.debug(
|
|
941
|
+
{
|
|
942
|
+
src: "plugin:telegram",
|
|
943
|
+
agentId: this.runtime.agentId,
|
|
944
|
+
hasContent: !!processedContent,
|
|
945
|
+
attachmentsCount: attachments.length
|
|
946
|
+
},
|
|
947
|
+
"Message processed"
|
|
948
|
+
);
|
|
444
949
|
return { processedContent, attachments };
|
|
445
950
|
}
|
|
446
951
|
/**
|
|
@@ -451,7 +956,7 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
451
956
|
* @param {number} [replyToMessageId] - The ID of the message to reply to, if any
|
|
452
957
|
* @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
|
|
453
958
|
*/
|
|
454
|
-
async sendMessageInChunks(ctx, content, replyToMessageId) {
|
|
959
|
+
async sendMessageInChunks(ctx, content, replyToMessageId, messageThreadId) {
|
|
455
960
|
if (content.attachments && content.attachments.length > 0) {
|
|
456
961
|
content.attachments.map(async (attachment) => {
|
|
457
962
|
const typeMap = {
|
|
@@ -461,7 +966,7 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
461
966
|
video: "video" /* VIDEO */,
|
|
462
967
|
audio: "audio" /* AUDIO */
|
|
463
968
|
};
|
|
464
|
-
let mediaType
|
|
969
|
+
let mediaType;
|
|
465
970
|
for (const prefix in typeMap) {
|
|
466
971
|
if (attachment.contentType?.startsWith(prefix)) {
|
|
467
972
|
mediaType = typeMap[prefix];
|
|
@@ -473,7 +978,12 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
473
978
|
`Unsupported Telegram attachment content type: ${attachment.contentType}`
|
|
474
979
|
);
|
|
475
980
|
}
|
|
476
|
-
await this.sendMedia(
|
|
981
|
+
await this.sendMedia(
|
|
982
|
+
ctx,
|
|
983
|
+
attachment.url,
|
|
984
|
+
mediaType,
|
|
985
|
+
attachment.description
|
|
986
|
+
);
|
|
477
987
|
});
|
|
478
988
|
return [];
|
|
479
989
|
} else {
|
|
@@ -481,21 +991,32 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
481
991
|
const sentMessages = [];
|
|
482
992
|
const telegramButtons = convertToTelegramButtons(content.buttons ?? []);
|
|
483
993
|
if (!ctx.chat) {
|
|
484
|
-
logger2.error(
|
|
994
|
+
logger2.error(
|
|
995
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
996
|
+
"sendMessageInChunks: ctx.chat is undefined"
|
|
997
|
+
);
|
|
485
998
|
return [];
|
|
486
999
|
}
|
|
487
1000
|
await ctx.telegram.sendChatAction(ctx.chat.id, "typing");
|
|
488
1001
|
for (let i = 0; i < chunks.length; i++) {
|
|
489
1002
|
const chunk = convertMarkdownToTelegram(chunks[i]);
|
|
490
1003
|
if (!ctx.chat) {
|
|
491
|
-
logger2.error(
|
|
1004
|
+
logger2.error(
|
|
1005
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
1006
|
+
"sendMessageInChunks loop: ctx.chat is undefined"
|
|
1007
|
+
);
|
|
492
1008
|
continue;
|
|
493
1009
|
}
|
|
494
|
-
const sentMessage = await ctx.telegram.sendMessage(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
1010
|
+
const sentMessage = await ctx.telegram.sendMessage(
|
|
1011
|
+
ctx.chat.id,
|
|
1012
|
+
chunk,
|
|
1013
|
+
{
|
|
1014
|
+
reply_parameters: i === 0 && replyToMessageId ? { message_id: replyToMessageId } : void 0,
|
|
1015
|
+
message_thread_id: messageThreadId,
|
|
1016
|
+
parse_mode: "MarkdownV2",
|
|
1017
|
+
...Markup2.inlineKeyboard(telegramButtons)
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
499
1020
|
sentMessages.push(sentMessage);
|
|
500
1021
|
}
|
|
501
1022
|
return sentMessages;
|
|
@@ -544,9 +1065,26 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
544
1065
|
fileStream.destroy();
|
|
545
1066
|
}
|
|
546
1067
|
}
|
|
547
|
-
logger2.debug(
|
|
1068
|
+
logger2.debug(
|
|
1069
|
+
{
|
|
1070
|
+
src: "plugin:telegram",
|
|
1071
|
+
agentId: this.runtime.agentId,
|
|
1072
|
+
mediaType: type,
|
|
1073
|
+
mediaPath
|
|
1074
|
+
},
|
|
1075
|
+
"Media sent successfully"
|
|
1076
|
+
);
|
|
548
1077
|
} catch (error) {
|
|
549
|
-
logger2.error(
|
|
1078
|
+
logger2.error(
|
|
1079
|
+
{
|
|
1080
|
+
src: "plugin:telegram",
|
|
1081
|
+
agentId: this.runtime.agentId,
|
|
1082
|
+
mediaType: type,
|
|
1083
|
+
mediaPath,
|
|
1084
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1085
|
+
},
|
|
1086
|
+
"Failed to send media"
|
|
1087
|
+
);
|
|
550
1088
|
throw error;
|
|
551
1089
|
}
|
|
552
1090
|
}
|
|
@@ -558,18 +1096,24 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
558
1096
|
*/
|
|
559
1097
|
splitMessage(text) {
|
|
560
1098
|
const chunks = [];
|
|
561
|
-
if (!text)
|
|
1099
|
+
if (!text) {
|
|
1100
|
+
return chunks;
|
|
1101
|
+
}
|
|
562
1102
|
let currentChunk = "";
|
|
563
1103
|
const lines = text.split("\n");
|
|
564
1104
|
for (const line of lines) {
|
|
565
1105
|
if (currentChunk.length + line.length + 1 <= MAX_MESSAGE_LENGTH) {
|
|
566
1106
|
currentChunk += (currentChunk ? "\n" : "") + line;
|
|
567
1107
|
} else {
|
|
568
|
-
if (currentChunk)
|
|
1108
|
+
if (currentChunk) {
|
|
1109
|
+
chunks.push(currentChunk);
|
|
1110
|
+
}
|
|
569
1111
|
currentChunk = line;
|
|
570
1112
|
}
|
|
571
1113
|
}
|
|
572
|
-
if (currentChunk)
|
|
1114
|
+
if (currentChunk) {
|
|
1115
|
+
chunks.push(currentChunk);
|
|
1116
|
+
}
|
|
573
1117
|
return chunks;
|
|
574
1118
|
}
|
|
575
1119
|
/**
|
|
@@ -578,18 +1122,35 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
578
1122
|
* @returns {Promise<void>}
|
|
579
1123
|
*/
|
|
580
1124
|
async handleMessage(ctx) {
|
|
581
|
-
if (!ctx.message || !ctx.from)
|
|
1125
|
+
if (!ctx.message || !ctx.from) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
582
1128
|
const message = ctx.message;
|
|
583
1129
|
try {
|
|
584
|
-
const
|
|
1130
|
+
const telegramUserId = ctx.from.id.toString();
|
|
1131
|
+
const entityId = createUniqueUuid(
|
|
1132
|
+
this.runtime,
|
|
1133
|
+
this.scopedTelegramKey(telegramUserId)
|
|
1134
|
+
);
|
|
585
1135
|
const threadId = "is_topic_message" in message && message.is_topic_message ? message.message_thread_id?.toString() : void 0;
|
|
586
1136
|
if (!ctx.chat) {
|
|
587
|
-
logger2.error(
|
|
1137
|
+
logger2.error(
|
|
1138
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
1139
|
+
"handleMessage: ctx.chat is undefined"
|
|
1140
|
+
);
|
|
588
1141
|
return;
|
|
589
1142
|
}
|
|
590
1143
|
const telegramRoomid = threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString();
|
|
591
|
-
const
|
|
592
|
-
const
|
|
1144
|
+
const telegramChatId = ctx.chat.id.toString();
|
|
1145
|
+
const scopedRoomKey = this.scopedTelegramKey(telegramRoomid);
|
|
1146
|
+
const scopedChatKey = this.scopedTelegramKey(telegramChatId);
|
|
1147
|
+
const roomId = createUniqueUuid(this.runtime, scopedRoomKey);
|
|
1148
|
+
const worldId = createUniqueUuid(this.runtime, scopedChatKey);
|
|
1149
|
+
const telegramMessageId = message.message_id.toString();
|
|
1150
|
+
const messageId = createUniqueUuid(
|
|
1151
|
+
this.runtime,
|
|
1152
|
+
this.scopedTelegramKey(telegramMessageId)
|
|
1153
|
+
);
|
|
593
1154
|
const { processedContent, attachments } = await this.processMessage(message);
|
|
594
1155
|
const cleanedContent = cleanText(processedContent);
|
|
595
1156
|
const cleanedAttachments = attachments.map((att) => ({
|
|
@@ -603,16 +1164,17 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
603
1164
|
}
|
|
604
1165
|
const chat = message.chat;
|
|
605
1166
|
const channelType = getChannelType(chat);
|
|
606
|
-
const sourceId = createUniqueUuid(this.runtime, "" + chat.id);
|
|
607
1167
|
await this.runtime.ensureConnection({
|
|
608
1168
|
entityId,
|
|
609
1169
|
roomId,
|
|
1170
|
+
roomName: "title" in chat && typeof chat.title === "string" && chat.title || "first_name" in chat && typeof chat.first_name === "string" && chat.first_name || "username" in chat && typeof chat.username === "string" && chat.username || telegramRoomid,
|
|
610
1171
|
userName: ctx.from.username,
|
|
611
1172
|
name: ctx.from.first_name,
|
|
1173
|
+
userId: telegramUserId,
|
|
612
1174
|
source: "telegram",
|
|
613
1175
|
channelId: telegramRoomid,
|
|
614
1176
|
type: channelType,
|
|
615
|
-
worldId
|
|
1177
|
+
worldId,
|
|
616
1178
|
worldName: telegramRoomid
|
|
617
1179
|
});
|
|
618
1180
|
const memory = {
|
|
@@ -624,44 +1186,76 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
624
1186
|
text: cleanedContent || " ",
|
|
625
1187
|
attachments: cleanedAttachments,
|
|
626
1188
|
source: "telegram",
|
|
1189
|
+
metadata: { accountId: this.accountId },
|
|
627
1190
|
channelType,
|
|
628
|
-
inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
|
|
1191
|
+
inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
|
|
1192
|
+
this.runtime,
|
|
1193
|
+
this.scopedTelegramKey(
|
|
1194
|
+
message.reply_to_message.message_id.toString()
|
|
1195
|
+
)
|
|
1196
|
+
) : void 0
|
|
629
1197
|
},
|
|
630
1198
|
metadata: {
|
|
1199
|
+
type: "message",
|
|
1200
|
+
source: "telegram",
|
|
1201
|
+
accountId: this.accountId,
|
|
1202
|
+
provider: "telegram",
|
|
1203
|
+
timestamp: message.date * 1e3,
|
|
631
1204
|
entityName: ctx.from.first_name,
|
|
632
1205
|
entityUserName: ctx.from.username,
|
|
633
1206
|
fromBot: ctx.from.is_bot,
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1207
|
+
fromId: telegramUserId,
|
|
1208
|
+
sourceId: entityId,
|
|
1209
|
+
chatType: chat.type,
|
|
1210
|
+
messageIdFull: telegramMessageId,
|
|
1211
|
+
sender: {
|
|
1212
|
+
id: telegramUserId,
|
|
1213
|
+
name: ctx.from.first_name,
|
|
1214
|
+
username: ctx.from.username
|
|
1215
|
+
},
|
|
1216
|
+
telegram: {
|
|
1217
|
+
chatId: telegramChatId,
|
|
1218
|
+
messageId: telegramMessageId,
|
|
1219
|
+
threadId
|
|
1220
|
+
},
|
|
1221
|
+
telegramUserId,
|
|
1222
|
+
telegramChatId
|
|
643
1223
|
},
|
|
644
1224
|
createdAt: message.date * 1e3
|
|
645
1225
|
};
|
|
646
|
-
const callback = async (content,
|
|
1226
|
+
const callback = async (content, _actionName) => {
|
|
647
1227
|
try {
|
|
648
|
-
if (!content.text)
|
|
1228
|
+
if (!content.text) {
|
|
1229
|
+
return [];
|
|
1230
|
+
}
|
|
649
1231
|
let sentMessages = false;
|
|
650
1232
|
if (content?.channelType === "DM") {
|
|
651
1233
|
sentMessages = [];
|
|
652
1234
|
if (ctx.from) {
|
|
653
|
-
const res = await this.bot.telegram.sendMessage(
|
|
1235
|
+
const res = await this.bot.telegram.sendMessage(
|
|
1236
|
+
ctx.from.id,
|
|
1237
|
+
content.text
|
|
1238
|
+
);
|
|
654
1239
|
sentMessages.push(res);
|
|
655
1240
|
}
|
|
656
1241
|
} else {
|
|
657
|
-
sentMessages = await this.sendMessageInChunks(
|
|
1242
|
+
sentMessages = await this.sendMessageInChunks(
|
|
1243
|
+
ctx,
|
|
1244
|
+
content,
|
|
1245
|
+
message.message_id
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
if (!Array.isArray(sentMessages)) {
|
|
1249
|
+
return [];
|
|
658
1250
|
}
|
|
659
|
-
if (!Array.isArray(sentMessages)) return [];
|
|
660
1251
|
const memories = [];
|
|
661
1252
|
for (let i = 0; i < sentMessages.length; i++) {
|
|
662
1253
|
const sentMessage = sentMessages[i];
|
|
663
1254
|
const responseMemory = {
|
|
664
|
-
id: createUniqueUuid(
|
|
1255
|
+
id: createUniqueUuid(
|
|
1256
|
+
this.runtime,
|
|
1257
|
+
this.scopedTelegramKey(sentMessage.message_id.toString())
|
|
1258
|
+
),
|
|
665
1259
|
entityId: this.runtime.agentId,
|
|
666
1260
|
agentId: this.runtime.agentId,
|
|
667
1261
|
roomId,
|
|
@@ -670,7 +1264,25 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
670
1264
|
source: "telegram",
|
|
671
1265
|
text: sentMessage.text,
|
|
672
1266
|
inReplyTo: messageId,
|
|
673
|
-
channelType
|
|
1267
|
+
channelType,
|
|
1268
|
+
metadata: { accountId: this.accountId }
|
|
1269
|
+
},
|
|
1270
|
+
metadata: {
|
|
1271
|
+
type: "message",
|
|
1272
|
+
source: "telegram",
|
|
1273
|
+
accountId: this.accountId,
|
|
1274
|
+
provider: "telegram",
|
|
1275
|
+
timestamp: sentMessage.date * 1e3,
|
|
1276
|
+
fromBot: true,
|
|
1277
|
+
fromId: this.runtime.agentId,
|
|
1278
|
+
sourceId: this.runtime.agentId,
|
|
1279
|
+
chatType: chat.type,
|
|
1280
|
+
messageIdFull: sentMessage.message_id.toString(),
|
|
1281
|
+
telegram: {
|
|
1282
|
+
chatId: sentMessage.chat.id,
|
|
1283
|
+
messageId: sentMessage.message_id.toString(),
|
|
1284
|
+
threadId
|
|
1285
|
+
}
|
|
674
1286
|
},
|
|
675
1287
|
createdAt: sentMessage.date * 1e3
|
|
676
1288
|
};
|
|
@@ -679,26 +1291,65 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
679
1291
|
}
|
|
680
1292
|
return memories;
|
|
681
1293
|
} catch (error) {
|
|
682
|
-
logger2.error(
|
|
1294
|
+
logger2.error(
|
|
1295
|
+
{
|
|
1296
|
+
src: "plugin:telegram",
|
|
1297
|
+
agentId: this.runtime.agentId,
|
|
1298
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1299
|
+
},
|
|
1300
|
+
"Error in message callback"
|
|
1301
|
+
);
|
|
683
1302
|
return [];
|
|
684
1303
|
}
|
|
685
1304
|
};
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
1305
|
+
const telegramAutoReplyRaw = this.runtime.getSetting(
|
|
1306
|
+
"TELEGRAM_AUTO_REPLY"
|
|
1307
|
+
);
|
|
1308
|
+
const telegramAutoReply = !lifeOpsPassiveConnectorsEnabled(this.runtime) && (telegramAutoReplyRaw === true || telegramAutoReplyRaw === "true");
|
|
1309
|
+
if (!telegramAutoReply) {
|
|
1310
|
+
try {
|
|
1311
|
+
await this.runtime.createMemory(memory, "messages");
|
|
1312
|
+
} catch (persistError) {
|
|
1313
|
+
logger2.warn(
|
|
1314
|
+
{
|
|
1315
|
+
src: "plugin:telegram",
|
|
1316
|
+
agentId: this.runtime.agentId,
|
|
1317
|
+
error: persistError instanceof Error ? persistError.message : String(persistError)
|
|
1318
|
+
},
|
|
1319
|
+
"Failed to persist inbound memory while auto-reply is disabled"
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
logger2.debug(
|
|
1323
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
1324
|
+
"Auto-reply disabled (TELEGRAM_AUTO_REPLY=false); message ingested without response"
|
|
1325
|
+
);
|
|
692
1326
|
} else if (this.runtime.messageService) {
|
|
693
|
-
await this.runtime.messageService.handleMessage(
|
|
1327
|
+
await this.runtime.messageService.handleMessage(
|
|
1328
|
+
this.runtime,
|
|
1329
|
+
memory,
|
|
1330
|
+
callback
|
|
1331
|
+
);
|
|
694
1332
|
} else {
|
|
695
|
-
logger2.error(
|
|
1333
|
+
logger2.error(
|
|
1334
|
+
{ src: "plugin:telegram", agentId: this.runtime.agentId },
|
|
1335
|
+
"Message service is not available"
|
|
1336
|
+
);
|
|
696
1337
|
throw new Error(
|
|
697
1338
|
"Message service is not initialized. Ensure the message service is properly configured."
|
|
698
1339
|
);
|
|
699
1340
|
}
|
|
700
1341
|
} catch (error) {
|
|
701
|
-
logger2.error(
|
|
1342
|
+
logger2.error(
|
|
1343
|
+
{
|
|
1344
|
+
src: "plugin:telegram",
|
|
1345
|
+
agentId: this.runtime.agentId,
|
|
1346
|
+
chatId: ctx.chat?.id,
|
|
1347
|
+
messageId: ctx.message?.message_id,
|
|
1348
|
+
from: ctx.from?.username || ctx.from?.id,
|
|
1349
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1350
|
+
},
|
|
1351
|
+
"Error handling Telegram message"
|
|
1352
|
+
);
|
|
702
1353
|
throw error;
|
|
703
1354
|
}
|
|
704
1355
|
}
|
|
@@ -708,7 +1359,9 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
708
1359
|
* @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
|
|
709
1360
|
*/
|
|
710
1361
|
async handleReaction(ctx) {
|
|
711
|
-
if (!ctx.update.message_reaction || !ctx.from)
|
|
1362
|
+
if (!ctx.update.message_reaction || !ctx.from) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
712
1365
|
const reaction = ctx.update.message_reaction;
|
|
713
1366
|
const reactedToMessageId = reaction.message_id;
|
|
714
1367
|
const originalMessagePlaceholder = {
|
|
@@ -720,11 +1373,19 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
720
1373
|
const reactionType = reaction.new_reaction[0].type;
|
|
721
1374
|
const reactionEmoji = reaction.new_reaction[0].type;
|
|
722
1375
|
try {
|
|
723
|
-
const entityId = createUniqueUuid(
|
|
724
|
-
|
|
1376
|
+
const entityId = createUniqueUuid(
|
|
1377
|
+
this.runtime,
|
|
1378
|
+
this.scopedTelegramKey(ctx.from.id.toString())
|
|
1379
|
+
);
|
|
1380
|
+
const roomId = createUniqueUuid(
|
|
1381
|
+
this.runtime,
|
|
1382
|
+
this.scopedTelegramKey(ctx.chat.id.toString())
|
|
1383
|
+
);
|
|
725
1384
|
const reactionId = createUniqueUuid(
|
|
726
1385
|
this.runtime,
|
|
727
|
-
|
|
1386
|
+
this.scopedTelegramKey(
|
|
1387
|
+
`${reaction.message_id}-${ctx.from.id}-${Date.now()}`
|
|
1388
|
+
)
|
|
728
1389
|
);
|
|
729
1390
|
const memory = {
|
|
730
1391
|
id: reactionId,
|
|
@@ -735,7 +1396,22 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
735
1396
|
channelType: getChannelType(reaction.chat),
|
|
736
1397
|
text: `Reacted with: ${reactionType === "emoji" ? reactionEmoji : reactionType}`,
|
|
737
1398
|
source: "telegram",
|
|
738
|
-
inReplyTo: createUniqueUuid(
|
|
1399
|
+
inReplyTo: createUniqueUuid(
|
|
1400
|
+
this.runtime,
|
|
1401
|
+
this.scopedTelegramKey(reaction.message_id.toString())
|
|
1402
|
+
),
|
|
1403
|
+
metadata: { accountId: this.accountId }
|
|
1404
|
+
},
|
|
1405
|
+
metadata: {
|
|
1406
|
+
type: "custom",
|
|
1407
|
+
eventType: "reaction",
|
|
1408
|
+
source: "telegram",
|
|
1409
|
+
accountId: this.accountId,
|
|
1410
|
+
provider: "telegram",
|
|
1411
|
+
telegram: {
|
|
1412
|
+
chatId: reaction.chat.id.toString(),
|
|
1413
|
+
messageId: reaction.message_id.toString()
|
|
1414
|
+
}
|
|
739
1415
|
},
|
|
740
1416
|
createdAt: Date.now()
|
|
741
1417
|
};
|
|
@@ -744,19 +1420,36 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
744
1420
|
const replyText = content.text ?? "";
|
|
745
1421
|
const sentMessage = await ctx.reply(replyText);
|
|
746
1422
|
const responseMemory = {
|
|
747
|
-
id: createUniqueUuid(
|
|
1423
|
+
id: createUniqueUuid(
|
|
1424
|
+
this.runtime,
|
|
1425
|
+
this.scopedTelegramKey(sentMessage.message_id.toString())
|
|
1426
|
+
),
|
|
748
1427
|
entityId: this.runtime.agentId,
|
|
749
1428
|
agentId: this.runtime.agentId,
|
|
750
1429
|
roomId,
|
|
751
1430
|
content: {
|
|
752
1431
|
...content,
|
|
753
|
-
inReplyTo: reactionId
|
|
1432
|
+
inReplyTo: reactionId,
|
|
1433
|
+
metadata: { accountId: this.accountId }
|
|
1434
|
+
},
|
|
1435
|
+
metadata: {
|
|
1436
|
+
type: "message",
|
|
1437
|
+
source: "telegram",
|
|
1438
|
+
accountId: this.accountId,
|
|
1439
|
+
provider: "telegram"
|
|
754
1440
|
},
|
|
755
1441
|
createdAt: sentMessage.date * 1e3
|
|
756
1442
|
};
|
|
757
1443
|
return [responseMemory];
|
|
758
1444
|
} catch (error) {
|
|
759
|
-
logger2.error(
|
|
1445
|
+
logger2.error(
|
|
1446
|
+
{
|
|
1447
|
+
src: "plugin:telegram",
|
|
1448
|
+
agentId: this.runtime.agentId,
|
|
1449
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1450
|
+
},
|
|
1451
|
+
"Error in reaction callback"
|
|
1452
|
+
);
|
|
760
1453
|
return [];
|
|
761
1454
|
}
|
|
762
1455
|
};
|
|
@@ -765,6 +1458,8 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
765
1458
|
message: memory,
|
|
766
1459
|
callback,
|
|
767
1460
|
source: "telegram",
|
|
1461
|
+
accountId: this.accountId,
|
|
1462
|
+
metadata: { accountId: this.accountId },
|
|
768
1463
|
ctx,
|
|
769
1464
|
originalMessage: originalMessagePlaceholder,
|
|
770
1465
|
// Cast needed due to placeholder
|
|
@@ -776,6 +1471,8 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
776
1471
|
message: memory,
|
|
777
1472
|
callback,
|
|
778
1473
|
source: "telegram",
|
|
1474
|
+
accountId: this.accountId,
|
|
1475
|
+
metadata: { accountId: this.accountId },
|
|
779
1476
|
ctx,
|
|
780
1477
|
originalMessage: originalMessagePlaceholder,
|
|
781
1478
|
// Cast needed due to placeholder
|
|
@@ -783,7 +1480,14 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
783
1480
|
originalReaction: reaction.new_reaction[0]
|
|
784
1481
|
});
|
|
785
1482
|
} catch (error) {
|
|
786
|
-
logger2.error(
|
|
1483
|
+
logger2.error(
|
|
1484
|
+
{
|
|
1485
|
+
src: "plugin:telegram",
|
|
1486
|
+
agentId: this.runtime.agentId,
|
|
1487
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1488
|
+
},
|
|
1489
|
+
"Error handling reaction"
|
|
1490
|
+
);
|
|
787
1491
|
}
|
|
788
1492
|
}
|
|
789
1493
|
/**
|
|
@@ -793,7 +1497,7 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
793
1497
|
* @param {number} [replyToMessageId] - Optional message ID to reply to
|
|
794
1498
|
* @returns {Promise<Message.TextMessage[]>} The sent messages
|
|
795
1499
|
*/
|
|
796
|
-
async sendMessage(chatId, content, replyToMessageId) {
|
|
1500
|
+
async sendMessage(chatId, content, replyToMessageId, messageThreadId) {
|
|
797
1501
|
try {
|
|
798
1502
|
const ctx = {
|
|
799
1503
|
chat: { id: chatId },
|
|
@@ -802,14 +1506,25 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
802
1506
|
const sentMessages = await this.sendMessageInChunks(
|
|
803
1507
|
ctx,
|
|
804
1508
|
content,
|
|
805
|
-
replyToMessageId
|
|
1509
|
+
replyToMessageId,
|
|
1510
|
+
messageThreadId
|
|
1511
|
+
);
|
|
1512
|
+
if (!sentMessages?.length) {
|
|
1513
|
+
return [];
|
|
1514
|
+
}
|
|
1515
|
+
const roomKey = messageThreadId ? `${chatId.toString()}-${messageThreadId}` : chatId.toString();
|
|
1516
|
+
const roomId = createUniqueUuid(
|
|
1517
|
+
this.runtime,
|
|
1518
|
+
this.scopedTelegramKey(roomKey)
|
|
806
1519
|
);
|
|
807
|
-
if (!sentMessages?.length) return [];
|
|
808
|
-
const roomId = createUniqueUuid(this.runtime, chatId.toString());
|
|
809
1520
|
const memories = [];
|
|
1521
|
+
const contentMetadata = content.metadata && typeof content.metadata === "object" && !Array.isArray(content.metadata) ? content.metadata : {};
|
|
810
1522
|
for (const sentMessage of sentMessages) {
|
|
811
1523
|
const memory = {
|
|
812
|
-
id: createUniqueUuid(
|
|
1524
|
+
id: createUniqueUuid(
|
|
1525
|
+
this.runtime,
|
|
1526
|
+
this.scopedTelegramKey(sentMessage.message_id.toString())
|
|
1527
|
+
),
|
|
813
1528
|
entityId: this.runtime.agentId,
|
|
814
1529
|
agentId: this.runtime.agentId,
|
|
815
1530
|
roomId,
|
|
@@ -817,11 +1532,34 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
817
1532
|
...content,
|
|
818
1533
|
text: sentMessage.text,
|
|
819
1534
|
source: "telegram",
|
|
1535
|
+
metadata: { ...contentMetadata, accountId: this.accountId },
|
|
820
1536
|
channelType: getChannelType({
|
|
821
1537
|
id: typeof chatId === "string" ? Number.parseInt(chatId, 10) : chatId,
|
|
822
1538
|
type: "private"
|
|
823
1539
|
// Default to private, will be overridden if in context
|
|
824
|
-
})
|
|
1540
|
+
}),
|
|
1541
|
+
...messageThreadId ? {
|
|
1542
|
+
metadata: {
|
|
1543
|
+
...contentMetadata,
|
|
1544
|
+
accountId: this.accountId,
|
|
1545
|
+
threadId: messageThreadId
|
|
1546
|
+
}
|
|
1547
|
+
} : {}
|
|
1548
|
+
},
|
|
1549
|
+
metadata: {
|
|
1550
|
+
type: "message",
|
|
1551
|
+
source: "telegram",
|
|
1552
|
+
accountId: this.accountId,
|
|
1553
|
+
provider: "telegram",
|
|
1554
|
+
fromBot: true,
|
|
1555
|
+
fromId: this.runtime.agentId,
|
|
1556
|
+
sourceId: this.runtime.agentId,
|
|
1557
|
+
messageIdFull: sentMessage.message_id.toString(),
|
|
1558
|
+
telegram: {
|
|
1559
|
+
chatId: sentMessage.chat.id.toString(),
|
|
1560
|
+
messageId: sentMessage.message_id.toString(),
|
|
1561
|
+
threadId: messageThreadId?.toString()
|
|
1562
|
+
}
|
|
825
1563
|
},
|
|
826
1564
|
createdAt: sentMessage.date * 1e3
|
|
827
1565
|
};
|
|
@@ -829,39 +1567,440 @@ Type: ${document.mime_type || "unknown"}`
|
|
|
829
1567
|
memories.push(memory);
|
|
830
1568
|
}
|
|
831
1569
|
if (memories.length > 0) {
|
|
1570
|
+
const firstMemory = memories[0];
|
|
832
1571
|
this.runtime.emitEvent(EventType.MESSAGE_SENT, {
|
|
833
1572
|
runtime: this.runtime,
|
|
834
|
-
message:
|
|
835
|
-
source: "telegram"
|
|
1573
|
+
message: firstMemory,
|
|
1574
|
+
source: "telegram",
|
|
1575
|
+
accountId: this.accountId,
|
|
1576
|
+
metadata: { accountId: this.accountId }
|
|
836
1577
|
});
|
|
837
|
-
|
|
838
|
-
this.runtime.emitEvent(
|
|
839
|
-
"TELEGRAM_MESSAGE_SENT" /* MESSAGE_SENT */,
|
|
840
|
-
{
|
|
1578
|
+
const telegramMessageSentPayload = {
|
|
841
1579
|
runtime: this.runtime,
|
|
842
1580
|
source: "telegram",
|
|
1581
|
+
accountId: this.accountId,
|
|
1582
|
+
metadata: { accountId: this.accountId },
|
|
843
1583
|
originalMessages: sentMessages,
|
|
844
1584
|
chatId,
|
|
845
|
-
message:
|
|
846
|
-
}
|
|
847
|
-
|
|
1585
|
+
message: firstMemory
|
|
1586
|
+
};
|
|
1587
|
+
this.runtime.emitEvent(
|
|
1588
|
+
"TELEGRAM_MESSAGE_SENT" /* MESSAGE_SENT */,
|
|
1589
|
+
telegramMessageSentPayload
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
848
1592
|
return sentMessages;
|
|
849
1593
|
} catch (error) {
|
|
850
|
-
logger2.error(
|
|
1594
|
+
logger2.error(
|
|
1595
|
+
{
|
|
1596
|
+
src: "plugin:telegram",
|
|
1597
|
+
agentId: this.runtime.agentId,
|
|
1598
|
+
chatId,
|
|
1599
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1600
|
+
},
|
|
1601
|
+
"Error sending message to Telegram"
|
|
1602
|
+
);
|
|
851
1603
|
return [];
|
|
852
1604
|
}
|
|
853
1605
|
}
|
|
854
1606
|
};
|
|
855
1607
|
|
|
1608
|
+
// src/owner-pairing-service.ts
|
|
1609
|
+
import { logger as logger3, Service } from "@elizaos/core";
|
|
1610
|
+
var TELEGRAM_OWNER_PAIRING_SERVICE_TYPE = "OWNER_PAIRING_TELEGRAM";
|
|
1611
|
+
var RATE_LIMIT_MAX_ATTEMPTS = 5;
|
|
1612
|
+
var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
1613
|
+
async function auditEmit(runtime, action, outcome, metadata) {
|
|
1614
|
+
try {
|
|
1615
|
+
await runtime.emitEvent(
|
|
1616
|
+
["AUTH_AUDIT"],
|
|
1617
|
+
{
|
|
1618
|
+
runtime,
|
|
1619
|
+
action,
|
|
1620
|
+
outcome,
|
|
1621
|
+
metadata,
|
|
1622
|
+
source: "telegram"
|
|
1623
|
+
}
|
|
1624
|
+
);
|
|
1625
|
+
} catch {
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
var pairAttempts = /* @__PURE__ */ new Map();
|
|
1629
|
+
function isRateLimited(userId) {
|
|
1630
|
+
const now = Date.now();
|
|
1631
|
+
const windowStart = now - RATE_LIMIT_WINDOW_MS;
|
|
1632
|
+
const attempts = (pairAttempts.get(userId) ?? []).filter(
|
|
1633
|
+
(ts) => ts > windowStart
|
|
1634
|
+
);
|
|
1635
|
+
pairAttempts.set(userId, attempts);
|
|
1636
|
+
if (attempts.length >= RATE_LIMIT_MAX_ATTEMPTS) {
|
|
1637
|
+
return true;
|
|
1638
|
+
}
|
|
1639
|
+
attempts.push(now);
|
|
1640
|
+
pairAttempts.set(userId, attempts);
|
|
1641
|
+
return false;
|
|
1642
|
+
}
|
|
1643
|
+
function isValidPairCode(code) {
|
|
1644
|
+
return /^\d{6}$/.test(code.trim());
|
|
1645
|
+
}
|
|
1646
|
+
function resolveVerifyService(runtime) {
|
|
1647
|
+
try {
|
|
1648
|
+
const svc = runtime.getService("OWNER_BIND_VERIFY");
|
|
1649
|
+
if (svc && typeof svc === "object" && typeof svc.verifyOwnerBindFromConnector === "function") {
|
|
1650
|
+
return svc;
|
|
1651
|
+
}
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
return null;
|
|
1655
|
+
}
|
|
1656
|
+
function resolveDisplayHandle(from) {
|
|
1657
|
+
if (from.username) {
|
|
1658
|
+
return `@${from.username}`;
|
|
1659
|
+
}
|
|
1660
|
+
if (from.first_name) {
|
|
1661
|
+
return from.first_name;
|
|
1662
|
+
}
|
|
1663
|
+
return String(from.id);
|
|
1664
|
+
}
|
|
1665
|
+
async function handleElizaPairCommand(ctx, runtime) {
|
|
1666
|
+
const from = ctx.from;
|
|
1667
|
+
if (!from) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const userId = String(from.id);
|
|
1671
|
+
const displayHandle = resolveDisplayHandle(from);
|
|
1672
|
+
if (isRateLimited(userId)) {
|
|
1673
|
+
logger3.warn(
|
|
1674
|
+
{ src: "plugin:telegram:owner-pairing", userId },
|
|
1675
|
+
"Rate limit hit for /eliza_pair"
|
|
1676
|
+
);
|
|
1677
|
+
await auditEmit(
|
|
1678
|
+
runtime,
|
|
1679
|
+
"auth.owner.pair.telegram.rate_limited",
|
|
1680
|
+
"failure",
|
|
1681
|
+
{ externalId: userId }
|
|
1682
|
+
);
|
|
1683
|
+
await ctx.reply(
|
|
1684
|
+
"Too many pairing attempts. Please wait a moment before trying again."
|
|
1685
|
+
);
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
const message = ctx.message;
|
|
1689
|
+
const rawText = message && "text" in message ? message.text : void 0;
|
|
1690
|
+
let code = null;
|
|
1691
|
+
if (typeof rawText === "string") {
|
|
1692
|
+
const parts = rawText.trim().split(/\s+/);
|
|
1693
|
+
if (parts.length >= 2) {
|
|
1694
|
+
code = parts[1] ?? null;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (!code?.trim()) {
|
|
1698
|
+
await ctx.reply(
|
|
1699
|
+
"Usage: /eliza\\_pair <code> \u2014 enter the 6-digit code shown in the Eliza dashboard."
|
|
1700
|
+
);
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
code = code.trim();
|
|
1704
|
+
if (!isValidPairCode(code)) {
|
|
1705
|
+
await ctx.reply(
|
|
1706
|
+
"The pairing code must be exactly 6 digits. Check the Eliza dashboard and try again."
|
|
1707
|
+
);
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
const verifySvc = resolveVerifyService(runtime);
|
|
1711
|
+
if (!verifySvc) {
|
|
1712
|
+
logger3.error(
|
|
1713
|
+
{ src: "plugin:telegram:owner-pairing", userId },
|
|
1714
|
+
"OWNER_BIND_VERIFY service not available \u2014 cannot complete pairing"
|
|
1715
|
+
);
|
|
1716
|
+
await auditEmit(
|
|
1717
|
+
runtime,
|
|
1718
|
+
"auth.owner.pair.telegram.service_unavailable",
|
|
1719
|
+
"failure",
|
|
1720
|
+
{ externalId: userId }
|
|
1721
|
+
);
|
|
1722
|
+
await ctx.reply(
|
|
1723
|
+
"Eliza could not reach the pairing service right now. Please try again in a moment."
|
|
1724
|
+
);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
let result;
|
|
1728
|
+
try {
|
|
1729
|
+
result = await verifySvc.verifyOwnerBindFromConnector({
|
|
1730
|
+
connector: "telegram",
|
|
1731
|
+
externalId: userId,
|
|
1732
|
+
displayHandle,
|
|
1733
|
+
code
|
|
1734
|
+
});
|
|
1735
|
+
} catch (err) {
|
|
1736
|
+
logger3.error(
|
|
1737
|
+
{
|
|
1738
|
+
src: "plugin:telegram:owner-pairing",
|
|
1739
|
+
userId,
|
|
1740
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1741
|
+
},
|
|
1742
|
+
"verifyOwnerBindFromConnector threw unexpectedly"
|
|
1743
|
+
);
|
|
1744
|
+
await auditEmit(
|
|
1745
|
+
runtime,
|
|
1746
|
+
"auth.owner.pair.telegram.verify_error",
|
|
1747
|
+
"failure",
|
|
1748
|
+
{ externalId: userId }
|
|
1749
|
+
);
|
|
1750
|
+
await ctx.reply(
|
|
1751
|
+
"Something went wrong while verifying the pairing code. Please try again."
|
|
1752
|
+
);
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
if (result.success) {
|
|
1756
|
+
logger3.info(
|
|
1757
|
+
{ src: "plugin:telegram:owner-pairing", userId, displayHandle },
|
|
1758
|
+
"Owner pairing completed successfully"
|
|
1759
|
+
);
|
|
1760
|
+
await auditEmit(runtime, "auth.owner.pair.telegram.success", "success", {
|
|
1761
|
+
externalId: userId,
|
|
1762
|
+
displayHandle
|
|
1763
|
+
});
|
|
1764
|
+
await ctx.reply("Paired with Eliza. You can now log in via Telegram.");
|
|
1765
|
+
} else {
|
|
1766
|
+
logger3.warn(
|
|
1767
|
+
{
|
|
1768
|
+
src: "plugin:telegram:owner-pairing",
|
|
1769
|
+
userId,
|
|
1770
|
+
backendError: result.error
|
|
1771
|
+
},
|
|
1772
|
+
"Owner pairing rejected by backend"
|
|
1773
|
+
);
|
|
1774
|
+
await auditEmit(runtime, "auth.owner.pair.telegram.failure", "failure", {
|
|
1775
|
+
externalId: userId
|
|
1776
|
+
});
|
|
1777
|
+
await ctx.reply(
|
|
1778
|
+
"Pair code invalid or expired. Check the Eliza dashboard for a fresh code."
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
var TelegramOwnerPairingServiceImpl = class _TelegramOwnerPairingServiceImpl extends Service {
|
|
1783
|
+
static serviceType = TELEGRAM_OWNER_PAIRING_SERVICE_TYPE;
|
|
1784
|
+
capabilityDescription = "Handles Telegram-side owner pairing (command code verification) and DM login-link delivery for Eliza remote auth";
|
|
1785
|
+
static async start(runtime) {
|
|
1786
|
+
const service = new _TelegramOwnerPairingServiceImpl(runtime);
|
|
1787
|
+
if (resolveVerifyService(runtime)) {
|
|
1788
|
+
service.registerPairCommand(runtime);
|
|
1789
|
+
logger3.info(
|
|
1790
|
+
{
|
|
1791
|
+
src: "plugin:telegram:owner-pairing",
|
|
1792
|
+
agentId: runtime.agentId
|
|
1793
|
+
},
|
|
1794
|
+
"TelegramOwnerPairingService started; /eliza_pair command registered"
|
|
1795
|
+
);
|
|
1796
|
+
} else {
|
|
1797
|
+
logger3.info(
|
|
1798
|
+
{
|
|
1799
|
+
src: "plugin:telegram:owner-pairing",
|
|
1800
|
+
agentId: runtime.agentId
|
|
1801
|
+
},
|
|
1802
|
+
"TelegramOwnerPairingService started without /eliza_pair because OWNER_BIND_VERIFY is not registered"
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
return service;
|
|
1806
|
+
}
|
|
1807
|
+
async stop() {
|
|
1808
|
+
pairAttempts.clear();
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Registers the /eliza_pair command with the active Telegraf bot instance
|
|
1812
|
+
* by looking up the TelegramService from the runtime service registry.
|
|
1813
|
+
* Called during `start`; it is safe to call this before or after the bot
|
|
1814
|
+
* has finished initialising because Telegraf accepts handler registration
|
|
1815
|
+
* at any point before `launch()`.
|
|
1816
|
+
*
|
|
1817
|
+
* If the TelegramService is unavailable, the command is not registered.
|
|
1818
|
+
*/
|
|
1819
|
+
registerPairCommand(runtime) {
|
|
1820
|
+
const telegramSvc = runtime.getService(TELEGRAM_SERVICE_NAME);
|
|
1821
|
+
if (!telegramSvc || typeof telegramSvc !== "object") {
|
|
1822
|
+
logger3.warn(
|
|
1823
|
+
{ src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
|
|
1824
|
+
"TelegramService unavailable during owner-pairing start; /eliza_pair command not registered"
|
|
1825
|
+
);
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
const bot = "bot" in telegramSvc ? telegramSvc.bot : null;
|
|
1829
|
+
if (!bot || typeof bot.command !== "function") {
|
|
1830
|
+
logger3.warn(
|
|
1831
|
+
{ src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
|
|
1832
|
+
"Telegraf bot instance not available \u2014 /eliza_pair will not be registered"
|
|
1833
|
+
);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
const telegrafBot = bot;
|
|
1837
|
+
telegrafBot.command("eliza_pair", async (ctx) => {
|
|
1838
|
+
await handleElizaPairCommand(ctx, runtime);
|
|
1839
|
+
});
|
|
1840
|
+
logger3.debug(
|
|
1841
|
+
{ src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
|
|
1842
|
+
"/eliza_pair command registered with Telegraf bot"
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
async sendOwnerLoginDmLink(params) {
|
|
1846
|
+
const { externalId, link } = params;
|
|
1847
|
+
const telegramSvc = this.runtime.getService(
|
|
1848
|
+
TELEGRAM_SERVICE_NAME
|
|
1849
|
+
);
|
|
1850
|
+
const bot = telegramSvc && typeof telegramSvc === "object" && "bot" in telegramSvc ? telegramSvc.bot : null;
|
|
1851
|
+
if (!bot || typeof bot.telegram !== "object") {
|
|
1852
|
+
throw new Error(
|
|
1853
|
+
"Telegram bot is not available \u2014 cannot send DM login link"
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
const telegrafBot = bot;
|
|
1857
|
+
const chatId = Number(externalId);
|
|
1858
|
+
if (!Number.isFinite(chatId) || chatId <= 0) {
|
|
1859
|
+
throw new Error(
|
|
1860
|
+
`Invalid Telegram externalId "${externalId}" \u2014 must be a positive numeric user ID`
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
const message = `Click to log in to Eliza: ${link}
|
|
1864
|
+
|
|
1865
|
+
_This link expires in 5 minutes. Do not share it._`;
|
|
1866
|
+
try {
|
|
1867
|
+
await telegrafBot.telegram.sendMessage(chatId, message, {
|
|
1868
|
+
parse_mode: "Markdown"
|
|
1869
|
+
});
|
|
1870
|
+
logger3.info(
|
|
1871
|
+
{ src: "plugin:telegram:owner-pairing", externalId },
|
|
1872
|
+
"Login DM link sent"
|
|
1873
|
+
);
|
|
1874
|
+
} catch (err) {
|
|
1875
|
+
throw new Error(
|
|
1876
|
+
`Failed to send DM login link to Telegram user ${externalId}: ${err instanceof Error ? err.message : String(err)}`
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
|
|
856
1882
|
// src/service.ts
|
|
857
|
-
|
|
1883
|
+
import {
|
|
1884
|
+
ChannelType as ChannelType2,
|
|
1885
|
+
createUniqueUuid as createUniqueUuid2,
|
|
1886
|
+
EventType as EventType2,
|
|
1887
|
+
logger as logger4,
|
|
1888
|
+
Role,
|
|
1889
|
+
Service as Service2
|
|
1890
|
+
} from "@elizaos/core";
|
|
1891
|
+
import { Telegraf } from "telegraf";
|
|
1892
|
+
var CANONICAL_OWNER_SETTING_KEYS = ["ELIZA_ADMIN_ENTITY_ID"];
|
|
1893
|
+
var TELEGRAM_CONNECTOR_CONTEXTS = ["social", "connectors"];
|
|
1894
|
+
var TELEGRAM_CONNECTOR_CAPABILITIES = [
|
|
1895
|
+
"send_message",
|
|
1896
|
+
"resolve_targets",
|
|
1897
|
+
"list_rooms",
|
|
1898
|
+
"chat_context",
|
|
1899
|
+
"user_context"
|
|
1900
|
+
];
|
|
1901
|
+
var TELEGRAM_CHAT_ID_PATTERN = /^-?\d+$/;
|
|
1902
|
+
var TELEGRAM_THREADED_CHANNEL_PATTERN = /^(-?\d+)-(\d+)$/;
|
|
1903
|
+
function normalizeConnectorLimit(limit, fallback = 50) {
|
|
1904
|
+
if (!Number.isFinite(limit) || !limit || limit <= 0) {
|
|
1905
|
+
return fallback;
|
|
1906
|
+
}
|
|
1907
|
+
return Math.min(Math.floor(limit), 200);
|
|
1908
|
+
}
|
|
1909
|
+
function filterMemoriesByQuery(memories, query, limit) {
|
|
1910
|
+
const normalized = query.trim().toLowerCase();
|
|
1911
|
+
if (!normalized) {
|
|
1912
|
+
return memories.slice(0, limit);
|
|
1913
|
+
}
|
|
1914
|
+
return memories.filter((memory) => {
|
|
1915
|
+
const text = typeof memory.content?.text === "string" ? memory.content.text : "";
|
|
1916
|
+
return text.toLowerCase().includes(normalized);
|
|
1917
|
+
}).slice(0, limit);
|
|
1918
|
+
}
|
|
1919
|
+
var ACTIVE_TELEGRAM_POLLERS = /* @__PURE__ */ new Map();
|
|
1920
|
+
function getCanonicalOwnerId(runtime) {
|
|
1921
|
+
for (const key of CANONICAL_OWNER_SETTING_KEYS) {
|
|
1922
|
+
const value = runtime.getSetting(key);
|
|
1923
|
+
if (typeof value !== "string") {
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
const trimmed = value.trim();
|
|
1927
|
+
if (trimmed.length > 0) {
|
|
1928
|
+
return trimmed;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
function getTelegramChatDisplayName(chat, fallback) {
|
|
1934
|
+
if (!chat) {
|
|
1935
|
+
return fallback;
|
|
1936
|
+
}
|
|
1937
|
+
if ("title" in chat && typeof chat.title === "string" && chat.title.trim()) {
|
|
1938
|
+
return chat.title;
|
|
1939
|
+
}
|
|
1940
|
+
if ("first_name" in chat && typeof chat.first_name === "string" && chat.first_name.trim()) {
|
|
1941
|
+
return chat.first_name;
|
|
1942
|
+
}
|
|
1943
|
+
if ("username" in chat && typeof chat.username === "string" && chat.username.trim()) {
|
|
1944
|
+
return chat.username;
|
|
1945
|
+
}
|
|
1946
|
+
return fallback;
|
|
1947
|
+
}
|
|
1948
|
+
function normalizeTelegramConnectorQuery(value) {
|
|
1949
|
+
return value.trim().replace(/^@/, "").toLowerCase();
|
|
1950
|
+
}
|
|
1951
|
+
function scoreTelegramConnectorMatch(query, id, labels) {
|
|
1952
|
+
if (!query) {
|
|
1953
|
+
return 0.45;
|
|
1954
|
+
}
|
|
1955
|
+
if (id.toLowerCase() === query) {
|
|
1956
|
+
return 1;
|
|
1957
|
+
}
|
|
1958
|
+
let bestScore = 0;
|
|
1959
|
+
for (const label of labels) {
|
|
1960
|
+
const normalized = label?.trim().replace(/^@/, "").toLowerCase();
|
|
1961
|
+
if (!normalized) {
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
if (normalized === query) {
|
|
1965
|
+
bestScore = Math.max(bestScore, 0.95);
|
|
1966
|
+
} else if (normalized.startsWith(query)) {
|
|
1967
|
+
bestScore = Math.max(bestScore, 0.85);
|
|
1968
|
+
} else if (normalized.includes(query)) {
|
|
1969
|
+
bestScore = Math.max(bestScore, 0.7);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
return bestScore;
|
|
1973
|
+
}
|
|
1974
|
+
function parseTelegramTargetParts(channelId, explicitThreadId) {
|
|
1975
|
+
const explicitThreadNumber = explicitThreadId && /^\d+$/.test(explicitThreadId) ? Number.parseInt(explicitThreadId, 10) : void 0;
|
|
1976
|
+
const threadedMatch = channelId.match(TELEGRAM_THREADED_CHANNEL_PATTERN);
|
|
1977
|
+
if (threadedMatch) {
|
|
1978
|
+
return {
|
|
1979
|
+
chatId: threadedMatch[1],
|
|
1980
|
+
threadId: explicitThreadNumber ?? Number.parseInt(threadedMatch[2], 10)
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
return { chatId: channelId, threadId: explicitThreadNumber };
|
|
1984
|
+
}
|
|
1985
|
+
function telegramChatKind(chat) {
|
|
1986
|
+
if (chat.type === "private") {
|
|
1987
|
+
return "user";
|
|
1988
|
+
}
|
|
1989
|
+
if (chat.type === "channel") {
|
|
1990
|
+
return "channel";
|
|
1991
|
+
}
|
|
1992
|
+
return "group";
|
|
1993
|
+
}
|
|
1994
|
+
var TelegramService = class _TelegramService extends Service2 {
|
|
858
1995
|
static serviceType = TELEGRAM_SERVICE_NAME;
|
|
859
1996
|
capabilityDescription = "The agent is able to send and receive messages on telegram";
|
|
860
|
-
bot;
|
|
861
|
-
messageManager;
|
|
862
|
-
options;
|
|
1997
|
+
bot = null;
|
|
1998
|
+
messageManager = null;
|
|
863
1999
|
knownChats = /* @__PURE__ */ new Map();
|
|
864
2000
|
syncedEntityIds = /* @__PURE__ */ new Set();
|
|
2001
|
+
botToken;
|
|
2002
|
+
defaultAccountId = DEFAULT_ACCOUNT_ID;
|
|
2003
|
+
accountStates = /* @__PURE__ */ new Map();
|
|
865
2004
|
/**
|
|
866
2005
|
* Constructor for TelegramService class.
|
|
867
2006
|
* @param {IAgentRuntime} runtime - The runtime object for the agent.
|
|
@@ -871,31 +2010,134 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
871
2010
|
if (!runtime) {
|
|
872
2011
|
this.bot = null;
|
|
873
2012
|
this.messageManager = null;
|
|
2013
|
+
this.botToken = null;
|
|
874
2014
|
return;
|
|
875
2015
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
2016
|
+
logger4.debug(
|
|
2017
|
+
{ src: "plugin:telegram", agentId: runtime.agentId },
|
|
2018
|
+
"Constructing TelegramService"
|
|
2019
|
+
);
|
|
2020
|
+
this.defaultAccountId = resolveDefaultTelegramAccountId(runtime);
|
|
2021
|
+
const account = resolveTelegramAccount(runtime, this.defaultAccountId);
|
|
2022
|
+
this.botToken = account.botToken ?? null;
|
|
2023
|
+
if (!account.botToken) {
|
|
2024
|
+
logger4.warn(
|
|
2025
|
+
{
|
|
2026
|
+
src: "plugin:telegram",
|
|
2027
|
+
agentId: runtime.agentId,
|
|
2028
|
+
accountId: account.accountId
|
|
2029
|
+
},
|
|
2030
|
+
"Bot token not provided, Telegram functionality unavailable"
|
|
2031
|
+
);
|
|
880
2032
|
this.bot = null;
|
|
881
2033
|
this.messageManager = null;
|
|
882
2034
|
return;
|
|
883
2035
|
}
|
|
884
|
-
this.options = {
|
|
885
|
-
telegram: {
|
|
886
|
-
apiRoot: runtime.getSetting("TELEGRAM_API_ROOT") || process.env.TELEGRAM_API_ROOT || "https://api.telegram.org"
|
|
887
|
-
}
|
|
888
|
-
};
|
|
889
2036
|
try {
|
|
890
|
-
|
|
891
|
-
this.
|
|
892
|
-
|
|
2037
|
+
const state = this.createAccountRuntime(account);
|
|
2038
|
+
this.setDefaultAccountState(state);
|
|
2039
|
+
logger4.debug(
|
|
2040
|
+
{
|
|
2041
|
+
src: "plugin:telegram",
|
|
2042
|
+
agentId: runtime.agentId,
|
|
2043
|
+
accountId: account.accountId
|
|
2044
|
+
},
|
|
2045
|
+
"TelegramService constructor completed"
|
|
2046
|
+
);
|
|
893
2047
|
} catch (error) {
|
|
894
|
-
|
|
2048
|
+
logger4.error(
|
|
2049
|
+
{
|
|
2050
|
+
src: "plugin:telegram",
|
|
2051
|
+
agentId: runtime.agentId,
|
|
2052
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2053
|
+
},
|
|
2054
|
+
"Failed to initialize Telegram bot"
|
|
2055
|
+
);
|
|
895
2056
|
this.bot = null;
|
|
896
2057
|
this.messageManager = null;
|
|
897
2058
|
}
|
|
898
2059
|
}
|
|
2060
|
+
createAccountRuntime(account) {
|
|
2061
|
+
if (!account.botToken) {
|
|
2062
|
+
throw new Error(`Telegram account ${account.accountId} has no bot token`);
|
|
2063
|
+
}
|
|
2064
|
+
const bot = new Telegraf(account.botToken, {
|
|
2065
|
+
telegram: { apiRoot: account.apiRoot }
|
|
2066
|
+
});
|
|
2067
|
+
const messageManager = new MessageManager(
|
|
2068
|
+
bot,
|
|
2069
|
+
this.runtime,
|
|
2070
|
+
account.accountId
|
|
2071
|
+
);
|
|
2072
|
+
return {
|
|
2073
|
+
accountId: account.accountId,
|
|
2074
|
+
account,
|
|
2075
|
+
bot,
|
|
2076
|
+
messageManager
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
setDefaultAccountState(state) {
|
|
2080
|
+
this.accountStates.set(state.accountId, state);
|
|
2081
|
+
if (state.accountId === this.defaultAccountId || !this.bot) {
|
|
2082
|
+
this.bot = state.bot;
|
|
2083
|
+
this.messageManager = state.messageManager;
|
|
2084
|
+
this.botToken = state.account.botToken ?? null;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
getDefaultAccountState() {
|
|
2088
|
+
if (!(this.accountStates instanceof Map) || this.accountStates.size === 0) {
|
|
2089
|
+
return null;
|
|
2090
|
+
}
|
|
2091
|
+
return this.accountStates.get(this.defaultAccountId) ?? this.accountStates.values().next().value ?? null;
|
|
2092
|
+
}
|
|
2093
|
+
getAccountState(accountId) {
|
|
2094
|
+
if (!(this.accountStates instanceof Map) || this.accountStates.size === 0) {
|
|
2095
|
+
return null;
|
|
2096
|
+
}
|
|
2097
|
+
if (accountId) {
|
|
2098
|
+
return this.accountStates.get(normalizeTelegramAccountId(accountId)) ?? null;
|
|
2099
|
+
}
|
|
2100
|
+
return this.getDefaultAccountState();
|
|
2101
|
+
}
|
|
2102
|
+
getAccountIds() {
|
|
2103
|
+
if (this.accountStates instanceof Map && this.accountStates.size > 0) {
|
|
2104
|
+
return Array.from(this.accountStates.keys());
|
|
2105
|
+
}
|
|
2106
|
+
return [normalizeTelegramAccountId(this.defaultAccountId)];
|
|
2107
|
+
}
|
|
2108
|
+
resolveAccountIdFromContext(context, target) {
|
|
2109
|
+
const scopedTarget = target;
|
|
2110
|
+
const scopedContext = context;
|
|
2111
|
+
return scopedTarget?.accountId ?? scopedContext?.target?.accountId ?? scopedContext?.accountId ?? scopedContext?.account?.accountId ?? void 0;
|
|
2112
|
+
}
|
|
2113
|
+
async resolveAccountIdForTarget(runtime, target, fallback) {
|
|
2114
|
+
const direct = target?.accountId ?? fallback?.accountId;
|
|
2115
|
+
if (direct) {
|
|
2116
|
+
return normalizeTelegramAccountId(direct);
|
|
2117
|
+
}
|
|
2118
|
+
const roomId = target?.roomId ?? fallback?.roomId;
|
|
2119
|
+
if (roomId && typeof runtime.getRoom === "function") {
|
|
2120
|
+
const room = await runtime.getRoom(roomId);
|
|
2121
|
+
const metadata = room?.metadata;
|
|
2122
|
+
if (typeof metadata?.accountId === "string" && metadata.accountId.trim()) {
|
|
2123
|
+
return normalizeTelegramAccountId(metadata.accountId);
|
|
2124
|
+
}
|
|
2125
|
+
const telegram = metadata?.telegram && typeof metadata.telegram === "object" ? metadata.telegram : void 0;
|
|
2126
|
+
if (typeof telegram?.accountId === "string" && telegram.accountId.trim()) {
|
|
2127
|
+
return normalizeTelegramAccountId(telegram.accountId);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return normalizeTelegramAccountId(this.defaultAccountId);
|
|
2131
|
+
}
|
|
2132
|
+
scopedTelegramKey(key, accountId) {
|
|
2133
|
+
const normalized = normalizeTelegramAccountId(
|
|
2134
|
+
accountId ?? this.defaultAccountId
|
|
2135
|
+
);
|
|
2136
|
+
return normalized === DEFAULT_ACCOUNT_ID ? key : `${normalized}:${key}`;
|
|
2137
|
+
}
|
|
2138
|
+
knownChatKeyMatchesAccount(key, chat, accountId) {
|
|
2139
|
+
return key === this.scopedTelegramKey(chat.id.toString(), accountId);
|
|
2140
|
+
}
|
|
899
2141
|
/**
|
|
900
2142
|
* Starts the Telegram service for the given runtime.
|
|
901
2143
|
*
|
|
@@ -904,34 +2146,88 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
904
2146
|
*/
|
|
905
2147
|
static async start(runtime) {
|
|
906
2148
|
const service = new _TelegramService(runtime);
|
|
2149
|
+
for (const account of listEnabledTelegramAccounts(runtime)) {
|
|
2150
|
+
if (!service.getAccountState(account.accountId)) {
|
|
2151
|
+
service.setDefaultAccountState(service.createAccountRuntime(account));
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
907
2154
|
if (!service.bot) {
|
|
908
|
-
|
|
2155
|
+
logger4.warn(
|
|
2156
|
+
{ src: "plugin:telegram", agentId: runtime.agentId },
|
|
2157
|
+
"Service started without bot functionality"
|
|
2158
|
+
);
|
|
909
2159
|
return service;
|
|
910
2160
|
}
|
|
911
2161
|
const maxRetries = 5;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
2162
|
+
for (const state of service.accountStates.values()) {
|
|
2163
|
+
let retryCount = 0;
|
|
2164
|
+
let lastError = null;
|
|
2165
|
+
while (retryCount < maxRetries) {
|
|
2166
|
+
try {
|
|
2167
|
+
logger4.info(
|
|
2168
|
+
{
|
|
2169
|
+
src: "plugin:telegram",
|
|
2170
|
+
agentId: runtime.agentId,
|
|
2171
|
+
agentName: runtime.character.name,
|
|
2172
|
+
accountId: state.accountId
|
|
2173
|
+
},
|
|
2174
|
+
"Starting Telegram bot"
|
|
2175
|
+
);
|
|
2176
|
+
await service.initializeBot(state);
|
|
2177
|
+
service.setupMiddlewares(state);
|
|
2178
|
+
service.setupMessageHandlers(state);
|
|
2179
|
+
await state.bot.telegram.getMe();
|
|
2180
|
+
logger4.success(
|
|
2181
|
+
{
|
|
2182
|
+
src: "plugin:telegram",
|
|
2183
|
+
agentId: runtime.agentId,
|
|
2184
|
+
agentName: runtime.character.name,
|
|
2185
|
+
accountId: state.accountId
|
|
2186
|
+
},
|
|
2187
|
+
"Telegram bot started successfully"
|
|
2188
|
+
);
|
|
2189
|
+
break;
|
|
2190
|
+
} catch (error) {
|
|
2191
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2192
|
+
logger4.error(
|
|
2193
|
+
{
|
|
2194
|
+
src: "plugin:telegram",
|
|
2195
|
+
agentId: runtime.agentId,
|
|
2196
|
+
accountId: state.accountId,
|
|
2197
|
+
attempt: retryCount + 1,
|
|
2198
|
+
error: lastError.message
|
|
2199
|
+
},
|
|
2200
|
+
"Initialization attempt failed"
|
|
2201
|
+
);
|
|
2202
|
+
retryCount++;
|
|
2203
|
+
if (retryCount < maxRetries) {
|
|
2204
|
+
const delay = 2 ** retryCount * 1e3;
|
|
2205
|
+
logger4.info(
|
|
2206
|
+
{
|
|
2207
|
+
src: "plugin:telegram",
|
|
2208
|
+
agentId: runtime.agentId,
|
|
2209
|
+
accountId: state.accountId,
|
|
2210
|
+
delaySeconds: delay / 1e3
|
|
2211
|
+
},
|
|
2212
|
+
"Retrying initialization"
|
|
2213
|
+
);
|
|
2214
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2215
|
+
}
|
|
931
2216
|
}
|
|
932
2217
|
}
|
|
2218
|
+
if (retryCount >= maxRetries) {
|
|
2219
|
+
logger4.error(
|
|
2220
|
+
{
|
|
2221
|
+
src: "plugin:telegram",
|
|
2222
|
+
agentId: runtime.agentId,
|
|
2223
|
+
accountId: state.accountId,
|
|
2224
|
+
maxRetries,
|
|
2225
|
+
error: lastError?.message
|
|
2226
|
+
},
|
|
2227
|
+
"Initialization failed after all attempts"
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
933
2230
|
}
|
|
934
|
-
logger3.error({ src: "plugin:telegram", agentId: runtime.agentId, maxRetries, error: lastError?.message }, "Initialization failed after all attempts");
|
|
935
2231
|
return service;
|
|
936
2232
|
}
|
|
937
2233
|
/**
|
|
@@ -939,7 +2235,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
939
2235
|
* @param {IAgentRuntime} runtime - The agent runtime to stop
|
|
940
2236
|
*/
|
|
941
2237
|
static async stop(runtime) {
|
|
942
|
-
const tgClient = runtime.getService(TELEGRAM_SERVICE_NAME);
|
|
2238
|
+
const tgClient = await runtime.getService(TELEGRAM_SERVICE_NAME);
|
|
943
2239
|
if (tgClient) {
|
|
944
2240
|
await tgClient.stop();
|
|
945
2241
|
}
|
|
@@ -950,31 +2246,109 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
950
2246
|
* @returns A Promise that resolves once the bot has stopped.
|
|
951
2247
|
*/
|
|
952
2248
|
async stop() {
|
|
953
|
-
this.
|
|
2249
|
+
const states = this.accountStates instanceof Map ? Array.from(this.accountStates.values()) : [];
|
|
2250
|
+
if (states.length > 0) {
|
|
2251
|
+
for (const state of states) {
|
|
2252
|
+
state.bot.stop("service-stop");
|
|
2253
|
+
const token = state.account.botToken;
|
|
2254
|
+
if (token) {
|
|
2255
|
+
const active = ACTIVE_TELEGRAM_POLLERS.get(token);
|
|
2256
|
+
if (active?.bot === state.bot) {
|
|
2257
|
+
ACTIVE_TELEGRAM_POLLERS.delete(token);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
const bot = this.bot;
|
|
2264
|
+
if (bot) {
|
|
2265
|
+
bot.stop("service-stop");
|
|
2266
|
+
if (this.botToken) {
|
|
2267
|
+
const active = ACTIVE_TELEGRAM_POLLERS.get(this.botToken);
|
|
2268
|
+
if (active?.bot === bot) {
|
|
2269
|
+
ACTIVE_TELEGRAM_POLLERS.delete(this.botToken);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
954
2273
|
}
|
|
955
2274
|
/**
|
|
956
2275
|
* Initializes the Telegram bot by launching it, getting bot info, and setting up message manager.
|
|
957
2276
|
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
958
2277
|
*/
|
|
959
|
-
async initializeBot() {
|
|
960
|
-
this.
|
|
2278
|
+
async initializeBot(state) {
|
|
2279
|
+
const activeState = state ?? this.getDefaultAccountState();
|
|
2280
|
+
const bot = activeState?.bot ?? this.bot;
|
|
2281
|
+
if (!bot) {
|
|
2282
|
+
throw new Error("Telegram bot is not initialized");
|
|
2283
|
+
}
|
|
2284
|
+
const botToken = activeState?.account.botToken ?? this.botToken;
|
|
2285
|
+
const accountId = activeState?.accountId ?? this.defaultAccountId;
|
|
2286
|
+
if (botToken) {
|
|
2287
|
+
const active = ACTIVE_TELEGRAM_POLLERS.get(botToken);
|
|
2288
|
+
if (active && active.bot !== bot) {
|
|
2289
|
+
logger4.warn(
|
|
2290
|
+
{
|
|
2291
|
+
src: "plugin:telegram",
|
|
2292
|
+
agentId: this.runtime.agentId,
|
|
2293
|
+
accountId,
|
|
2294
|
+
previousAgentId: active.agentId
|
|
2295
|
+
},
|
|
2296
|
+
"Stopping existing Telegram poller before launching a new one"
|
|
2297
|
+
);
|
|
2298
|
+
try {
|
|
2299
|
+
active.bot.stop("replaced-by-new-runtime");
|
|
2300
|
+
} catch (error) {
|
|
2301
|
+
logger4.warn(
|
|
2302
|
+
{
|
|
2303
|
+
src: "plugin:telegram",
|
|
2304
|
+
agentId: this.runtime.agentId,
|
|
2305
|
+
accountId,
|
|
2306
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2307
|
+
},
|
|
2308
|
+
"Failed to stop previous Telegram poller cleanly"
|
|
2309
|
+
);
|
|
2310
|
+
}
|
|
2311
|
+
ACTIVE_TELEGRAM_POLLERS.delete(botToken);
|
|
2312
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
bot.start((ctx) => {
|
|
2316
|
+
const slashStartPayload = {
|
|
2317
|
+
ctx,
|
|
2318
|
+
runtime: this.runtime,
|
|
2319
|
+
source: "telegram",
|
|
2320
|
+
accountId,
|
|
2321
|
+
metadata: { accountId }
|
|
2322
|
+
};
|
|
961
2323
|
this.runtime.emitEvent(
|
|
962
2324
|
"TELEGRAM_SLASH_START" /* SLASH_START */,
|
|
963
|
-
|
|
964
|
-
ctx,
|
|
965
|
-
runtime: this.runtime,
|
|
966
|
-
source: "telegram"
|
|
967
|
-
}
|
|
2325
|
+
slashStartPayload
|
|
968
2326
|
);
|
|
969
2327
|
});
|
|
970
|
-
|
|
2328
|
+
await bot.launch({
|
|
971
2329
|
dropPendingUpdates: true,
|
|
972
2330
|
allowedUpdates: ["message", "message_reaction"]
|
|
973
2331
|
});
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
2332
|
+
if (botToken) {
|
|
2333
|
+
ACTIVE_TELEGRAM_POLLERS.set(botToken, {
|
|
2334
|
+
bot,
|
|
2335
|
+
agentId: this.runtime.agentId,
|
|
2336
|
+
accountId
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
const botInfo = await bot.telegram.getMe();
|
|
2340
|
+
logger4.debug(
|
|
2341
|
+
{
|
|
2342
|
+
src: "plugin:telegram",
|
|
2343
|
+
agentId: this.runtime.agentId,
|
|
2344
|
+
accountId,
|
|
2345
|
+
botId: botInfo.id,
|
|
2346
|
+
botUsername: botInfo.username
|
|
2347
|
+
},
|
|
2348
|
+
"Bot info retrieved"
|
|
2349
|
+
);
|
|
2350
|
+
process.once("SIGINT", () => bot.stop("SIGINT"));
|
|
2351
|
+
process.once("SIGTERM", () => bot.stop("SIGTERM"));
|
|
978
2352
|
}
|
|
979
2353
|
/**
|
|
980
2354
|
* Sets up the middleware chain for preprocessing messages before they reach handlers.
|
|
@@ -991,9 +2365,11 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
991
2365
|
*
|
|
992
2366
|
* @private
|
|
993
2367
|
*/
|
|
994
|
-
setupMiddlewares() {
|
|
995
|
-
|
|
996
|
-
|
|
2368
|
+
setupMiddlewares(state) {
|
|
2369
|
+
const bot = state?.bot ?? this.bot;
|
|
2370
|
+
const accountId = state?.accountId ?? this.defaultAccountId;
|
|
2371
|
+
bot?.use((ctx, next) => this.authorizationMiddleware(ctx, next, accountId));
|
|
2372
|
+
bot?.use((ctx, next) => this.chatAndEntityMiddleware(ctx, next, accountId));
|
|
997
2373
|
}
|
|
998
2374
|
/**
|
|
999
2375
|
* Authorization middleware - checks if chat is allowed to interact with the bot
|
|
@@ -1004,9 +2380,17 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1004
2380
|
* @returns {Promise<void>}
|
|
1005
2381
|
* @private
|
|
1006
2382
|
*/
|
|
1007
|
-
async authorizationMiddleware(ctx, next) {
|
|
1008
|
-
if (!await this.isGroupAuthorized(ctx)) {
|
|
1009
|
-
|
|
2383
|
+
async authorizationMiddleware(ctx, next, accountId = this.defaultAccountId) {
|
|
2384
|
+
if (!await this.isGroupAuthorized(ctx, accountId)) {
|
|
2385
|
+
logger4.debug(
|
|
2386
|
+
{
|
|
2387
|
+
src: "plugin:telegram",
|
|
2388
|
+
agentId: this.runtime.agentId,
|
|
2389
|
+
accountId,
|
|
2390
|
+
chatId: ctx.chat?.id
|
|
2391
|
+
},
|
|
2392
|
+
"Chat not authorized, skipping"
|
|
2393
|
+
);
|
|
1010
2394
|
return;
|
|
1011
2395
|
}
|
|
1012
2396
|
await next();
|
|
@@ -1021,14 +2405,16 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1021
2405
|
* @returns {Promise<void>}
|
|
1022
2406
|
* @private
|
|
1023
2407
|
*/
|
|
1024
|
-
async chatAndEntityMiddleware(ctx, next) {
|
|
1025
|
-
if (!ctx.chat)
|
|
2408
|
+
async chatAndEntityMiddleware(ctx, next, accountId = this.defaultAccountId) {
|
|
2409
|
+
if (!ctx.chat) {
|
|
2410
|
+
return next();
|
|
2411
|
+
}
|
|
1026
2412
|
const chatId = ctx.chat.id.toString();
|
|
1027
|
-
if (!this.knownChats.has(chatId)) {
|
|
1028
|
-
await this.handleNewChat(ctx);
|
|
2413
|
+
if (!this.knownChats.has(this.scopedTelegramKey(chatId, accountId))) {
|
|
2414
|
+
await this.handleNewChat(ctx, accountId);
|
|
1029
2415
|
return next();
|
|
1030
2416
|
}
|
|
1031
|
-
await this.processExistingChat(ctx);
|
|
2417
|
+
await this.processExistingChat(ctx, accountId);
|
|
1032
2418
|
await next();
|
|
1033
2419
|
}
|
|
1034
2420
|
/**
|
|
@@ -1039,18 +2425,29 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1039
2425
|
* @returns {Promise<void>}
|
|
1040
2426
|
* @private
|
|
1041
2427
|
*/
|
|
1042
|
-
async processExistingChat(ctx) {
|
|
1043
|
-
if (!ctx.chat)
|
|
2428
|
+
async processExistingChat(ctx, accountId = this.defaultAccountId) {
|
|
2429
|
+
if (!ctx.chat) {
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
1044
2432
|
const chat = ctx.chat;
|
|
1045
2433
|
if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
|
|
1046
2434
|
try {
|
|
1047
|
-
await this.handleForumTopic(ctx);
|
|
2435
|
+
await this.handleForumTopic(ctx, accountId);
|
|
1048
2436
|
} catch (error) {
|
|
1049
|
-
|
|
2437
|
+
logger4.error(
|
|
2438
|
+
{
|
|
2439
|
+
src: "plugin:telegram",
|
|
2440
|
+
agentId: this.runtime.agentId,
|
|
2441
|
+
accountId,
|
|
2442
|
+
chatId: chat.id,
|
|
2443
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2444
|
+
},
|
|
2445
|
+
"Error handling forum topic"
|
|
2446
|
+
);
|
|
1050
2447
|
}
|
|
1051
2448
|
}
|
|
1052
2449
|
if (ctx.from && ctx.chat.type !== "private") {
|
|
1053
|
-
await this.syncEntity(ctx);
|
|
2450
|
+
await this.syncEntity(ctx, accountId);
|
|
1054
2451
|
}
|
|
1055
2452
|
}
|
|
1056
2453
|
/**
|
|
@@ -1059,19 +2456,38 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1059
2456
|
*
|
|
1060
2457
|
* @private
|
|
1061
2458
|
*/
|
|
1062
|
-
setupMessageHandlers() {
|
|
1063
|
-
|
|
2459
|
+
setupMessageHandlers(state) {
|
|
2460
|
+
const bot = state?.bot ?? this.bot;
|
|
2461
|
+
const messageManager = state?.messageManager ?? this.messageManager;
|
|
2462
|
+
const accountId = state?.accountId ?? this.defaultAccountId;
|
|
2463
|
+
bot?.on("message", async (ctx) => {
|
|
1064
2464
|
try {
|
|
1065
|
-
await
|
|
2465
|
+
await messageManager?.handleMessage(ctx);
|
|
1066
2466
|
} catch (error) {
|
|
1067
|
-
|
|
2467
|
+
logger4.error(
|
|
2468
|
+
{
|
|
2469
|
+
src: "plugin:telegram",
|
|
2470
|
+
agentId: this.runtime.agentId,
|
|
2471
|
+
accountId,
|
|
2472
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2473
|
+
},
|
|
2474
|
+
"Error handling message"
|
|
2475
|
+
);
|
|
1068
2476
|
}
|
|
1069
2477
|
});
|
|
1070
|
-
|
|
2478
|
+
bot?.on("message_reaction", async (ctx) => {
|
|
1071
2479
|
try {
|
|
1072
|
-
await
|
|
2480
|
+
await messageManager?.handleReaction(ctx);
|
|
1073
2481
|
} catch (error) {
|
|
1074
|
-
|
|
2482
|
+
logger4.error(
|
|
2483
|
+
{
|
|
2484
|
+
src: "plugin:telegram",
|
|
2485
|
+
agentId: this.runtime.agentId,
|
|
2486
|
+
accountId,
|
|
2487
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2488
|
+
},
|
|
2489
|
+
"Error handling reaction"
|
|
2490
|
+
);
|
|
1075
2491
|
}
|
|
1076
2492
|
});
|
|
1077
2493
|
}
|
|
@@ -1080,9 +2496,15 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1080
2496
|
* @param {Context} ctx - The context of the incoming update.
|
|
1081
2497
|
* @returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the group is authorized.
|
|
1082
2498
|
*/
|
|
1083
|
-
async isGroupAuthorized(ctx) {
|
|
2499
|
+
async isGroupAuthorized(ctx, accountId = this.defaultAccountId) {
|
|
1084
2500
|
const chatId = ctx.chat?.id.toString();
|
|
1085
|
-
if (!chatId)
|
|
2501
|
+
if (!chatId) {
|
|
2502
|
+
return false;
|
|
2503
|
+
}
|
|
2504
|
+
const accountAllowedChats = this.getAccountState(accountId)?.account.config.allowedChats;
|
|
2505
|
+
if (accountAllowedChats?.length) {
|
|
2506
|
+
return accountAllowedChats.includes(chatId);
|
|
2507
|
+
}
|
|
1086
2508
|
const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
|
|
1087
2509
|
if (!allowedChats) {
|
|
1088
2510
|
return true;
|
|
@@ -1091,7 +2513,15 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1091
2513
|
const allowedChatsList = JSON.parse(allowedChats);
|
|
1092
2514
|
return allowedChatsList.includes(chatId);
|
|
1093
2515
|
} catch (error) {
|
|
1094
|
-
|
|
2516
|
+
logger4.error(
|
|
2517
|
+
{
|
|
2518
|
+
src: "plugin:telegram",
|
|
2519
|
+
agentId: this.runtime.agentId,
|
|
2520
|
+
accountId,
|
|
2521
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2522
|
+
},
|
|
2523
|
+
"Error parsing TELEGRAM_ALLOWED_CHATS"
|
|
2524
|
+
);
|
|
1095
2525
|
return false;
|
|
1096
2526
|
}
|
|
1097
2527
|
}
|
|
@@ -1106,18 +2536,26 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1106
2536
|
* @returns {Promise<void>}
|
|
1107
2537
|
* @private
|
|
1108
2538
|
*/
|
|
1109
|
-
async syncEntity(ctx) {
|
|
1110
|
-
if (!ctx.chat)
|
|
2539
|
+
async syncEntity(ctx, accountId = this.defaultAccountId) {
|
|
2540
|
+
if (!ctx.chat) {
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
1111
2543
|
const chat = ctx.chat;
|
|
1112
2544
|
const chatId = chat.id.toString();
|
|
1113
|
-
const worldId = createUniqueUuid2(
|
|
2545
|
+
const worldId = createUniqueUuid2(
|
|
2546
|
+
this.runtime,
|
|
2547
|
+
this.scopedTelegramKey(chatId, accountId)
|
|
2548
|
+
);
|
|
1114
2549
|
const roomId = createUniqueUuid2(
|
|
1115
2550
|
this.runtime,
|
|
1116
|
-
|
|
2551
|
+
this.scopedTelegramKey(
|
|
2552
|
+
ctx.message?.message_thread_id ? `${ctx.chat.id}-${ctx.message.message_thread_id}` : ctx.chat.id.toString(),
|
|
2553
|
+
accountId
|
|
2554
|
+
)
|
|
1117
2555
|
);
|
|
1118
|
-
await this.syncMessageSender(ctx, worldId, roomId, chatId);
|
|
1119
|
-
await this.syncNewChatMember(ctx, worldId, roomId, chatId);
|
|
1120
|
-
await this.syncLeftChatMember(ctx);
|
|
2556
|
+
await this.syncMessageSender(ctx, worldId, roomId, chatId, accountId);
|
|
2557
|
+
await this.syncNewChatMember(ctx, worldId, roomId, chatId, accountId);
|
|
2558
|
+
await this.syncLeftChatMember(ctx, accountId);
|
|
1121
2559
|
}
|
|
1122
2560
|
/**
|
|
1123
2561
|
* Synchronizes the message sender entity with the runtime system.
|
|
@@ -1130,13 +2568,20 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1130
2568
|
* @returns {Promise<void>}
|
|
1131
2569
|
* @private
|
|
1132
2570
|
*/
|
|
1133
|
-
async syncMessageSender(ctx, worldId, roomId, chatId) {
|
|
1134
|
-
if (ctx.from
|
|
2571
|
+
async syncMessageSender(ctx, worldId, roomId, chatId, accountId = this.defaultAccountId) {
|
|
2572
|
+
if (ctx.from) {
|
|
1135
2573
|
const telegramId = ctx.from.id.toString();
|
|
1136
|
-
const entityId = createUniqueUuid2(
|
|
2574
|
+
const entityId = createUniqueUuid2(
|
|
2575
|
+
this.runtime,
|
|
2576
|
+
this.scopedTelegramKey(telegramId, accountId)
|
|
2577
|
+
);
|
|
2578
|
+
if (this.syncedEntityIds.has(entityId)) {
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
1137
2581
|
await this.runtime.ensureConnection({
|
|
1138
2582
|
entityId,
|
|
1139
2583
|
roomId,
|
|
2584
|
+
roomName: getTelegramChatDisplayName(ctx.chat, chatId),
|
|
1140
2585
|
userName: ctx.from.username,
|
|
1141
2586
|
userId: telegramId,
|
|
1142
2587
|
name: ctx.from.first_name || ctx.from.username || "Unknown User",
|
|
@@ -1159,38 +2604,48 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1159
2604
|
* @returns {Promise<void>}
|
|
1160
2605
|
* @private
|
|
1161
2606
|
*/
|
|
1162
|
-
async syncNewChatMember(ctx, worldId, roomId, chatId) {
|
|
1163
|
-
if (ctx.message && "
|
|
1164
|
-
const newMember
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
2607
|
+
async syncNewChatMember(ctx, worldId, roomId, chatId, accountId = this.defaultAccountId) {
|
|
2608
|
+
if (ctx.message && "new_chat_members" in ctx.message) {
|
|
2609
|
+
for (const newMember of ctx.message.new_chat_members) {
|
|
2610
|
+
const telegramId = newMember.id.toString();
|
|
2611
|
+
const entityId = createUniqueUuid2(
|
|
2612
|
+
this.runtime,
|
|
2613
|
+
this.scopedTelegramKey(telegramId, accountId)
|
|
2614
|
+
);
|
|
2615
|
+
if (this.syncedEntityIds.has(entityId)) {
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
await this.runtime.ensureConnection({
|
|
2619
|
+
entityId,
|
|
2620
|
+
roomId,
|
|
2621
|
+
roomName: getTelegramChatDisplayName(ctx.chat, chatId),
|
|
2622
|
+
userName: newMember.username,
|
|
2623
|
+
userId: telegramId,
|
|
2624
|
+
name: newMember.first_name || newMember.username || "Unknown User",
|
|
2625
|
+
source: "telegram",
|
|
2626
|
+
channelId: chatId,
|
|
2627
|
+
type: ChannelType2.GROUP,
|
|
2628
|
+
worldId
|
|
2629
|
+
});
|
|
2630
|
+
this.syncedEntityIds.add(entityId);
|
|
2631
|
+
const entityJoinedPayload = {
|
|
1183
2632
|
runtime: this.runtime,
|
|
1184
2633
|
entityId,
|
|
1185
2634
|
worldId,
|
|
1186
2635
|
source: "telegram",
|
|
2636
|
+
accountId,
|
|
2637
|
+
metadata: { accountId },
|
|
1187
2638
|
telegramUser: {
|
|
1188
2639
|
id: newMember.id,
|
|
1189
2640
|
username: newMember.username,
|
|
1190
2641
|
first_name: newMember.first_name
|
|
1191
2642
|
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
2643
|
+
};
|
|
2644
|
+
this.runtime.emitEvent(
|
|
2645
|
+
"TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */,
|
|
2646
|
+
entityJoinedPayload
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
1194
2649
|
}
|
|
1195
2650
|
}
|
|
1196
2651
|
/**
|
|
@@ -1200,15 +2655,19 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1200
2655
|
* @returns {Promise<void>}
|
|
1201
2656
|
* @private
|
|
1202
2657
|
*/
|
|
1203
|
-
async syncLeftChatMember(ctx) {
|
|
2658
|
+
async syncLeftChatMember(ctx, accountId = this.defaultAccountId) {
|
|
1204
2659
|
if (ctx.message && "left_chat_member" in ctx.message) {
|
|
1205
2660
|
const leftMember = ctx.message.left_chat_member;
|
|
1206
2661
|
const telegramId = leftMember.id.toString();
|
|
1207
|
-
const entityId = createUniqueUuid2(
|
|
2662
|
+
const entityId = createUniqueUuid2(
|
|
2663
|
+
this.runtime,
|
|
2664
|
+
this.scopedTelegramKey(telegramId, accountId)
|
|
2665
|
+
);
|
|
1208
2666
|
const existingEntity = await this.runtime.getEntityById(entityId);
|
|
1209
2667
|
if (existingEntity) {
|
|
1210
2668
|
existingEntity.metadata = {
|
|
1211
2669
|
...existingEntity.metadata,
|
|
2670
|
+
accountId,
|
|
1212
2671
|
status: "INACTIVE",
|
|
1213
2672
|
leftAt: Date.now()
|
|
1214
2673
|
};
|
|
@@ -1224,28 +2683,43 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1224
2683
|
* @returns {Promise<void>}
|
|
1225
2684
|
* @private
|
|
1226
2685
|
*/
|
|
1227
|
-
async handleForumTopic(ctx) {
|
|
1228
|
-
if (!ctx.chat || !ctx.message?.message_thread_id)
|
|
2686
|
+
async handleForumTopic(ctx, accountId = this.defaultAccountId) {
|
|
2687
|
+
if (!ctx.chat || !ctx.message?.message_thread_id) {
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
1229
2690
|
const chat = ctx.chat;
|
|
1230
2691
|
const chatId = chat.id.toString();
|
|
1231
|
-
const worldId = createUniqueUuid2(
|
|
1232
|
-
|
|
1233
|
-
|
|
2692
|
+
const worldId = createUniqueUuid2(
|
|
2693
|
+
this.runtime,
|
|
2694
|
+
this.scopedTelegramKey(chatId, accountId)
|
|
2695
|
+
);
|
|
2696
|
+
const room = await this.buildForumTopicRoom(ctx, worldId, accountId);
|
|
2697
|
+
if (!room) {
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
1234
2700
|
await this.runtime.ensureRoomExists(room);
|
|
1235
2701
|
}
|
|
1236
2702
|
/**
|
|
1237
2703
|
* Builds entity for message sender
|
|
1238
2704
|
*/
|
|
1239
|
-
buildMsgSenderEntity(from) {
|
|
1240
|
-
if (!from)
|
|
1241
|
-
|
|
2705
|
+
buildMsgSenderEntity(from, accountId = this.defaultAccountId) {
|
|
2706
|
+
if (!from) {
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2709
|
+
const userId = createUniqueUuid2(
|
|
2710
|
+
this.runtime,
|
|
2711
|
+
this.scopedTelegramKey(from.id.toString(), accountId)
|
|
2712
|
+
);
|
|
1242
2713
|
const telegramId = from.id.toString();
|
|
1243
2714
|
return {
|
|
1244
2715
|
id: userId,
|
|
1245
2716
|
agentId: this.runtime.agentId,
|
|
1246
2717
|
names: [from.first_name || from.username || "Unknown User"],
|
|
1247
2718
|
metadata: {
|
|
2719
|
+
source: "telegram",
|
|
2720
|
+
accountId,
|
|
1248
2721
|
telegram: {
|
|
2722
|
+
accountId,
|
|
1249
2723
|
id: telegramId,
|
|
1250
2724
|
username: from.username,
|
|
1251
2725
|
name: from.first_name || from.username || "Unknown User"
|
|
@@ -1262,18 +2736,26 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1262
2736
|
* @returns {Promise<void>}
|
|
1263
2737
|
* @private
|
|
1264
2738
|
*/
|
|
1265
|
-
async handleNewChat(ctx) {
|
|
1266
|
-
if (!ctx.chat)
|
|
2739
|
+
async handleNewChat(ctx, accountId = this.defaultAccountId) {
|
|
2740
|
+
if (!ctx.chat) {
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
1267
2743
|
const chat = ctx.chat;
|
|
1268
2744
|
const chatId = chat.id.toString();
|
|
1269
|
-
this.knownChats.set(chatId, chat);
|
|
2745
|
+
this.knownChats.set(this.scopedTelegramKey(chatId, accountId), chat);
|
|
1270
2746
|
const { chatTitle, channelType } = this.getChatTypeInfo(chat);
|
|
1271
|
-
const worldId = createUniqueUuid2(
|
|
2747
|
+
const worldId = createUniqueUuid2(
|
|
2748
|
+
this.runtime,
|
|
2749
|
+
this.scopedTelegramKey(chatId, accountId)
|
|
2750
|
+
);
|
|
1272
2751
|
const existingWorld = await this.runtime.getWorld(worldId);
|
|
1273
2752
|
if (existingWorld) {
|
|
1274
2753
|
return;
|
|
1275
2754
|
}
|
|
1276
|
-
const userId = ctx.from ? createUniqueUuid2(
|
|
2755
|
+
const userId = ctx.from ? createUniqueUuid2(
|
|
2756
|
+
this.runtime,
|
|
2757
|
+
this.scopedTelegramKey(ctx.from.id.toString(), accountId)
|
|
2758
|
+
) : null;
|
|
1277
2759
|
let admins = [];
|
|
1278
2760
|
let owner = null;
|
|
1279
2761
|
if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
|
|
@@ -1285,20 +2767,33 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1285
2767
|
);
|
|
1286
2768
|
owner = foundOwner || null;
|
|
1287
2769
|
} catch (error) {
|
|
1288
|
-
|
|
2770
|
+
logger4.warn(
|
|
2771
|
+
{
|
|
2772
|
+
src: "plugin:telegram",
|
|
2773
|
+
agentId: this.runtime.agentId,
|
|
2774
|
+
chatId,
|
|
2775
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2776
|
+
},
|
|
2777
|
+
"Could not get chat administrators"
|
|
2778
|
+
);
|
|
1289
2779
|
}
|
|
1290
2780
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
2781
|
+
const canonicalOwnerId = getCanonicalOwnerId(this.runtime);
|
|
2782
|
+
let ownerId = canonicalOwnerId ?? userId;
|
|
2783
|
+
if (!canonicalOwnerId && owner) {
|
|
2784
|
+
ownerId = createUniqueUuid2(
|
|
2785
|
+
this.runtime,
|
|
2786
|
+
this.scopedTelegramKey(String(owner.user.id), accountId)
|
|
2787
|
+
);
|
|
1294
2788
|
}
|
|
1295
2789
|
const world = {
|
|
1296
2790
|
id: worldId,
|
|
1297
2791
|
name: chatTitle,
|
|
1298
2792
|
agentId: this.runtime.agentId,
|
|
1299
|
-
|
|
2793
|
+
messageServerId: chatId,
|
|
1300
2794
|
metadata: {
|
|
1301
2795
|
source: "telegram",
|
|
2796
|
+
accountId,
|
|
1302
2797
|
...ownerId && { ownership: { ownerId } },
|
|
1303
2798
|
roles: ownerId ? {
|
|
1304
2799
|
[ownerId]: Role.OWNER
|
|
@@ -1309,27 +2804,35 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1309
2804
|
};
|
|
1310
2805
|
await this.runtime.ensureWorldExists(world);
|
|
1311
2806
|
const generalRoom = {
|
|
1312
|
-
id: createUniqueUuid2(
|
|
2807
|
+
id: createUniqueUuid2(
|
|
2808
|
+
this.runtime,
|
|
2809
|
+
this.scopedTelegramKey(chatId, accountId)
|
|
2810
|
+
),
|
|
1313
2811
|
name: chatTitle,
|
|
1314
2812
|
source: "telegram",
|
|
1315
2813
|
type: channelType,
|
|
1316
2814
|
channelId: chatId,
|
|
1317
2815
|
serverId: chatId,
|
|
1318
|
-
worldId
|
|
2816
|
+
worldId,
|
|
2817
|
+
metadata: {
|
|
2818
|
+
source: "telegram",
|
|
2819
|
+
accountId,
|
|
2820
|
+
telegram: { accountId, chatId }
|
|
2821
|
+
}
|
|
1319
2822
|
};
|
|
1320
2823
|
await this.runtime.ensureRoomExists(generalRoom);
|
|
1321
2824
|
const rooms = [generalRoom];
|
|
1322
2825
|
if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
|
|
1323
|
-
const topicRoom = await this.buildForumTopicRoom(ctx, worldId);
|
|
2826
|
+
const topicRoom = await this.buildForumTopicRoom(ctx, worldId, accountId);
|
|
1324
2827
|
if (topicRoom) {
|
|
1325
2828
|
rooms.push(topicRoom);
|
|
1326
2829
|
await this.runtime.ensureRoomExists(topicRoom);
|
|
1327
2830
|
}
|
|
1328
2831
|
}
|
|
1329
|
-
const entities = await this.buildStandardizedEntities(chat);
|
|
2832
|
+
const entities = await this.buildStandardizedEntities(chat, accountId);
|
|
1330
2833
|
if (ctx.from) {
|
|
1331
|
-
const senderEntity = this.buildMsgSenderEntity(ctx.from);
|
|
1332
|
-
if (senderEntity
|
|
2834
|
+
const senderEntity = this.buildMsgSenderEntity(ctx.from, accountId);
|
|
2835
|
+
if (senderEntity?.id && !entities.some((e) => e.id === senderEntity.id)) {
|
|
1333
2836
|
entities.push(senderEntity);
|
|
1334
2837
|
this.syncedEntityIds.add(senderEntity.id);
|
|
1335
2838
|
}
|
|
@@ -1337,9 +2840,11 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1337
2840
|
await this.batchProcessEntities(
|
|
1338
2841
|
entities,
|
|
1339
2842
|
generalRoom.id,
|
|
1340
|
-
generalRoom.channelId,
|
|
2843
|
+
generalRoom.name || generalRoom.channelId || chatId,
|
|
2844
|
+
generalRoom.channelId || chatId,
|
|
1341
2845
|
generalRoom.type,
|
|
1342
|
-
worldId
|
|
2846
|
+
worldId,
|
|
2847
|
+
accountId
|
|
1343
2848
|
);
|
|
1344
2849
|
const telegramWorldPayload = {
|
|
1345
2850
|
runtime: this.runtime,
|
|
@@ -1347,18 +2852,25 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1347
2852
|
rooms,
|
|
1348
2853
|
entities,
|
|
1349
2854
|
source: "telegram",
|
|
2855
|
+
accountId,
|
|
2856
|
+
metadata: { accountId },
|
|
1350
2857
|
chat,
|
|
1351
|
-
botUsername: this.bot
|
|
2858
|
+
botUsername: this.getAccountState(accountId)?.bot.botInfo?.username
|
|
1352
2859
|
};
|
|
1353
2860
|
if (chat.type !== "private") {
|
|
1354
|
-
await this.runtime.emitEvent(
|
|
2861
|
+
await this.runtime.emitEvent(
|
|
2862
|
+
"TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */,
|
|
2863
|
+
telegramWorldPayload
|
|
2864
|
+
);
|
|
1355
2865
|
}
|
|
1356
2866
|
await this.runtime.emitEvent(EventType2.WORLD_JOINED, {
|
|
1357
2867
|
runtime: this.runtime,
|
|
1358
2868
|
world,
|
|
1359
2869
|
rooms,
|
|
1360
2870
|
entities,
|
|
1361
|
-
source: "telegram"
|
|
2871
|
+
source: "telegram",
|
|
2872
|
+
accountId,
|
|
2873
|
+
metadata: { accountId }
|
|
1362
2874
|
});
|
|
1363
2875
|
}
|
|
1364
2876
|
/**
|
|
@@ -1372,7 +2884,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1372
2884
|
* @returns {Promise<void>}
|
|
1373
2885
|
* @private
|
|
1374
2886
|
*/
|
|
1375
|
-
async batchProcessEntities(entities, roomId, channelId, roomType, worldId) {
|
|
2887
|
+
async batchProcessEntities(entities, roomId, roomName, channelId, roomType, worldId, accountId = this.defaultAccountId) {
|
|
1376
2888
|
const batchSize = 50;
|
|
1377
2889
|
for (let i = 0; i < entities.length; i += batchSize) {
|
|
1378
2890
|
const entityBatch = entities.slice(i, i + batchSize);
|
|
@@ -1384,6 +2896,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1384
2896
|
await this.runtime.ensureConnection({
|
|
1385
2897
|
entityId: entity.id,
|
|
1386
2898
|
roomId,
|
|
2899
|
+
roomName,
|
|
1387
2900
|
userName: telegramMetadata?.username,
|
|
1388
2901
|
name: telegramMetadata?.name,
|
|
1389
2902
|
userId: telegramMetadata?.id,
|
|
@@ -1393,11 +2906,28 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1393
2906
|
worldId
|
|
1394
2907
|
});
|
|
1395
2908
|
} else {
|
|
1396
|
-
|
|
2909
|
+
logger4.warn(
|
|
2910
|
+
{
|
|
2911
|
+
src: "plugin:telegram",
|
|
2912
|
+
agentId: this.runtime.agentId,
|
|
2913
|
+
accountId,
|
|
2914
|
+
entityNames: entity.names
|
|
2915
|
+
},
|
|
2916
|
+
"Skipping entity sync due to missing ID"
|
|
2917
|
+
);
|
|
1397
2918
|
}
|
|
1398
2919
|
} catch (err) {
|
|
1399
2920
|
const telegramMetadata = entity.metadata?.telegram;
|
|
1400
|
-
|
|
2921
|
+
logger4.warn(
|
|
2922
|
+
{
|
|
2923
|
+
src: "plugin:telegram",
|
|
2924
|
+
agentId: this.runtime.agentId,
|
|
2925
|
+
accountId,
|
|
2926
|
+
username: telegramMetadata?.username,
|
|
2927
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2928
|
+
},
|
|
2929
|
+
"Failed to sync user"
|
|
2930
|
+
);
|
|
1401
2931
|
}
|
|
1402
2932
|
})
|
|
1403
2933
|
);
|
|
@@ -1415,9 +2945,10 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1415
2945
|
* @private
|
|
1416
2946
|
*/
|
|
1417
2947
|
getChatTypeInfo(chat) {
|
|
2948
|
+
const chatType = chat.type;
|
|
1418
2949
|
let chatTitle;
|
|
1419
2950
|
let channelType;
|
|
1420
|
-
switch (
|
|
2951
|
+
switch (chatType) {
|
|
1421
2952
|
case "private":
|
|
1422
2953
|
chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
|
|
1423
2954
|
channelType = ChannelType2.DM;
|
|
@@ -1435,8 +2966,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1435
2966
|
channelType = ChannelType2.FEED;
|
|
1436
2967
|
break;
|
|
1437
2968
|
default:
|
|
1438
|
-
|
|
1439
|
-
channelType = ChannelType2.GROUP;
|
|
2969
|
+
throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
|
|
1440
2970
|
}
|
|
1441
2971
|
return { chatTitle, channelType };
|
|
1442
2972
|
}
|
|
@@ -1448,37 +2978,50 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1448
2978
|
* @returns {Promise<Entity[]>} Array of standardized Entity objects
|
|
1449
2979
|
* @private
|
|
1450
2980
|
*/
|
|
1451
|
-
async buildStandardizedEntities(chat) {
|
|
2981
|
+
async buildStandardizedEntities(chat, accountId = this.defaultAccountId) {
|
|
1452
2982
|
const entities = [];
|
|
1453
2983
|
try {
|
|
1454
2984
|
if (chat.type === "private" && chat.id) {
|
|
1455
|
-
const userId = createUniqueUuid2(
|
|
2985
|
+
const userId = createUniqueUuid2(
|
|
2986
|
+
this.runtime,
|
|
2987
|
+
this.scopedTelegramKey(chat.id.toString(), accountId)
|
|
2988
|
+
);
|
|
1456
2989
|
entities.push({
|
|
1457
2990
|
id: userId,
|
|
1458
2991
|
names: [chat.first_name || "Unknown User"],
|
|
1459
2992
|
agentId: this.runtime.agentId,
|
|
1460
2993
|
metadata: {
|
|
1461
2994
|
telegram: {
|
|
2995
|
+
accountId,
|
|
1462
2996
|
id: chat.id.toString(),
|
|
1463
2997
|
username: chat.username || "unknown",
|
|
1464
2998
|
name: chat.first_name || "Unknown User"
|
|
1465
2999
|
},
|
|
1466
|
-
source: "telegram"
|
|
3000
|
+
source: "telegram",
|
|
3001
|
+
accountId
|
|
1467
3002
|
}
|
|
1468
3003
|
});
|
|
1469
3004
|
this.syncedEntityIds.add(userId);
|
|
1470
3005
|
} else if (chat.type === "group" || chat.type === "supergroup") {
|
|
1471
3006
|
try {
|
|
1472
|
-
const admins = await this.
|
|
3007
|
+
const admins = await this.getAccountState(
|
|
3008
|
+
accountId
|
|
3009
|
+
)?.bot.telegram.getChatAdministrators(chat.id);
|
|
1473
3010
|
if (admins && admins.length > 0) {
|
|
1474
3011
|
for (const admin of admins) {
|
|
1475
|
-
const userId = createUniqueUuid2(
|
|
3012
|
+
const userId = createUniqueUuid2(
|
|
3013
|
+
this.runtime,
|
|
3014
|
+
this.scopedTelegramKey(admin.user.id.toString(), accountId)
|
|
3015
|
+
);
|
|
1476
3016
|
entities.push({
|
|
1477
3017
|
id: userId,
|
|
1478
|
-
names: [
|
|
3018
|
+
names: [
|
|
3019
|
+
admin.user.first_name || admin.user.username || "Unknown Admin"
|
|
3020
|
+
],
|
|
1479
3021
|
agentId: this.runtime.agentId,
|
|
1480
3022
|
metadata: {
|
|
1481
3023
|
telegram: {
|
|
3024
|
+
accountId,
|
|
1482
3025
|
id: admin.user.id.toString(),
|
|
1483
3026
|
username: admin.user.username || "unknown",
|
|
1484
3027
|
name: admin.user.first_name || "Unknown Admin",
|
|
@@ -1486,6 +3029,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1486
3029
|
adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
|
|
1487
3030
|
},
|
|
1488
3031
|
source: "telegram",
|
|
3032
|
+
accountId,
|
|
1489
3033
|
roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
|
|
1490
3034
|
}
|
|
1491
3035
|
});
|
|
@@ -1493,11 +3037,26 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1493
3037
|
}
|
|
1494
3038
|
}
|
|
1495
3039
|
} catch (error) {
|
|
1496
|
-
|
|
3040
|
+
logger4.warn(
|
|
3041
|
+
{
|
|
3042
|
+
src: "plugin:telegram",
|
|
3043
|
+
agentId: this.runtime.agentId,
|
|
3044
|
+
chatId: chat.id,
|
|
3045
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3046
|
+
},
|
|
3047
|
+
"Could not fetch administrators"
|
|
3048
|
+
);
|
|
1497
3049
|
}
|
|
1498
3050
|
}
|
|
1499
3051
|
} catch (error) {
|
|
1500
|
-
|
|
3052
|
+
logger4.error(
|
|
3053
|
+
{
|
|
3054
|
+
src: "plugin:telegram",
|
|
3055
|
+
agentId: this.runtime.agentId,
|
|
3056
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3057
|
+
},
|
|
3058
|
+
"Error building standardized entities"
|
|
3059
|
+
);
|
|
1501
3060
|
}
|
|
1502
3061
|
return entities;
|
|
1503
3062
|
}
|
|
@@ -1510,13 +3069,20 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1510
3069
|
* @returns {Promise<Room | null>} A Promise that resolves with the room or null if not a topic
|
|
1511
3070
|
* @private
|
|
1512
3071
|
*/
|
|
1513
|
-
async buildForumTopicRoom(ctx, worldId) {
|
|
1514
|
-
if (!ctx.chat || !ctx.message?.message_thread_id)
|
|
1515
|
-
|
|
3072
|
+
async buildForumTopicRoom(ctx, worldId, accountId = this.defaultAccountId) {
|
|
3073
|
+
if (!ctx.chat || !ctx.message?.message_thread_id) {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
if (ctx.chat.type !== "supergroup" || !ctx.chat.is_forum) {
|
|
3077
|
+
return null;
|
|
3078
|
+
}
|
|
1516
3079
|
const chat = ctx.chat;
|
|
1517
3080
|
const chatId = chat.id.toString();
|
|
1518
3081
|
const threadId = ctx.message.message_thread_id.toString();
|
|
1519
|
-
const roomId = createUniqueUuid2(
|
|
3082
|
+
const roomId = createUniqueUuid2(
|
|
3083
|
+
this.runtime,
|
|
3084
|
+
this.scopedTelegramKey(`${chatId}-${threadId}`, accountId)
|
|
3085
|
+
);
|
|
1520
3086
|
try {
|
|
1521
3087
|
const replyMessage = JSON.parse(JSON.stringify(ctx.message));
|
|
1522
3088
|
let topicName = `Topic #${threadId}`;
|
|
@@ -1540,46 +3106,572 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1540
3106
|
serverId: chatId,
|
|
1541
3107
|
worldId,
|
|
1542
3108
|
metadata: {
|
|
3109
|
+
source: "telegram",
|
|
3110
|
+
accountId,
|
|
1543
3111
|
threadId,
|
|
1544
3112
|
isForumTopic: true,
|
|
1545
|
-
parentChatId: chatId
|
|
3113
|
+
parentChatId: chatId,
|
|
3114
|
+
telegram: {
|
|
3115
|
+
accountId,
|
|
3116
|
+
chatId,
|
|
3117
|
+
threadId
|
|
3118
|
+
}
|
|
1546
3119
|
}
|
|
1547
3120
|
};
|
|
1548
3121
|
return room;
|
|
1549
3122
|
} catch (error) {
|
|
1550
|
-
|
|
3123
|
+
logger4.error(
|
|
3124
|
+
{
|
|
3125
|
+
src: "plugin:telegram",
|
|
3126
|
+
agentId: this.runtime.agentId,
|
|
3127
|
+
accountId,
|
|
3128
|
+
chatId,
|
|
3129
|
+
threadId,
|
|
3130
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3131
|
+
},
|
|
3132
|
+
"Error building forum topic room"
|
|
3133
|
+
);
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
buildConnectorChatTarget(chat, score = 0.5, threadId, accountId = this.defaultAccountId) {
|
|
3138
|
+
const chatId = chat.id.toString();
|
|
3139
|
+
const roomKey = threadId ? `${chatId}-${threadId}` : chatId;
|
|
3140
|
+
const roomId = createUniqueUuid2(
|
|
3141
|
+
this.runtime,
|
|
3142
|
+
this.scopedTelegramKey(roomKey, accountId)
|
|
3143
|
+
);
|
|
3144
|
+
const label = getTelegramChatDisplayName(chat, chatId);
|
|
3145
|
+
return {
|
|
3146
|
+
target: {
|
|
3147
|
+
source: "telegram",
|
|
3148
|
+
accountId,
|
|
3149
|
+
roomId,
|
|
3150
|
+
channelId: roomKey,
|
|
3151
|
+
threadId: threadId?.toString()
|
|
3152
|
+
},
|
|
3153
|
+
label,
|
|
3154
|
+
kind: threadId ? "thread" : telegramChatKind(chat),
|
|
3155
|
+
description: threadId && "title" in chat ? `Telegram topic ${threadId} in ${chat.title}` : `Telegram ${chat.type}`,
|
|
3156
|
+
score,
|
|
3157
|
+
contexts: ["social", "connectors"],
|
|
3158
|
+
metadata: {
|
|
3159
|
+
accountId,
|
|
3160
|
+
telegramChatId: chatId,
|
|
3161
|
+
telegramThreadId: threadId,
|
|
3162
|
+
telegramChatType: chat.type,
|
|
3163
|
+
username: "username" in chat ? chat.username : void 0,
|
|
3164
|
+
title: "title" in chat ? chat.title : void 0
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
buildConnectorRoomTarget(room, score = 0.5) {
|
|
3169
|
+
if (room.source !== "telegram" || !room.channelId) {
|
|
3170
|
+
return null;
|
|
3171
|
+
}
|
|
3172
|
+
const metadata = room.metadata;
|
|
3173
|
+
const accountId = typeof metadata?.accountId === "string" && metadata.accountId.trim() ? normalizeTelegramAccountId(metadata.accountId) : normalizeTelegramAccountId(this.defaultAccountId);
|
|
3174
|
+
const threadId = typeof metadata?.threadId === "string" ? metadata.threadId : typeof room.channelId === "string" ? parseTelegramTargetParts(room.channelId).threadId?.toString() : void 0;
|
|
3175
|
+
return {
|
|
3176
|
+
target: {
|
|
3177
|
+
source: "telegram",
|
|
3178
|
+
accountId,
|
|
3179
|
+
roomId: room.id,
|
|
3180
|
+
channelId: room.channelId,
|
|
3181
|
+
threadId
|
|
3182
|
+
},
|
|
3183
|
+
label: room.name || room.channelId,
|
|
3184
|
+
kind: threadId ? "thread" : "group",
|
|
3185
|
+
description: threadId ? `Telegram topic ${threadId}` : "Telegram chat room",
|
|
3186
|
+
score,
|
|
3187
|
+
contexts: ["social", "connectors"],
|
|
3188
|
+
metadata: {
|
|
3189
|
+
accountId,
|
|
3190
|
+
telegramChatId: room.channelId,
|
|
3191
|
+
telegramThreadId: threadId,
|
|
3192
|
+
roomName: room.name
|
|
3193
|
+
}
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
dedupeConnectorTargets(targets) {
|
|
3197
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3198
|
+
for (const target of targets) {
|
|
3199
|
+
const key = [
|
|
3200
|
+
target.target.accountId ?? "",
|
|
3201
|
+
target.kind ?? "target",
|
|
3202
|
+
target.target.channelId ?? "",
|
|
3203
|
+
target.target.entityId ?? "",
|
|
3204
|
+
target.target.threadId ?? ""
|
|
3205
|
+
].join(":");
|
|
3206
|
+
const existing = byKey.get(key);
|
|
3207
|
+
if (!existing || (target.score ?? 0) > (existing.score ?? 0)) {
|
|
3208
|
+
byKey.set(key, target);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
return Array.from(byKey.values()).sort(
|
|
3212
|
+
(a, b) => (b.score ?? 0) - (a.score ?? 0)
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
async getTelegramChatForTarget(chatId, accountId = this.defaultAccountId) {
|
|
3216
|
+
const known = this.knownChats.get(this.scopedTelegramKey(String(chatId), accountId)) ?? (normalizeTelegramAccountId(accountId) === DEFAULT_ACCOUNT_ID ? this.knownChats.get(String(chatId)) : void 0);
|
|
3217
|
+
if (known) {
|
|
3218
|
+
return known;
|
|
3219
|
+
}
|
|
3220
|
+
const bot = this.getAccountState(accountId)?.bot ?? this.bot;
|
|
3221
|
+
if (!bot) {
|
|
3222
|
+
return null;
|
|
3223
|
+
}
|
|
3224
|
+
try {
|
|
3225
|
+
const chat = await bot.telegram.getChat(chatId);
|
|
3226
|
+
this.knownChats.set(
|
|
3227
|
+
this.scopedTelegramKey(String(chat.id), accountId),
|
|
3228
|
+
chat
|
|
3229
|
+
);
|
|
3230
|
+
return chat;
|
|
3231
|
+
} catch {
|
|
3232
|
+
return null;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
async resolveConnectorTargets(query, context) {
|
|
3236
|
+
const normalizedQuery = normalizeTelegramConnectorQuery(query);
|
|
3237
|
+
const targets = [];
|
|
3238
|
+
const explicitAccountId = this.resolveAccountIdFromContext(
|
|
3239
|
+
context,
|
|
3240
|
+
context.target
|
|
3241
|
+
);
|
|
3242
|
+
const accountIds = explicitAccountId ? [normalizeTelegramAccountId(explicitAccountId)] : this.getAccountIds();
|
|
3243
|
+
for (const accountId of accountIds) {
|
|
3244
|
+
for (const [key, chat] of this.knownChats.entries()) {
|
|
3245
|
+
if (!this.knownChatKeyMatchesAccount(key, chat, accountId)) {
|
|
3246
|
+
continue;
|
|
3247
|
+
}
|
|
3248
|
+
const score = scoreTelegramConnectorMatch(
|
|
3249
|
+
normalizedQuery,
|
|
3250
|
+
chat.id.toString(),
|
|
3251
|
+
[
|
|
3252
|
+
"title" in chat ? chat.title : void 0,
|
|
3253
|
+
"username" in chat ? chat.username : void 0,
|
|
3254
|
+
"first_name" in chat ? chat.first_name : void 0,
|
|
3255
|
+
"last_name" in chat ? chat.last_name : void 0
|
|
3256
|
+
]
|
|
3257
|
+
);
|
|
3258
|
+
if (score <= 0) {
|
|
3259
|
+
continue;
|
|
3260
|
+
}
|
|
3261
|
+
targets.push(
|
|
3262
|
+
this.buildConnectorChatTarget(chat, score, void 0, accountId)
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
if (normalizedQuery && (TELEGRAM_CHAT_ID_PATTERN.test(normalizedQuery) || query.trim().startsWith("@"))) {
|
|
3267
|
+
const lookup = TELEGRAM_CHAT_ID_PATTERN.test(normalizedQuery) ? normalizedQuery : query.trim();
|
|
3268
|
+
const accountId = accountIds[0] ?? this.defaultAccountId;
|
|
3269
|
+
const chat = await this.getTelegramChatForTarget(lookup, accountId);
|
|
3270
|
+
if (chat) {
|
|
3271
|
+
targets.push(
|
|
3272
|
+
this.buildConnectorChatTarget(chat, 1, void 0, accountId)
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
|
|
3277
|
+
if (room) {
|
|
3278
|
+
const roomTarget = this.buildConnectorRoomTarget(room, 0.6);
|
|
3279
|
+
if (roomTarget) {
|
|
3280
|
+
targets.push(roomTarget);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
return this.dedupeConnectorTargets(targets).slice(0, 25);
|
|
3284
|
+
}
|
|
3285
|
+
async listConnectorRooms(context) {
|
|
3286
|
+
const explicitAccountId = this.resolveAccountIdFromContext(
|
|
3287
|
+
context,
|
|
3288
|
+
context.target
|
|
3289
|
+
);
|
|
3290
|
+
const accountIds = explicitAccountId ? [normalizeTelegramAccountId(explicitAccountId)] : this.getAccountIds();
|
|
3291
|
+
const targets = [];
|
|
3292
|
+
for (const accountId of accountIds) {
|
|
3293
|
+
for (const [key, chat] of this.knownChats.entries()) {
|
|
3294
|
+
if (!this.knownChatKeyMatchesAccount(key, chat, accountId)) {
|
|
3295
|
+
continue;
|
|
3296
|
+
}
|
|
3297
|
+
targets.push(
|
|
3298
|
+
this.buildConnectorChatTarget(chat, 0.5, void 0, accountId)
|
|
3299
|
+
);
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
|
|
3303
|
+
if (room) {
|
|
3304
|
+
const roomTarget = this.buildConnectorRoomTarget(room, 0.7);
|
|
3305
|
+
if (roomTarget) {
|
|
3306
|
+
targets.push(roomTarget);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
return this.dedupeConnectorTargets(targets).slice(0, 50);
|
|
3310
|
+
}
|
|
3311
|
+
async listRecentConnectorTargets(context) {
|
|
3312
|
+
return this.listConnectorRooms(context);
|
|
3313
|
+
}
|
|
3314
|
+
async fetchConnectorMessages(context, params = {}) {
|
|
3315
|
+
const limit = normalizeConnectorLimit(params.limit);
|
|
3316
|
+
const target = params.target ?? context.target;
|
|
3317
|
+
const accountId = await this.resolveAccountIdForTarget(
|
|
3318
|
+
context.runtime,
|
|
3319
|
+
target,
|
|
3320
|
+
{ accountId: context.accountId }
|
|
3321
|
+
);
|
|
3322
|
+
if (target?.roomId) {
|
|
3323
|
+
const memories = await context.runtime.getMemories({
|
|
3324
|
+
tableName: "messages",
|
|
3325
|
+
roomId: target.roomId,
|
|
3326
|
+
limit,
|
|
3327
|
+
orderBy: "createdAt",
|
|
3328
|
+
orderDirection: "desc"
|
|
3329
|
+
});
|
|
3330
|
+
return memories.filter((memory) => {
|
|
3331
|
+
const metadata = memory.metadata;
|
|
3332
|
+
return !metadata?.accountId || metadata.accountId === accountId;
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
const targets = (await this.listRecentConnectorTargets(context)).slice(
|
|
3336
|
+
0,
|
|
3337
|
+
10
|
|
3338
|
+
);
|
|
3339
|
+
const roomIds = Array.from(
|
|
3340
|
+
new Set(
|
|
3341
|
+
targets.map((candidate) => candidate.target.roomId).filter((roomId) => Boolean(roomId))
|
|
3342
|
+
)
|
|
3343
|
+
);
|
|
3344
|
+
const chunks = await Promise.all(
|
|
3345
|
+
roomIds.map(
|
|
3346
|
+
(roomId) => context.runtime.getMemories({
|
|
3347
|
+
tableName: "messages",
|
|
3348
|
+
roomId,
|
|
3349
|
+
limit,
|
|
3350
|
+
orderBy: "createdAt",
|
|
3351
|
+
orderDirection: "desc"
|
|
3352
|
+
})
|
|
3353
|
+
)
|
|
3354
|
+
);
|
|
3355
|
+
return chunks.flat().filter((memory) => {
|
|
3356
|
+
const metadata = memory.metadata;
|
|
3357
|
+
return !metadata?.accountId || metadata.accountId === accountId;
|
|
3358
|
+
}).sort((left, right) => (right.createdAt ?? 0) - (left.createdAt ?? 0)).slice(0, limit);
|
|
3359
|
+
}
|
|
3360
|
+
async searchConnectorMessages(context, params) {
|
|
3361
|
+
const limit = normalizeConnectorLimit(params.limit);
|
|
3362
|
+
const messages = await this.fetchConnectorMessages(context, {
|
|
3363
|
+
target: params.target ?? context.target,
|
|
3364
|
+
limit: Math.max(limit, 100)
|
|
3365
|
+
});
|
|
3366
|
+
return filterMemoriesByQuery(messages, params.query, limit);
|
|
3367
|
+
}
|
|
3368
|
+
async getConnectorChatContext(target, context) {
|
|
3369
|
+
const room = target.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(target.roomId) : null;
|
|
3370
|
+
const accountId = await this.resolveAccountIdForTarget(
|
|
3371
|
+
context.runtime,
|
|
3372
|
+
target,
|
|
3373
|
+
{
|
|
3374
|
+
accountId: context.accountId,
|
|
3375
|
+
roomId: target.roomId ?? context.roomId
|
|
3376
|
+
}
|
|
3377
|
+
);
|
|
3378
|
+
const channelId = target.channelId ?? room?.channelId;
|
|
3379
|
+
if (!channelId) {
|
|
3380
|
+
return null;
|
|
3381
|
+
}
|
|
3382
|
+
const parts = parseTelegramTargetParts(channelId, target.threadId);
|
|
3383
|
+
const chat = await this.getTelegramChatForTarget(parts.chatId, accountId);
|
|
3384
|
+
const roomId = target.roomId ?? room?.id ?? createUniqueUuid2(
|
|
3385
|
+
this.runtime,
|
|
3386
|
+
this.scopedTelegramKey(
|
|
3387
|
+
parts.threadId ? `${parts.chatId}-${parts.threadId}` : String(parts.chatId),
|
|
3388
|
+
accountId
|
|
3389
|
+
)
|
|
3390
|
+
);
|
|
3391
|
+
const memories = await context.runtime.getMemories({
|
|
3392
|
+
tableName: "messages",
|
|
3393
|
+
roomId,
|
|
3394
|
+
count: 10,
|
|
3395
|
+
orderBy: "createdAt",
|
|
3396
|
+
orderDirection: "desc"
|
|
3397
|
+
});
|
|
3398
|
+
const recentMessages = memories.slice().reverse().map((memory) => ({
|
|
3399
|
+
entityId: memory.entityId,
|
|
3400
|
+
name: typeof memory.content?.name === "string" ? memory.content.name : void 0,
|
|
3401
|
+
text: memory.content?.text ?? "",
|
|
3402
|
+
timestamp: memory.createdAt,
|
|
3403
|
+
metadata: {
|
|
3404
|
+
memoryId: memory.id,
|
|
3405
|
+
accountId,
|
|
3406
|
+
source: memory.content?.source
|
|
3407
|
+
}
|
|
3408
|
+
})).filter((message) => message.text.trim().length > 0);
|
|
3409
|
+
return {
|
|
3410
|
+
target: {
|
|
3411
|
+
source: "telegram",
|
|
3412
|
+
accountId,
|
|
3413
|
+
roomId,
|
|
3414
|
+
channelId,
|
|
3415
|
+
threadId: parts.threadId?.toString()
|
|
3416
|
+
},
|
|
3417
|
+
label: room?.name || (chat ? getTelegramChatDisplayName(chat, String(parts.chatId)) : channelId),
|
|
3418
|
+
summary: chat ? `Telegram ${chat.type}` : void 0,
|
|
3419
|
+
recentMessages,
|
|
3420
|
+
metadata: {
|
|
3421
|
+
accountId,
|
|
3422
|
+
telegramChatId: String(parts.chatId),
|
|
3423
|
+
telegramThreadId: parts.threadId,
|
|
3424
|
+
telegramChatType: chat?.type
|
|
3425
|
+
}
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
async getConnectorUserContext(entityId, context) {
|
|
3429
|
+
const accountId = normalizeTelegramAccountId(
|
|
3430
|
+
context.target?.accountId ?? context.accountId ?? context.account?.accountId ?? this.defaultAccountId
|
|
3431
|
+
);
|
|
3432
|
+
const entity = typeof context.runtime.getEntityById === "function" ? await context.runtime.getEntityById(String(entityId)) : null;
|
|
3433
|
+
const telegramMetadata = entity?.metadata?.telegram && typeof entity.metadata.telegram === "object" ? entity.metadata.telegram : null;
|
|
3434
|
+
const telegramId = typeof telegramMetadata?.id === "number" || typeof telegramMetadata?.id === "string" ? telegramMetadata.id : TELEGRAM_CHAT_ID_PATTERN.test(String(entityId)) ? entityId : null;
|
|
3435
|
+
if (!telegramId) {
|
|
1551
3436
|
return null;
|
|
1552
3437
|
}
|
|
3438
|
+
const chat = await this.getTelegramChatForTarget(telegramId, accountId);
|
|
3439
|
+
const aliases = [
|
|
3440
|
+
entity?.names?.[0],
|
|
3441
|
+
chat && "username" in chat ? chat.username : void 0,
|
|
3442
|
+
chat && "first_name" in chat ? chat.first_name : void 0,
|
|
3443
|
+
chat && "last_name" in chat ? chat.last_name : void 0
|
|
3444
|
+
].filter((value) => Boolean(value));
|
|
3445
|
+
return {
|
|
3446
|
+
entityId,
|
|
3447
|
+
label: aliases[0] ?? String(telegramId),
|
|
3448
|
+
aliases,
|
|
3449
|
+
handles: { telegram: String(telegramId) },
|
|
3450
|
+
metadata: {
|
|
3451
|
+
accountId,
|
|
3452
|
+
telegramId: String(telegramId),
|
|
3453
|
+
telegramChatType: chat?.type,
|
|
3454
|
+
username: chat && "username" in chat ? chat.username : void 0
|
|
3455
|
+
}
|
|
3456
|
+
};
|
|
1553
3457
|
}
|
|
1554
3458
|
static registerSendHandlers(runtime, serviceInstance) {
|
|
1555
|
-
if (serviceInstance
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
serviceInstance.
|
|
3459
|
+
if (serviceInstance?.bot) {
|
|
3460
|
+
const registerConnector = (accountId) => {
|
|
3461
|
+
const normalizedAccountId = accountId ? normalizeTelegramAccountId(accountId) : void 0;
|
|
3462
|
+
const state = normalizedAccountId ? serviceInstance.getAccountState(normalizedAccountId) : serviceInstance.getDefaultAccountState();
|
|
3463
|
+
const sendHandler = async (handlerRuntime, target, content) => {
|
|
3464
|
+
await serviceInstance.handleSendMessage(
|
|
3465
|
+
handlerRuntime,
|
|
3466
|
+
normalizedAccountId && !target.accountId ? { ...target, accountId: normalizedAccountId } : target,
|
|
3467
|
+
content
|
|
3468
|
+
);
|
|
3469
|
+
return void 0;
|
|
3470
|
+
};
|
|
3471
|
+
const withContextAccount = (context) => normalizedAccountId && !context.accountId ? {
|
|
3472
|
+
...context,
|
|
3473
|
+
accountId: normalizedAccountId,
|
|
3474
|
+
account: context.account ?? {
|
|
3475
|
+
source: "telegram",
|
|
3476
|
+
accountId: normalizedAccountId,
|
|
3477
|
+
label: state?.account.name ?? normalizedAccountId
|
|
3478
|
+
}
|
|
3479
|
+
} : context;
|
|
3480
|
+
const registration = {
|
|
3481
|
+
source: "telegram",
|
|
3482
|
+
...normalizedAccountId ? { accountId: normalizedAccountId } : {},
|
|
3483
|
+
...normalizedAccountId ? {
|
|
3484
|
+
account: {
|
|
3485
|
+
source: "telegram",
|
|
3486
|
+
accountId: normalizedAccountId,
|
|
3487
|
+
label: state?.account.name ?? normalizedAccountId,
|
|
3488
|
+
authMethod: "BOT_TOKEN"
|
|
3489
|
+
}
|
|
3490
|
+
} : {},
|
|
3491
|
+
label: state?.account.name ? `Telegram (${state.account.name})` : "Telegram",
|
|
3492
|
+
description: "Telegram connector for sending messages to chats, topics, and users.",
|
|
3493
|
+
capabilities: [...TELEGRAM_CONNECTOR_CAPABILITIES],
|
|
3494
|
+
supportedTargetKinds: ["channel", "group", "thread", "user"],
|
|
3495
|
+
contexts: [...TELEGRAM_CONNECTOR_CONTEXTS],
|
|
3496
|
+
metadata: {
|
|
3497
|
+
service: TELEGRAM_SERVICE_NAME,
|
|
3498
|
+
...normalizedAccountId ? { accountId: normalizedAccountId } : {}
|
|
3499
|
+
},
|
|
3500
|
+
resolveTargets: (query, context) => serviceInstance.resolveConnectorTargets(
|
|
3501
|
+
query,
|
|
3502
|
+
withContextAccount(context)
|
|
3503
|
+
),
|
|
3504
|
+
listRecentTargets: (context) => serviceInstance.listRecentConnectorTargets(
|
|
3505
|
+
withContextAccount(context)
|
|
3506
|
+
),
|
|
3507
|
+
listRooms: (context) => serviceInstance.listConnectorRooms(withContextAccount(context)),
|
|
3508
|
+
fetchMessages: (context, params) => {
|
|
3509
|
+
const readParams = params ?? {};
|
|
3510
|
+
return serviceInstance.fetchConnectorMessages(
|
|
3511
|
+
withContextAccount(context),
|
|
3512
|
+
{
|
|
3513
|
+
...readParams,
|
|
3514
|
+
target: normalizedAccountId && readParams.target && !readParams.target.accountId ? {
|
|
3515
|
+
...readParams.target,
|
|
3516
|
+
accountId: normalizedAccountId
|
|
3517
|
+
} : readParams.target
|
|
3518
|
+
}
|
|
3519
|
+
);
|
|
3520
|
+
},
|
|
3521
|
+
searchMessages: (context, params) => serviceInstance.searchConnectorMessages(
|
|
3522
|
+
withContextAccount(context),
|
|
3523
|
+
{
|
|
3524
|
+
...params,
|
|
3525
|
+
target: normalizedAccountId && params.target && !params.target.accountId ? {
|
|
3526
|
+
...params.target,
|
|
3527
|
+
accountId: normalizedAccountId
|
|
3528
|
+
} : params.target
|
|
3529
|
+
}
|
|
3530
|
+
),
|
|
3531
|
+
getChatContext: (target, context) => serviceInstance.getConnectorChatContext(
|
|
3532
|
+
normalizedAccountId && !target.accountId ? { ...target, accountId: normalizedAccountId } : target,
|
|
3533
|
+
withContextAccount(context)
|
|
3534
|
+
),
|
|
3535
|
+
getUserContext: (entityId, context) => serviceInstance.getConnectorUserContext(
|
|
3536
|
+
entityId,
|
|
3537
|
+
withContextAccount(context)
|
|
3538
|
+
),
|
|
3539
|
+
getUser: async (handlerRuntime, params) => {
|
|
3540
|
+
const userParams = params;
|
|
3541
|
+
const entityId = String(
|
|
3542
|
+
userParams.entityId ?? userParams.userId ?? userParams.username ?? userParams.handle ?? ""
|
|
3543
|
+
).trim();
|
|
3544
|
+
if (!entityId) {
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
const entity = typeof handlerRuntime.getEntityById === "function" ? await handlerRuntime.getEntityById(entityId).catch(() => null) : null;
|
|
3548
|
+
if (entity) {
|
|
3549
|
+
return entity;
|
|
3550
|
+
}
|
|
3551
|
+
const context = await serviceInstance.getConnectorUserContext(
|
|
3552
|
+
entityId,
|
|
3553
|
+
{
|
|
3554
|
+
runtime: handlerRuntime,
|
|
3555
|
+
accountId: normalizedAccountId
|
|
3556
|
+
}
|
|
3557
|
+
);
|
|
3558
|
+
if (!context) {
|
|
3559
|
+
return null;
|
|
3560
|
+
}
|
|
3561
|
+
return {
|
|
3562
|
+
id: createUniqueUuid2(
|
|
3563
|
+
handlerRuntime,
|
|
3564
|
+
serviceInstance.scopedTelegramKey(
|
|
3565
|
+
`telegram:${context.handles?.telegram ?? entityId}`,
|
|
3566
|
+
normalizedAccountId
|
|
3567
|
+
)
|
|
3568
|
+
),
|
|
3569
|
+
names: context.aliases?.length ? context.aliases : [context.label ?? entityId],
|
|
3570
|
+
agentId: handlerRuntime.agentId,
|
|
3571
|
+
metadata: {
|
|
3572
|
+
...context.metadata,
|
|
3573
|
+
...normalizedAccountId ? { accountId: normalizedAccountId } : {}
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
},
|
|
3577
|
+
sendHandler
|
|
3578
|
+
};
|
|
3579
|
+
runtime.registerMessageConnector(registration);
|
|
3580
|
+
};
|
|
3581
|
+
if (typeof runtime.registerMessageConnector === "function") {
|
|
3582
|
+
registerConnector();
|
|
3583
|
+
for (const accountId of serviceInstance.getAccountIds()) {
|
|
3584
|
+
registerConnector(accountId);
|
|
3585
|
+
}
|
|
3586
|
+
} else {
|
|
3587
|
+
runtime.registerSendHandler(
|
|
3588
|
+
"telegram",
|
|
3589
|
+
serviceInstance.handleSendMessage.bind(serviceInstance)
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
logger4.info(
|
|
3593
|
+
{ src: "plugin:telegram", agentId: runtime.agentId },
|
|
3594
|
+
"Registered Telegram message connector"
|
|
1559
3595
|
);
|
|
1560
|
-
logger3.info({ src: "plugin:telegram", agentId: runtime.agentId }, "Registered send handler");
|
|
1561
3596
|
} else {
|
|
1562
|
-
|
|
3597
|
+
logger4.warn(
|
|
3598
|
+
{ src: "plugin:telegram", agentId: runtime.agentId },
|
|
3599
|
+
"Cannot register send handler, bot not initialized"
|
|
3600
|
+
);
|
|
1563
3601
|
}
|
|
1564
3602
|
}
|
|
1565
3603
|
async handleSendMessage(runtime, target, content) {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
3604
|
+
const accountId = await this.resolveAccountIdForTarget(runtime, target);
|
|
3605
|
+
const accountState = this.getAccountState(accountId);
|
|
3606
|
+
const messageManager = accountState?.messageManager ?? this.messageManager;
|
|
3607
|
+
const bot = accountState?.bot ?? this.bot;
|
|
3608
|
+
if (!bot || !messageManager) {
|
|
3609
|
+
logger4.error(
|
|
3610
|
+
{ src: "plugin:telegram", agentId: runtime.agentId, accountId },
|
|
3611
|
+
"Bot not initialized, cannot send messages"
|
|
3612
|
+
);
|
|
3613
|
+
throw new Error(
|
|
3614
|
+
"Telegram bot is not initialized. Please provide TELEGRAM_BOT_TOKEN."
|
|
3615
|
+
);
|
|
1569
3616
|
}
|
|
1570
3617
|
let chatId;
|
|
3618
|
+
let threadId;
|
|
1571
3619
|
if (target.channelId) {
|
|
1572
|
-
|
|
3620
|
+
const parts = parseTelegramTargetParts(target.channelId, target.threadId);
|
|
3621
|
+
chatId = parts.chatId;
|
|
3622
|
+
threadId = parts.threadId;
|
|
1573
3623
|
} else if (target.roomId) {
|
|
1574
3624
|
const room = await runtime.getRoom(target.roomId);
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
3625
|
+
const metadata = room?.metadata;
|
|
3626
|
+
const metadataThreadId = typeof metadata?.threadId === "string" ? metadata.threadId : void 0;
|
|
3627
|
+
if (room?.channelId) {
|
|
3628
|
+
const parts = parseTelegramTargetParts(
|
|
3629
|
+
room.channelId,
|
|
3630
|
+
metadataThreadId
|
|
3631
|
+
);
|
|
3632
|
+
chatId = parts.chatId;
|
|
3633
|
+
threadId = parts.threadId;
|
|
3634
|
+
}
|
|
3635
|
+
if (!chatId) {
|
|
3636
|
+
throw new Error(
|
|
3637
|
+
`Could not resolve Telegram chat ID from roomId ${target.roomId}`
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
1578
3640
|
} else if (target.entityId) {
|
|
1579
|
-
|
|
1580
|
-
|
|
3641
|
+
const entity = await runtime.getEntityById(target.entityId);
|
|
3642
|
+
if (!entity) {
|
|
3643
|
+
throw new Error(`Entity ${target.entityId} not found`);
|
|
3644
|
+
}
|
|
3645
|
+
const telegramMeta = entity.metadata?.telegram;
|
|
3646
|
+
const entityAccountId = typeof entity.metadata?.accountId === "string" ? entity.metadata.accountId : typeof telegramMeta?.accountId === "string" ? telegramMeta.accountId : void 0;
|
|
3647
|
+
if (entityAccountId && normalizeTelegramAccountId(entityAccountId) !== accountId) {
|
|
3648
|
+
throw new Error(
|
|
3649
|
+
`Entity ${target.entityId} is linked to Telegram account ${entityAccountId}, not ${accountId}`
|
|
3650
|
+
);
|
|
3651
|
+
}
|
|
3652
|
+
const telegramId = telegramMeta?.id;
|
|
3653
|
+
if (!telegramId) {
|
|
3654
|
+
logger4.error(
|
|
3655
|
+
{
|
|
3656
|
+
src: "plugin:telegram",
|
|
3657
|
+
agentId: runtime.agentId,
|
|
3658
|
+
accountId,
|
|
3659
|
+
entityId: target.entityId
|
|
3660
|
+
},
|
|
3661
|
+
"Entity has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID"
|
|
3662
|
+
);
|
|
3663
|
+
throw new Error(
|
|
3664
|
+
`Entity ${target.entityId} has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID`
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
chatId = telegramId;
|
|
3668
|
+
if (target.threadId && /^\d+$/.test(target.threadId)) {
|
|
3669
|
+
threadId = Number.parseInt(target.threadId, 10);
|
|
3670
|
+
}
|
|
1581
3671
|
} else {
|
|
1582
|
-
throw new Error(
|
|
3672
|
+
throw new Error(
|
|
3673
|
+
"Telegram SendHandler requires channelId, roomId, or entityId."
|
|
3674
|
+
);
|
|
1583
3675
|
}
|
|
1584
3676
|
if (!chatId) {
|
|
1585
3677
|
throw new Error(
|
|
@@ -1587,17 +3679,189 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1587
3679
|
);
|
|
1588
3680
|
}
|
|
1589
3681
|
try {
|
|
1590
|
-
await
|
|
1591
|
-
|
|
3682
|
+
await messageManager.sendMessage(
|
|
3683
|
+
chatId,
|
|
3684
|
+
{
|
|
3685
|
+
...content,
|
|
3686
|
+
metadata: {
|
|
3687
|
+
...content.metadata && typeof content.metadata === "object" ? content.metadata : {},
|
|
3688
|
+
accountId
|
|
3689
|
+
}
|
|
3690
|
+
},
|
|
3691
|
+
void 0,
|
|
3692
|
+
threadId
|
|
3693
|
+
);
|
|
3694
|
+
logger4.info(
|
|
3695
|
+
{
|
|
3696
|
+
src: "plugin:telegram",
|
|
3697
|
+
agentId: runtime.agentId,
|
|
3698
|
+
accountId,
|
|
3699
|
+
chatId,
|
|
3700
|
+
threadId
|
|
3701
|
+
},
|
|
3702
|
+
"Message sent"
|
|
3703
|
+
);
|
|
1592
3704
|
} catch (error) {
|
|
1593
|
-
|
|
3705
|
+
logger4.error(
|
|
3706
|
+
{
|
|
3707
|
+
src: "plugin:telegram",
|
|
3708
|
+
agentId: runtime.agentId,
|
|
3709
|
+
accountId,
|
|
3710
|
+
chatId,
|
|
3711
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3712
|
+
},
|
|
3713
|
+
"Error sending message"
|
|
3714
|
+
);
|
|
1594
3715
|
throw error;
|
|
1595
3716
|
}
|
|
1596
3717
|
}
|
|
1597
3718
|
};
|
|
1598
3719
|
|
|
3720
|
+
// src/setup-routes.ts
|
|
3721
|
+
import { logger as logger5 } from "@elizaos/core";
|
|
3722
|
+
var TELEGRAM_API_BASE = "https://api.telegram.org";
|
|
3723
|
+
function isConnectorSetupService2(service) {
|
|
3724
|
+
if (!service || typeof service !== "object") {
|
|
3725
|
+
return false;
|
|
3726
|
+
}
|
|
3727
|
+
const candidate = service;
|
|
3728
|
+
return typeof candidate.getConfig === "function" && typeof candidate.updateConfig === "function" && typeof candidate.persistConfig === "function" && typeof candidate.registerEscalationChannel === "function" && typeof candidate.setOwnerContact === "function";
|
|
3729
|
+
}
|
|
3730
|
+
function getSetupService2(runtime) {
|
|
3731
|
+
const service = runtime.getService("connector-setup");
|
|
3732
|
+
return isConnectorSetupService2(service) ? service : null;
|
|
3733
|
+
}
|
|
3734
|
+
async function readJsonBody(req) {
|
|
3735
|
+
return req.body ?? null;
|
|
3736
|
+
}
|
|
3737
|
+
async function handleValidateToken(req, res, runtime) {
|
|
3738
|
+
const body = await readJsonBody(req);
|
|
3739
|
+
const token = typeof body?.token === "string" ? body.token.trim() : "";
|
|
3740
|
+
if (!token) {
|
|
3741
|
+
res.status(200).json({ ok: false, error: "token is required" });
|
|
3742
|
+
return;
|
|
3743
|
+
}
|
|
3744
|
+
if (!/^\d+:[A-Za-z0-9_-]{30,}$/.test(token)) {
|
|
3745
|
+
res.status(200).json({
|
|
3746
|
+
ok: false,
|
|
3747
|
+
error: "Token format invalid. Expected format: 123456:ABC-DEF..."
|
|
3748
|
+
});
|
|
3749
|
+
return;
|
|
3750
|
+
}
|
|
3751
|
+
try {
|
|
3752
|
+
const apiRes = await fetch(`${TELEGRAM_API_BASE}/bot${token}/getMe`, {
|
|
3753
|
+
signal: AbortSignal.timeout(1e4)
|
|
3754
|
+
});
|
|
3755
|
+
if (!apiRes.ok) {
|
|
3756
|
+
res.status(200).json({
|
|
3757
|
+
ok: false,
|
|
3758
|
+
error: `Telegram API returned ${apiRes.status}. Check that the token is correct.`
|
|
3759
|
+
});
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
const data = await apiRes.json();
|
|
3763
|
+
if (!data.ok || !data.result) {
|
|
3764
|
+
res.status(200).json({
|
|
3765
|
+
ok: false,
|
|
3766
|
+
error: "Telegram API returned unexpected response"
|
|
3767
|
+
});
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3770
|
+
const bot = data.result;
|
|
3771
|
+
const setupService = getSetupService2(runtime);
|
|
3772
|
+
if (setupService) {
|
|
3773
|
+
setupService.updateConfig((config) => {
|
|
3774
|
+
if (!config.connectors) {
|
|
3775
|
+
config.connectors = {};
|
|
3776
|
+
}
|
|
3777
|
+
const connectors = config.connectors;
|
|
3778
|
+
if (!connectors.telegram || typeof connectors.telegram !== "object") {
|
|
3779
|
+
connectors.telegram = {};
|
|
3780
|
+
}
|
|
3781
|
+
connectors.telegram.botToken = token;
|
|
3782
|
+
});
|
|
3783
|
+
setupService.setOwnerContact({
|
|
3784
|
+
source: "telegram",
|
|
3785
|
+
channelId: String(bot.id)
|
|
3786
|
+
});
|
|
3787
|
+
setupService.registerEscalationChannel("telegram");
|
|
3788
|
+
} else {
|
|
3789
|
+
logger5.warn(
|
|
3790
|
+
"[telegram-setup] connector-setup service not available \u2014 token saved to runtime only"
|
|
3791
|
+
);
|
|
3792
|
+
}
|
|
3793
|
+
res.status(200).json({
|
|
3794
|
+
ok: true,
|
|
3795
|
+
bot: {
|
|
3796
|
+
id: bot.id,
|
|
3797
|
+
username: bot.username,
|
|
3798
|
+
firstName: bot.first_name
|
|
3799
|
+
}
|
|
3800
|
+
});
|
|
3801
|
+
} catch (err) {
|
|
3802
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3803
|
+
res.status(200).json({
|
|
3804
|
+
ok: false,
|
|
3805
|
+
error: `Failed to reach Telegram API: ${message}`
|
|
3806
|
+
});
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
async function handleStatus2(_req, res, runtime) {
|
|
3810
|
+
const setupService = getSetupService2(runtime);
|
|
3811
|
+
let hasToken = false;
|
|
3812
|
+
if (setupService) {
|
|
3813
|
+
const config = setupService.getConfig();
|
|
3814
|
+
const connectors = config.connectors ?? {};
|
|
3815
|
+
const tgConfig = connectors.telegram;
|
|
3816
|
+
hasToken = Boolean(tgConfig?.botToken);
|
|
3817
|
+
}
|
|
3818
|
+
if (!hasToken) {
|
|
3819
|
+
hasToken = Boolean(runtime.getSetting("TELEGRAM_BOT_TOKEN"));
|
|
3820
|
+
}
|
|
3821
|
+
const service = runtime.getService("telegram");
|
|
3822
|
+
const connected = Boolean(service);
|
|
3823
|
+
res.status(200).json({
|
|
3824
|
+
available: true,
|
|
3825
|
+
hasToken,
|
|
3826
|
+
connected
|
|
3827
|
+
});
|
|
3828
|
+
}
|
|
3829
|
+
async function handleDisconnect2(_req, res, runtime) {
|
|
3830
|
+
const setupService = getSetupService2(runtime);
|
|
3831
|
+
if (setupService) {
|
|
3832
|
+
setupService.updateConfig((config) => {
|
|
3833
|
+
const connectors = config.connectors ?? {};
|
|
3834
|
+
const tgConfig = connectors.telegram;
|
|
3835
|
+
if (tgConfig) {
|
|
3836
|
+
delete tgConfig.botToken;
|
|
3837
|
+
}
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3840
|
+
res.status(200).json({ ok: true });
|
|
3841
|
+
}
|
|
3842
|
+
var telegramSetupRoutes = [
|
|
3843
|
+
{
|
|
3844
|
+
type: "POST",
|
|
3845
|
+
path: "/api/telegram-setup/validate-token",
|
|
3846
|
+
handler: handleValidateToken,
|
|
3847
|
+
rawPath: true
|
|
3848
|
+
},
|
|
3849
|
+
{
|
|
3850
|
+
type: "GET",
|
|
3851
|
+
path: "/api/telegram-setup/status",
|
|
3852
|
+
handler: handleStatus2,
|
|
3853
|
+
rawPath: true
|
|
3854
|
+
},
|
|
3855
|
+
{
|
|
3856
|
+
type: "POST",
|
|
3857
|
+
path: "/api/telegram-setup/disconnect",
|
|
3858
|
+
handler: handleDisconnect2,
|
|
3859
|
+
rawPath: true
|
|
3860
|
+
}
|
|
3861
|
+
];
|
|
3862
|
+
|
|
1599
3863
|
// src/tests.ts
|
|
1600
|
-
import { logger as
|
|
3864
|
+
import { logger as logger6 } from "@elizaos/core";
|
|
1601
3865
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
1602
3866
|
var TelegramTestSuite = class {
|
|
1603
3867
|
name = "telegram";
|
|
@@ -1666,7 +3930,7 @@ var TelegramTestSuite = class {
|
|
|
1666
3930
|
throw new Error("Bot is not initialized.");
|
|
1667
3931
|
}
|
|
1668
3932
|
const chat = await this.bot.telegram.getChat(chatId);
|
|
1669
|
-
|
|
3933
|
+
logger6.debug({ src: "plugin:telegram", chatId }, "Fetched real chat");
|
|
1670
3934
|
return chat;
|
|
1671
3935
|
} catch (error) {
|
|
1672
3936
|
throw new Error(`Error fetching real Telegram chat: ${error}`);
|
|
@@ -1674,29 +3938,41 @@ var TelegramTestSuite = class {
|
|
|
1674
3938
|
}
|
|
1675
3939
|
async testCreatingTelegramBot(runtime) {
|
|
1676
3940
|
this.telegramClient = runtime.getService("telegram");
|
|
1677
|
-
if (!this.telegramClient
|
|
3941
|
+
if (!this.telegramClient?.messageManager) {
|
|
1678
3942
|
throw new Error(
|
|
1679
3943
|
"Telegram service or message manager not initialized - check TELEGRAM_BOT_TOKEN"
|
|
1680
3944
|
);
|
|
1681
3945
|
}
|
|
1682
3946
|
this.bot = this.telegramClient.messageManager.bot;
|
|
1683
3947
|
this.messageManager = this.telegramClient.messageManager;
|
|
1684
|
-
|
|
3948
|
+
logger6.debug(
|
|
3949
|
+
{ src: "plugin:telegram" },
|
|
3950
|
+
"Telegram bot initialized successfully"
|
|
3951
|
+
);
|
|
1685
3952
|
}
|
|
1686
3953
|
async testSendingTextMessage(runtime) {
|
|
1687
3954
|
try {
|
|
1688
|
-
if (!this.bot)
|
|
3955
|
+
if (!this.bot) {
|
|
3956
|
+
throw new Error("Bot not initialized.");
|
|
3957
|
+
}
|
|
1689
3958
|
const chatId = this.validateChatId(runtime);
|
|
1690
3959
|
await this.bot.telegram.sendMessage(chatId, "Testing Telegram message!");
|
|
1691
|
-
|
|
3960
|
+
logger6.debug(
|
|
3961
|
+
{ src: "plugin:telegram", chatId },
|
|
3962
|
+
"Message sent successfully"
|
|
3963
|
+
);
|
|
1692
3964
|
} catch (error) {
|
|
1693
3965
|
throw new Error(`Error sending Telegram message: ${error}`);
|
|
1694
3966
|
}
|
|
1695
3967
|
}
|
|
1696
3968
|
async testSendingMessageWithAttachment(runtime) {
|
|
1697
3969
|
try {
|
|
1698
|
-
if (!this.messageManager)
|
|
1699
|
-
|
|
3970
|
+
if (!this.messageManager) {
|
|
3971
|
+
throw new Error("MessageManager not initialized.");
|
|
3972
|
+
}
|
|
3973
|
+
if (!this.bot) {
|
|
3974
|
+
throw new Error("Bot not initialized.");
|
|
3975
|
+
}
|
|
1700
3976
|
const chat = await this.getChatInfo(runtime);
|
|
1701
3977
|
const mockContext = {
|
|
1702
3978
|
chat,
|
|
@@ -1721,15 +3997,24 @@ var TelegramTestSuite = class {
|
|
|
1721
3997
|
mockContext,
|
|
1722
3998
|
messageContent
|
|
1723
3999
|
);
|
|
1724
|
-
|
|
4000
|
+
logger6.success(
|
|
4001
|
+
{ src: "plugin:telegram" },
|
|
4002
|
+
"Message with image attachment sent successfully"
|
|
4003
|
+
);
|
|
1725
4004
|
} catch (error) {
|
|
1726
|
-
throw new Error(
|
|
4005
|
+
throw new Error(
|
|
4006
|
+
`Error sending Telegram message with attachment: ${error}`
|
|
4007
|
+
);
|
|
1727
4008
|
}
|
|
1728
4009
|
}
|
|
1729
4010
|
async testHandlingMessage(runtime) {
|
|
1730
4011
|
try {
|
|
1731
|
-
if (!this.bot)
|
|
1732
|
-
|
|
4012
|
+
if (!this.bot) {
|
|
4013
|
+
throw new Error("Bot not initialized.");
|
|
4014
|
+
}
|
|
4015
|
+
if (!this.messageManager) {
|
|
4016
|
+
throw new Error("MessageManager not initialized.");
|
|
4017
|
+
}
|
|
1733
4018
|
const chat = await this.getChatInfo(runtime);
|
|
1734
4019
|
const mockContext = {
|
|
1735
4020
|
chat,
|
|
@@ -1741,7 +4026,7 @@ var TelegramTestSuite = class {
|
|
|
1741
4026
|
last_name: "User"
|
|
1742
4027
|
},
|
|
1743
4028
|
message: {
|
|
1744
|
-
message_id:
|
|
4029
|
+
message_id: 12345,
|
|
1745
4030
|
text: `@${this.bot.botInfo?.username}! Hello!`,
|
|
1746
4031
|
date: Math.floor(Date.now() / 1e3),
|
|
1747
4032
|
chat
|
|
@@ -1759,8 +4044,12 @@ var TelegramTestSuite = class {
|
|
|
1759
4044
|
}
|
|
1760
4045
|
async testProcessingImages(runtime) {
|
|
1761
4046
|
try {
|
|
1762
|
-
if (!this.bot)
|
|
1763
|
-
|
|
4047
|
+
if (!this.bot) {
|
|
4048
|
+
throw new Error("Bot not initialized.");
|
|
4049
|
+
}
|
|
4050
|
+
if (!this.messageManager) {
|
|
4051
|
+
throw new Error("MessageManager not initialized.");
|
|
4052
|
+
}
|
|
1764
4053
|
const chatId = this.validateChatId(runtime);
|
|
1765
4054
|
const fileId = await this.getFileId(String(chatId), TEST_IMAGE_URL);
|
|
1766
4055
|
const mockMessage = {
|
|
@@ -1777,12 +4066,19 @@ var TelegramTestSuite = class {
|
|
|
1777
4066
|
],
|
|
1778
4067
|
text: `@${this.bot.botInfo?.username}!`
|
|
1779
4068
|
};
|
|
1780
|
-
const result = await this.messageManager.processImage(
|
|
1781
|
-
|
|
1782
|
-
|
|
4069
|
+
const result = await this.messageManager.processImage(
|
|
4070
|
+
mockMessage
|
|
4071
|
+
);
|
|
4072
|
+
if (!result?.description) {
|
|
4073
|
+
throw new Error(
|
|
4074
|
+
"Error processing Telegram image or description not found"
|
|
4075
|
+
);
|
|
1783
4076
|
}
|
|
1784
4077
|
const { description } = result;
|
|
1785
|
-
|
|
4078
|
+
logger6.debug(
|
|
4079
|
+
{ src: "plugin:telegram", description },
|
|
4080
|
+
"Processing Telegram image successfully"
|
|
4081
|
+
);
|
|
1786
4082
|
} catch (error) {
|
|
1787
4083
|
throw new Error(`Error processing Telegram image: ${error}`);
|
|
1788
4084
|
}
|
|
@@ -1793,12 +4089,19 @@ var TelegramTestSuite = class {
|
|
|
1793
4089
|
throw new Error("Bot is not initialized.");
|
|
1794
4090
|
}
|
|
1795
4091
|
const message = await this.bot.telegram.sendPhoto(chatId, imageUrl);
|
|
1796
|
-
if (
|
|
4092
|
+
if (message.photo.length === 0) {
|
|
1797
4093
|
throw new Error("No photo received in the message response.");
|
|
1798
4094
|
}
|
|
1799
4095
|
return message.photo[message.photo.length - 1].file_id;
|
|
1800
4096
|
} catch (error) {
|
|
1801
|
-
|
|
4097
|
+
logger6.error(
|
|
4098
|
+
{
|
|
4099
|
+
src: "plugin:telegram",
|
|
4100
|
+
chatId,
|
|
4101
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4102
|
+
},
|
|
4103
|
+
"Error sending image"
|
|
4104
|
+
);
|
|
1802
4105
|
throw error;
|
|
1803
4106
|
}
|
|
1804
4107
|
}
|
|
@@ -1808,13 +4111,57 @@ var TelegramTestSuite = class {
|
|
|
1808
4111
|
var telegramPlugin = {
|
|
1809
4112
|
name: TELEGRAM_SERVICE_NAME,
|
|
1810
4113
|
description: "Telegram client plugin",
|
|
1811
|
-
|
|
1812
|
-
|
|
4114
|
+
// TelegramService must come before TelegramOwnerPairingServiceImpl so the
|
|
4115
|
+
// bot instance exists when the pairing service registers its command.
|
|
4116
|
+
services: [TelegramService, TelegramOwnerPairingServiceImpl],
|
|
4117
|
+
routes: [...telegramSetupRoutes, ...telegramAccountRoutes],
|
|
4118
|
+
tests: [new TelegramTestSuite()],
|
|
4119
|
+
// Self-declared auto-enable: activate when the "telegram" connector is
|
|
4120
|
+
// configured in eliza.json / eliza.json. The hardcoded CONNECTOR_PLUGINS
|
|
4121
|
+
// map in plugin-auto-enable.ts still serves as a fallback.
|
|
4122
|
+
autoEnable: {
|
|
4123
|
+
connectorKeys: ["telegram"]
|
|
4124
|
+
},
|
|
4125
|
+
init: async (_config, runtime) => {
|
|
4126
|
+
try {
|
|
4127
|
+
const manager = getConnectorAccountManager(runtime);
|
|
4128
|
+
manager.registerProvider(createTelegramConnectorAccountProvider(runtime));
|
|
4129
|
+
} catch (err) {
|
|
4130
|
+
logger7.warn(
|
|
4131
|
+
{
|
|
4132
|
+
src: "plugin:telegram",
|
|
4133
|
+
err: err instanceof Error ? err.message : String(err)
|
|
4134
|
+
},
|
|
4135
|
+
"Failed to register Telegram provider with ConnectorAccountManager"
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
1813
4139
|
};
|
|
1814
4140
|
var index_default = telegramPlugin;
|
|
1815
4141
|
export {
|
|
4142
|
+
DEFAULT_ACCOUNT_ID,
|
|
1816
4143
|
MessageManager,
|
|
4144
|
+
TELEGRAM_OWNER_PAIRING_SERVICE_TYPE,
|
|
4145
|
+
TelegramAccountAuthSession,
|
|
4146
|
+
TelegramOwnerPairingServiceImpl,
|
|
1817
4147
|
TelegramService,
|
|
1818
|
-
|
|
4148
|
+
clearTelegramAccountAuthState,
|
|
4149
|
+
clearTelegramAccountSession,
|
|
4150
|
+
createTelegramConnectorAccountProvider,
|
|
4151
|
+
index_default as default,
|
|
4152
|
+
defaultTelegramAccountDeviceModel,
|
|
4153
|
+
defaultTelegramAccountSystemVersion,
|
|
4154
|
+
getTelegramMultiAccountConfig,
|
|
4155
|
+
listEnabledTelegramAccounts,
|
|
4156
|
+
listTelegramAccountIds,
|
|
4157
|
+
loadTelegramAccountSessionString,
|
|
4158
|
+
normalizeTelegramAccountId,
|
|
4159
|
+
resolveDefaultTelegramAccountId,
|
|
4160
|
+
resolveTelegramAccount,
|
|
4161
|
+
resolveTelegramAccountSessionFile,
|
|
4162
|
+
saveTelegramAccountSessionString,
|
|
4163
|
+
stopTelegramAccountAuthSession,
|
|
4164
|
+
telegramAccountAuthStateExists,
|
|
4165
|
+
telegramAccountSessionExists
|
|
1819
4166
|
};
|
|
1820
4167
|
//# sourceMappingURL=index.js.map
|