← back to Daily Reporting

slack-notifier.ts

178 lines

/**
 * Slack Notifier for Daily Reports
 *
 * Sends formatted reports to Slack channels
 */

import fetch from 'node-fetch';
import type { DailyReport } from './report-generator';
import { formatSlackReport, formatSlackBlocks } from './slack-formatter';

export interface SlackConfig {
  webhookUrl: string;
  channel?: string;
  username?: string;
  iconEmoji?: string;
  mentionChannelOnIssues?: boolean;
}

/**
 * Send report to Slack webhook
 */
export async function sendToSlack(
  report: DailyReport,
  config: SlackConfig
): Promise<boolean> {
  try {
    const mentionChannel = config.mentionChannelOnIssues && report.executiveSummary.criticalIssues > 0;

    // Format the message
    const text = formatSlackReport(report, mentionChannel);

    // Prepare payload
    const payload: any = {
      username: config.username || 'DW-Agents Reporter',
      icon_emoji: config.iconEmoji || (report.reportType === 'morning' ? ':sunrise:' : ':city_sunset:'),
      text: text
    };

    // Add channel if specified
    if (config.channel) {
      payload.channel = config.channel;
    }

    // Send to Slack
    const response = await fetch(config.webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      throw new Error(`Slack API error: ${response.status} ${response.statusText}`);
    }

    console.log(`✅ Successfully sent ${report.reportType} report to Slack`);
    return true;

  } catch (error) {
    console.error('❌ Failed to send report to Slack:', error);
    return false;
  }
}

/**
 * Send report with rich formatting (blocks)
 */
export async function sendToSlackWithBlocks(
  report: DailyReport,
  config: SlackConfig
): Promise<boolean> {
  try {
    const mentionChannel = config.mentionChannelOnIssues && report.executiveSummary.criticalIssues > 0;

    // Format blocks
    const blocks = formatSlackBlocks(report);
    const text = formatSlackReport(report, false); // Fallback text

    // Prepare payload
    const payload: any = {
      username: config.username || 'DW-Agents Reporter',
      icon_emoji: config.iconEmoji || (report.reportType === 'morning' ? ':sunrise:' : ':city_sunset:'),
      text: text, // Fallback
      blocks: blocks
    };

    // Add channel if specified
    if (config.channel) {
      payload.channel = config.channel;
    }

    // Add channel mention if needed
    if (mentionChannel) {
      payload.text = '<!channel> Critical issues detected!\n\n' + payload.text;
    }

    // Send to Slack
    const response = await fetch(config.webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      throw new Error(`Slack API error: ${response.status} ${response.statusText}`);
    }

    console.log(`✅ Successfully sent ${report.reportType} report with blocks to Slack`);
    return true;

  } catch (error) {
    console.error('❌ Failed to send report with blocks to Slack:', error);
    // Fallback to simple text
    return sendToSlack(report, config);
  }
}

/**
 * Test Slack connection
 */
export async function testSlackConnection(webhookUrl: string): Promise<boolean> {
  try {
    const response = await fetch(webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        text: '🧪 Test message from DW-Agents Daily Reporting System\n\nIf you see this, the webhook is configured correctly! ✅'
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    console.log('✅ Slack connection test successful');
    return true;

  } catch (error) {
    console.error('❌ Slack connection test failed:', error);
    return false;
  }
}

/**
 * Save report to file (backup)
 */
export async function saveReportToFile(report: DailyReport, directory: string = '/root/DW-Agents/reports'): Promise<string> {
  const fs = require('fs').promises;
  const path = require('path');

  try {
    // Ensure directory exists
    await fs.mkdir(directory, { recursive: true });

    // Generate filename
    const date = new Date(report.timestamp);
    const dateStr = date.toISOString().split('T')[0];
    const timeStr = date.toTimeString().split(' ')[0].replace(/:/g, '-');
    const filename = `${report.reportType}-report-${dateStr}-${timeStr}.json`;
    const filepath = path.join(directory, filename);

    // Save report
    await fs.writeFile(filepath, JSON.stringify(report, null, 2));

    console.log(`📁 Report saved to ${filepath}`);
    return filepath;

  } catch (error) {
    console.error('❌ Failed to save report to file:', error);
    throw error;
  }
}