import { eq } from "drizzle-orm"; import { db } from "@/lib/db/client"; import { customers, tickets } from "@/lib/db/schema"; import { recordAgentReply, recordInternalNote, getEmailThreadContext } from "@/lib/tickets/service"; import { sendTelegramMessage, sendTelegramFile } from "@/lib/telegram/bot"; import { sendTicketReplyEmail } from "@/lib/mail/smtp"; interface AttachmentPayload { filename: string; mimeType: string; sizeBytes: number; storageKey: string; buffer: Buffer; } /** * Shared by the text-reply and attachment-upload admin routes: creates the * message (public reply or internal note) and, for public replies, delivers * it over the ticket's channel (email/Telegram). Not used for portal/widget * replies — those never need outbound channel delivery. */ export async function deliverAgentMessage(params: { ticketId: string; agentId: string; agentName: string; body: string; visibility: "public" | "internal"; attachment?: AttachmentPayload; }) { const ticket = await db.query.tickets.findFirst({ where: eq(tickets.id, params.ticketId) }); if (!ticket) throw new Error("Ticket not found"); const attachmentInput = params.attachment ? { filename: params.attachment.filename, mimeType: params.attachment.mimeType, sizeBytes: params.attachment.sizeBytes, storageKey: params.attachment.storageKey, } : undefined; if (params.visibility === "internal") { return recordInternalNote({ ticketId: params.ticketId, agentId: params.agentId, agentName: params.agentName, body: params.body, attachments: attachmentInput ? [attachmentInput] : undefined, }); } let emailMessageId: string | undefined; if (ticket.channel === "email") { const context = await getEmailThreadContext(params.ticketId); if (context) { emailMessageId = await sendTicketReplyEmail({ to: context.customerEmail, subject: context.subject, body: params.body, inReplyTo: context.inReplyTo, attachment: params.attachment ? { filename: params.attachment.filename, content: params.attachment.buffer } : undefined, }); } } const result = await recordAgentReply({ ticketId: params.ticketId, agentId: params.agentId, agentName: params.agentName, body: params.body, emailMessageId, attachments: attachmentInput ? [attachmentInput] : undefined, }); if (result.ticket.channel === "telegram") { const customer = await db.query.customers.findFirst({ where: eq(customers.id, result.ticket.customerId), }); if (customer?.telegramChatId) { if (params.attachment) { await sendTelegramFile( customer.telegramChatId, { buffer: params.attachment.buffer, filename: params.attachment.filename, mimeType: params.attachment.mimeType }, params.body, ); } else { await sendTelegramMessage(customer.telegramChatId, params.body); } } } return result; }