From d76dcfa69d09bdfbebf430de89eb9f8ba7b6029f Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Sun, 8 Dec 2024 14:39:29 +0100 Subject: [PATCH 1/6] Fix JSON Parsing Error for Ladder Top Retrieval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request addresses a recurring JSON parsing error (“Unexpected token <”) when fetching ladder top data. The code now validates the response before parsing, gracefully handles non-JSON responses, and prevents crashes by returning null if the data is invalid. This ensures that unexpected server responses (such as HTML errors) no longer cause runtime exceptions. --- server/chat-plugins/seasons.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index 6e79e7fe89fb..ed23e9bb1fc5 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -143,14 +143,20 @@ export function generateFormatSchedule() { } export async function getLadderTop(format: string) { - try { - const results = await Net(`https://${Config.routes.root}/ladder/?format=${toID(format)}&json`).get(); - const reply = JSON.parse(results); - return reply.toplist; - } catch (e) { - Monitor.crashlog(e, "A season ladder request"); - return null; - } + try { + const results = await Net(`https://${Config.routes.root}/ladder/?format=${toID(format)}&json`).get(); + let reply; + try { + reply = JSON.parse(results); + } catch (parseError) { + Monitor.crashlog(parseError, "Invalid JSON response from ladder request"); + return null; + } + return reply.toplist; + } catch (e) { + Monitor.crashlog(e, "A season ladder request"); + return null; + } } export async function updateBadgeholders() { From 7e6f4483d320b469ba2e22874ee4a686efc5ce9a Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Sun, 8 Dec 2024 14:47:18 +0100 Subject: [PATCH 2/6] fix: correct indentation to comply with @typescript-eslint/indent rule - Replaced spaces with tabs in server/chat-plugins/seasons.ts. - Resolved eslint errors related to indentation. --- server/chat-plugins/seasons.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index ed23e9bb1fc5..1a4bdf4a7a6d 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -143,20 +143,20 @@ export function generateFormatSchedule() { } export async function getLadderTop(format: string) { - try { - const results = await Net(`https://${Config.routes.root}/ladder/?format=${toID(format)}&json`).get(); - let reply; - try { - reply = JSON.parse(results); - } catch (parseError) { - Monitor.crashlog(parseError, "Invalid JSON response from ladder request"); - return null; - } - return reply.toplist; - } catch (e) { - Monitor.crashlog(e, "A season ladder request"); - return null; - } + try { + const results = await Net(`https://${Config.routes.root}/ladder/?format=${toID(format)}&json`).get(); + let reply; + try { + reply = JSON.parse(results); + } catch (parseError) { + Monitor.crashlog(parseError, "Invalid JSON response from ladder request"); + return null; + } + return reply.toplist; + } catch (e) { + Monitor.crashlog(e, "A season ladder request"); + return null; + } } export async function updateBadgeholders() { From 487be05c6552564104a16daa3dc480eb0cbcd784 Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Sun, 8 Dec 2024 20:49:54 +0100 Subject: [PATCH 3/6] Add safety checks for toplist array and user IDs in updateBadgeholders --- server/chat-plugins/seasons.ts | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index 1a4bdf4a7a6d..6592be24cc95 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -152,6 +152,12 @@ export async function getLadderTop(format: string) { Monitor.crashlog(parseError, "Invalid JSON response from ladder request"); return null; } + + // Check that toplist is defined and is an array + if (!reply || !Array.isArray(reply.toplist)) { + Monitor.crashlog(new Error(`Invalid toplist format: ${JSON.stringify(reply)}`), "Ladder toplist read error"); + return null; + } return reply.toplist; } catch (e) { Monitor.crashlog(e, "A season ladder request"); @@ -165,22 +171,42 @@ export async function updateBadgeholders() { if (!data.badgeholders[period]) { data.badgeholders[period] = {}; } - for (const formatName of data.formatSchedule[findPeriod()]) { + + const currentPeriod = findPeriod(); + const scheduledFormats = data.formatSchedule[currentPeriod]; + if (!scheduledFormats) { + Monitor.crashlog(new Error(`No format schedule found for period ${currentPeriod}`), "Season badge update"); + return; + } + + for (const formatName of scheduledFormats) { const formatid = `gen${Dex.gen}${formatName}`; const response = await getLadderTop(formatid); - if (!response) continue; // ?? + + // Ensure response is an array + if (!response || !Array.isArray(response)) { + continue; + } + const newHolders: Record = {}; for (const [i, row] of response.entries()) { - let badgeType = null; + // Ensure row.userid is safe and a string + const userid = Utils.getString(row.userid); + if (!userid) { + continue; // skip if userid isn't readable + } + + let badgeType: string | null = null; for (const type in BADGE_THRESHOLDS) { if ((i + 1) <= BADGE_THRESHOLDS[type]) { badgeType = type; break; } } + if (!badgeType) break; if (!newHolders[badgeType]) newHolders[badgeType] = []; - newHolders[badgeType].push(row.userid); + newHolders[badgeType].push(userid); } data.badgeholders[period][formatid] = newHolders; } From 5b3db6cfc1227b713f41dbfc7d5f22fbfc6990d2 Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Sun, 8 Dec 2024 20:52:18 +0100 Subject: [PATCH 4/6] fix: trailing space --- server/chat-plugins/seasons.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index 6592be24cc95..1ab2a83294b0 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -171,7 +171,7 @@ export async function updateBadgeholders() { if (!data.badgeholders[period]) { data.badgeholders[period] = {}; } - + const currentPeriod = findPeriod(); const scheduledFormats = data.formatSchedule[currentPeriod]; if (!scheduledFormats) { @@ -182,7 +182,7 @@ export async function updateBadgeholders() { for (const formatName of scheduledFormats) { const formatid = `gen${Dex.gen}${formatName}`; const response = await getLadderTop(formatid); - + // Ensure response is an array if (!response || !Array.isArray(response)) { continue; From becd43e8b8f2bfad72188cb5df053e69d62451fb Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Mon, 9 Dec 2024 08:58:58 +0100 Subject: [PATCH 5/6] Improve user ID validation --- server/chat-plugins/seasons.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index 1ab2a83294b0..1286eab0eda3 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -190,9 +190,11 @@ export async function updateBadgeholders() { const newHolders: Record = {}; for (const [i, row] of response.entries()) { - // Ensure row.userid is safe and a string - const userid = Utils.getString(row.userid); - if (!userid) { + if (!row) { + continue; + } + const userid = toID(row.userid); + if (!userid || userid.length > 18) { continue; // skip if userid isn't readable } From 8bed2e935966f935a868d09e71adeefc64a8d9ed Mon Sep 17 00:00:00 2001 From: Dieter Reinert Date: Tue, 17 Dec 2024 08:35:38 +0100 Subject: [PATCH 6/6] Remove unnecessary response type check Co-authored-by: Mia <49593536+mia-pi-git@users.noreply.github.com> --- server/chat-plugins/seasons.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/chat-plugins/seasons.ts b/server/chat-plugins/seasons.ts index 1286eab0eda3..8744f156b2fa 100644 --- a/server/chat-plugins/seasons.ts +++ b/server/chat-plugins/seasons.ts @@ -184,9 +184,6 @@ export async function updateBadgeholders() { const response = await getLadderTop(formatid); // Ensure response is an array - if (!response || !Array.isArray(response)) { - continue; - } const newHolders: Record = {}; for (const [i, row] of response.entries()) {