248 lines
9.4 KiB
TypeScript
248 lines
9.4 KiB
TypeScript
// @ts-nocheck
|
|
|
|
import { resolveEffectiveMessagesConfig } from "../agents/identity.js";
|
|
import {
|
|
buildCommandText,
|
|
listNativeCommandSpecsForConfig,
|
|
} from "../auto-reply/commands-registry.js";
|
|
import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
|
|
import { danger, logVerbose } from "../globals.js";
|
|
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
|
import { deliverReplies } from "./bot/delivery.js";
|
|
import {
|
|
buildSenderName,
|
|
buildTelegramGroupFrom,
|
|
buildTelegramGroupPeerId,
|
|
resolveTelegramForumThreadId,
|
|
} from "./bot/helpers.js";
|
|
import { firstDefined, isSenderAllowed, normalizeAllowFrom } from "./bot-access.js";
|
|
import { readTelegramAllowFromStore } from "./pairing-store.js";
|
|
|
|
export const registerTelegramNativeCommands = ({
|
|
bot,
|
|
cfg,
|
|
runtime,
|
|
accountId,
|
|
telegramCfg,
|
|
allowFrom,
|
|
groupAllowFrom,
|
|
replyToMode,
|
|
textLimit,
|
|
useAccessGroups,
|
|
nativeEnabled,
|
|
nativeDisabledExplicit,
|
|
resolveGroupPolicy,
|
|
resolveTelegramGroupConfig,
|
|
shouldSkipUpdate,
|
|
opts,
|
|
}) => {
|
|
const nativeCommands = nativeEnabled ? listNativeCommandSpecsForConfig(cfg) : [];
|
|
if (nativeCommands.length > 0) {
|
|
const api = bot.api as unknown as {
|
|
setMyCommands?: (
|
|
commands: Array<{ command: string; description: string }>,
|
|
) => Promise<unknown>;
|
|
};
|
|
if (typeof api.setMyCommands === "function") {
|
|
api
|
|
.setMyCommands(
|
|
nativeCommands.map((command) => ({
|
|
command: command.name,
|
|
description: command.description,
|
|
})),
|
|
)
|
|
.catch((err) => {
|
|
runtime.error?.(danger(`telegram setMyCommands failed: ${String(err)}`));
|
|
});
|
|
} else {
|
|
logVerbose("telegram: setMyCommands unavailable; skipping registration");
|
|
}
|
|
|
|
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
|
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
|
} else {
|
|
for (const command of nativeCommands) {
|
|
bot.command(command.name, async (ctx) => {
|
|
const msg = ctx.message;
|
|
if (!msg) return;
|
|
if (shouldSkipUpdate(ctx)) return;
|
|
const chatId = msg.chat.id;
|
|
const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
|
|
const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id;
|
|
const isForum = (msg.chat as { is_forum?: boolean }).is_forum === true;
|
|
const resolvedThreadId = resolveTelegramForumThreadId({
|
|
isForum,
|
|
messageThreadId,
|
|
});
|
|
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
|
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
|
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
|
const effectiveGroupAllow = normalizeAllowFrom([
|
|
...(groupAllowOverride ?? groupAllowFrom ?? []),
|
|
...storeAllowFrom,
|
|
]);
|
|
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
|
|
|
if (isGroup && groupConfig?.enabled === false) {
|
|
await bot.api.sendMessage(chatId, "This group is disabled.");
|
|
return;
|
|
}
|
|
if (isGroup && topicConfig?.enabled === false) {
|
|
await bot.api.sendMessage(chatId, "This topic is disabled.");
|
|
return;
|
|
}
|
|
if (isGroup && hasGroupAllowOverride) {
|
|
const senderId = msg.from?.id;
|
|
const senderUsername = msg.from?.username ?? "";
|
|
if (
|
|
senderId == null ||
|
|
!isSenderAllowed({
|
|
allow: effectiveGroupAllow,
|
|
senderId: String(senderId),
|
|
senderUsername,
|
|
})
|
|
) {
|
|
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (isGroup && useAccessGroups) {
|
|
const groupPolicy = telegramCfg.groupPolicy ?? "open";
|
|
if (groupPolicy === "disabled") {
|
|
await bot.api.sendMessage(chatId, "Telegram group commands are disabled.");
|
|
return;
|
|
}
|
|
if (groupPolicy === "allowlist") {
|
|
const senderId = msg.from?.id;
|
|
if (senderId == null) {
|
|
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
|
return;
|
|
}
|
|
const senderUsername = msg.from?.username ?? "";
|
|
if (
|
|
!isSenderAllowed({
|
|
allow: effectiveGroupAllow,
|
|
senderId: String(senderId),
|
|
senderUsername,
|
|
})
|
|
) {
|
|
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
|
return;
|
|
}
|
|
}
|
|
const groupAllowlist = resolveGroupPolicy(chatId);
|
|
if (groupAllowlist.allowlistEnabled && !groupAllowlist.allowed) {
|
|
await bot.api.sendMessage(chatId, "This group is not allowed.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
const allowFromList = Array.isArray(allowFrom)
|
|
? allowFrom.map((entry) => String(entry).trim()).filter(Boolean)
|
|
: [];
|
|
const senderId = msg.from?.id ? String(msg.from.id) : "";
|
|
const senderUsername = msg.from?.username ?? "";
|
|
const commandAuthorized =
|
|
allowFromList.length === 0 ||
|
|
allowFromList.includes("*") ||
|
|
(senderId && allowFromList.includes(senderId)) ||
|
|
(senderId && allowFromList.includes(`telegram:${senderId}`)) ||
|
|
(senderUsername &&
|
|
allowFromList.some(
|
|
(entry) =>
|
|
entry.toLowerCase() === senderUsername.toLowerCase() ||
|
|
entry.toLowerCase() === `@${senderUsername.toLowerCase()}`,
|
|
));
|
|
if (!commandAuthorized) {
|
|
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
|
return;
|
|
}
|
|
|
|
const prompt = buildCommandText(command.name, ctx.match ?? "");
|
|
const route = resolveAgentRoute({
|
|
cfg,
|
|
channel: "telegram",
|
|
accountId,
|
|
peer: {
|
|
kind: isGroup ? "group" : "dm",
|
|
id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
|
|
},
|
|
});
|
|
const skillFilter = firstDefined(topicConfig?.skills, groupConfig?.skills);
|
|
const systemPromptParts = [
|
|
groupConfig?.systemPrompt?.trim() || null,
|
|
topicConfig?.systemPrompt?.trim() || null,
|
|
].filter((entry): entry is string => Boolean(entry));
|
|
const groupSystemPrompt =
|
|
systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
|
|
const ctxPayload = {
|
|
Body: prompt,
|
|
From: isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`,
|
|
To: `slash:${senderId || chatId}`,
|
|
ChatType: isGroup ? "group" : "direct",
|
|
GroupSubject: isGroup ? (msg.chat.title ?? undefined) : undefined,
|
|
GroupSystemPrompt: isGroup ? groupSystemPrompt : undefined,
|
|
SenderName: buildSenderName(msg),
|
|
SenderId: senderId || undefined,
|
|
SenderUsername: senderUsername || undefined,
|
|
Surface: "telegram",
|
|
MessageSid: String(msg.message_id),
|
|
Timestamp: msg.date ? msg.date * 1000 : undefined,
|
|
WasMentioned: true,
|
|
CommandAuthorized: commandAuthorized,
|
|
CommandSource: "native" as const,
|
|
SessionKey: `telegram:slash:${senderId || chatId}`,
|
|
CommandTargetSessionKey: route.sessionKey,
|
|
MessageThreadId: resolvedThreadId,
|
|
IsForum: isForum,
|
|
};
|
|
|
|
const disableBlockStreaming =
|
|
typeof telegramCfg.blockStreaming === "boolean"
|
|
? !telegramCfg.blockStreaming
|
|
: undefined;
|
|
|
|
await dispatchReplyWithBufferedBlockDispatcher({
|
|
ctx: ctxPayload,
|
|
cfg,
|
|
dispatcherOptions: {
|
|
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
|
deliver: async (payload) => {
|
|
await deliverReplies({
|
|
replies: [payload],
|
|
chatId: String(chatId),
|
|
token: opts.token,
|
|
runtime,
|
|
bot,
|
|
replyToMode,
|
|
textLimit,
|
|
messageThreadId: resolvedThreadId,
|
|
});
|
|
},
|
|
onError: (err, info) => {
|
|
runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(err)}`));
|
|
},
|
|
},
|
|
replyOptions: {
|
|
skillFilter,
|
|
disableBlockStreaming,
|
|
},
|
|
});
|
|
});
|
|
}
|
|
}
|
|
} else if (nativeDisabledExplicit) {
|
|
const api = bot.api as unknown as {
|
|
setMyCommands?: (commands: []) => Promise<unknown>;
|
|
};
|
|
if (typeof api.setMyCommands === "function") {
|
|
api.setMyCommands([]).catch((err) => {
|
|
runtime.error?.(danger(`telegram clear commands failed: ${String(err)}`));
|
|
});
|
|
} else {
|
|
logVerbose("telegram: setMyCommands unavailable; skipping clear");
|
|
}
|
|
}
|
|
};
|