@symerian/symi 3.0.20 → 3.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{audio-preflight-BaCdNfrk.js → audio-preflight-D7BVT-ls.js} +4 -4
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{chrome-UfmVM0xR.js → chrome-B5CO2vB5.js} +7 -7
- package/dist/{deliver-BqXdac6W.js → deliver-CrwjsDwv.js} +1 -1
- package/dist/extensionAPI.js +7 -7
- package/dist/{image-DIWsXYcW.js → image-Csu7WcLW.js} +1 -1
- package/dist/{manager-DW3SxcPr.js → manager-BkkVjTO8.js} +1 -1
- package/dist/{pi-embedded-BNch0U5F.js → pi-embedded-Dhp64z5l.js} +16 -16
- package/dist/{pi-embedded-helpers-IkHl02JF.js → pi-embedded-helpers-840E4hop.js} +4 -4
- package/dist/{pw-ai-nMkA-oDJ.js → pw-ai-CBgJf_RR.js} +1 -1
- package/dist/{runner-DNEC58JI.js → runner-BbFKo1ne.js} +1 -1
- package/dist/{synthesis-BWAr0sZ9.js → synthesis-DoEM0E8_.js} +7 -7
- package/dist/{web-7a-m_UxL.js → web-BYXJn-Ps.js} +7 -7
- package/package.json +1 -1
- package/extensions/imessage/index.ts +0 -17
- package/extensions/imessage/node_modules/.bin/symi +0 -21
- package/extensions/imessage/package.json +0 -15
- package/extensions/imessage/src/channel.outbound.test.ts +0 -66
- package/extensions/imessage/src/channel.ts +0 -298
- package/extensions/imessage/src/runtime.ts +0 -14
- package/extensions/imessage/symi.plugin.json +0 -9
- package/extensions/line/index.ts +0 -19
- package/extensions/line/node_modules/.bin/symi +0 -21
- package/extensions/line/package.json +0 -30
- package/extensions/line/src/card-command.ts +0 -344
- package/extensions/line/src/channel.logout.test.ts +0 -133
- package/extensions/line/src/channel.sendPayload.test.ts +0 -312
- package/extensions/line/src/channel.startup.test.ts +0 -133
- package/extensions/line/src/channel.ts +0 -801
- package/extensions/line/src/runtime.ts +0 -14
- package/extensions/line/symi.plugin.json +0 -9
- package/extensions/signal/index.ts +0 -17
- package/extensions/signal/node_modules/.bin/symi +0 -21
- package/extensions/signal/package.json +0 -15
- package/extensions/signal/src/channel.ts +0 -302
- package/extensions/signal/src/runtime.ts +0 -14
- package/extensions/signal/symi.plugin.json +0 -9
- package/extensions/telegram/index.ts +0 -17
- package/extensions/telegram/node_modules/.bin/symi +0 -21
- package/extensions/telegram/package.json +0 -15
- package/extensions/telegram/src/channel.test.ts +0 -125
- package/extensions/telegram/src/channel.ts +0 -560
- package/extensions/telegram/src/runtime.ts +0 -14
- package/extensions/telegram/symi.plugin.json +0 -9
- package/extensions/whatsapp/index.ts +0 -17
- package/extensions/whatsapp/node_modules/.bin/symi +0 -21
- package/extensions/whatsapp/package.json +0 -15
- package/extensions/whatsapp/src/channel.ts +0 -465
- package/extensions/whatsapp/src/resolve-target.test.ts +0 -170
- package/extensions/whatsapp/src/runtime.ts +0 -14
- package/extensions/whatsapp/symi.plugin.json +0 -9
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
applyAccountNameToChannelSection,
|
|
3
|
-
buildChannelConfigSchema,
|
|
4
|
-
collectTelegramStatusIssues,
|
|
5
|
-
DEFAULT_ACCOUNT_ID,
|
|
6
|
-
deleteAccountFromConfigSection,
|
|
7
|
-
formatPairingApproveHint,
|
|
8
|
-
getChatChannelMeta,
|
|
9
|
-
listTelegramAccountIds,
|
|
10
|
-
listTelegramDirectoryGroupsFromConfig,
|
|
11
|
-
listTelegramDirectoryPeersFromConfig,
|
|
12
|
-
looksLikeTelegramTargetId,
|
|
13
|
-
migrateBaseNameToDefaultAccount,
|
|
14
|
-
normalizeAccountId,
|
|
15
|
-
normalizeTelegramMessagingTarget,
|
|
16
|
-
PAIRING_APPROVED_MESSAGE,
|
|
17
|
-
parseTelegramReplyToMessageId,
|
|
18
|
-
parseTelegramThreadId,
|
|
19
|
-
resolveDefaultTelegramAccountId,
|
|
20
|
-
resolveTelegramAccount,
|
|
21
|
-
resolveTelegramGroupRequireMention,
|
|
22
|
-
resolveTelegramGroupToolPolicy,
|
|
23
|
-
setAccountEnabledInConfigSection,
|
|
24
|
-
telegramOnboardingAdapter,
|
|
25
|
-
TelegramConfigSchema,
|
|
26
|
-
type ChannelMessageActionAdapter,
|
|
27
|
-
type ChannelPlugin,
|
|
28
|
-
type SymiConfig,
|
|
29
|
-
type ResolvedTelegramAccount,
|
|
30
|
-
type TelegramProbe,
|
|
31
|
-
} from "symi/plugin-sdk";
|
|
32
|
-
import { getTelegramRuntime } from "./runtime.js";
|
|
33
|
-
|
|
34
|
-
const meta = getChatChannelMeta("telegram");
|
|
35
|
-
|
|
36
|
-
function findTelegramTokenOwnerAccountId(params: {
|
|
37
|
-
cfg: SymiConfig;
|
|
38
|
-
accountId: string;
|
|
39
|
-
}): string | null {
|
|
40
|
-
const normalizedAccountId = normalizeAccountId(params.accountId);
|
|
41
|
-
const tokenOwners = new Map<string, string>();
|
|
42
|
-
for (const id of listTelegramAccountIds(params.cfg)) {
|
|
43
|
-
const account = resolveTelegramAccount({ cfg: params.cfg, accountId: id });
|
|
44
|
-
const token = account.token.trim();
|
|
45
|
-
if (!token) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const ownerAccountId = tokenOwners.get(token);
|
|
49
|
-
if (!ownerAccountId) {
|
|
50
|
-
tokenOwners.set(token, account.accountId);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (account.accountId === normalizedAccountId) {
|
|
54
|
-
return ownerAccountId;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function formatDuplicateTelegramTokenReason(params: {
|
|
61
|
-
accountId: string;
|
|
62
|
-
ownerAccountId: string;
|
|
63
|
-
}): string {
|
|
64
|
-
return (
|
|
65
|
-
`Duplicate Telegram bot token: account "${params.accountId}" shares a token with ` +
|
|
66
|
-
`account "${params.ownerAccountId}". Keep one owner account per bot token.`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const telegramMessageActions: ChannelMessageActionAdapter = {
|
|
71
|
-
listActions: (ctx) =>
|
|
72
|
-
getTelegramRuntime().channel.telegram.messageActions?.listActions?.(ctx) ?? [],
|
|
73
|
-
extractToolSend: (ctx) =>
|
|
74
|
-
getTelegramRuntime().channel.telegram.messageActions?.extractToolSend?.(ctx) ?? null,
|
|
75
|
-
handleAction: async (ctx) => {
|
|
76
|
-
const ma = getTelegramRuntime().channel.telegram.messageActions;
|
|
77
|
-
if (!ma?.handleAction) {
|
|
78
|
-
throw new Error("Telegram message actions not available");
|
|
79
|
-
}
|
|
80
|
-
return ma.handleAction(ctx);
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export const telegramPlugin: ChannelPlugin<ResolvedTelegramAccount, TelegramProbe> = {
|
|
85
|
-
id: "telegram",
|
|
86
|
-
meta: {
|
|
87
|
-
...meta,
|
|
88
|
-
quickstartAllowFrom: true,
|
|
89
|
-
},
|
|
90
|
-
onboarding: telegramOnboardingAdapter,
|
|
91
|
-
pairing: {
|
|
92
|
-
idLabel: "telegramUserId",
|
|
93
|
-
normalizeAllowEntry: (entry) => entry.replace(/^(telegram|tg):/i, ""),
|
|
94
|
-
notifyApproval: async ({ cfg, id }) => {
|
|
95
|
-
const { token } = getTelegramRuntime().channel.telegram.resolveTelegramToken(cfg);
|
|
96
|
-
if (!token) {
|
|
97
|
-
throw new Error("telegram token not configured");
|
|
98
|
-
}
|
|
99
|
-
await getTelegramRuntime().channel.telegram.sendMessageTelegram(
|
|
100
|
-
id,
|
|
101
|
-
PAIRING_APPROVED_MESSAGE,
|
|
102
|
-
{
|
|
103
|
-
token,
|
|
104
|
-
},
|
|
105
|
-
);
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
capabilities: {
|
|
109
|
-
chatTypes: ["direct", "group", "channel", "thread"],
|
|
110
|
-
reactions: true,
|
|
111
|
-
threads: true,
|
|
112
|
-
media: true,
|
|
113
|
-
polls: true,
|
|
114
|
-
nativeCommands: true,
|
|
115
|
-
blockStreaming: true,
|
|
116
|
-
},
|
|
117
|
-
reload: { configPrefixes: ["channels.telegram"] },
|
|
118
|
-
configSchema: buildChannelConfigSchema(TelegramConfigSchema),
|
|
119
|
-
config: {
|
|
120
|
-
listAccountIds: (cfg) => listTelegramAccountIds(cfg),
|
|
121
|
-
resolveAccount: (cfg, accountId) => resolveTelegramAccount({ cfg, accountId }),
|
|
122
|
-
defaultAccountId: (cfg) => resolveDefaultTelegramAccountId(cfg),
|
|
123
|
-
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
|
124
|
-
setAccountEnabledInConfigSection({
|
|
125
|
-
cfg,
|
|
126
|
-
sectionKey: "telegram",
|
|
127
|
-
accountId,
|
|
128
|
-
enabled,
|
|
129
|
-
allowTopLevel: true,
|
|
130
|
-
}),
|
|
131
|
-
deleteAccount: ({ cfg, accountId }) =>
|
|
132
|
-
deleteAccountFromConfigSection({
|
|
133
|
-
cfg,
|
|
134
|
-
sectionKey: "telegram",
|
|
135
|
-
accountId,
|
|
136
|
-
clearBaseFields: ["botToken", "tokenFile", "name"],
|
|
137
|
-
}),
|
|
138
|
-
isConfigured: (account, cfg) => {
|
|
139
|
-
if (!account.token?.trim()) {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
return !findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId });
|
|
143
|
-
},
|
|
144
|
-
unconfiguredReason: (account, cfg) => {
|
|
145
|
-
if (!account.token?.trim()) {
|
|
146
|
-
return "not configured";
|
|
147
|
-
}
|
|
148
|
-
const ownerAccountId = findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId });
|
|
149
|
-
if (!ownerAccountId) {
|
|
150
|
-
return "not configured";
|
|
151
|
-
}
|
|
152
|
-
return formatDuplicateTelegramTokenReason({
|
|
153
|
-
accountId: account.accountId,
|
|
154
|
-
ownerAccountId,
|
|
155
|
-
});
|
|
156
|
-
},
|
|
157
|
-
describeAccount: (account, cfg) => ({
|
|
158
|
-
accountId: account.accountId,
|
|
159
|
-
name: account.name,
|
|
160
|
-
enabled: account.enabled,
|
|
161
|
-
configured:
|
|
162
|
-
Boolean(account.token?.trim()) &&
|
|
163
|
-
!findTelegramTokenOwnerAccountId({ cfg, accountId: account.accountId }),
|
|
164
|
-
tokenSource: account.tokenSource,
|
|
165
|
-
}),
|
|
166
|
-
resolveAllowFrom: ({ cfg, accountId }) =>
|
|
167
|
-
(resolveTelegramAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) =>
|
|
168
|
-
String(entry),
|
|
169
|
-
),
|
|
170
|
-
formatAllowFrom: ({ allowFrom }) =>
|
|
171
|
-
allowFrom
|
|
172
|
-
.map((entry) => String(entry).trim())
|
|
173
|
-
.filter(Boolean)
|
|
174
|
-
.map((entry) => entry.replace(/^(telegram|tg):/i, ""))
|
|
175
|
-
.map((entry) => entry.toLowerCase()),
|
|
176
|
-
resolveDefaultTo: ({ cfg, accountId }) => {
|
|
177
|
-
const val = resolveTelegramAccount({ cfg, accountId }).config.defaultTo;
|
|
178
|
-
return val != null ? String(val) : undefined;
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
security: {
|
|
182
|
-
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
|
183
|
-
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
|
184
|
-
const useAccountPath = Boolean(cfg.channels?.telegram?.accounts?.[resolvedAccountId]);
|
|
185
|
-
const basePath = useAccountPath
|
|
186
|
-
? `channels.telegram.accounts.${resolvedAccountId}.`
|
|
187
|
-
: "channels.telegram.";
|
|
188
|
-
return {
|
|
189
|
-
policy: account.config.dmPolicy ?? "pairing",
|
|
190
|
-
allowFrom: account.config.allowFrom ?? [],
|
|
191
|
-
policyPath: `${basePath}dmPolicy`,
|
|
192
|
-
allowFromPath: basePath,
|
|
193
|
-
approveHint: formatPairingApproveHint("telegram"),
|
|
194
|
-
normalizeEntry: (raw) => raw.replace(/^(telegram|tg):/i, ""),
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
collectWarnings: ({ account, cfg }) => {
|
|
198
|
-
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
199
|
-
const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
|
200
|
-
if (groupPolicy !== "open") {
|
|
201
|
-
return [];
|
|
202
|
-
}
|
|
203
|
-
const groupAllowlistConfigured =
|
|
204
|
-
account.config.groups && Object.keys(account.config.groups).length > 0;
|
|
205
|
-
if (groupAllowlistConfigured) {
|
|
206
|
-
return [
|
|
207
|
-
`- Telegram groups: groupPolicy="open" allows any member in allowed groups to trigger (mention-gated). Set channels.telegram.groupPolicy="allowlist" + channels.telegram.groupAllowFrom to restrict senders.`,
|
|
208
|
-
];
|
|
209
|
-
}
|
|
210
|
-
return [
|
|
211
|
-
`- Telegram groups: groupPolicy="open" with no channels.telegram.groups allowlist; any group can add + ping (mention-gated). Set channels.telegram.groupPolicy="allowlist" + channels.telegram.groupAllowFrom or configure channels.telegram.groups.`,
|
|
212
|
-
];
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
groups: {
|
|
216
|
-
resolveRequireMention: resolveTelegramGroupRequireMention,
|
|
217
|
-
resolveToolPolicy: resolveTelegramGroupToolPolicy,
|
|
218
|
-
},
|
|
219
|
-
threading: {
|
|
220
|
-
resolveReplyToMode: ({ cfg }) => cfg.channels?.telegram?.replyToMode ?? "off",
|
|
221
|
-
},
|
|
222
|
-
messaging: {
|
|
223
|
-
normalizeTarget: normalizeTelegramMessagingTarget,
|
|
224
|
-
targetResolver: {
|
|
225
|
-
looksLikeId: looksLikeTelegramTargetId,
|
|
226
|
-
hint: "<chatId>",
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
directory: {
|
|
230
|
-
self: async () => null,
|
|
231
|
-
listPeers: async (params) => listTelegramDirectoryPeersFromConfig(params),
|
|
232
|
-
listGroups: async (params) => listTelegramDirectoryGroupsFromConfig(params),
|
|
233
|
-
},
|
|
234
|
-
actions: telegramMessageActions,
|
|
235
|
-
setup: {
|
|
236
|
-
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
|
|
237
|
-
applyAccountName: ({ cfg, accountId, name }) =>
|
|
238
|
-
applyAccountNameToChannelSection({
|
|
239
|
-
cfg,
|
|
240
|
-
channelKey: "telegram",
|
|
241
|
-
accountId,
|
|
242
|
-
name,
|
|
243
|
-
}),
|
|
244
|
-
validateInput: ({ accountId, input }) => {
|
|
245
|
-
if (input.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
|
|
246
|
-
return "TELEGRAM_BOT_TOKEN can only be used for the default account.";
|
|
247
|
-
}
|
|
248
|
-
if (!input.useEnv && !input.token && !input.tokenFile) {
|
|
249
|
-
return "Telegram requires token or --token-file (or --use-env).";
|
|
250
|
-
}
|
|
251
|
-
return null;
|
|
252
|
-
},
|
|
253
|
-
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
254
|
-
const namedConfig = applyAccountNameToChannelSection({
|
|
255
|
-
cfg,
|
|
256
|
-
channelKey: "telegram",
|
|
257
|
-
accountId,
|
|
258
|
-
name: input.name,
|
|
259
|
-
});
|
|
260
|
-
const next =
|
|
261
|
-
accountId !== DEFAULT_ACCOUNT_ID
|
|
262
|
-
? migrateBaseNameToDefaultAccount({
|
|
263
|
-
cfg: namedConfig,
|
|
264
|
-
channelKey: "telegram",
|
|
265
|
-
})
|
|
266
|
-
: namedConfig;
|
|
267
|
-
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
268
|
-
return {
|
|
269
|
-
...next,
|
|
270
|
-
channels: {
|
|
271
|
-
...next.channels,
|
|
272
|
-
telegram: {
|
|
273
|
-
...next.channels?.telegram,
|
|
274
|
-
enabled: true,
|
|
275
|
-
...(input.useEnv
|
|
276
|
-
? {}
|
|
277
|
-
: input.tokenFile
|
|
278
|
-
? { tokenFile: input.tokenFile }
|
|
279
|
-
: input.token
|
|
280
|
-
? { botToken: input.token }
|
|
281
|
-
: {}),
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
287
|
-
...next,
|
|
288
|
-
channels: {
|
|
289
|
-
...next.channels,
|
|
290
|
-
telegram: {
|
|
291
|
-
...next.channels?.telegram,
|
|
292
|
-
enabled: true,
|
|
293
|
-
accounts: {
|
|
294
|
-
...next.channels?.telegram?.accounts,
|
|
295
|
-
[accountId]: {
|
|
296
|
-
...next.channels?.telegram?.accounts?.[accountId],
|
|
297
|
-
enabled: true,
|
|
298
|
-
...(input.tokenFile
|
|
299
|
-
? { tokenFile: input.tokenFile }
|
|
300
|
-
: input.token
|
|
301
|
-
? { botToken: input.token }
|
|
302
|
-
: {}),
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
};
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
outbound: {
|
|
311
|
-
deliveryMode: "direct",
|
|
312
|
-
chunker: (text, limit) => getTelegramRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
313
|
-
chunkerMode: "markdown",
|
|
314
|
-
textChunkLimit: 4000,
|
|
315
|
-
pollMaxOptions: 10,
|
|
316
|
-
sendText: async ({ to, text, accountId, deps, replyToId, threadId, silent }) => {
|
|
317
|
-
const send = deps?.sendTelegram ?? getTelegramRuntime().channel.telegram.sendMessageTelegram;
|
|
318
|
-
const replyToMessageId = parseTelegramReplyToMessageId(replyToId);
|
|
319
|
-
const messageThreadId = parseTelegramThreadId(threadId);
|
|
320
|
-
const result = await send(to, text, {
|
|
321
|
-
verbose: false,
|
|
322
|
-
messageThreadId,
|
|
323
|
-
replyToMessageId,
|
|
324
|
-
accountId: accountId ?? undefined,
|
|
325
|
-
silent: silent ?? undefined,
|
|
326
|
-
});
|
|
327
|
-
return { channel: "telegram", ...result };
|
|
328
|
-
},
|
|
329
|
-
sendMedia: async ({ to, text, mediaUrl, accountId, deps, replyToId, threadId, silent }) => {
|
|
330
|
-
const send = deps?.sendTelegram ?? getTelegramRuntime().channel.telegram.sendMessageTelegram;
|
|
331
|
-
const replyToMessageId = parseTelegramReplyToMessageId(replyToId);
|
|
332
|
-
const messageThreadId = parseTelegramThreadId(threadId);
|
|
333
|
-
const result = await send(to, text, {
|
|
334
|
-
verbose: false,
|
|
335
|
-
mediaUrl,
|
|
336
|
-
messageThreadId,
|
|
337
|
-
replyToMessageId,
|
|
338
|
-
accountId: accountId ?? undefined,
|
|
339
|
-
silent: silent ?? undefined,
|
|
340
|
-
});
|
|
341
|
-
return { channel: "telegram", ...result };
|
|
342
|
-
},
|
|
343
|
-
sendPoll: async ({ to, poll, accountId, threadId, silent, isAnonymous }) =>
|
|
344
|
-
await getTelegramRuntime().channel.telegram.sendPollTelegram(to, poll, {
|
|
345
|
-
accountId: accountId ?? undefined,
|
|
346
|
-
messageThreadId: parseTelegramThreadId(threadId),
|
|
347
|
-
silent: silent ?? undefined,
|
|
348
|
-
isAnonymous: isAnonymous ?? undefined,
|
|
349
|
-
}),
|
|
350
|
-
},
|
|
351
|
-
status: {
|
|
352
|
-
defaultRuntime: {
|
|
353
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
354
|
-
running: false,
|
|
355
|
-
lastStartAt: null,
|
|
356
|
-
lastStopAt: null,
|
|
357
|
-
lastError: null,
|
|
358
|
-
},
|
|
359
|
-
collectStatusIssues: collectTelegramStatusIssues,
|
|
360
|
-
buildChannelSummary: ({ snapshot }) => ({
|
|
361
|
-
configured: snapshot.configured ?? false,
|
|
362
|
-
tokenSource: snapshot.tokenSource ?? "none",
|
|
363
|
-
running: snapshot.running ?? false,
|
|
364
|
-
mode: snapshot.mode ?? null,
|
|
365
|
-
lastStartAt: snapshot.lastStartAt ?? null,
|
|
366
|
-
lastStopAt: snapshot.lastStopAt ?? null,
|
|
367
|
-
lastError: snapshot.lastError ?? null,
|
|
368
|
-
probe: snapshot.probe,
|
|
369
|
-
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
370
|
-
}),
|
|
371
|
-
probeAccount: async ({ account, timeoutMs }) =>
|
|
372
|
-
getTelegramRuntime().channel.telegram.probeTelegram(
|
|
373
|
-
account.token,
|
|
374
|
-
timeoutMs,
|
|
375
|
-
account.config.proxy,
|
|
376
|
-
),
|
|
377
|
-
auditAccount: async ({ account, timeoutMs, probe, cfg }) => {
|
|
378
|
-
const groups =
|
|
379
|
-
cfg.channels?.telegram?.accounts?.[account.accountId]?.groups ??
|
|
380
|
-
cfg.channels?.telegram?.groups;
|
|
381
|
-
const { groupIds, unresolvedGroups, hasWildcardUnmentionedGroups } =
|
|
382
|
-
getTelegramRuntime().channel.telegram.collectUnmentionedGroupIds(groups);
|
|
383
|
-
if (!groupIds.length && unresolvedGroups === 0 && !hasWildcardUnmentionedGroups) {
|
|
384
|
-
return undefined;
|
|
385
|
-
}
|
|
386
|
-
const botId = probe?.ok && probe.bot?.id != null ? probe.bot.id : null;
|
|
387
|
-
if (!botId) {
|
|
388
|
-
return {
|
|
389
|
-
ok: unresolvedGroups === 0 && !hasWildcardUnmentionedGroups,
|
|
390
|
-
checkedGroups: 0,
|
|
391
|
-
unresolvedGroups,
|
|
392
|
-
hasWildcardUnmentionedGroups,
|
|
393
|
-
groups: [],
|
|
394
|
-
elapsedMs: 0,
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
const audit = await getTelegramRuntime().channel.telegram.auditGroupMembership({
|
|
398
|
-
token: account.token,
|
|
399
|
-
botId,
|
|
400
|
-
groupIds,
|
|
401
|
-
proxyUrl: account.config.proxy,
|
|
402
|
-
timeoutMs,
|
|
403
|
-
});
|
|
404
|
-
return { ...audit, unresolvedGroups, hasWildcardUnmentionedGroups };
|
|
405
|
-
},
|
|
406
|
-
buildAccountSnapshot: ({ account, cfg, runtime, probe, audit }) => {
|
|
407
|
-
const ownerAccountId = findTelegramTokenOwnerAccountId({
|
|
408
|
-
cfg,
|
|
409
|
-
accountId: account.accountId,
|
|
410
|
-
});
|
|
411
|
-
const duplicateTokenReason = ownerAccountId
|
|
412
|
-
? formatDuplicateTelegramTokenReason({
|
|
413
|
-
accountId: account.accountId,
|
|
414
|
-
ownerAccountId,
|
|
415
|
-
})
|
|
416
|
-
: null;
|
|
417
|
-
const configured = Boolean(account.token?.trim()) && !ownerAccountId;
|
|
418
|
-
const groups =
|
|
419
|
-
cfg.channels?.telegram?.accounts?.[account.accountId]?.groups ??
|
|
420
|
-
cfg.channels?.telegram?.groups;
|
|
421
|
-
const allowUnmentionedGroups =
|
|
422
|
-
groups?.["*"]?.requireMention === false ||
|
|
423
|
-
Object.entries(groups ?? {}).some(
|
|
424
|
-
([key, value]) => key !== "*" && value?.requireMention === false,
|
|
425
|
-
);
|
|
426
|
-
return {
|
|
427
|
-
accountId: account.accountId,
|
|
428
|
-
name: account.name,
|
|
429
|
-
enabled: account.enabled,
|
|
430
|
-
configured,
|
|
431
|
-
tokenSource: account.tokenSource,
|
|
432
|
-
running: runtime?.running ?? false,
|
|
433
|
-
lastStartAt: runtime?.lastStartAt ?? null,
|
|
434
|
-
lastStopAt: runtime?.lastStopAt ?? null,
|
|
435
|
-
lastError: runtime?.lastError ?? duplicateTokenReason,
|
|
436
|
-
mode: runtime?.mode ?? (account.config.webhookUrl ? "webhook" : "polling"),
|
|
437
|
-
probe,
|
|
438
|
-
audit,
|
|
439
|
-
allowUnmentionedGroups,
|
|
440
|
-
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
441
|
-
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
442
|
-
};
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
gateway: {
|
|
446
|
-
startAccount: async (ctx) => {
|
|
447
|
-
const account = ctx.account;
|
|
448
|
-
const ownerAccountId = findTelegramTokenOwnerAccountId({
|
|
449
|
-
cfg: ctx.cfg,
|
|
450
|
-
accountId: account.accountId,
|
|
451
|
-
});
|
|
452
|
-
if (ownerAccountId) {
|
|
453
|
-
const reason = formatDuplicateTelegramTokenReason({
|
|
454
|
-
accountId: account.accountId,
|
|
455
|
-
ownerAccountId,
|
|
456
|
-
});
|
|
457
|
-
ctx.log?.error?.(`[${account.accountId}] ${reason}`);
|
|
458
|
-
throw new Error(reason);
|
|
459
|
-
}
|
|
460
|
-
const token = account.token.trim();
|
|
461
|
-
let telegramBotLabel = "";
|
|
462
|
-
try {
|
|
463
|
-
const probe = await getTelegramRuntime().channel.telegram.probeTelegram(
|
|
464
|
-
token,
|
|
465
|
-
2500,
|
|
466
|
-
account.config.proxy,
|
|
467
|
-
);
|
|
468
|
-
const username = probe.ok ? probe.bot?.username?.trim() : null;
|
|
469
|
-
if (username) {
|
|
470
|
-
telegramBotLabel = ` (@${username})`;
|
|
471
|
-
}
|
|
472
|
-
} catch (err) {
|
|
473
|
-
if (getTelegramRuntime().logging.shouldLogVerbose()) {
|
|
474
|
-
ctx.log?.debug?.(`[${account.accountId}] bot probe failed: ${String(err)}`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
ctx.log?.info(`[${account.accountId}] starting provider${telegramBotLabel}`);
|
|
478
|
-
return getTelegramRuntime().channel.telegram.monitorTelegramProvider({
|
|
479
|
-
token,
|
|
480
|
-
accountId: account.accountId,
|
|
481
|
-
config: ctx.cfg,
|
|
482
|
-
runtime: ctx.runtime,
|
|
483
|
-
abortSignal: ctx.abortSignal,
|
|
484
|
-
useWebhook: Boolean(account.config.webhookUrl),
|
|
485
|
-
webhookUrl: account.config.webhookUrl,
|
|
486
|
-
webhookSecret: account.config.webhookSecret,
|
|
487
|
-
webhookPath: account.config.webhookPath,
|
|
488
|
-
webhookHost: account.config.webhookHost,
|
|
489
|
-
});
|
|
490
|
-
},
|
|
491
|
-
logoutAccount: async ({ accountId, cfg }) => {
|
|
492
|
-
const envToken = process.env.TELEGRAM_BOT_TOKEN?.trim() ?? "";
|
|
493
|
-
const nextCfg = { ...cfg } as SymiConfig;
|
|
494
|
-
const nextTelegram = cfg.channels?.telegram ? { ...cfg.channels.telegram } : undefined;
|
|
495
|
-
let cleared = false;
|
|
496
|
-
let changed = false;
|
|
497
|
-
if (nextTelegram) {
|
|
498
|
-
if (accountId === DEFAULT_ACCOUNT_ID && nextTelegram.botToken) {
|
|
499
|
-
delete nextTelegram.botToken;
|
|
500
|
-
cleared = true;
|
|
501
|
-
changed = true;
|
|
502
|
-
}
|
|
503
|
-
const accounts =
|
|
504
|
-
nextTelegram.accounts && typeof nextTelegram.accounts === "object"
|
|
505
|
-
? { ...nextTelegram.accounts }
|
|
506
|
-
: undefined;
|
|
507
|
-
if (accounts && accountId in accounts) {
|
|
508
|
-
const entry = accounts[accountId];
|
|
509
|
-
if (entry && typeof entry === "object") {
|
|
510
|
-
const nextEntry = { ...entry } as Record<string, unknown>;
|
|
511
|
-
if ("botToken" in nextEntry) {
|
|
512
|
-
const token = nextEntry.botToken;
|
|
513
|
-
if (typeof token === "string" ? token.trim() : token) {
|
|
514
|
-
cleared = true;
|
|
515
|
-
}
|
|
516
|
-
delete nextEntry.botToken;
|
|
517
|
-
changed = true;
|
|
518
|
-
}
|
|
519
|
-
if (Object.keys(nextEntry).length === 0) {
|
|
520
|
-
delete accounts[accountId];
|
|
521
|
-
changed = true;
|
|
522
|
-
} else {
|
|
523
|
-
accounts[accountId] = nextEntry as typeof entry;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
if (accounts) {
|
|
528
|
-
if (Object.keys(accounts).length === 0) {
|
|
529
|
-
delete nextTelegram.accounts;
|
|
530
|
-
changed = true;
|
|
531
|
-
} else {
|
|
532
|
-
nextTelegram.accounts = accounts;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
if (changed) {
|
|
537
|
-
if (nextTelegram && Object.keys(nextTelegram).length > 0) {
|
|
538
|
-
nextCfg.channels = { ...nextCfg.channels, telegram: nextTelegram };
|
|
539
|
-
} else {
|
|
540
|
-
const nextChannels = { ...nextCfg.channels };
|
|
541
|
-
delete nextChannels.telegram;
|
|
542
|
-
if (Object.keys(nextChannels).length > 0) {
|
|
543
|
-
nextCfg.channels = nextChannels;
|
|
544
|
-
} else {
|
|
545
|
-
delete nextCfg.channels;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
const resolved = resolveTelegramAccount({
|
|
550
|
-
cfg: changed ? nextCfg : cfg,
|
|
551
|
-
accountId,
|
|
552
|
-
});
|
|
553
|
-
const loggedOut = resolved.tokenSource === "none";
|
|
554
|
-
if (changed) {
|
|
555
|
-
await getTelegramRuntime().config.writeConfigFile(nextCfg);
|
|
556
|
-
}
|
|
557
|
-
return { cleared, envToken: Boolean(envToken), loggedOut };
|
|
558
|
-
},
|
|
559
|
-
},
|
|
560
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { PluginRuntime } from "symi/plugin-sdk";
|
|
2
|
-
|
|
3
|
-
let runtime: PluginRuntime | null = null;
|
|
4
|
-
|
|
5
|
-
export function setTelegramRuntime(next: PluginRuntime) {
|
|
6
|
-
runtime = next;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getTelegramRuntime(): PluginRuntime {
|
|
10
|
-
if (!runtime) {
|
|
11
|
-
throw new Error("Telegram runtime not initialized");
|
|
12
|
-
}
|
|
13
|
-
return runtime;
|
|
14
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { SymiPluginApi } from "symi/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "symi/plugin-sdk";
|
|
3
|
-
import { whatsappPlugin } from "./src/channel.js";
|
|
4
|
-
import { setWhatsAppRuntime } from "./src/runtime.js";
|
|
5
|
-
|
|
6
|
-
const plugin = {
|
|
7
|
-
id: "whatsapp",
|
|
8
|
-
name: "WhatsApp",
|
|
9
|
-
description: "WhatsApp channel plugin",
|
|
10
|
-
configSchema: emptyPluginConfigSchema(),
|
|
11
|
-
register(api: SymiPluginApi) {
|
|
12
|
-
setWhatsAppRuntime(api.runtime);
|
|
13
|
-
api.registerChannel({ plugin: whatsappPlugin });
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default plugin;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
-
|
|
4
|
-
case `uname` in
|
|
5
|
-
*CYGWIN*|*MINGW*|*MSYS*)
|
|
6
|
-
if command -v cygpath > /dev/null 2>&1; then
|
|
7
|
-
basedir=`cygpath -w "$basedir"`
|
|
8
|
-
fi
|
|
9
|
-
;;
|
|
10
|
-
esac
|
|
11
|
-
|
|
12
|
-
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/home/symi/projects/symi/node_modules:/home/symi/projects/node_modules:/home/symi/node_modules:/home/node_modules:/node_modules:/home/symi/projects/symi/node_modules/.pnpm/node_modules"
|
|
14
|
-
else
|
|
15
|
-
export NODE_PATH="/home/symi/projects/symi/node_modules:/home/symi/projects/node_modules:/home/symi/node_modules:/home/node_modules:/node_modules:/home/symi/projects/symi/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
|
-
fi
|
|
17
|
-
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir/../@symerian/symi/symi.mjs" "$@"
|
|
19
|
-
else
|
|
20
|
-
exec node "$basedir/../@symerian/symi/symi.mjs" "$@"
|
|
21
|
-
fi
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@symi/whatsapp",
|
|
3
|
-
"version": "3.0.9",
|
|
4
|
-
"private": true,
|
|
5
|
-
"description": "Symi WhatsApp channel plugin",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"devDependencies": {
|
|
8
|
-
"@symerian/symi": "workspace:*"
|
|
9
|
-
},
|
|
10
|
-
"symi": {
|
|
11
|
-
"extensions": [
|
|
12
|
-
"./index.ts"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
}
|