import { getHeader, readBody } from "h3"; import { prisma } from "../../../../utils/prisma"; import { telegramBotApi } from "../../../../utils/telegram"; import { extractLinkTokenFromStartText, getBusinessConnectionFromUpdate, getTelegramChatIdFromUpdate, verifyLinkToken, } from "../../../../utils/telegramBusinessConnect"; function hasValidSecret(event: any) { const expected = String(process.env.TELEGRAM_WEBHOOK_SECRET || "").trim(); if (!expected) return true; const incoming = String(getHeader(event, "x-telegram-bot-api-secret-token") || "").trim(); return incoming !== "" && incoming === expected; } function pickStartText(update: any): string | null { const text = update?.message?.text ?? update?.business_message?.text ?? update?.edited_business_message?.text ?? null; if (typeof text !== "string") return null; return text; } export default defineEventHandler(async (event) => { if (!hasValidSecret(event)) { throw createError({ statusCode: 401, statusMessage: "invalid webhook secret" }); } const update = await readBody(event); const nowIso = new Date().toISOString(); const startText = pickStartText(update); const linkToken = startText ? extractLinkTokenFromStartText(startText) : null; if (linkToken) { const payload = verifyLinkToken(linkToken); if (!payload) return { ok: true, accepted: false, reason: "invalid_or_expired_link_token" }; const pendingId = `pending:${payload.nonce}`; const chatId = getTelegramChatIdFromUpdate(update); await prisma.telegramBusinessConnection.updateMany({ where: { teamId: payload.teamId, businessConnectionId: pendingId, }, data: { rawJson: { state: "pending_business_connection", link: { nonce: payload.nonce, exp: payload.exp, linkedAt: nowIso, telegramUserId: chatId, chatId, }, lastStartUpdate: update, }, }, }); if (chatId) { void telegramBotApi("sendMessage", { chat_id: chatId, text: "CRM: связка аккаунта получена. Теперь подключите Telegram Business в настройках Telegram, затем вернитесь в CRM и обновите статус.", }).catch(() => {}); } return { ok: true, accepted: true, type: "start_link" }; } const businessConnection = getBusinessConnectionFromUpdate(update); if (businessConnection) { const pendingRows = await prisma.telegramBusinessConnection.findMany({ where: { businessConnectionId: { startsWith: "pending:", }, }, orderBy: { updatedAt: "desc" }, take: 200, }); const matchedPending = pendingRows.find((row) => { const raw = (row.rawJson ?? {}) as any; const linkedTelegramUserId = raw?.link?.telegramUserId != null ? String(raw.link.telegramUserId) : null; if (!businessConnection.userChatId) return false; return linkedTelegramUserId === businessConnection.userChatId; }); if (!matchedPending) { return { ok: true, accepted: false, reason: "team_not_linked_for_business_connection" }; } await prisma.$transaction([ prisma.telegramBusinessConnection.upsert({ where: { teamId_businessConnectionId: { teamId: matchedPending.teamId, businessConnectionId: businessConnection.id, }, }, create: { teamId: matchedPending.teamId, businessConnectionId: businessConnection.id, isEnabled: businessConnection.isEnabled, canReply: businessConnection.canReply, rawJson: { state: "connected", connectedAt: nowIso, userChatId: businessConnection.userChatId, businessConnection: businessConnection.raw, update, }, }, update: { isEnabled: businessConnection.isEnabled, canReply: businessConnection.canReply, rawJson: { state: "connected", connectedAt: nowIso, userChatId: businessConnection.userChatId, businessConnection: businessConnection.raw, update, }, }, }), prisma.telegramBusinessConnection.delete({ where: { id: matchedPending.id } }), ]); if (businessConnection.userChatId) { void telegramBotApi("sendMessage", { chat_id: businessConnection.userChatId, text: "CRM: Telegram Business подключен. Теперь входящие сообщения будут появляться в CRM.", }).catch(() => {}); } return { ok: true, accepted: true, type: "business_connection" }; } return { ok: true, accepted: true, type: "ignored" }; });