From 0111933d926d451833c4dd92e5e046542f3673ff Mon Sep 17 00:00:00 2001 From: bootunloader Date: Mon, 5 Jan 2026 22:05:48 +0200 Subject: [PATCH] SHIT WORKS --- data_raw_getData_2026-01-03_8.json | 5 - .../server/external/api.scraping.helpers.ts | 62 +- .../postdata/postdata.gen.controller.ts | 775 ++++++++---------- 3 files changed, 398 insertions(+), 444 deletions(-) delete mode 100644 data_raw_getData_2026-01-03_8.json diff --git a/data_raw_getData_2026-01-03_8.json b/data_raw_getData_2026-01-03_8.json deleted file mode 100644 index 45d059f..0000000 --- a/data_raw_getData_2026-01-03_8.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "error": "Error with your request, please try again (you will not be charged for this request).You should: 1) check that your URL is correctly encoded 2) try with render_js=True (5 credits per request) 3) try with premium_proxy=True see documentation: https://www.scrapingbee.com/documentation#premium_proxy (10-25 credits per request) 4) try with stealth_proxy=True see documentation: https://www.scrapingbee.com/documentation#stealth_proxy (75 credits per request)Do not hesitate to check our troubleshooting guide:https://www.scrapingbee.com/help", - "reason": "Server responded with 500", - "help": "('Received response with content-encoding: br, but failed to decode it.', error("brotli: decoder process called with data when 'can_accept_more_data()' is False"))" -} \ No newline at end of file diff --git a/src/lib/server/external/api.scraping.helpers.ts b/src/lib/server/external/api.scraping.helpers.ts index 9f41570..cb78555 100755 --- a/src/lib/server/external/api.scraping.helpers.ts +++ b/src/lib/server/external/api.scraping.helpers.ts @@ -6,8 +6,8 @@ import { baseDistributorId, constants } from "$lib/utils/constants"; import type { BookingEntry, Draw, LooseApiUser } from "$lib/utils/data.types"; import { rng } from "$lib/utils/rng"; import fs from "fs"; -import { HttpProxyAgent } from "http-proxy-agent"; import fetch from "node-fetch"; +import { tryCatch } from "../result"; // function dumpDistributors(distributors: LooseApiUser[]) { // fs.writeFileSync("distributors.json", JSON.stringify(distributors, null, 2)); @@ -150,8 +150,10 @@ export async function getUsersBalance(userId: number, jwt: string) { scrapingbeeUrl.searchParams.set("forward_headers", "true"); scrapingbeeUrl.searchParams.set("forward_headers_pure", "true"); + const encodedJwt = encodeURIComponent(jwt); + const res = await fetch(scrapingbeeUrl.toString(), { - headers: { "Spb-Authorization": jwt, ...forwardHeaders }, + headers: { "Spb-Authorization": jwt, "Spb-Cookie": `AuthorizationToken=${encodedJwt}` }, }); const rj = (await res.json()) as { @@ -161,6 +163,7 @@ export async function getUsersBalance(userId: number, jwt: string) { data: { allowedBalance: number; balance: number }; time: string; }; + logger.debug(`[getUsersBalance] Response for ${userId}: ${JSON.stringify(rj)}`); if (res.status !== 200 || rj.code !== 200 || !rj.success) { logger.warn(`[getUsersBalance] Error getting balance for ${userId}: ${rj.message}`); return false; @@ -372,27 +375,31 @@ export const getData = async ( drawId: number, chosenDate: string, ) => { - const scraperApiKey = env.SCRAPERAPI_API_KEY ?? ""; + if (!jwt || jwt.length < 1) { + logger.warn(`[getData] JWT is invalid : ${jwt}`); + return { ok: false, message: "JWT is invalid", data: [] }; + } + const scrapeDoApiKey = env.SCRAPEDO_API_KEY ?? ""; const targetUrl = `${constants.SCRAP_API_URL}/v1/book/list2`; logger.info( `[getData] Fetching draw data from API for ${chosenDate} ${drawId} for ${userIds.length} users`, ); - const proxyUsername = "scraperapi.keep_headers=true"; + const jwtUrlEncoded = encodeURIComponent(jwt); + const finalUrl = new URL("http://api.scrape.do/"); + finalUrl.searchParams.append("url", targetUrl); + finalUrl.searchParams.append("token", scrapeDoApiKey); + finalUrl.searchParams.append("extraHeaders", "true"); - // Configure HTTP proxy agent - const proxyAgent = new HttpProxyAgent( - `http://${proxyUsername}:${scraperApiKey}@proxy-server.scraperapi.com:8001`, - ); + logger.debug(`[getData] Using proxy to fetch data from : ${finalUrl.toString()}`); - logger.debug(`[getData] Using proxy to fetch data from : ${targetUrl}`); - - const res = await fetch(targetUrl, { + const res = await fetch(finalUrl.toString(), { method: "POST", headers: { - "Content-Type": "application/json", - Authorization: jwt, + "sd-Content-Type": "application/json", + "sd-Authorization": jwt, + "sd-Cookie": `AuthorizationToken=${jwtUrlEncoded}`, }, body: JSON.stringify({ userType: 3, @@ -404,7 +411,6 @@ export const getData = async ( containImported: false, keyword: "", }), - agent: proxyAgent, }); type J = { @@ -414,21 +420,21 @@ export const getData = async ( data: { book: BookingEntry; user: any }[]; }; - res.headers.forEach((value: string, key: string) => { - logger.debug(`[getData] response headers - ${key}: ${value}`); - }); - - if (res.headers.get("content-type") !== "application/json") { - logger.warn(`[getData] Error: Content type is not application/json`); - const rt = await res.text(); - logger.debug(`[getData] Response: ${rt}`); - return { - ok: false, - message: `Content type is not application/json`, - data: [], - }; + logger.info(`[getData] headers : ${JSON.stringify(res.headers)}`); + if (res.status !== 200 || !res.ok) { + logger.warn(`[getData] Error: ${JSON.stringify(res.status)}`); + const resTxt = await res.text(); + logger.debug(`[getData] Response text: ${resTxt}`); + return { ok: false, message: `Status code ${res.status}`, data: [] }; } - let decoded = (await res.json()) as { data: J }; + + const result = await tryCatch(res.json()); + if (result.error) { + logger.error(`[getData] Error parsing response : ${JSON.stringify(result.error)}`); + return { ok: false, message: result.error.message, data: [] }; + } + + let decoded = result.data as { data: J }; dumpDataRaw(decoded, `getData_${chosenDate}_${drawId}`); diff --git a/src/lib/server/postdata/postdata.gen.controller.ts b/src/lib/server/postdata/postdata.gen.controller.ts index 0da11b3..2f5be71 100644 --- a/src/lib/server/postdata/postdata.gen.controller.ts +++ b/src/lib/server/postdata/postdata.gen.controller.ts @@ -1,463 +1,416 @@ import { dbApiPostData } from "$lib/server/db/apipostdata.db"; import { dbApiUser } from "$lib/server/db/apiuser.db"; +import { dbPresetData } from "$lib/server/db/presetdata.db"; +import { getUsersBalance } from "$lib/server/external/api.scraping.helpers"; import { getReducedFinalSheet } from "$lib/server/finalsheet.helpers"; import { - adjustRatesIfDuplicatesFound, - pairRatesWithNumbers, - removeNumbersWithRepeatingDigits, - splitRatesIntoSmallerForRowsWithLargerRates, - spreadRatesForNumbersBetweenUsers, + adjustRatesIfDuplicatesFound, + pairRatesWithNumbers, + removeNumbersWithRepeatingDigits, + splitRatesIntoSmallerForRowsWithLargerRates, + spreadRatesForNumbersBetweenUsers, } from "$lib/server/postdata/postdata.gen.helpers"; import { getAllSessions } from "$lib/server/utils/session.service"; import { getDefaultTotals, getULID } from "$lib/utils"; import type { - ApiPostUser, - ApiPostUserWithParent, - PresetDataEntry, - PostDataEntry, - PostDataFilters, - PostDataHistoryFilters, - ReducedFinalSheetData, - ReducedFinalSheetRow, - ServerError, + ApiPostUser, + ApiPostUserWithParent, + PostDataEntry, + PostDataFilters, + PostDataHistoryFilters, + PresetDataEntry, + ReducedFinalSheetData, + ReducedFinalSheetRow, + ServerError, } from "$lib/utils/data.types"; -import { dbPresetData } from "$lib/server/db/presetdata.db"; -import { getUsersBalance } from "$lib/server/external/api.scraping.helpers"; +import { logger } from "../logger"; function filterMatching( - data: ReducedFinalSheetRow[], - min: number, - max: number, - sheetType: "first" | "second", + data: ReducedFinalSheetRow[], + min: number, + max: number, + sheetType: "first" | "second", ) { - let abNums = new Set(); - let abcNums = new Set(); + let abNums = new Set(); + let abcNums = new Set(); - for (const row of data) { - if (row.prize[sheetType] >= min && row.prize[sheetType] <= max) { - abNums.add(`${row.number[0]}${row.number[1]}`); - abcNums.add(`${row.number[0]}${row.number[1]}${row.number[2]}`); - } + for (const row of data) { + if (row.prize[sheetType] >= min && row.prize[sheetType] <= max) { + abNums.add(`${row.number[0]}${row.number[1]}`); + abcNums.add(`${row.number[0]}${row.number[1]}${row.number[2]}`); } - return { abNums, abcNums }; + } + return { abNums, abcNums }; } export async function updateBalanceOfPostUsers(users: ApiPostUserWithParent[]) { - const sessions = await getAllSessions(); - console.log(sessions); - console.log(`Fetching balances`); - const balances = [] as { id: string; balance: number }[]; - for (const user of users) { - console.log(`Finding ${user.id} in sessions`); - const session = sessions.find((e) => e.value.userId.includes(user.id)); - const jwt = session?.value.sessionToken; - if (!jwt) { - return { - ok: false, - detail: `Session not found for user ${user.userId}`, - }; - } - const uid = session.value.userId.toString(); - console.log(`Found ${uid} in session`); - const out = await getUsersBalance(+uid.split(":")[1], jwt); - if (!out) { - return { - ok: false, - detail: `Error fetching balance for user ${user.userName}`, - }; - } - balances.push({ id: uid, balance: out }); + const sessions = await getAllSessions(); + logger.debug(sessions); + logger.info(`Fetching balances`); + const balances = [] as { id: string; balance: number }[]; + for (const user of users) { + logger.info(`Finding ${user.id} in sessions`); + const session = sessions.find((e) => e.value.userId.includes(user.id)); + const jwt = session?.value.sessionToken; + if (!jwt) { + return { + ok: false, + detail: `Session not found for user ${user.userId}`, + }; } - console.log(`Updating balances`); - console.log(balances); - await dbApiUser.updatePostUsersBalances(balances); - return { - ok: true, - detail: "", - data: users.map((u) => { - const bal = balances.find((b) => b.id === u.id); - if (!bal) { - console.log(`ERROR: Balance not found for user ${u.userName}`); - } - return { ...u, balance: bal?.balance ?? 0 }; - }), - }; + const uid = session.value.userId.toString(); + logger.info(`Found ${uid} in session`); + const out = await getUsersBalance(+uid.split(":")[1], jwt); + if (!out) { + return { + ok: false, + detail: `Error fetching balance for user ${user.userName}`, + }; + } + balances.push({ id: uid, balance: out }); + } + logger.info(`Updating balances`); + logger.debug(balances); + await dbApiUser.updatePostUsersBalances(balances); + return { + ok: true, + detail: "", + data: users.map((u) => { + const bal = balances.find((b) => b.id === u.id); + if (!bal) { + console.log(`ERROR: Balance not found for user ${u.userName}`); + } + return { ...u, balance: bal?.balance ?? 0 }; + }), + }; } export async function fetchDataForPosting( - date: string, - input: PostDataFilters, - users: ApiPostUser[], + date: string, + input: PostDataFilters, + users: ApiPostUser[], ) { - console.log(`The input ${JSON.stringify(input, null, 2)}`); - const { minPrize, maxPrize } = input; - const draw = input.draw!; - const fsData = { - id: getULID(), - date, - drawId: draw.id, - data: [], - totals: getDefaultTotals(), - } as ReducedFinalSheetData; - if (!draw) { - return { - ok: false, - detail: `Draw for the passed draw ID not found`, - data: [], - users: [], - errors: [ - { message: `Draw for the passed draw ID not found` }, - ] as ServerError, - }; - } - const data = await getReducedFinalSheet(fsData); - if (!data.ok) { - return { - ok: false, - detail: `Error compiling final sheet`, - data: [], - users: [], - errors: data.errors, - }; - } - - console.log("[+] Filtering the fs data to get the numbers"); - const filteredF = filterMatching(fsData.data, minPrize, maxPrize, "first"); - console.log( - `Filtered data: ${filteredF.abNums.size}; ${filteredF.abcNums.size}`, - ); - - // ------------------------------------------ - - let _abNums = new Set(), - _abcNums = new Set(); - for (const each of filteredF.abNums) _abNums.add(each); - for (const each of filteredF.abcNums) _abcNums.add(each); - let abNums = Array.from(_abNums), - abcNums = Array.from(_abcNums); - - if (draw.filterDuplicatesWhilePosting === true) { - console.log(`[+] Removing numbers that have repeating digits`); - console.log( - `[=] Original : AB: ${abNums.length}, ABC: ${abcNums.length}`, - ); - abNums = removeNumbersWithRepeatingDigits(abNums); - abcNums = removeNumbersWithRepeatingDigits(abcNums); - } - console.log(`[=] AB: ${abNums.length}, ABC: ${abcNums.length}`); - - console.log(`Fetching preset data`); - const presetData = await dbPresetData.getDataGroupedBySheetByDraw( - date, - +draw.id.split(":")[1], - ); - console.log(`${presetData.all.length} preset entries found`); - - for (let tries = 0; tries < 3; tries++) { - console.log(`[✍️] Try ${tries + 1} of generating the result`); - const out = await generatePostDataArrayFromBaseInfo( - input, - users, - abNums, - abcNums, - presetData, - ); - if (out.ok) { - return out; - } - if (out.detail.includes("Not enough balance")) { - return { - ok: false, - detail: `Users don't have enough balance to post the data, try reducing the rates`, - data: [], - users: [], - }; - } - } - + console.log(`The input ${JSON.stringify(input, null, 2)}`); + const { minPrize, maxPrize } = input; + const draw = input.draw!; + const fsData = { + id: getULID(), + date, + drawId: draw.id, + data: [], + totals: getDefaultTotals(), + } as ReducedFinalSheetData; + if (!draw) { return { - ok: false, - detail: `Could not generate data, please try adjusting the filters`, - data: [], + ok: false, + detail: `Draw for the passed draw ID not found`, + data: [], + users: [], + errors: [{ message: `Draw for the passed draw ID not found` }] as ServerError, }; + } + const data = await getReducedFinalSheet(fsData); + if (!data.ok) { + return { + ok: false, + detail: `Error compiling final sheet`, + data: [], + users: [], + errors: data.errors, + }; + } + + console.log("[+] Filtering the fs data to get the numbers"); + const filteredF = filterMatching(fsData.data, minPrize, maxPrize, "first"); + console.log(`Filtered data: ${filteredF.abNums.size}; ${filteredF.abcNums.size}`); + + // ------------------------------------------ + + let _abNums = new Set(), + _abcNums = new Set(); + for (const each of filteredF.abNums) _abNums.add(each); + for (const each of filteredF.abcNums) _abcNums.add(each); + let abNums = Array.from(_abNums), + abcNums = Array.from(_abcNums); + + if (draw.filterDuplicatesWhilePosting === true) { + console.log(`[+] Removing numbers that have repeating digits`); + console.log(`[=] Original : AB: ${abNums.length}, ABC: ${abcNums.length}`); + abNums = removeNumbersWithRepeatingDigits(abNums); + abcNums = removeNumbersWithRepeatingDigits(abcNums); + } + console.log(`[=] AB: ${abNums.length}, ABC: ${abcNums.length}`); + + console.log(`Fetching preset data`); + const presetData = await dbPresetData.getDataGroupedBySheetByDraw(date, +draw.id.split(":")[1]); + console.log(`${presetData.all.length} preset entries found`); + + for (let tries = 0; tries < 3; tries++) { + console.log(`[✍️] Try ${tries + 1} of generating the result`); + const out = await generatePostDataArrayFromBaseInfo(input, users, abNums, abcNums, presetData); + if (out.ok) { + return out; + } + if (out.detail.includes("Not enough balance")) { + return { + ok: false, + detail: `Users don't have enough balance to post the data, try reducing the rates`, + data: [], + users: [], + }; + } + } + + return { + ok: false, + detail: `Could not generate data, please try adjusting the filters`, + data: [], + }; } export async function generatePostDataArrayFromBaseInfo( - input: PostDataFilters, - users: ApiPostUser[], - abNums: string[], - abcNums: string[], - presetData: { - all: PostDataEntry[]; - abData: PresetDataEntry[]; - abcData: PresetDataEntry[]; - }, + input: PostDataFilters, + users: ApiPostUser[], + abNums: string[], + abcNums: string[], + presetData: { + all: PostDataEntry[]; + abData: PresetDataEntry[]; + abcData: PresetDataEntry[]; + }, ) { - console.log("[+] Spreading the rates for the numbers for all post user"); - const abData = splitRatesIntoSmallerForRowsWithLargerRates( - spreadRatesForNumbersBetweenUsers( - adjustRatesIfDuplicatesFound( - pairRatesWithNumbers(abNums, input.twoDigitRates), - presetData.abData, - ), - users.map((u) => u.userId), - ), + console.log("[+] Spreading the rates for the numbers for all post user"); + const abData = splitRatesIntoSmallerForRowsWithLargerRates( + spreadRatesForNumbersBetweenUsers( + adjustRatesIfDuplicatesFound( + pairRatesWithNumbers(abNums, input.twoDigitRates), + presetData.abData, + ), + users.map((u) => u.userId), + ), + ); + const abcData = splitRatesIntoSmallerForRowsWithLargerRates( + spreadRatesForNumbersBetweenUsers( + adjustRatesIfDuplicatesFound( + pairRatesWithNumbers(abcNums, input.threeDigitRates), + presetData.abcData, + ), + users.map((u) => u.userId), + ), + ); + + // ------------------------------------------ + + console.log(`[+] Adding ${abData.length} ab entries to final list`); + console.log(`[+] Adding ${abcData.length} abc entries to final list`); + + const result = [] as PostDataEntry[]; + const alreadyPresent = new Set(); + for (const each of abData) { + alreadyPresent.add(each.number); + result.push(each); + } + for (const each of abcData) { + alreadyPresent.add(each.number); + result.push(each); + } + + // ------------------------------------------ + + const balanceCounts = {} as Record; + for (const each of result) { + const uid = each.userId ?? ""; + if (balanceCounts[uid] === undefined) { + balanceCounts[uid] = 0; + } + balanceCounts[uid] += each.first + each.second; + } + + // ------------------------------------------ + + console.log(`[+] Appending up to ${presetData.all.length} entries that are not ab, abc`); + for (const entry of presetData.all) { + if (alreadyPresent.has(entry.number) || (entry.first < 5 && entry.second < 5)) { + continue; + } + const randomUserId = users[Math.floor(Math.random() * users.length)].userId; + if (balanceCounts[randomUserId] === undefined) { + balanceCounts[randomUserId] = 0; + } + balanceCounts[randomUserId] += entry.first + entry.second; + result.push({ ...entry, userId: randomUserId }); + } + + // ------------------------------------------ + + const usersTotalbalance = users.reduce((a, b) => a + (b.balance ?? 0), 0); + let totalAmtForPostingData = Object.values(balanceCounts).reduce((acc, curr) => acc + curr, 0); + if (usersTotalbalance < totalAmtForPostingData) { + return { + ok: false, + detail: `Not enough balance to book overall with ${usersTotalbalance} < ${totalAmtForPostingData}`, + data: [], + users: [], + errors: [{ message: `Not enough balance to book overall` }] as ServerError, + }; + } + + function isDistributionUnbalanced() { + let out = false; + for (const key in balanceCounts) { + if (balanceCounts[key] > (users.find((u) => u.userId === key)?.balance ?? 0)) { + out = true; + break; + } + } + return out; + } + + for (let tries = 0; tries < 5; tries++) { + console.log(`Balance counts start : ${JSON.stringify(balanceCounts, null, 2)}`); + + rebalancePostDataListByBalanceOfUsers(balanceCounts, users, result); + + console.log(`Balance counts final : ${JSON.stringify(balanceCounts)}`); + + let totalAmtForPostingDataAfterRebalance = Object.values(balanceCounts).reduce( + (acc, curr) => acc + curr, + 0, ); - const abcData = splitRatesIntoSmallerForRowsWithLargerRates( - spreadRatesForNumbersBetweenUsers( - adjustRatesIfDuplicatesFound( - pairRatesWithNumbers(abcNums, input.threeDigitRates), - presetData.abcData, - ), - users.map((u) => u.userId), - ), - ); - - // ------------------------------------------ - - console.log(`[+] Adding ${abData.length} ab entries to final list`); - console.log(`[+] Adding ${abcData.length} abc entries to final list`); - - const result = [] as PostDataEntry[]; - const alreadyPresent = new Set(); - for (const each of abData) { - alreadyPresent.add(each.number); - result.push(each); - } - for (const each of abcData) { - alreadyPresent.add(each.number); - result.push(each); - } - - // ------------------------------------------ - - const balanceCounts = {} as Record; - for (const each of result) { - const uid = each.userId ?? ""; - if (balanceCounts[uid] === undefined) { - balanceCounts[uid] = 0; - } - balanceCounts[uid] += each.first + each.second; - } - - // ------------------------------------------ console.log( - `[+] Appending up to ${presetData.all.length} entries that are not ab, abc`, + `Total amount for posting data after rebalance: ${totalAmtForPostingDataAfterRebalance}`, + `Total balance of users: ${JSON.stringify(users.map((u) => ({ un: u.userName, b: u.balance })))}`, ); - for (const entry of presetData.all) { - if ( - alreadyPresent.has(entry.number) || - (entry.first < 5 && entry.second < 5) - ) { - continue; - } - const randomUserId = - users[Math.floor(Math.random() * users.length)].userId; - if (balanceCounts[randomUserId] === undefined) { - balanceCounts[randomUserId] = 0; - } - balanceCounts[randomUserId] += entry.first + entry.second; - result.push({ ...entry, userId: randomUserId }); + + if (!isDistributionUnbalanced()) { + console.log(`[+] Distribution is balanced`); + break; } + console.log(`[!] Rebalancing again`); + } - // ------------------------------------------ - - const usersTotalbalance = users.reduce((a, b) => a + (b.balance ?? 0), 0); - let totalAmtForPostingData = Object.values(balanceCounts).reduce( - (acc, curr) => acc + curr, - 0, - ); - if (usersTotalbalance < totalAmtForPostingData) { - return { - ok: false, - detail: `Not enough balance to book overall with ${usersTotalbalance} < ${totalAmtForPostingData}`, - data: [], - users: [], - errors: [ - { message: `Not enough balance to book overall` }, - ] as ServerError, - }; - } - - function isDistributionUnbalanced() { - let out = false; - for (const key in balanceCounts) { - if ( - balanceCounts[key] > - (users.find((u) => u.userId === key)?.balance ?? 0) - ) { - out = true; - break; - } - } - return out; - } - - for (let tries = 0; tries < 5; tries++) { - console.log( - `Balance counts start : ${JSON.stringify(balanceCounts, null, 2)}`, - ); - - rebalancePostDataListByBalanceOfUsers(balanceCounts, users, result); - - console.log(`Balance counts final : ${JSON.stringify(balanceCounts)}`); - - let totalAmtForPostingDataAfterRebalance = Object.values( - balanceCounts, - ).reduce((acc, curr) => acc + curr, 0); - - console.log( - `Total amount for posting data after rebalance: ${totalAmtForPostingDataAfterRebalance}`, - `Total balance of users: ${JSON.stringify(users.map((u) => ({ un: u.userName, b: u.balance })))}`, - ); - - if (!isDistributionUnbalanced()) { - console.log(`[+] Distribution is balanced`); - break; - } - console.log(`[!] Rebalancing again`); - } - - if (isDistributionUnbalanced()) { - return { - ok: false, - detail: `Please regenerate dataset as the some users have not enough balance to book their entries`, - data: [], - users: [], - }; - } - - // ------------------------------------------ - - console.log(`[+] Shuffling ${result.length} entries for posting`); - shuffleArray(result); - + if (isDistributionUnbalanced()) { return { - ok: true, - detail: `Fetched the data successfully`, - data: result, - users, - errors: undefined, + ok: false, + detail: `Please regenerate dataset as the some users have not enough balance to book their entries`, + data: [], + users: [], }; + } + + // ------------------------------------------ + + console.log(`[+] Shuffling ${result.length} entries for posting`); + shuffleArray(result); + + return { + ok: true, + detail: `Fetched the data successfully`, + data: result, + users, + errors: undefined, + }; } function shuffleArray(array: T[]): T[] { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; } export function rebalancePostDataListByBalanceOfUsers( - balanceCounts: Record, - users: ApiPostUser[], - result: PostDataEntry[], + balanceCounts: Record, + users: ApiPostUser[], + result: PostDataEntry[], ) { + console.log(`[+] Checking if the users have enough balance to book their assigned data`); + + for (const user of users) { + const usersBalance = user.balance ?? 0; + const dueForUser = balanceCounts[user.userId] ?? 0; + if (usersBalance === 0) { + console.log(`\n[!] ${user.userName} has no balance\n`); + continue; + } + if (usersBalance >= dueForUser) { + console.log(`[✅] ${user.userName} can book the data of ${usersBalance} > ${dueForUser} `); + continue; + } + console.log(`[!❎!] ${user.userName} can't book it all ${usersBalance} < ${dueForUser}`); + + const difference = dueForUser - usersBalance; + let differenceLeft = Number(difference); // make a copy + const entriesToMove = result + .filter((r) => { + if (r.userId === user.userId && differenceLeft > 0) { + differenceLeft -= r.first + r.second; + return true; + } + }) + .map((r) => r.id); + console.log(`Have to move ${entriesToMove.length} entries to other users`); + + // find a user who has enough balance + + const userWithEnoughBalance = users.find((u) => { + return (u.balance ?? 0) - balanceCounts[u.userId] >= difference && u.userId !== user.userId; + }); + if (!userWithEnoughBalance) { + return { + ok: false, + detail: `No user found with enough balance to cover balance shortage of ${difference} for ${user.userName}`, + data: [], + }; + } console.log( - `[+] Checking if the users have enough balance to book their assigned data`, + `Dude has enough balance to take on this other user's expenses ': ${JSON.stringify(userWithEnoughBalance)}`, ); - for (const user of users) { - const usersBalance = user.balance ?? 0; - const dueForUser = balanceCounts[user.userId] ?? 0; - if (usersBalance === 0) { - console.log(`\n[!] ${user.userName} has no balance\n`); - continue; - } - if (usersBalance >= dueForUser) { - console.log( - `[✅] ${user.userName} can book the data of ${usersBalance} > ${dueForUser} `, - ); - continue; - } - console.log( - `[!❎!] ${user.userName} can't book it all ${usersBalance} < ${dueForUser}`, - ); - - const difference = dueForUser - usersBalance; - let differenceLeft = Number(difference); // make a copy - const entriesToMove = result - .filter((r) => { - if (r.userId === user.userId && differenceLeft > 0) { - differenceLeft -= r.first + r.second; - return true; - } - }) - .map((r) => r.id); - console.log( - `Have to move ${entriesToMove.length} entries to other users`, - ); - - // find a user who has enough balance - - const userWithEnoughBalance = users.find((u) => { - return ( - (u.balance ?? 0) - balanceCounts[u.userId] >= difference && - u.userId !== user.userId - ); - }); - if (!userWithEnoughBalance) { - return { - ok: false, - detail: `No user found with enough balance to cover balance shortage of ${difference} for ${user.userName}`, - data: [], - }; - } - console.log( - `Dude has enough balance to take on this other user's expenses ': ${JSON.stringify(userWithEnoughBalance)}`, - ); - - for (let i = 0; i < result.length; i++) { - if (!entriesToMove.includes(result[i].id)) { - continue; - } - const entry = result[i]; - let amountMoved = 0; - if (entry.userId !== user.userId) { - continue; - } - entry.userId = userWithEnoughBalance.userId; - balanceCounts[userWithEnoughBalance.userId] += - entry.first + entry.second; - balanceCounts[user.userId] -= entry.first + entry.second; - amountMoved += entry.first + entry.second; - if (amountMoved >= difference) { - // don't move more than the difference' - break; - } - } - console.log( - `[+] Moved ${entriesToMove.length} entries to ${userWithEnoughBalance.userName}`, - ); + for (let i = 0; i < result.length; i++) { + if (!entriesToMove.includes(result[i].id)) { + continue; + } + const entry = result[i]; + let amountMoved = 0; + if (entry.userId !== user.userId) { + continue; + } + entry.userId = userWithEnoughBalance.userId; + balanceCounts[userWithEnoughBalance.userId] += entry.first + entry.second; + balanceCounts[user.userId] -= entry.first + entry.second; + amountMoved += entry.first + entry.second; + if (amountMoved >= difference) { + // don't move more than the difference' + break; + } } + console.log(`[+] Moved ${entriesToMove.length} entries to ${userWithEnoughBalance.userName}`); + } } export async function fetchPostDataHistory(input: PostDataHistoryFilters) { - const { draw, date } = input; - console.log( - `Fetching post data from HISTORY for draw: ${date} - ${draw.id}`, - ); - const found = await dbApiPostData.getPostDataByDraw(date, draw.id); - if (!found) { - return { data: [], users: [], ok: false, detail: "Data not found" }; + const { draw, date } = input; + console.log(`Fetching post data from HISTORY for draw: ${date} - ${draw.id}`); + const found = await dbApiPostData.getPostDataByDraw(date, draw.id); + if (!found) { + return { data: [], users: [], ok: false, detail: "Data not found" }; + } + console.log(`Data found for the passed draw: ${date} - ${draw.id}, returning that`); + const users = await dbApiUser.getAllPostUsers(); + const uniqueUserIds = [] as string[]; + for (const each of found) { + if (!each.userId || uniqueUserIds.includes(each.userId)) { + continue; } - console.log( - `Data found for the passed draw: ${date} - ${draw.id}, returning that`, - ); - const users = await dbApiUser.getAllPostUsers(); - const uniqueUserIds = [] as string[]; - for (const each of found) { - if (!each.userId || uniqueUserIds.includes(each.userId)) { - continue; - } - uniqueUserIds.push(each.userId); - } - return { - data: found, - users: users.filter((u) => uniqueUserIds.includes(u.userId)), - ok: true, - detail: "Data found", - }; + uniqueUserIds.push(each.userId); + } + return { + data: found, + users: users.filter((u) => uniqueUserIds.includes(u.userId)), + ok: true, + detail: "Data found", + }; }