文件预览

email.js

查看 Trading Signals 技能包中的文件内容。

文件内容

scripts/lib/email.js

const https = require('https');
const fs = require('fs');

function sendEmail(to, subject, htmlBody) {
  const credentialsPath = process.env.HOME + '/.config/resend/credentials.json';
  
  if (!fs.existsSync(credentialsPath)) {
    return Promise.reject(new Error('Resend credentials not found'));
  }
  
  const credentials = JSON.parse(fs.readFileSync(credentialsPath, 'utf8'));
  const apiKey = credentials.api_key;

  const payload = JSON.stringify({
    from: 'noreply@resend.dev',
    to: [to],
    subject: subject,
    html: htmlBody
  });

  return new Promise((resolve, reject) => {
    const req = https.request({
      hostname: 'api.resend.com',
      path: '/emails',
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(payload)
      }
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          resolve(JSON.parse(data));
        } else {
          reject(new Error(`HTTP ${res.statusCode}: ${data}`));
        }
      });
    });
    req.on('error', reject);
    req.write(payload);
    req.end();
  });
}

function formatSignalsEmail(results, title = 'Trading Signals') {
  const now = new Date().toLocaleString('de-DE');
  
  let html = `<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; }
h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
h2 { color: #34495e; margin-top: 30px; }
table { border-collapse: collapse; width: 100%; margin: 15px 0; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #3498db; color: white; }
tr:nth-child(even) { background-color: #f2f2f2; }
.buy { color: #27ae60; font-weight: bold; }
.sell { color: #e74c3c; font-weight: bold; }
.strong { background-color: #fff3cd; }
.medium { background-color: #e8f5e9; }
.weak { background-color: #f3e5f5; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; color: #7f8c8d; font-size: 0.9em; }
</style>
</head>
<body>
<h1>📈 ${title}</h1>
<p><strong>Erstellt:</strong> ${now}</p>
`;

  // Summary
  const allSignals = results.flatMap(r => r.signals.map(s => ({ ...s, asset: r.name, symbol: r.symbol })));
  const buySignals = allSignals.filter(s => s.type === 'BUY');
  const sellSignals = allSignals.filter(s => s.type === 'SELL');
  
  html += '<h2>📊 Signal-Übersicht</h2>';
  html += `<p><span class="buy">🟢 Kauf-Signale: ${buySignals.length}</span> | <span class="sell">🔴 Verkauf-Signale: ${sellSignals.length}</span></p>`;
  
  if (allSignals.length > 0) {
    html += '<table><tr><th>Asset</th><th>Typ</th><th>Grund</th><th>Stärke</th></tr>';
    
    // Sort by strength: strong first
    const strengthOrder = { strong: 0, medium: 1, weak: 2 };
    allSignals.sort((a, b) => strengthOrder[a.strength] - strengthOrder[b.strength]);
    
    for (const signal of allSignals) {
      const typeClass = signal.type === 'BUY' ? 'buy' : 'sell';
      const strengthClass = signal.strength;
      html += `<tr class="${strengthClass}"><td>${signal.asset}</td><td class="${typeClass}">${signal.type}</td><td>${signal.reason}</td><td>${signal.strength}</td></tr>`;
    }
    html += '</table>';
  }
  
  // Detailed analysis
  html += '<h2>📉 Detaillierte Analyse</h2>';
  for (const result of results) {
    html += `<h3>${result.name} (${result.symbol})</h3>`;
    html += `<p><strong>Preis:</strong> ${result.price?.toFixed(2) || 'n/a'} (${result.change?.toFixed(2) || 0}%)</p>`;
    html += `<p><strong>RSI:</strong> ${result.rsi?.toFixed(1) || 'n/a'} | <strong>SMA20:</strong> ${result.sma20?.toFixed(2) || 'n/a'} | <strong>SMA50:</strong> ${result.sma50?.toFixed(2) || 'n/a'}</p>`;
    
    if (result.signals.length > 0) {
      html += '<ul>';
      for (const signal of result.signals) {
        const emoji = signal.type === 'BUY' ? '🟢' : '🔴';
        html += `<li>${emoji} <strong>${signal.type}</strong> - ${signal.reason} (${signal.strength})</li>`;
      }
      html += '</ul>';
    } else {
      html += '<p>✅ Keine klaren Signale</p>';
    }
  }
  
  html += `<div class="footer">
    <p>Report generiert: ${now}</p>
    <p>Escher – Trading Signal Assistant</p>
  </div>`;
  
  html += '</body></html>';
  return html;
}

module.exports = { sendEmail, formatSignalsEmail };