agents
Version:
A home for your AI agents
198 lines (197 loc) • 7.93 kB
JavaScript
import { t as emptyIngressResponse } from "../../../internal-kmw70xwR.js";
import { t as matchesPath } from "../../../ingress-EFbrS547.js";
//#region src/experimental/channels/telegram/index.ts
const DEFAULT_TELEGRAM_WEBHOOK_PATH = "/webhooks/telegram";
const INTERACTION_FOOTER = /\n\nReply YES to approve or NO to reject\.\n\n\[channel-interaction:v1:([A-Za-z0-9_-]+)\]$/;
function channelIdentity(chatId) {
return `telegram:chat:${chatId}`;
}
function userIdentity(userId) {
return `telegram:user:${userId}`;
}
function messageIdentity(chatId, messageId) {
return `${channelIdentity(chatId)}:message:${messageId}`;
}
function updateIdentity(chatId, updateId) {
return `${channelIdentity(chatId)}:update:${updateId}`;
}
function decodeApprovalId(value) {
try {
const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
const padding = "=".repeat((4 - base64.length % 4) % 4);
const binary = atob(base64 + padding);
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes) || void 0;
} catch {
return;
}
}
function asTelegramMessage(value) {
return value !== null && typeof value === "object" ? value : void 0;
}
function telegramActor(message) {
const sender = message.from;
if (sender) {
const fullName = [sender.first_name, sender.last_name].filter(Boolean).join(" ");
return {
id: userIdentity(sender.id),
identity: { subject: `user:${sender.id}` },
...sender.username && { username: sender.username },
...fullName && { fullName },
isBot: sender.is_bot
};
}
const senderChat = message.sender_chat;
if (!senderChat) return void 0;
const fullName = senderChat.title ?? [senderChat.first_name, senderChat.last_name].filter(Boolean).join(" ");
return {
id: channelIdentity(senderChat.id),
identity: { subject: `chat:${senderChat.id}` },
...senderChat.username && { username: senderChat.username },
...fullName && { fullName },
isBot: "unknown"
};
}
function chatLabel(chat) {
return `Telegram · ${(chat.title ?? (chat.username ? `@${chat.username}` : void 0) ?? [chat.first_name, chat.last_name].filter(Boolean).join(" ") ?? String(chat.id)) || chat.id}`;
}
function eventContext(message, eventId, botUserId) {
const actor = telegramActor(message);
const channelId = channelIdentity(message.chat.id);
return {
eventId,
thread: {
id: typeof message.message_thread_id === "number" ? `${channelId}:topic:${message.message_thread_id}` : channelId,
isDirectMessage: message.chat.type === "private" ? true : message.chat.type === "group" || message.chat.type === "supergroup" || message.chat.type === "channel" ? false : "unknown"
},
replySurface: {
version: 1,
address: {
chatId: String(message.chat.id),
...botUserId && { botUserId },
...typeof message.message_thread_id === "number" && { messageThreadId: message.message_thread_id },
replyToMessageId: message.message_id
},
label: chatLabel(message.chat)
},
...actor && { actor }
};
}
function telegramTimestamp(value) {
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
const date = /* @__PURE__ */ new Date(value * 1e3);
return Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
}
function normalizedMessage(message, eventId, edited, botUserId) {
const reply = asTelegramMessage(message.reply_to_message);
const sentAt = telegramTimestamp(message.date);
const editedAt = telegramTimestamp(message.edit_date);
return {
type: "message",
...eventContext(message, eventId, botUserId),
message: {
id: messageIdentity(message.chat.id, message.message_id),
text: message.text,
...typeof reply?.message_id === "number" && { reply: {
id: messageIdentity(message.chat.id, reply.message_id),
...typeof reply.text === "string" && { text: reply.text }
} },
...(sentAt || edited || editedAt) && { metadata: {
...sentAt && { sentAt },
...edited && { edited: true },
...editedAt && { editedAt }
} }
}
};
}
function approvalResponse(message, eventId, botUserId) {
if (botUserId === void 0) return void 0;
if (message.text !== "YES" && message.text !== "NO") return void 0;
const reply = asTelegramMessage(message.reply_to_message);
if (typeof reply?.message_id !== "number") return void 0;
const author = reply.from;
if (author?.is_bot !== true || author.id !== botUserId) return void 0;
const encodedApprovalId = typeof reply.text === "string" ? reply.text.match(INTERACTION_FOOTER)?.[1] : void 0;
const approvalId = encodedApprovalId ? decodeApprovalId(encodedApprovalId) : void 0;
if (!approvalId) return void 0;
return {
type: "approval-response",
...eventContext(message, eventId, botUserId),
approvalId,
decision: message.text === "YES" ? "approve" : "reject",
reference: messageIdentity(message.chat.id, message.message_id)
};
}
function inboundEvent(update, botUserId) {
const edited = update.edited_message !== void 0;
const message = asTelegramMessage(edited ? update.edited_message : update.message);
if (typeof update.update_id !== "number" || typeof message?.message_id !== "number" || typeof message.text !== "string" || typeof message.chat?.id !== "number") return;
const eventId = updateIdentity(message.chat.id, update.update_id);
return approvalResponse(message, eventId, botUserId) ?? normalizedMessage(message, eventId, edited, botUserId);
}
/** Recover this bot's numeric user id from its BotFather token prefix. */
function telegramBotUserId(botToken) {
const prefix = botToken.match(/^(\d+):/)?.[1];
if (!prefix) return void 0;
const botUserId = Number(prefix);
return Number.isSafeInteger(botUserId) && botUserId > 0 ? botUserId : void 0;
}
/** Create dependency-free Telegram webhook ingress for a destination chat. */
function telegramWebhook(options) {
if (!options.secretToken.trim()) throw new Error("secretToken is required to create Telegram webhook ingress");
if (options.botUserId !== void 0 && (!Number.isSafeInteger(options.botUserId) || options.botUserId <= 0)) throw new Error("botUserId must be a positive Telegram user id");
const botUserId = options.botUserId;
const path = options.path ?? DEFAULT_TELEGRAM_WEBHOOK_PATH;
return { async receive(request) {
if (!matchesPath(request, path)) return null;
if (request.method !== "POST") return emptyIngressResponse(405);
if (request.headers.get("x-telegram-bot-api-secret-token") !== options.secretToken) return emptyIngressResponse(401);
let update;
try {
update = await request.json();
} catch {
return emptyIngressResponse(400);
}
if (update === null || typeof update !== "object") return emptyIngressResponse(400);
const raw = update;
const event = inboundEvent(raw, botUserId);
return {
events: event ? [{
event,
raw
}] : [],
response: new Response(null, { status: 200 })
};
} };
}
/** Create a configured Telegram ingress Channel. */
function telegram(options) {
if (!options.botToken.trim()) throw new Error("botToken is required to create a Telegram channel");
if (options.webhook && !options.participant) throw new Error("participant is required with a Telegram webhook");
const botUserId = telegramBotUserId(options.botToken);
const ingress = options.webhook ? telegramWebhook({
...options.webhook,
botUserId
}) : void 0;
return {
...options.participant && { participant: options.participant },
...options.route && { route: options.route },
...ingress && { ingress },
contactSurface(identity) {
if ((identity.scope ?? "default") !== "default") return null;
const match = identity.subject.match(/^(user|chat):(-?\d+)$/);
if (!match) return null;
return {
version: 1,
address: {
chatId: match[2],
...botUserId && { botUserId }
},
label: `Telegram · ${match[1]} ${match[2]}`
};
}
};
}
//#endregion
export { telegram, telegramWebhook };
//# sourceMappingURL=index.js.map