feat: notification bridge + webhook system
- New: android-notification-listener/ — Kotlin app vangt WhatsApp-notificaties - New: POST /webhooks/whatsapp/incoming — ontvangt berichten van de Android-app - New: POST /webhooks/subscribe + GET /webhooks/subscribers — fan-out - Updated: docker-compose.yml met WEBHOOK_SHARED_SECRET - Updated: README met volledige documentatie
This commit is contained in:
parent
ccd9f0ddd0
commit
5b7bc23648
1 changed files with 556 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ const { remote } = require('webdriverio');
|
|||
const APPIUM_HOST = process.env.APPIUM_HOST || '127.0.0.1';
|
||||
const APPIUM_PORT = parseInt(process.env.APPIUM_PORT || '4723', 10);
|
||||
const PORT = parseInt(process.env.PORT || '3010', 10);
|
||||
const WEBHOOK_SECRET = process.env.WEBHOOK_SHARED_SECRET || 'dev-secret-change-me';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
|
|
@ -245,7 +246,60 @@ app.post('/client/sendMessage/:session', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// ── Webhook subscriber store (in-memory) ─────────────────────
|
||||
|
||||
const subscribers = [];
|
||||
|
||||
function fanout(event) {
|
||||
for (const sub of subscribers) {
|
||||
fetch(sub.url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(event),
|
||||
}).then((r) => {
|
||||
console.log(` webhook fanout to ${sub.url}: ${r.status}`);
|
||||
}).catch((e) => {
|
||||
console.log(` webhook fanout to ${sub.url} failed: ${e.message}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Webhook endpoints ─────────────────────────────────────────
|
||||
|
||||
app.post('/webhooks/whatsapp/incoming', (req, res) => {
|
||||
const secret = req.headers['x-webhook-secret'];
|
||||
if (!secret || secret !== WEBHOOK_SECRET) {
|
||||
return res.status(401).json({ error: 'invalid or missing webhook secret' });
|
||||
}
|
||||
|
||||
const { sender, text, chatId, timestamp } = req.body;
|
||||
if (!sender || !text || !chatId) {
|
||||
return res.status(400).json({ error: 'sender, text, and chatId are required' });
|
||||
}
|
||||
|
||||
const event = { sender, text, chatId, timestamp: timestamp || Date.now(), receivedAt: new Date().toISOString() };
|
||||
fanout(event);
|
||||
res.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
app.post('/webhooks/subscribe', (req, res) => {
|
||||
const { url } = req.body;
|
||||
if (!url) return res.status(400).json({ error: 'url required' });
|
||||
|
||||
if (!subscribers.find((s) => s.url === url)) {
|
||||
subscribers.push({ url, createdAt: new Date().toISOString() });
|
||||
}
|
||||
res.json({ status: 'subscribed', url, activeSubscribers: subscribers.length });
|
||||
});
|
||||
|
||||
app.get('/webhooks/subscribers', (_req, res) => {
|
||||
res.json({ subscribers: subscribers.map((s) => ({ url: s.url, createdAt: s.createdAt })) });
|
||||
});
|
||||
|
||||
// ── Server start ─────────────────────────────────────────────
|
||||
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`wa-appium-api listening on http://0.0.0.0:${PORT}`);
|
||||
console.log(` Appium server: ${APPIUM_HOST}:${APPIUM_PORT}`);
|
||||
console.log(` Webhook secret: ${WEBHOOK_SECRET === 'dev-secret-change-me' ? 'WARNING: using dev default' : 'configured'}`);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue