button zum prüfen

This commit is contained in:
2025-11-17 21:37:22 +01:00
parent 8e308b0d99
commit cad72232d9
5 changed files with 477 additions and 129 deletions

View File

@@ -98,6 +98,16 @@ async function sendSlotNotification({ profileId, storeName, pickupDate, onlyNoti
});
}
function formatStoreWatchStatus(status) {
if (status === 1) {
return 'Suchend';
}
if (status === 0) {
return 'Nicht suchend';
}
return 'Status unbekannt';
}
async function sendStoreWatchNotification({ profileId, storeName, storeId, regionName }) {
const storeLink = storeId ? `https://foodsharing.de/store/${storeId}` : null;
const title = `Team sucht Verstärkung: ${storeName}`;
@@ -112,6 +122,33 @@ async function sendStoreWatchNotification({ profileId, storeName, storeId, regio
});
}
async function sendStoreWatchSummaryNotification({ profileId, entries = [], triggeredBy = 'manual' }) {
if (!profileId || !Array.isArray(entries) || entries.length === 0) {
return;
}
const lines = entries
.map((entry) => {
const regionSuffix = entry.regionName ? ` (${entry.regionName})` : '';
const statusLabel = formatStoreWatchStatus(entry.status);
const timestamp = entry.checkedAt ? ` Stand ${formatDateLabel(entry.checkedAt)}` : '';
const errorLabel = entry.error ? ` Fehler: ${entry.error}` : '';
return `${entry.storeName || `Store ${entry.storeId}`}${regionSuffix}: ${statusLabel}${timestamp}${errorLabel}`;
})
.join('\n');
const prefix =
triggeredBy === 'manual'
? 'Manuell angestoßene Store-Watch-Prüfung abgeschlossen:'
: 'Store-Watch-Prüfung abgeschlossen:';
const title =
triggeredBy === 'manual' ? 'Ad-hoc Store-Watch-Prüfung' : 'Store-Watch-Prüfung';
const message = `${prefix}\n${lines}`;
await notifyChannels(profileId, {
title,
message,
priority: 'default'
});
}
async function sendDesiredWindowMissedNotification({ profileId, storeName, desiredWindowLabel }) {
if (!profileId) {
return;
@@ -165,6 +202,7 @@ async function sendTestNotification(profileId, channel) {
module.exports = {
sendSlotNotification,
sendStoreWatchNotification,
sendStoreWatchSummaryNotification,
sendTestNotification,
sendDesiredWindowMissedNotification
};

View File

@@ -411,28 +411,30 @@ async function checkEntry(sessionId, entry, settings) {
}
}
async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS) {
async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, options = {}) {
const session = sessionStore.get(sessionId);
if (!session?.profile?.id) {
return;
return [];
}
const watchers = readStoreWatch(session.profile.id);
if (!Array.isArray(watchers) || watchers.length === 0) {
return;
return [];
}
const ready = await ensureSession(session);
if (!ready) {
return;
return [];
}
const perRequestDelay = Math.max(0, Number(settings?.storeWatchRequestDelayMs) || 0);
let changed = false;
const summary = [];
for (let index = 0; index < watchers.length; index += 1) {
const watcher = watchers[index];
try {
const details = await foodsharingClient.fetchStoreDetails(watcher.storeId, session.cookieHeader);
const status = details?.teamSearchStatus === 1 ? 1 : 0;
const checkedAt = Date.now();
if (status === 1 && watcher.lastTeamSearchStatus !== 1) {
await notificationService.sendStoreWatchNotification({
profileId: session.profile.id,
@@ -445,8 +447,25 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS) {
watcher.lastTeamSearchStatus = status;
changed = true;
}
watcher.lastStatusCheckAt = checkedAt;
changed = true;
summary.push({
storeId: watcher.storeId,
storeName: watcher.storeName,
regionName: watcher.regionName,
status,
checkedAt
});
} catch (error) {
console.error(`[WATCH] Prüfung für Store ${watcher.storeId} fehlgeschlagen:`, error.message);
summary.push({
storeId: watcher.storeId,
storeName: watcher.storeName,
regionName: watcher.regionName,
status: null,
checkedAt: Date.now(),
error: error.message || 'Unbekannter Fehler'
});
} finally {
const hasNext = index < watchers.length - 1;
if (hasNext && perRequestDelay > 0) {
@@ -458,6 +477,18 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS) {
if (changed) {
writeStoreWatch(session.profile.id, watchers);
}
if (options.sendSummary && summary.length > 0) {
try {
await notificationService.sendStoreWatchSummaryNotification({
profileId: session.profile.id,
entries: summary,
triggeredBy: options.triggeredBy || 'manual'
});
} catch (error) {
console.error('[WATCH] Zusammenfassung konnte nicht versendet werden:', error.message);
}
}
return summary;
}
function scheduleStoreWatchers(sessionId, settings) {
@@ -538,6 +569,12 @@ function scheduleConfig(sessionId, config, settings) {
);
}
async function runStoreWatchCheck(sessionId, settings, options = {}) {
const resolvedSettings = resolveSettings(settings);
return checkWatchedStores(sessionId, resolvedSettings, options);
}
module.exports = {
scheduleConfig
scheduleConfig,
runStoreWatchCheck
};

View File

@@ -17,6 +17,7 @@ function sanitizeEntry(entry) {
if (!entry || !entry.storeId) {
return null;
}
const parsedLastCheck = Number(entry.lastStatusCheckAt);
const normalized = {
storeId: String(entry.storeId),
storeName: entry.storeName ? String(entry.storeName).trim() : `Store ${entry.storeId}`,
@@ -27,7 +28,8 @@ function sanitizeEntry(entry) {
? 1
: entry.lastTeamSearchStatus === 0
? 0
: null
: null,
lastStatusCheckAt: Number.isFinite(parsedLastCheck) ? parsedLastCheck : null
};
if (!normalized.regionId) {
return null;