fantasy-manager/pages/api/transfer/[league].ts
Lukas Schaefer d3a5fe88f9
switch to kysely (#863)
Signed-off-by: Lukas Schaefer <lukas@lschaefer.xyz>
2025-05-09 19:12:32 -04:00

674 lines
25 KiB
TypeScript

import { NextApiRequest, NextApiResponse } from "next";
import db from "../../../Modules/database";
import { authOptions } from "#/pages/api/auth/[...nextauth]";
import { getServerSession } from "next-auth";
// This is the structure of the get response
export interface getLeagues {
money: number;
transferCount: number;
timeLeft: number;
ownership: { [Key: string]: (ownershipInfo | transferInfo)[] };
transferOpen: boolean;
}
interface ownershipInfo {
transfer: false;
owner: number;
}
interface transferInfo {
transfer: true;
seller: number;
buyer: number;
amount: number;
}
export interface GETResult {
money: number;
transferCount: number;
timeLeft: number;
ownership: { [Key: string]: (ownershipInfo | transferInfo)[] };
transferOpen: boolean;
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const session = await getServerSession(req, res, authOptions);
const league = parseInt(String(req.query.league));
if (!session) {
res.status(401).end("Not logged in");
} else {
const user = session.user.id;
const leagueSettings = await db
.selectFrom("leagueSettings")
.selectAll()
.where("leagueID", "=", league)
.executeTakeFirst();
// Checks if the league ia archived
if (leagueSettings?.archived !== 0) {
res.status(400).end("This league is archived");
return;
}
// Gets users money
const money =
(
await db
.selectFrom("leagueUsers")
.select("money")
.where("leagueID", "=", league)
.where("user", "=", user)
.executeTakeFirst()
)?.money || 0;
// Gets the leagues settings
if (leagueSettings.fantasyEnabled === 0) {
res.status(400).end("This league does not have fantasy enabled. ");
return;
}
// Checks if the transfer market is open
const transferOpen = await db
.selectFrom("data")
.select("value1")
.where("value1", "=", "transferOpen" + leagueSettings.league)
.where("value2", "=", "true")
.execute()
.then((e) => e.length > 0);
switch (req.method) {
// Used to return a dictionary of all transfers and ownerships
case "GET":
if (money !== undefined) {
const [transfers, squads, timeLeft] = await Promise.all([
// Gets list of all transfers
db
.selectFrom("transfers")
.selectAll()
.where("leagueID", "=", league)
.execute(),
// Gets squads
db
.selectFrom("squad")
.selectAll()
.where("leagueID", "=", league)
.execute(),
// Gets the amount of time left in the transfer period
db
.selectFrom("data")
.select("value2")
.where("value1", "=", "countdown" + leagueSettings.league)
.executeTakeFirst()
.then((e) => parseInt(String(e?.value2))),
]);
// Puts all the ownership and transfer info in a dictionary
const ownership: { [Key: string]: (ownershipInfo | transferInfo)[] } =
{};
let transferCount = 0;
squads.forEach((e) => {
if (ownership[e.playeruid] === undefined) {
ownership[e.playeruid] = [{ transfer: false, owner: e.user }];
} else {
ownership[e.playeruid].push({ transfer: false, owner: e.user });
}
});
transfers.forEach((e) => {
const data: transferInfo = {
transfer: true,
seller: e.seller,
buyer: e.buyer,
amount: e.value,
};
if (ownership[e.playeruid] === undefined) {
ownership[e.playeruid] = [data];
} else {
ownership[e.playeruid] = ownership[e.playeruid].filter(
(f) => e.seller !== (f as ownershipInfo).owner,
);
ownership[e.playeruid].push(data);
}
if (e.seller == user || e.buyer == user) {
transferCount++;
}
});
const result: GETResult = {
money,
transferCount,
timeLeft,
ownership,
transferOpen,
};
res.status(200).json(result);
} else {
res.status(404).end("League not found");
}
break;
// Used to create a new transfer
case "POST":
const playeruid = req.body.playeruid;
const amount = parseInt(req.body.amount);
// Checks if the transfer market is still open
if (!transferOpen && !leagueSettings.matchdayTransfers) {
res.status(400).end("Transfer Market is closed");
break;
}
const player = await db
.selectFrom("players")
.selectAll()
.where("uid", "=", playeruid)
.where("league", "=", leagueSettings.league)
.executeTakeFirst();
// Checks if the player exists
if (player === undefined) {
res.status(404).end("Player does not exist");
break;
}
// Says if the user still has transfers left
const emptySquad =
(await db
.selectFrom("squad")
.select(["playeruid"])
.where("leagueID", "=", league)
.where("user", "=", user)
.executeTakeFirst()) === undefined;
const transferCount = db
.selectFrom("transfers")
.select((eb) => [eb.fn.count("playeruid").as("count")])
.where("leagueID", "=", league)
.where((eb) =>
eb.or([eb("buyer", "=", user), eb("seller", "=", user)]),
)
.executeTakeFirst()
.then((e) => parseInt(String(e?.count)));
const transferLeft =
emptySquad || (await transferCount) < leagueSettings.transfers;
// Checks if this was a purchase
if (amount > 0) {
// Checks if the player is already owned by the user
if (
(await db
.selectFrom("squad")
.select(["playeruid"])
.where("leagueID", "=", league)
.where("user", "=", user)
.where("playeruid", "=", playeruid)
.executeTakeFirst()) !== undefined
) {
res.status(400).end("You already own the player");
break;
}
// Checks if the player is already being purchased
const purchaseTransfer = await db
.selectFrom("transfers")
.selectAll()
.where("buyer", "=", user)
.where("playeruid", "=", playeruid)
.where("leagueID", "=", league)
.executeTakeFirst();
if (purchaseTransfer !== undefined) {
// Makes sure the bid is greater than or equal to the current purchase amount
if (purchaseTransfer.value > amount) {
res
.status(400)
.end("You can not bid lower than your current purchase");
break;
}
await db
.updateTable("transfers")
.set({ max: amount })
.where("buyer", "=", user)
.where("playeruid", "=", playeruid)
.where("leagueID", "=", league)
.execute();
res.status(200).end(`Updated max bid to {amount} M`);
break;
}
// Checks if the user still has transfers left
if (!transferLeft) {
res.status(400).end("You have no more transfers");
break;
}
// Checks if the player can still be bought from the AI
const squadCount = await db
.selectFrom("squad")
.select((eb) => [eb.fn.count("user").as("count")])
.where("leagueID", "=", league)
.where("playeruid", "=", playeruid)
.executeTakeFirst()
.then((e) => parseInt(String(e?.count)));
const transferCountBuy = await db
.selectFrom("transfers")
.select((eb) => [eb.fn.count("buyer").as("count")])
.where("seller", "=", 0)
.where("leagueID", "=", league)
.where("playeruid", "=", playeruid)
.executeTakeFirst()
.then((e) => parseInt(String(e?.count)));
if (squadCount + transferCountBuy < leagueSettings.duplicatePlayers) {
// Checks if the user has offered enough money.
if (amount < player.sale_price) {
res
.status(400)
.end(
"You can not buy a player for less than the player's value",
);
break;
}
if (money < player.sale_price) {
res.status(400).end("You do not have enough money");
break;
}
await db
.insertInto("transfers")
.values({
leagueID: league,
seller: 0,
buyer: user,
playeruid,
value: player.sale_price,
max: amount,
})
.execute();
await db
.updateTable("leagueUsers")
.set({ money: money - player.sale_price })
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
console.log(
`Player ${playeruid} bought for ${player.sale_price} with max bid of ${amount} by user ${user} in league ${league}`,
);
res.status(200).end("Bought player");
break;
}
// Checks if the player can even be bought from anyone
const transferCountSell = await db
.selectFrom("transfers")
.select((eb) => [eb.fn.count("buyer").as("count")])
.where("leagueID", "=", league)
.where("playeruid", "=", playeruid)
.where("seller", "!=", 0)
.executeTakeFirst()
.then((e) => parseInt(String(e?.count)));
if (
squadCount - transferCountSell <
leagueSettings.duplicatePlayers
) {
// Increments all the offers by 100000 until someone drops their transfer
while (true) {
const cheapest = await db
.selectFrom("transfers")
.selectAll()
.where("leagueID", "=", league)
.where("playeruid", "=", playeruid)
.orderBy("value", "asc")
.limit(1)
.executeTakeFirst();
if (cheapest === undefined) {
res.status(500).end("Could not find transfer to outbid");
break;
}
// Checks if this is an AI
const isAI = cheapest.buyer === 0 || cheapest.buyer === -1;
// Checks if the player still wants to pay that amount
if (cheapest.value >= amount + (isAI ? 100000 : 0)) {
res.status(400).end("Not enough money offered");
break;
}
if (cheapest.value + 100000 > money + (isAI ? 100000 : 0)) {
res.status(400).end("You do not have enough money");
break;
}
// Checks if that player wants to increment the offer and if they can afford it
if (
cheapest.max > cheapest.value &&
(await db
.selectFrom("leagueUsers")
.select(["leagueID"])
.where("leagueID", "=", league)
.where("user", "=", cheapest.buyer)
.where("money", ">=", 100000)
.executeTakeFirst()) !== undefined
) {
console.log(
`User ${cheapest.buyer} increased bid to ${
cheapest.value + 100000
} for ${playeruid} due to automatic bid increase in league ${league}`,
);
// Increases the bidding amount by 100k for that bid
await Promise.all([
db
.updateTable("leagueUsers")
.set((eb) => ({
money: eb("money", "-", 100000),
}))
.where("user", "=", cheapest.buyer)
.where("leagueID", "=", league)
.execute(),
db
.updateTable("leagueUsers")
.set((eb) => ({
money: eb("money", "+", 100000),
}))
.where("user", "=", cheapest.seller)
.where("leagueID", "=", league)
.execute(),
db
.updateTable("transfers")
.set((eb) => ({
value: eb("value", "+", 100000),
}))
.where("buyer", "=", cheapest.buyer)
.where("leagueID", "=", league)
.where("playeruid", "=", playeruid)
.execute(),
]);
} else {
// Moves transfer to the new bidder
await Promise.all([
// Updates the original buyers money
db
.updateTable("leagueUsers")
.set((eb) => ({ money: eb("money", "+", cheapest.value) }))
.where("user", "=", cheapest.buyer)
.where("leagueID", "=", league)
.execute(),
// Updates the new buyers money
db
.updateTable("leagueUsers")
.set((eb) => ({
money: eb(
"money",
"-",
cheapest.value + (isAI ? 0 : 100000),
),
}))
.where("user", "=", user)
.where("leagueID", "=", league)
.execute(),
// Updates the sellers money
db
.updateTable("leagueUsers")
.set((eb) => ({
money: eb("money", "-", isAI ? 0 : 100000),
}))
.where("user", "=", cheapest.seller)
.where("leagueID", "=", league)
.execute(),
// Updates the transfers data
db
.updateTable("transfers")
.set((eb) => ({
value: eb("value", "+", isAI ? 0 : 100000),
position: "bench",
starred: 0,
buyer: user,
}))
.where("leagueID", "=", league)
.where("buyer", "=", cheapest.buyer)
.where("playeruid", "=", playeruid)
.execute(),
]);
console.log(
`User ${user} outbid ${cheapest.buyer} with ${
cheapest.value + (isAI ? 0 : 100000)
} for ${playeruid} in league ${league}`,
);
res.status(200).end("Bought player");
break;
}
}
break;
} else {
res.status(400).end("Player not for sale");
break;
}
// Checks if this is a sale
} else if (amount < 0) {
// Checks if the player is already being sold
const possibleTransfer = await db
.selectFrom("transfers")
.selectAll()
.where("leagueID", "=", league)
.where("seller", "=", user)
.where("playeruid", "=", playeruid)
.executeTakeFirst();
if (possibleTransfer !== undefined) {
if (possibleTransfer.value < amount * -1) {
// Used to check if the buyer has enough money
const enoughMoney = await db
.selectFrom("leagueUsers")
.where("leagueID", "=", league)
.where("user", "=", possibleTransfer.buyer)
.select("money")
.executeTakeFirst()
.then(
(e) =>
(e?.money || 0) >= amount * -1 - possibleTransfer.value,
);
// Checks if the user is willing to pay enough for the player
if (possibleTransfer.max >= amount * -1 && enoughMoney) {
await db
.updateTable("transfers")
.set({
value: amount * -1,
})
.where("leagueID", "=", league)
.where("buyer", "=", possibleTransfer.buyer)
.where("seller", "=", user)
.where("playeruid", "=", playeruid)
.execute();
await db
.updateTable("leagueUsers")
.set({
money: (eb) =>
eb("money", "-", amount * -1 - possibleTransfer.value),
})
.where("leagueID", "=", league)
.where("user", "=", possibleTransfer.buyer)
.execute();
await db
.updateTable("leagueUsers")
.set({
money: (eb) =>
eb("money", "+", amount * -1 - possibleTransfer.value),
})
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
} else {
if (possibleTransfer.buyer !== -1) {
// Makes sure to check if the player has enough money to cancel this
if (money < possibleTransfer.value) {
res
.status(400)
.end(
`You need to have ${
possibleTransfer.value / 1000000
}M to increase the minimum amount of the transfer`,
);
break;
}
// Makes the transaction a possible offer
await db
.updateTable("transfers")
.set({
buyer: -1,
max: amount * -1,
value: amount * -1,
})
.where("leagueID", "=", league)
.where("seller", "=", user)
.where("playeruid", "=", playeruid)
.execute();
await db
.updateTable("leagueUsers")
.set({
money: (eb) => eb("money", "-", possibleTransfer.value),
})
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
}
}
}
res.status(200).end("Updated Transfer");
break;
}
// Checks if the player is owned by the user
if (
(await db
.selectFrom("squad")
.selectAll()
.where("leagueID", "=", league)
.where("user", "=", user)
.where("playeruid", "=", playeruid)
.executeTakeFirst()) === undefined
) {
res.status(400).end("You do not own this player");
break;
}
// Checks if the user still has a transfer left
if (!transferLeft) {
res.status(400).end("You have no more transfers");
break;
}
// Sells the player
const playerValue = player.sale_price;
// Stores the amount that the user actually wants for the player
const actualAmount =
amount * -1 > player.sale_price ? amount * -1 : player.sale_price;
const success = await db
.insertInto("transfers")
.values({
leagueID: league,
seller: user,
buyer: -1,
playeruid: playeruid,
value: actualAmount,
max: actualAmount,
})
.execute()
.then(() => true)
.catch(() => false);
if (!success) {
res.status(400).end("Failed to sell player");
break;
}
// Checks if this transaction actually has a seller
if (actualAmount === playerValue) {
await db
.updateTable("leagueUsers")
.set((eb) => ({ money: eb("money", "+", actualAmount) }))
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
}
console.log(
`User ${user} is ${
actualAmount > player.sale_price ? "planning to sell" : "selling"
} ${playeruid} for ${actualAmount} in league ${league}`,
);
res
.status(200)
.end(
`${
actualAmount > player.sale_price
? "Planning to sell"
: "Selling"
} player for minimum of ${actualAmount / 1000000}M`,
);
break;
// Cancels the transaction
} else {
// Trys to find the transaction
const transfer = await db
.selectFrom("transfers")
.selectAll()
.where("leagueID", "=", league)
.where((eb) =>
eb.or([eb("seller", "=", user), eb("buyer", "=", user)]),
)
.where("playeruid", "=", playeruid)
.executeTakeFirst();
if (transfer === undefined) {
res.status(404).end("Transfer must exist to be cancelled");
break;
}
// Checks if it was a sale
if (transfer.seller == user) {
const sale = transfer;
// Checks if this offer was taken and if it was the seller has to give a refund
if (sale.buyer !== -1) {
if (money < sale.value) {
res
.status(400)
.end(
`You need to have ${
sale.value / 1000000
}M to cancel the transfer`,
);
break;
}
await db
.updateTable("leagueUsers")
.set((eb) => ({ money: eb("money", "-", sale.value) }))
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
}
// Removes the transaction and refunds all the money
await db
.updateTable("leagueUsers")
.set((eb) => ({ money: eb("money", "+", sale.value) }))
.where("leagueID", "=", league)
.where("user", "=", sale.buyer)
.execute();
await db
.deleteFrom("transfers")
.where("leagueID", "=", league)
.where("seller", "=", user)
.where("playeruid", "=", playeruid)
.execute();
res.status(200).end("Transfer cancelled");
console.log(
`User ${user} cancelled sale of ${playeruid} to ${sale.buyer} for ${sale.value} in league ${league}`,
);
break;
}
if (transfer.buyer == user) {
const purchase = transfer;
// Removes the canceller from the transaction and refunds them the value of the player
await db
.updateTable("leagueUsers")
.set((eb) => ({ money: eb("money", "+", player.sale_price) }))
.where("leagueID", "=", league)
.where("user", "=", user)
.execute();
await db
.updateTable("transfers")
.set({ max: purchase.value, buyer: 0 })
.where("leagueID", "=", league)
.where("buyer", "=", user)
.where("playeruid", "=", playeruid)
.execute();
await db
.deleteFrom("transfers")
.where("leagueID", "=", league)
.where("buyer", "=", 0)
.where("seller", "=", 0)
.where("playeruid", "=", playeruid)
.execute();
res.status(200).end("Transfer cancelled");
console.log(
`User ${user} cancelled purchase of ${playeruid} from ${purchase.buyer} for ${player.sale_price} in league ${league}`,
);
break;
}
}
// This will run if something went wrong
console.log("A transfer failed to finish");
res.status(500).end("An unknown error happened");
break;
default:
res.status(405).end(`Method ${req.method} Not Allowed`);
break;
}
}
}