From 676629bd8d95852acefe13a42cefe53542ec16fb Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 9 Dec 2025 00:06:09 +0100 Subject: [PATCH] Enhance notification enrichment by recursively collecting character IDs and attaching character names --- backend/services/falukantService.js | 126 +++++++++++++++++++--------- 1 file changed, 87 insertions(+), 39 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 9ee4451..03136e1 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -4539,39 +4539,52 @@ async function enrichNotificationsWithCharacterNames(notifications) { const charIds = new Set(); + // recursive collector that extracts any character id fields + function collectIds(obj) { + if (!obj) return; + if (Array.isArray(obj)) { + for (const it of obj) collectIds(it); + return; + } + if (typeof obj !== 'object') return; + for (const [k, v] of Object.entries(obj)) { + if (!v) continue; + if (k === 'character_id' || k === 'characterId') { + charIds.add(Number(v)); + continue; + } + if (k === 'character' && typeof v === 'object') { + if (v.id) charIds.add(Number(v.id)); + if (v.character_id) charIds.add(Number(v.character_id)); + if (v.characterId) charIds.add(Number(v.characterId)); + collectIds(v); + continue; + } + collectIds(v); + } + } + + // First pass: collect all referenced character ids from notifications for (const n of notifications) { - // Notification.tr may be either a translation key or a JSON string with params - let parsed = null; + // parse n.tr if it's JSON try { - parsed = typeof n.tr === 'string' && n.tr.trim().startsWith('{') ? JSON.parse(n.tr) : null; - } catch (err) { - parsed = null; - } + if (typeof n.tr === 'string' && n.tr.trim().startsWith('{')) { + const parsed = JSON.parse(n.tr); + collectIds(parsed); + } + } catch (err) { /* ignore */ } - // If parsed JSON contains character_id or characterId or character, collect it - if (parsed) { - if (parsed.character_id) charIds.add(parsed.character_id); - if (parsed.characterId) charIds.add(parsed.characterId); - if (parsed.character && typeof parsed.character === 'object' && parsed.character.id) charIds.add(parsed.character.id); - } - - // Also check effects or other fields on the notification row + // parse n.effects if present try { if (n.effects) { const eff = typeof n.effects === 'string' && n.effects.trim().startsWith('{') ? JSON.parse(n.effects) : n.effects; - if (Array.isArray(eff)) { - for (const e of eff) { - if (e.character_id) charIds.add(e.character_id); - if (e.characterId) charIds.add(e.characterId); - } - } else if (eff && typeof eff === 'object') { - if (eff.character_id) charIds.add(eff.character_id); - if (eff.characterId) charIds.add(eff.characterId); - } + collectIds(eff); } - } catch (err) { - // ignore parse errors - } + } catch (err) { /* ignore */ } + + // top-level fields + if (n.character_id) charIds.add(Number(n.character_id)); + if (n.characterId) charIds.add(Number(n.characterId)); } const ids = Array.from(charIds).filter(Boolean); @@ -4592,23 +4605,58 @@ async function enrichNotificationsWithCharacterNames(notifications) { const first = c.definedFirstName?.name || ''; const last = c.definedLastName?.name || ''; const display = `${first} ${last}`.trim() || null; - nameMap.set(c.id, display || `#${c.id}`); + nameMap.set(Number(c.id), display || `#${c.id}`); } - // Attach resolved name to notifications - for (const n of notifications) { - let parsed = null; - try { parsed = typeof n.tr === 'string' && n.tr.trim().startsWith('{') ? JSON.parse(n.tr) : null; } catch (e) { parsed = null; } - let foundId = null; - if (parsed) { - foundId = parsed.character_id || parsed.characterId || (parsed.character && parsed.character.id) || null; + // helper to find first character id in an object + function findFirstId(obj) { + if (!obj) return null; + if (Array.isArray(obj)) { + for (const it of obj) { + const r = findFirstId(it); + if (r) return r; + } + return null; } - if (!foundId && n.character_id) foundId = n.character_id; - if (!foundId && n.characterId) foundId = n.characterId; + if (typeof obj !== 'object') return null; + for (const [k, v] of Object.entries(obj)) { + if (!v) continue; + if (k === 'character_id' || k === 'characterId') return Number(v); + if (k === 'character' && typeof v === 'object') { + if (v.id) return Number(v.id); + const r = findFirstId(v); + if (r) return r; + } + const r = findFirstId(v); + if (r) return r; + } + return null; + } - if (foundId && nameMap.has(foundId)) { - // attach property used by frontend - n.characterName = nameMap.get(foundId); + // Attach resolved name to notifications (set both characterName and character_name) + for (const n of notifications) { + let foundId = null; + try { + if (typeof n.tr === 'string' && n.tr.trim().startsWith('{')) { + const parsed = JSON.parse(n.tr); + foundId = findFirstId(parsed) || foundId; + } + } catch (err) { /* ignore */ } + + try { + if (n.effects) { + const eff = typeof n.effects === 'string' && n.effects.trim().startsWith('{') ? JSON.parse(n.effects) : n.effects; + foundId = findFirstId(eff) || foundId; + } + } catch (err) { /* ignore */ } + + if (!foundId && n.character_id) foundId = Number(n.character_id); + if (!foundId && n.characterId) foundId = Number(n.characterId); + + if (foundId && nameMap.has(Number(foundId))) { + const resolved = nameMap.get(Number(foundId)); + n.characterName = resolved; + n.character_name = resolved; } } }