Files
Ninjaserver/scripts/enhanced_player_notifications.js
2025-09-23 14:13:24 +02:00

286 lines
8.7 KiB
JavaScript

const { Pool } = require('pg');
const webpush = require('web-push');
// Database connection
const pool = new Pool({
user: process.env.DB_USER || 'postgres',
host: process.env.DB_HOST || 'localhost',
database: process.env.DB_NAME || 'ninjacross',
password: process.env.DB_PASSWORD || 'postgres',
port: process.env.DB_PORT || 5432,
});
// VAPID keys (same as push-service.js)
const vapidKeys = {
publicKey: 'BJmNVx0C3XeVxeKGTP9c-Z4HcuZNmdk6QdiLocZgCmb-miCS0ESFO3W2TvJlRhhNAShV63pWA5p36BTVSetyTds',
privateKey: 'HBdRCtmZUAzsWpVjZ2LDaoWliIPHldAb5ExAt8bvDeg'
};
webpush.setVapidDetails(
'mailto:admin@ninjacross.com',
vapidKeys.publicKey,
vapidKeys.privateKey
);
/**
* Send push notification to a specific player
*/
async function sendPushToPlayer(playerId, title, message, data = {}) {
try {
console.log(`📱 Sending push to player ${playerId}: ${title}`);
const result = await pool.query(`
SELECT endpoint, p256dh, auth
FROM player_subscriptions
WHERE player_id = $1
`, [playerId]);
if (result.rows.length === 0) {
console.log(`❌ No subscription found for player ${playerId}`);
return { success: false, reason: 'No subscription' };
}
// Send to all subscriptions for this player
const results = [];
for (const row of result.rows) {
const subscription = {
endpoint: row.endpoint,
keys: {
p256dh: row.p256dh,
auth: row.auth
}
};
const payload = JSON.stringify({
title: title,
body: message,
icon: '/pictures/favicon.ico',
badge: '/pictures/favicon.ico',
data: {
url: '/dashboard.html',
timestamp: Date.now(),
...data
},
actions: [
{
action: 'view',
title: 'Dashboard öffnen'
}
]
});
try {
await webpush.sendNotification(subscription, payload);
results.push({ success: true });
} catch (error) {
console.error(`❌ Error sending to subscription:`, error);
results.push({ success: false, error: error.message });
}
}
const successCount = results.filter(r => r.success).length;
console.log(`✅ Push notification sent to ${successCount}/${result.rows.length} subscriptions for player ${playerId}`);
return {
success: successCount > 0,
sent: successCount,
total: result.rows.length
};
} catch (error) {
console.error(`❌ Error sending push to player ${playerId}:`, error);
return { success: false, error: error.message };
}
}
/**
* Send achievement notification to player
*/
async function sendAchievementNotification(playerId, achievement) {
const title = `🏆 ${achievement.name}`;
const message = achievement.description;
return await sendPushToPlayer(playerId, title, message, {
type: 'achievement',
achievementId: achievement.id,
points: achievement.points
});
}
/**
* Send best time notification to player
*/
async function sendBestTimeNotification(playerId, timeType, locationName, time) {
const title = `🏁 ${timeType} Bestzeit!`;
const message = `Du hast die beste Zeit in ${locationName} mit ${time} erreicht!`;
return await sendPushToPlayer(playerId, title, message, {
type: 'best_time',
timeType: timeType,
location: locationName,
time: time
});
}
/**
* Send daily summary notification to player
*/
async function sendDailySummaryNotification(playerId, summary) {
const title = `📊 Dein Tagesrückblick`;
const message = `Du hattest ${summary.runs} Läufe heute. Beste Zeit: ${summary.bestTime}`;
return await sendPushToPlayer(playerId, title, message, {
type: 'daily_summary',
runs: summary.runs,
bestTime: summary.bestTime
});
}
/**
* Send weekly summary notification to player
*/
async function sendWeeklySummaryNotification(playerId, summary) {
const title = `📈 Deine Wochenzusammenfassung`;
const message = `Diese Woche: ${summary.runs} Läufe, ${summary.improvement}% Verbesserung!`;
return await sendPushToPlayer(playerId, title, message, {
type: 'weekly_summary',
runs: summary.runs,
improvement: summary.improvement
});
}
/**
* Send notification to all logged in players
*/
async function sendNotificationToAllPlayers(title, message, data = {}) {
try {
console.log(`📢 Sending notification to all players: ${title}`);
const result = await pool.query(`
SELECT ps.player_id, ps.endpoint, ps.p256dh, ps.auth, p.firstname, p.lastname
FROM player_subscriptions ps
JOIN players p ON ps.player_id = p.id
WHERE ps.player_id IS NOT NULL
`);
if (result.rows.length === 0) {
console.log('❌ No player subscriptions found');
return { success: false, reason: 'No subscriptions' };
}
const promises = result.rows.map(async (row) => {
const subscription = {
endpoint: row.endpoint,
keys: {
p256dh: row.p256dh,
auth: row.auth
}
};
const payload = JSON.stringify({
title: title,
body: message,
icon: '/pictures/favicon.ico',
badge: '/pictures/favicon.ico',
data: {
url: '/dashboard.html',
timestamp: Date.now(),
...data
}
});
try {
await webpush.sendNotification(subscription, payload);
return {
playerId: row.player_id,
playerName: `${row.firstname} ${row.lastname}`,
success: true
};
} catch (error) {
console.error(`❌ Error sending to player ${row.player_id}:`, error);
return {
playerId: row.player_id,
playerName: `${row.firstname} ${row.lastname}`,
success: false,
error: error.message
};
}
});
const results = await Promise.all(promises);
const successCount = results.filter(r => r.success).length;
console.log(`✅ Sent notifications to ${successCount}/${results.length} players`);
return {
success: true,
sent: successCount,
total: results.length,
results: results
};
} catch (error) {
console.error('❌ Error sending notifications to all players:', error);
return { success: false, error: error.message };
}
}
/**
* Get player statistics for notifications
*/
async function getPlayerStats(playerId) {
try {
const result = await pool.query(`
SELECT
p.firstname,
p.lastname,
COUNT(t.id) as total_runs,
MIN(t.recorded_time) as best_time,
COUNT(DISTINCT t.location_id) as locations_visited
FROM players p
LEFT JOIN times t ON p.id = t.player_id
WHERE p.id = $1
GROUP BY p.id, p.firstname, p.lastname
`, [playerId]);
if (result.rows.length === 0) {
return null;
}
return result.rows[0];
} catch (error) {
console.error('Error getting player stats:', error);
return null;
}
}
/**
* Check if player has push notifications enabled
*/
async function isPlayerPushEnabled(playerId) {
try {
const result = await pool.query(`
SELECT COUNT(*) as count
FROM player_subscriptions
WHERE player_id = $1
`, [playerId]);
return result.rows[0].count > 0;
} catch (error) {
console.error('Error checking push status:', error);
return false;
}
}
module.exports = {
sendPushToPlayer,
sendAchievementNotification,
sendBestTimeNotification,
sendDailySummaryNotification,
sendWeeklySummaryNotification,
sendNotificationToAllPlayers,
getPlayerStats,
isPlayerPushEnabled
};