Benachrichtigungen¶
Übersicht¶
Die BO-Engine betreibt zwei Benachrichtigungskanäle:
| Kanal | Zweck | Technologie |
|---|---|---|
| Push-Notifications | Gewinner informieren | Web Push (VAPID) |
| Re-Engagement-Kampagnen | Resend API |
Push-Notifications¶
Winner-Notifications¶
Datei:
workers/notifyWinners.ts(98 Zeilen) Trigger: Nach Settlement (non-fatal)
Ablauf¶
// notifyWinners.ts:41-97
async function notifyWinners(dayId: number) {
// 1. Gewinner mit 4+ Treffern finden
const winners = await pg.query(`
SELECT DISTINCT player, MAX(matches) AS best_match
FROM tickets t
JOIN ticket_tips tp ON tp.ticket_id = t.ticket_id
WHERE day_id = $1 AND matches >= 4
GROUP BY player
`, [dayId]);
// 2. Sprache aus User-Profil
const user = await pg.query(
`SELECT preferred_language FROM users WHERE wallet_address = $1`,
[player]
);
// 3. Push senden
await sendPushToUser(player, getPayload(bestMatch, locale));
}
Notification-Texte¶
// notifyWinners.ts:13-26
const messages = {
en: {
4: { title: "You won!", body: "You matched 4 numbers!" },
5: { title: "Big Win!", body: "You matched 5 numbers!" },
6: { title: "JACKPOT!", body: "You matched ALL 6 numbers!" }
},
de: {
4: { title: "Gewonnen!", body: "Du hast 4 Richtige!" },
5: { title: "Großer Gewinn!", body: "Du hast 5 Richtige!" },
6: { title: "JACKPOT!", body: "Du hast ALLE 6 Richtige!" }
}
};
Deep Link: /{locale}/claim - direkt zur Auszahlungsseite.
Push-Service¶
Datei:
services/pushService.ts(73 Zeilen)
// pushService.ts:35-72
async function sendPushToUser(walletAddress: string, payload: object) {
// 1. Alle Subscriptions des Users laden
const subs = await pg.query(
`SELECT endpoint, p256dh, auth
FROM push_subscriptions
WHERE wallet_address = $1`,
[walletAddress]
);
// 2. Push an jedes Gerät senden
for (const sub of subs.rows) {
try {
await webpush.sendNotification(sub, JSON.stringify(payload));
} catch (e) {
if (e.statusCode === 410 || e.statusCode === 404) {
// Subscription abgelaufen → löschen
await pg.query(
`DELETE FROM push_subscriptions WHERE endpoint = $1`,
[sub.endpoint]
);
}
}
}
}
Konfiguration¶
| Variable | Beschreibung |
|---|---|
VAPID_PUBLIC_KEY |
VAPID Public Key |
VAPID_PRIVATE_KEY |
VAPID Private Key |
VAPID_SUBJECT |
mailto: oder URL |
Auto-Cleanup¶
Abgelaufene Subscriptions (HTTP 410/404) werden automatisch aus der push_subscriptions-Tabelle entfernt.
E-Mail-System¶
E-Mail-Service¶
Datei:
services/emailService.ts(67 Zeilen)
// emailService.ts:31-66
async function sendEmail(to, subject, html) {
const config = await getEmailConfig(pg);
const response = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${config.resend_api_key}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
from: config.resend_from_email ?? "noreply@chainbets.win",
to,
subject,
html
})
});
}
Konfiguration (bo_config)¶
| Key | Beschreibung | Default |
|---|---|---|
resend_api_key |
Resend API Key | - |
resend_from_email |
Absender-Adresse | noreply@chainbets.win |
E-Mail-Templates¶
Datei:
services/emailTemplates.ts(250 Zeilen)
// emailTemplates.ts:38-142
function buildReEngagementEmail(params) {
// Dark-Theme HTML-Template
// Lokalisiert (EN/DE)
// Platform-Stats integriert
// Mobile-optimiert (MSO conditional)
// "View in Browser" Link
}
Template-Features:
- Dark Theme (passend zur App)
- Plattform-Statistiken (7-Tage + Allzeit):
- Tickets gespielt
- Gewinne ausgezahlt
- Anzahl Gewinner
- Größter Gewinn
- Hot/Cold Numbers
- Bonus-Badge (wenn Bonus vergeben)
- CTA-Button zum Login
- "View in Browser"-Link mit Token
Telegram-Alerting (Watchdog)¶
Datei:
watchdog/alerting/telegram.ts(41 Zeilen)
Systemwarnungen werden via Telegram-Bot gesendet:
// telegram.ts:8-41
async function sendTelegramAlert(message: string) {
const url = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`;
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: CHAT_ID,
text: message,
parse_mode: "Markdown"
})
});
}
| Variable | Beschreibung |
|---|---|
WATCHDOG_TELEGRAM_BOT_TOKEN |
Bot-Token |
WATCHDOG_TELEGRAM_CHAT_ID |
Chat/Gruppe für Alerts |
Cooldown-System: Verhindert Alert-Spam (CRITICAL: 5min, HIGH: 30min, MEDIUM: 120min).