SHIT WORKS

This commit is contained in:
bootunloader
2026-01-05 22:05:48 +02:00
parent 714b8f737b
commit 0111933d92
3 changed files with 398 additions and 444 deletions

View File

@@ -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"))"
}

View File

@@ -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}`);

View File

@@ -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<string>();
let abcNums = new Set<string>();
let abNums = new Set<string>();
let abcNums = new Set<string>();
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<string>(),
_abcNums = new Set<string>();
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<string>(),
_abcNums = new Set<string>();
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<string>();
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<string, number>;
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<string>();
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<string, number>;
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<T>(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<string, number>,
users: ApiPostUser[],
result: PostDataEntry[],
balanceCounts: Record<string, number>,
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",
};
}