import { Controller, Get, Post, Body, Delete, Param } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; import { WhatsappService } from './whatsapp.service'; import { AttendanceAssignmentService } from '../attendance/attendance-assignment.service'; @ApiTags('WhatsApp') @Controller('whatsapp') export class WhatsappController { constructor( private readonly whatsappService: WhatsappService, private readonly assignmentService: AttendanceAssignmentService ) {} @ApiOperation({ summary: 'Consulta status da conexao WhatsApp', description: 'Retorna o estado atual da sessao WhatsApp usada pelo backend, como conectado, aguardando QR Code ou desconectado.', }) @ApiResponse({ status: 200, description: 'Status atual da conexao retornado.' }) @Get('status') getStatus() { return { status: this.whatsappService.getStatus() }; } @ApiOperation({ summary: 'Lista conversas do WhatsApp', description: 'Retorna os chats conhecidos pelo backend para alimentar o painel de atendimento em tempo real.', }) @ApiResponse({ status: 200, description: 'Lista de conversas retornada.' }) @Get('chats') async getChats() { return this.whatsappService.getChats(); } @ApiOperation({ summary: 'Lista mensagens de uma conversa', description: 'Busca o historico de mensagens de um chat especifico para abrir a conversa no painel do atendente.', }) @ApiParam({ name: 'chatId', description: 'Identificador do chat no WhatsApp.' }) @ApiResponse({ status: 200, description: 'Mensagens do chat retornadas.' }) @Get('messages/:chatId') async getChatMessages(@Param('chatId') chatId: string) { return this.whatsappService.getChatMessages(chatId); } @ApiOperation({ summary: 'Baixa midia de uma mensagem', description: 'Retorna a midia associada a uma mensagem do WhatsApp quando a mensagem possui anexo.', }) @ApiParam({ name: 'chatId', description: 'Identificador do chat no WhatsApp.' }) @ApiParam({ name: 'messageId', description: 'Identificador da mensagem com midia.' }) @ApiResponse({ status: 200, description: 'Midia da mensagem retornada.' }) @Get('media/:chatId/:messageId') async getMessageMedia(@Param('chatId') chatId: string, @Param('messageId') messageId: string) { return this.whatsappService.getMessageMedia(chatId, messageId); } @ApiOperation({ summary: 'Envia mensagem no WhatsApp', description: 'Envia uma mensagem ativa para um contato ou responde uma conversa existente, opcionalmente com midia em base64.', }) @ApiBody({ schema: { type: 'object', required: ['to', 'message'], properties: { to: { type: 'string', example: '5511999999999@c.us' }, message: { type: 'string', example: 'Ola, como posso ajudar?' }, senderName: { type: 'string', example: 'Rafael Lopes' }, media: { type: 'object', nullable: true, properties: { data: { type: 'string', description: 'Arquivo em base64.' }, mimetype: { type: 'string', example: 'application/pdf' }, filename: { type: 'string', example: 'documento.pdf' }, }, }, }, }, }) @ApiResponse({ status: 201, description: 'Mensagem enviada.' }) @Post('send') async sendMessage(@Body() body: { to: string; message: string; senderName?: string; media?: { data: string; mimetype: string; filename?: string } }) { return this.whatsappService.sendMessage(body.to, body.message, body.media, body.senderName); } @ApiOperation({ summary: 'Inicia atendimento ativo', description: 'Abre uma conversa ativa usando um template aprovado e registra o atendimento para acompanhamento no painel.', }) @ApiBody({ schema: { type: 'object', required: ['to', 'templateId', 'userId'], properties: { to: { type: 'string', example: '5511999999999' }, templateId: { type: 'number', example: 1 }, userId: { type: 'number', example: 10 }, areaId: { type: 'number', nullable: true, example: 2 }, variables: { type: 'object', additionalProperties: { type: 'string', nullable: true } }, }, }, }) @ApiResponse({ status: 201, description: 'Atendimento ativo iniciado.' }) @Post('start-attendance') async startAttendance(@Body() body: { to: string; templateId: number; userId: number; areaId?: number | null; variables?: Record }) { return this.whatsappService.startAttendance(body.to, body.templateId, body.userId, body.areaId, body.variables); } @ApiOperation({ summary: 'Assume uma conversa', description: 'Vincula um chat a um atendente e, opcionalmente, a uma area de atendimento.', }) @ApiBody({ schema: { type: 'object', required: ['chatId', 'userId'], properties: { chatId: { type: 'string', example: '5511999999999@c.us' }, userId: { type: 'string', example: '10' }, areaId: { type: 'string', example: '2' }, }, }, }) @Post('assign') async assignChat(@Body() body: { chatId: string; userId: string; areaId?: string }) { return this.assignmentService.assignChat(body.chatId, body.userId, body.areaId); } @ApiOperation({ summary: 'Transfere uma conversa', description: 'Encaminha o chat para outra area ou responsavel e registra observacao para contextualizar o proximo atendimento.', }) @ApiBody({ schema: { type: 'object', required: ['chatId', 'areaId'], properties: { chatId: { type: 'string', example: '5511999999999@c.us' }, areaId: { type: 'number', example: 2 }, userId: { type: 'number', nullable: true, example: 10 }, note: { type: 'string', nullable: true, example: 'Tony e um colaborador e quer saber sobre ferias.' }, }, }, }) @Post('transfer') async transferChat(@Body() body: { chatId: string; areaId: number; userId?: number | null; note?: string | null }) { return this.assignmentService.transferChat(body); } @ApiOperation({ summary: 'Libera conversa assumida', description: 'Remove o vinculo de atendimento do chat, permitindo que outro atendente assuma a conversa.', }) @ApiParam({ name: 'chatId', description: 'Identificador do chat no WhatsApp.' }) @Delete('release/:chatId') async releaseChat(@Param('chatId') chatId: string) { return this.assignmentService.releaseChat(chatId); } @ApiOperation({ summary: 'Fecha atendimento', description: 'Finaliza o atendimento de um chat e remove o vinculo ativo com o atendente.', }) @ApiBody({ schema: { type: 'object', required: ['chatId'], properties: { chatId: { type: 'string', example: '5511999999999@c.us' }, userId: { oneOf: [{ type: 'string' }, { type: 'number' }], nullable: true, example: 10 }, }, }, }) @Post('close') async closeChat(@Body() body: { chatId: string; userId?: string | number | null }) { return this.assignmentService.closeChat(body.chatId, body.userId); } @ApiOperation({ summary: 'Consulta atribuicao do chat', description: 'Retorna qual atendente ou area esta responsavel pela conversa no momento.', }) @ApiParam({ name: 'chatId', description: 'Identificador do chat no WhatsApp.' }) @Get('assignment/:chatId') async getAssignment(@Param('chatId') chatId: string) { return this.assignmentService.getAssignment(chatId); } @ApiOperation({ summary: 'Lista templates de atendimento', description: 'Retorna os templates cadastrados para abertura ativa e mensagens padronizadas.', }) @Get('templates') async getTemplates() { return this.whatsappService.getTemplates(); } @ApiOperation({ summary: 'Cria template de mensagem', description: 'Cadastra um template para abertura ativa ou resposta padronizada. Pode depender de aprovacao conforme o perfil solicitante.', }) @ApiBody({ schema: { type: 'object', required: ['name', 'content'], properties: { name: { type: 'string', example: 'Boas vindas' }, content: { type: 'string', example: 'Ola {{nome}}, tudo bem?' }, areaId: { type: 'number', nullable: true, example: 2 }, requestedByRole: { type: 'string', example: 'admin' }, category: { type: 'string', example: 'atendimento' }, }, }, }) @Post('templates') async saveTemplate(@Body() body: { name: string; content: string; areaId?: number | null; requestedByRole?: string; category?: string }) { return this.whatsappService.saveTemplate(body.name, body.content, body.areaId, body.requestedByRole, body.category); } @ApiOperation({ summary: 'Atualiza template de mensagem', description: 'Altera nome, conteudo, area ou categoria de um template existente.', }) @ApiParam({ name: 'id', description: 'ID do template.' }) @Post('templates/update/:id') async updateTemplate(@Param('id') id: string, @Body() body: { name: string; content: string; areaId?: number | null; category?: string }) { return this.whatsappService.updateTemplate(Number(id), body.name, body.content, body.areaId, body.category); } @ApiOperation({ summary: 'Aprova template pelo admin', description: 'Marca um template como aprovado para uso nos fluxos de atendimento.', }) @ApiParam({ name: 'id', description: 'ID do template.' }) @Post('templates/approve-admin/:id') async approveTemplateByAdmin(@Param('id') id: string) { return this.whatsappService.approveTemplateByAdmin(Number(id)); } @ApiOperation({ summary: 'Rejeita template pelo admin', description: 'Marca um template como rejeitado, impedindo seu uso nos fluxos de atendimento.', }) @ApiParam({ name: 'id', description: 'ID do template.' }) @Post('templates/reject-admin/:id') async rejectTemplateByAdmin(@Param('id') id: string) { return this.whatsappService.rejectTemplateByAdmin(Number(id)); } @ApiOperation({ summary: 'Remove template', description: 'Exclui um template cadastrado.', }) @ApiParam({ name: 'id', description: 'ID do template.' }) @Delete('templates/:id') async deleteTemplate(@Param('id') id: string) { return this.whatsappService.deleteTemplate(Number(id)); } }