SHIT WORKS
This commit is contained in:
@@ -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"))"
|
||||
}
|
||||
62
src/lib/server/external/api.scraping.helpers.ts
vendored
62
src/lib/server/external/api.scraping.helpers.ts
vendored
@@ -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}`);
|
||||
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user