// src/commands/jiraHistory.js import { getJiraTicketsForStore, getJiraTicketsForComponentWithStore, getStatusEmoji, calculateDaysOpen } from '../services/jiraService.js'; import { summarizeJiraTicket } from '../services/jiraSummarizer.js'; import { analyzeCommonIssues } from '../services/jiraSummarizer.js'; import jira from '../integrations/jira/JiraClient.js'; import { formatDisplayTime } from '../utils/time.js'; import { logger } from '../utils/logger.js'; const MAX_TICKETS = 20; export async function handleJiraHistory(bot, trigger) { logger('jira:history', 'Handler entered', 'debug'); // Support both Webex (args) and HTTP (query) const query = trigger.query || {}; const args = trigger.args || []; const arg1 = args[0]?.trim() || query.storeNum || query.store || query.s; const arg2 = args[1]?.trim() || query.component || query.components; logger('jira:history', `Request for store/component: ${arg1} | ${arg2 || 'all'}`); if (!arg1) { const usage = '**Jira History Usage:**\n' + '`/jiraHistory 782` → All tickets (max 20)\n' + '`/jiraHistory 782 phone` → Communication Services only\n' + '`/jiraHistory 782 av,voice,mobility` → Multiple components'; await bot.say('markdown', usage); return; } if (!/^\d{2,4}$/.test(arg1)) { await bot.say('markdown', 'Please provide a valid 2-4 digit store number.'); return; } const storeNum = arg1.padStart(5, '0'); // normalize to 5 digits if needed try { let reply = `**Jira Tickets - Store ${storeNum}**\n\n`; let ticketKeys = []; if (!arg2 || arg2.toLowerCase() === 'all') { logger('jira:history', `Fetching all tickets for store ${storeNum}`, 'debug'); const results = await getJiraTicketsForStore(storeNum); ticketKeys = results.map(t => t.key); } else { const components = arg2.split(',').map(c => c.trim().toLowerCase()); logger('jira:history', `Fetching tickets for components: ${components.join(', ')}`, 'debug'); for (const comp of components) { const results = await getJiraTicketsForComponentWithStore(storeNum, comp); ticketKeys = ticketKeys.concat(results.map(t => t.key)); } } ticketKeys = [...new Set(ticketKeys)]; // dedup across components if any // Limit to most recent if (ticketKeys.length > MAX_TICKETS) { ticketKeys = ticketKeys.slice(0, MAX_TICKETS); } if (ticketKeys.length === 0) { reply += 'No matching Jira tickets found.\n'; } else { logger('jira:history', `Fetching details for ${ticketKeys.length} tickets in parallel`, 'debug'); const ticketPromises = ticketKeys.map(async (key) => { try { const fullTicket = await jira.getTicket(key); const aiSummary = await summarizeJiraTicket(fullTicket).catch(() => "**Reported Problem:** No details available.\n**Steps Taken:** No information recorded." ); const fields = fullTicket.fields || {}; const statusEmoji = getStatusEmoji(fields.status?.name); const component = fields.components?.[0]?.name || '—'; const assignee = fields.assignee?.displayName || '**Unassigned**'; const days = calculateDaysOpen(fields.created, fields.resolved || fields.updated); return { key, summary: fields.summary || '—', statusEmoji, status: fields.status?.name || '—', component, assignee, days, aiSummary: aiSummary.substring(0, 800) // Limit summary length }; } catch (err) { logger('jira:history', `Failed to process ticket ${key}: ${err.message}`, 'warn'); return null; } }); const ticketResults = (await Promise.all(ticketPromises)).filter(Boolean); // Build reply with length control let currentLength = reply.length; for (const t of ticketResults) { let ticketBlock = `**${t.key}** - ${t.summary}\n\n`; ticketBlock += `${t.aiSummary}\n\n`; ticketBlock += `${t.statusEmoji} **Status:** ${t.status} • Component: ${t.component}\n`; ticketBlock += `Assigned: ${t.assignee} • Open for: ${t.days} days\n\n`; ticketBlock += `---\n\n`; // Check if adding this ticket would exceed safe limit if (currentLength + ticketBlock.length > 6500) { reply += `\n**... and ${ticketResults.length - ticketResults.indexOf(t)} more tickets.**\n`; reply += `Use /jiraHistory ${storeNum} for full list.\n`; break; } reply += ticketBlock; currentLength += ticketBlock.length; } // Common Issues Analysis (only if we have enough tickets and space) if (ticketResults.length >= 3 && currentLength < 6000) { reply += `**Common Issues Across These Tickets:**\n`; const common = await analyzeCommonIssues(ticketResults).catch(() => "Unable to identify common patterns at this time." ); reply += `${common}\n`; } } reply += `\n*Last checked: ${formatDisplayTime()}*`; await bot.say('markdown', reply.trim()); } catch (err) { logger('jira:history', `Error processing jiraHistory: ${err.message}`, 'error'); await bot.say('markdown', `❌ Error retrieving Jira history: ${err.message}`); } } // (helpers imported from jiraService for consistency with jiraTicket etc.)