UNPKG

@tiflux/mcp

Version:

TiFlux MCP Server - Model Context Protocol integration for Claude Code and other AI clients

372 lines (326 loc) 13 kB
/** * Handlers para operações relacionadas a comunicações internas dos tickets */ const TiFluxAPI = require('../api/tiflux-api'); class InternalCommunicationsHandlers { constructor() { this.api = new TiFluxAPI(); } /** * Handler para criar uma comunicação interna */ async handleCreateInternalCommunication(args) { const { ticket_number, text, files = [], files_base64 = [] } = args; if (!ticket_number) { throw new Error('ticket_number é obrigatório'); } if (!text) { throw new Error('text é obrigatório'); } try { // Combinar arquivos locais e base64 const allFiles = [...files, ...files_base64]; // Validar número total de arquivos if (allFiles.length > 10) { return { content: [ { type: 'text', text: `**⚠️ Muitos arquivos**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Arquivos fornecidos:** ${allFiles.length} (${files.length} locais + ${files_base64.length} base64)\n` + `**Limite:** 10 arquivos por comunicação\n\n` + `*Remova alguns arquivos e tente novamente.*` } ] }; } // Validar estrutura dos arquivos base64 if (files_base64.length > 0) { for (let i = 0; i < files_base64.length; i++) { const file = files_base64[i]; if (!file || typeof file !== 'object') { return { content: [ { type: 'text', text: `**❌ Erro de validação no arquivo base64 #${i + 1}**\n\n` + `O arquivo deve ser um objeto com as propriedades "content" e "filename".\n\n` + `**Exemplo correto:**\n` + `\`\`\`json\n` + `{\n` + ` "content": "base64string...",\n` + ` "filename": "documento.pdf"\n` + `}\n` + `\`\`\`\n\n` + `*Verifique a estrutura do arquivo e tente novamente.*` } ] }; } if (!file.content || typeof file.content !== 'string') { return { content: [ { type: 'text', text: `**❌ Erro de validação no arquivo base64 #${i + 1}**\n\n` + `A propriedade "content" é obrigatória e deve ser uma string em base64.\n\n` + `*Verifique o conteúdo do arquivo e tente novamente.*` } ] }; } if (!file.filename || typeof file.filename !== 'string') { return { content: [ { type: 'text', text: `**❌ Erro de validação no arquivo base64 #${i + 1}**\n\n` + `A propriedade "filename" é obrigatória e deve ser uma string.\n\n` + `*Exemplo: "documento.pdf", "planilha.csv", "imagem.png"*` } ] }; } // Validar tamanho do base64 antes de enviar (aproximado) const estimatedSize = Math.ceil((file.content.length * 3) / 4); const maxSize = 26214400; // 25MB if (estimatedSize > maxSize) { return { content: [ { type: 'text', text: `**❌ Arquivo base64 muito grande**\n\n` + `**Arquivo:** ${file.filename}\n` + `**Tamanho estimado:** ${Math.round(estimatedSize / 1024 / 1024)}MB\n` + `**Limite:** 25MB\n\n` + `*Reduza o tamanho do arquivo ou envie em múltiplas comunicações.*` } ] }; } } } // Criar comunicação interna via API const response = await this.api.createInternalCommunication(ticket_number, text, allFiles); if (response.error) { return { content: [ { type: 'text', text: `**❌ Erro ao criar comunicação interna**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Código:** ${response.status}\n` + `**Mensagem:** ${response.error}\n\n` + `*Verifique se o ticket existe e se você tem permissão para adicionar comunicações internas.*` } ] }; } const communication = response.data; // Preparar informações sobre arquivos anexados let filesInfo = ''; if (communication.files && communication.files.length > 0) { filesInfo = `\n**📎 Arquivos anexados:** ${communication.files_count} arquivo(s)\n`; communication.files.forEach((file, index) => { const fileSize = file.size ? `(${Math.round(file.size / 1024)}KB)` : ''; filesInfo += `${index + 1}. **${file.file_name}** ${fileSize}\n`; }); } // Formatear texto da comunicação (remover HTML se presente) const communicationText = communication.text ? communication.text.replace(/<[^>]*>/g, '').substring(0, 200) : 'Comunicação criada'; return { content: [ { type: 'text', text: `**✅ Comunicação interna criada com sucesso!**\n\n` + `**Ticket:** #${ticket_number}\n` + `**ID da Comunicação:** ${communication.id}\n` + `**Autor:** ${communication.user?.name || 'Usuário não informado'}\n` + `**Criada em:** ${communication.created_at}\n` + `**Conteúdo:** ${communicationText}${communicationText.length >= 200 ? '...' : ''}\n` + `${filesInfo}\n` + `*✅ Comunicação interna adicionada via API TiFlux*` } ] }; } catch (error) { return { content: [ { type: 'text', text: `**❌ Erro interno ao criar comunicação interna**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Erro:** ${error.message}\n\n` + `*Verifique sua conexão e configurações da API.*` } ] }; } } /** * Handler para listar comunicações internas */ async handleListInternalCommunications(args) { const { ticket_number, offset = 1, limit = 20 } = args; if (!ticket_number) { throw new Error('ticket_number é obrigatório'); } try { // Buscar comunicações internas via API const response = await this.api.listInternalCommunications(ticket_number, offset, limit); if (response.error) { return { content: [ { type: 'text', text: `**❌ Erro ao listar comunicações internas**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Código:** ${response.status}\n` + `**Mensagem:** ${response.error}\n\n` + `*Verifique se o ticket existe e se você tem permissão para visualizar comunicações internas.*` } ] }; } const communications = response.data || []; if (communications.length === 0) { return { content: [ { type: 'text', text: `**📋 Nenhuma comunicação interna encontrada**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Página:** ${offset}\n\n` + `*Este ticket ainda não possui comunicações internas ou você chegou ao final da lista.*` } ] }; } // Formatar lista de comunicações let communicationsList = `**📋 Comunicações Internas do Ticket #${ticket_number}** (${communications.length} encontradas)\n\n`; communications.forEach((comm, index) => { const commId = comm.id || 'N/A'; const authorName = comm.user?.name || 'Autor não informado'; const createdAt = comm.created_at ? new Date(comm.created_at).toLocaleString('pt-BR') : 'Data não informada'; // Formatear conteúdo da comunicação (remover HTML e limitar) let content = ''; if (comm.text) { content = comm.text.replace(/<[^>]*>/g, '').trim(); if (content.length > 150) { content = content.substring(0, 150) + '...'; } } else { content = 'Conteúdo não disponível'; } // Informação sobre arquivos let filesInfo = ''; if (comm.files_count && comm.files_count > 0) { filesInfo = ` 📎 ${comm.files_count} arquivo(s)`; } communicationsList += `**${index + 1}. Comunicação #${commId}**\n` + ` 👤 **Autor:** ${authorName}\n` + ` 📅 **Data:** ${createdAt}${filesInfo}\n` + ` 💬 **Conteúdo:** ${content}\n\n`; }); // Informações de paginação const currentOffset = parseInt(offset) || 1; const currentLimit = parseInt(limit) || 20; const hasMoreCommunications = communications.length === currentLimit; let paginationInfo = `**📊 Paginação:**\n`; paginationInfo += `• Página atual: ${currentOffset}\n`; paginationInfo += `• Comunicações por página: ${currentLimit}\n`; paginationInfo += `• Comunicações nesta página: ${communications.length}\n`; if (hasMoreCommunications) { const nextOffset = currentOffset + 1; paginationInfo += `• Próxima página: Use \`offset: ${nextOffset}\` para ver mais comunicações\n`; } else { paginationInfo += `• Esta é a última página disponível\n`; } return { content: [ { type: 'text', text: `${communicationsList}${paginationInfo}\n*✅ Dados obtidos da API TiFlux em tempo real*` } ] }; } catch (error) { return { content: [ { type: 'text', text: `**❌ Erro interno ao listar comunicações internas**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Erro:** ${error.message}\n\n` + `*Verifique sua conexão e configurações da API.*` } ] }; } } /** * Handler para obter uma comunicação interna específica */ async handleGetInternalCommunication(args) { const { ticket_number, communication_id } = args; if (!ticket_number) { throw new Error('ticket_number é obrigatório'); } if (!communication_id) { throw new Error('communication_id é obrigatório'); } try { // Buscar comunicação interna específica via API const response = await this.api.getInternalCommunication(ticket_number, communication_id); if (response.error) { return { content: [ { type: 'text', text: `**❌ Erro ao buscar comunicação interna**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Comunicação ID:** ${communication_id}\n` + `**Código:** ${response.status}\n` + `**Mensagem:** ${response.error}\n\n` + `*Verifique se o ticket e a comunicação existem e se você tem permissão para visualizar.*` } ] }; } const communication = response.data; // Formatear texto da comunicação (remover HTML se presente) const communicationText = communication.text ? communication.text.replace(/<[^>]*>/g, '').trim() : 'Conteúdo não disponível'; return { content: [ { type: 'text', text: `**📋 Comunicação Interna #${communication.id}**\n\n` + `${communicationText}\n\n` + `*✅ Texto completo obtido da API TiFlux*` } ] }; } catch (error) { return { content: [ { type: 'text', text: `**❌ Erro interno ao buscar comunicação interna**\n\n` + `**Ticket:** #${ticket_number}\n` + `**Comunicação ID:** ${communication_id}\n` + `**Erro:** ${error.message}\n\n` + `*Verifique sua conexão e configurações da API.*` } ] }; } } } module.exports = InternalCommunicationsHandlers;