// src/integrations/red/players.js import { fetchPlayersStatus } from './client.js'; import { logger } from '../../utils/logger.js'; /** * Get RED player status for one company + search term * @param {string} storeNum padded 4-digit store number (e.g. "02477") * @param {string} companyId * @returns {Promise} active players (Status === "A") */ export async function getREDPlayersForCompany(storeNum, companyId) { const params = { companyId, searchString: storeNum, searchColumn: 'Name', sortColumn: 'Name', sortDirection: 'ASC', exactMatch: false, includeInactive: true, // we filter active later }; try { const allPlayers = await fetchPlayersStatus(params); // Filter to active only (Status === "A") const active = allPlayers.filter(p => p.Status === 'A'); if (active.length > 0) { logger('red:players', `Found ${active.length} active players for company ${companyId}, store ${storeNum}`, 'debug'); } return active; } catch (err) { logger('red:players', `Failed for company ${companyId}, store ${storeNum}: ${err.message}`); return []; // soft fail → don't break entire multi-company request } } /** * Get all active RED players across multiple companies for a store * Uses Promise.allSettled so one company failing doesn't kill everything * @param {string|number} storeNumber e.g. "2477" or 2477 * @returns {Promise} combined active players from all companies */ export async function getREDStatusForStore(storeNumber) { const start = Date.now(); const storeNum = String(Number(storeNumber)).padStart(4, '0'); logger('red:status', `Collecting status for store ${storeNum}`, 'debug'); // Support comma-separated RED_COMPANY_IDS env var (primary) or fall back to empty const companyIDs = (process.env.RED_COMPANY_IDS || '') .split(',') .map(id => id.trim()) .filter(Boolean); if (companyIDs.length === 0) { logger('red:status', 'No company IDs configured'); return []; } const results = await Promise.allSettled( companyIDs.map(cid => getREDPlayersForCompany(storeNum, cid)) ); const allActivePlayers = []; results.forEach((result, index) => { const cid = companyIDs[index]; if (result.status === 'fulfilled') { allActivePlayers.push(...result.value); } else { logger('red:status', `Company ${cid} failed: ${result.reason?.message || result.reason}`); } }); logger( 'red:status', `Collected ${allActivePlayers.length} active RED players for store ${storeNum} (${Date.now() - start} ms)`, 'debug' ); return allActivePlayers; } /** * Format RED players into markdown text (for /deviceStatus or similar) * @param {Array} players * @param {string} storeNumber * @returns {string} markdown */ export function formatREDPlayersMarkdown(players, storeNumber) { if (players.length === 0) { return `No **active** RED devices found for store ${storeNumber}.\n`; } let md = `# RED Devices – Store ${storeNumber}\n\n`; players.forEach(player => { md += `### ${player.DeviceID || 'Unknown ID'}\n`; md += `- **Connectivity:** ${player.Connectivity || '—'}\n`; md += `- **Last Ping:** ${simpleTimeAgo(player.LastPingTimeUTC) || '—'}\n`; md += `- **Deployment:** ${player.DeploymentStatusName || '—'} | Transition: ${player.StateTransitionStatus || '—'}\n`; md += `\n---\n\n`; }); return md; } // Reuse your existing time helper (move to utils/time.js later) function simpleTimeAgo(isoString) { if (!isoString) return 'Never'; const date = new Date(isoString.endsWith('Z') ? isoString : isoString + 'Z'); if (isNaN(date.getTime())) return 'Invalid date'; const seconds = Math.floor((Date.now() - date) / 1000); // ... your existing logic for year/month/day/hour/min/sec ago // (copy-paste or import from utils) return `${seconds} seconds ago`; // placeholder }