From 08a18e825cd86651befdd4271fc78b3a67fe0d6e Mon Sep 17 00:00:00 2001 From: Antigravity Date: Fri, 29 May 2026 00:11:43 -0400 Subject: [PATCH] feat: send weather to group + personal (+14085505485) simultaneously --- agents/k8s/weather-cronjob.yaml | 63 +++++++++++-------- .../app/api/send-whatsapp/route.js | 42 ++++++++++--- 2 files changed, 69 insertions(+), 36 deletions(-) diff --git a/agents/k8s/weather-cronjob.yaml b/agents/k8s/weather-cronjob.yaml index 3404799..56f8bd2 100644 --- a/agents/k8s/weather-cronjob.yaml +++ b/agents/k8s/weather-cronjob.yaml @@ -6,7 +6,6 @@ metadata: labels: app: weather-report spec: - # 7:00 AM ET = 11:00 UTC (EDT offset UTC-4) schedule: "0 11 * * *" timeZone: "America/New_York" # K8s 1.27+ supports timeZone natively concurrencyPolicy: Forbid @@ -30,6 +29,7 @@ spec: const ZIP = '16046'; const GATEWAY = 'http://whatsapp-gateway.ai-agents.svc.cluster.local:5001/send-message'; const GROUP = 'https://chat.whatsapp.com/0Txfg7iZBbvIuvTbTqqgAX'; + const PERSONAL = '+14085505485'; function weatherEmoji(desc) { const d = desc.toLowerCase(); @@ -119,32 +119,43 @@ spec: `_Powered by Agentic OS • hq.applaude.net_ 🏠`, ].filter(l => l !== null && l !== undefined).join('\n'); - const body = JSON.stringify({ to: GROUP, message: msg }); - const gwUrl = new URL(GATEWAY); - const opts = { - hostname: gwUrl.hostname, - port: parseInt(gwUrl.port) || 80, - path: gwUrl.pathname, - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } - }; - - const req = http.request(opts, (r) => { - let resp = ''; - r.on('data', d => resp += d); - r.on('end', () => { - const parsed = JSON.parse(resp); - if (parsed.success) { - console.log(`[WeatherBot] ✅ Weather report sent to group. MsgID: ${parsed.messageId}`); - } else { - console.error(`[WeatherBot] ❌ Gateway error: ${parsed.error}`); - process.exit(1); - } + // Helper: send msg to one recipient + function sendTo(to, label) { + return new Promise((resolve) => { + const body = JSON.stringify({ to, message: msg }); + const gwUrl = new URL(GATEWAY); + const opts = { + hostname: gwUrl.hostname, + port: parseInt(gwUrl.port) || 80, + path: gwUrl.pathname, + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } + }; + const req = http.request(opts, (r) => { + let resp = ''; + r.on('data', d => resp += d); + r.on('end', () => { + try { + const parsed = JSON.parse(resp); + if (parsed.success) { + console.log(`[WeatherBot] ✅ Sent to ${label}. MsgID: ${parsed.messageId}`); + } else { + console.error(`[WeatherBot] ❌ ${label} error: ${parsed.error}`); + } + } catch(e) { console.error(`[WeatherBot] ${label} parse error:`, e.message); } + resolve(); + }); + }); + req.on('error', e => { console.error(`[WeatherBot] ${label} request failed:`, e.message); resolve(); }); + req.write(body); + req.end(); }); - }); - req.on('error', e => { console.error('[WeatherBot] Request failed:', e.message); process.exit(1); }); - req.write(body); - req.end(); + } + + // Send to group then personal number + sendTo(GROUP, 'WhatsApp Group') + .then(() => sendTo(PERSONAL, 'Personal (+14085505485)')) + .catch(e => { console.error('[WeatherBot] Fatal:', e.message); process.exit(1); }); } catch (e) { console.error('[WeatherBot] Parse error:', e.message); diff --git a/interface/hq-dashboard/app/api/send-whatsapp/route.js b/interface/hq-dashboard/app/api/send-whatsapp/route.js index 590c733..6d73015 100644 --- a/interface/hq-dashboard/app/api/send-whatsapp/route.js +++ b/interface/hq-dashboard/app/api/send-whatsapp/route.js @@ -4,27 +4,49 @@ const GATEWAY = process.env.WHATSAPP_GATEWAY_URL || 'http://whatsapp-gateway.ai-agents.svc.cluster.local:5001/send-message'; const DEFAULT_GROUP = process.env.WHATSAPP_GROUP || 'https://chat.whatsapp.com/0Txfg7iZBbvIuvTbTqqgAX'; +const PERSONAL_NUMBER = process.env.WHATSAPP_PERSONAL || '+14085505485'; + +async function sendToRecipient(to, message) { + const res = await fetch(GATEWAY, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ to, message }), + }); + return res.json(); +} export async function POST(request) { try { - const { message, to } = await request.json(); + const { message, to, personalOnly } = await request.json(); if (!message) { return NextResponse.json({ error: 'message is required' }, { status: 400 }); } - const res = await fetch(GATEWAY, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ to: to || DEFAULT_GROUP, message }), - }); + // personalOnly=true → only me; default → group + me + const targets = personalOnly + ? [{ to: PERSONAL_NUMBER, label: 'personal' }] + : [ + { to: to || DEFAULT_GROUP, label: 'group' }, + { to: PERSONAL_NUMBER, label: 'personal' }, + ]; - const data = await res.json(); + const results = await Promise.allSettled( + targets.map(t => sendToRecipient(t.to, message)) + ); - if (!res.ok || !data.success) { - return NextResponse.json({ error: data.error || 'Gateway error' }, { status: 502 }); + const successful = results.filter(r => r.status === 'fulfilled' && r.value?.success); + const failed = results.filter(r => r.status !== 'fulfilled' || !r.value?.success); + + if (successful.length === 0) { + return NextResponse.json({ error: 'All recipients failed', details: failed }, { status: 502 }); } - return NextResponse.json({ success: true, messageId: data.messageId, recipient: data.recipient }); + return NextResponse.json({ + success: true, + sent: successful.length, + failed: failed.length, + messageId: successful[0]?.value?.messageId, + }); } catch (err) { return NextResponse.json({ error: err.message }, { status: 500 }); }