diff --git a/bun.lockb b/bun.lockb index fb7b3d8..4b42ed1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/config/mpp_bots.yml b/config/mpp_bots.yml index 44583fc..310e654 100644 --- a/config/mpp_bots.yml +++ b/config/mpp_bots.yml @@ -2,7 +2,3 @@ channel: id: "✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧" allowColorChanging: true -- uri: wss://mppclone.com:8443 - channel: - id: "test/fishing" - allowColorChanging: true diff --git a/package.json b/package.json index 6f19a81..f39182f 100644 --- a/package.json +++ b/package.json @@ -8,22 +8,22 @@ "start-discord": "bun src/discord/index.ts" }, "devDependencies": { - "@types/bun": "^1.1.6" + "@types/bun": "^1.1.9" }, "peerDependencies": { "typescript": "^5.3.3" }, "dependencies": { - "@prisma/client": "^5.16.1", + "@prisma/client": "^5.19.1", "@trpc/client": "next", "@trpc/server": "next", - "@types/node": "^20.14.10", + "@types/node": "^20.16.5", "cli-markdown": "^3.4.0", - "discord.js": "^14.15.3", - "mpp-client-net": "^1.2.0", - "prisma": "^5.16.1", - "trpc-bun-adapter": "^1.1.1", - "yaml": "^2.4.5", + "discord.js": "^14.16.2", + "mpp-client-net": "^1.2.3", + "prisma": "^5.19.1", + "trpc-bun-adapter": "^1.1.2", + "yaml": "^2.5.1", "zod": "^3.23.8" } } diff --git a/src/api/commands/groups/index.ts b/src/api/commands/groups/index.ts index ea23a46..97c0d8b 100644 --- a/src/api/commands/groups/index.ts +++ b/src/api/commands/groups/index.ts @@ -25,6 +25,7 @@ import { fid } from "./util/fid"; import { chance } from "./util/chance"; import { info } from "./general/info"; import { burger } from "./util/burger"; +import { daily } from "./pokemon/daily"; // import { give } from "./inventory/give"; interface ICommandGroup { @@ -54,15 +55,23 @@ commandGroups.push(fishingGroup); const inventoryGroup: ICommandGroup = { id: "inventory", displayName: "Inventory", - commands: [inventory, take, eat, sack, pokemon, yeet, burger /* give */] + commands: [inventory, sack, pokemon, take, eat, yeet, burger /* give */] }; commandGroups.push(inventoryGroup); +const pokemonGroup: ICommandGroup = { + id: "pokemon", + displayName: "Pokémon", + commands: [daily, pokedex] +}; + +commandGroups.push(pokemonGroup); + const utilGroup: ICommandGroup = { id: "util", displayName: "Utility", - commands: [data, setcolor, memory, autofish, pokedex, fid, chance] + commands: [data, setcolor, memory, autofish, fid, chance] }; commandGroups.push(utilGroup); diff --git a/src/api/commands/groups/inventory/eat.ts b/src/api/commands/groups/inventory/eat.ts index b6c2559..603397f 100644 --- a/src/api/commands/groups/inventory/eat.ts +++ b/src/api/commands/groups/inventory/eat.ts @@ -1,5 +1,7 @@ import Command from "@server/commands/Command"; +import { logger } from "@server/commands/handler"; import { getInventory, updateInventory } from "@server/data/inventory"; +import { removeItem } from "@server/items"; import { itemBehaviorMap, runBehavior } from "@server/items/behavior"; export const eat = new Command( @@ -62,45 +64,9 @@ export const eat = new Command( if (shouldRemove) { if (foundObject.objtype == "fish") { - i = 0; - - for (const fish of inventory.fishSack as TFishSack) { - if (typeof fish.count !== "undefined") { - if (fish.count > 1) { - shouldRemove = false; - ((inventory.fishSack as TFishSack)[i] - .count as number)--; - } else { - shouldRemove = true; - } - } else { - shouldRemove = true; - } - - if (shouldRemove) - (inventory.fishSack as TFishSack).splice(i, 1); - break; - } + removeItem(inventory.fishSack, foundObject); } else if (foundObject.objtype == "item") { - i = 0; - - for (const item of inventory.items as unknown as IItem[]) { - if (typeof item.count == "number") { - if (item.count > 1) { - shouldRemove = false; - ((inventory.items as TInventoryItems)[i] - .count as number)--; - } else { - shouldRemove = true; - } - } else { - shouldRemove = true; - } - - if (shouldRemove) - (inventory.items as TInventoryItems).splice(i, 1); - break; - } + removeItem(inventory.items, foundObject); } await updateInventory(inventory); diff --git a/src/api/commands/groups/pokemon/daily.ts b/src/api/commands/groups/pokemon/daily.ts new file mode 100644 index 0000000..f8e2040 --- /dev/null +++ b/src/api/commands/groups/pokemon/daily.ts @@ -0,0 +1,21 @@ +import Command from "@server/commands/Command"; +import { logger } from "@server/commands/handler"; +import { claimDailyPokemon } from "@server/pokemon/daily"; + +export const daily = new Command( + "daily", + ["daily", "dailypokemon"], + "Claim your daily Pokémon reward", + "daily", + "command.inventory.daily", + async ({ id, command, args, prefix, part, user }) => { + try { + const message = await claimDailyPokemon(user.id); + return message; + } catch (err) { + logger.error("Unable to perform daily claim:", err); + return `Congratulations, you broke the bot. Your daily reward might not work now.`; + } + }, + true +); diff --git a/src/api/data/inventory.ts b/src/api/data/inventory.ts index 1abf554..4ced6a8 100644 --- a/src/api/data/inventory.ts +++ b/src/api/data/inventory.ts @@ -1,15 +1,16 @@ +import type { User } from "@prisma/client"; import prisma from "./prisma"; export async function createInventory(inventory: Partial) { - return await prisma.inventory.create({ + return (await prisma.inventory.create({ data: inventory - }); + })) as IInventory; } export async function getInventory(id: IInventory["id"]) { - return await prisma.inventory.findUnique({ + return (await prisma.inventory.findUnique({ where: { id } - }); + })) as IInventory; } export async function updateInventory(inventory: Partial) { diff --git a/src/api/fish/fish.ts b/src/api/fish/fish.ts index b3e985b..4731904 100644 --- a/src/api/fish/fish.ts +++ b/src/api/fish/fish.ts @@ -108,7 +108,11 @@ export function hasFishTime( export function getSizeString(cm: number) { const size = - cm < 30 + cm < 5 + ? "microscopic" + : cm < 10 + ? "tiny" + : cm < 30 ? "small" : cm < 60 ? "medium-sized" diff --git a/src/api/items/index.ts b/src/api/items/index.ts index 9743b1b..fc80d8a 100644 --- a/src/api/items/index.ts +++ b/src/api/items/index.ts @@ -23,3 +23,26 @@ export function addItem(arr: IObject[], item: IObject) { (arr[i].count as number) += inc; } } + +export function removeItem(arr: IObject[], item: IObject, count = 1) { + let found = false; + let i = 0; + + for (i = 0; i < arr.length; i++) { + if (item.id == arr[i].id) { + found = true; + break; + } + } + + const foundItem = arr[i]; + if (!found || !foundItem) return false; + + if (typeof foundItem.count == "number" && foundItem.count > 1) { + foundItem.count -= count; + } else { + arr.splice(i, 1); + } + + return true; +} diff --git a/src/api/pokemon/daily.ts b/src/api/pokemon/daily.ts new file mode 100644 index 0000000..56247b4 --- /dev/null +++ b/src/api/pokemon/daily.ts @@ -0,0 +1,62 @@ +import { kvGet, kvSet } from "@server/data/keyValueStore"; +import { Logger } from "@util/Logger"; +import { getRandomPokemon } from "./pokedex"; +import { getInventory, updateInventory } from "@server/data/inventory"; +import { getUser } from "@server/data/user"; +import { addItem } from "@server/items"; +import { getHHMMSS } from "@util/time"; + +const logger = new Logger("Daily Pokemon Manager"); +const oneDay = 1000 * 60 * 60 * 24; + +export async function claimDailyPokemon(userID: string) { + // Get the last daily timestamp + let timestampS = await kvGet(`pokedaily~${userID}`); + let timestamp = 0; + + if (typeof timestampS == "string") { + try { + timestamp = parseInt(timestampS); + } catch (err) { + logger.warn("Unable to parse JSON:", err); + } + } + + logger.debug("Time remaining:", Date.now() - timestamp); + + // Check if it has been over a day + if (Date.now() - timestamp > oneDay) { + // Give them a random pokemon and set new timestamp + const pokemon = getRandomPokemon(); + + const item = { + id: pokemon.pokeID.toString(), + name: pokemon.name, + pokeID: pokemon.pokeID, + base: pokemon.base, + type: pokemon.type, + count: 1, + objtype: "pokemon" + } as IPokemon; + + const user = await getUser(userID); + if (!user) throw new Error("No user found"); + + const inventory = await getInventory(user.inventoryId); + if (!inventory) throw new Error("No inventory found"); + + addItem(inventory.pokemon, pokemon); + await updateInventory(inventory); + kvSet(`pokedaily~${userID}`, Date.now().toString()); + + return `You claimed your daily Pokémon reward and got: ${ + item.emoji || "📦" + }${item.name}${item.count ? ` (x${item.count})` : ""}`; + } else { + // or tell them no + return `You already claimed today! Time remaining: ${getHHMMSS( + oneDay - (Date.now() - timestamp), + false + )}`; + } +} diff --git a/src/api/pokemon/pokedex.ts b/src/api/pokemon/pokedex.ts index b3599cf..2c41e7e 100644 --- a/src/api/pokemon/pokedex.ts +++ b/src/api/pokemon/pokedex.ts @@ -8,3 +8,8 @@ const logger = new Logger("Pokédex"); export function getPokemonByID(id: number) { return pokedex.find(p => p.pokeID == id); } + +export function getRandomPokemon() { + const r = Math.floor(Math.random() * pokedex.length); + return pokedex[r]; +} diff --git a/src/cli/index.ts b/src/cli/index.ts index 58485b6..edbe71d 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -1,8 +1,9 @@ import { Logger } from "@util/Logger"; import { createInterface } from "readline"; -import { EventEmitter } from "events"; +import { EventEmitter } from "node:events"; import gettRPC from "@util/api/trpc"; import { startAutorestart } from "@util/autorestart"; +import { CosmicColor } from "@util/CosmicColor"; const trpc = gettRPC(process.env.CLI_FISHING_TOKEN as string); @@ -15,7 +16,7 @@ const b = new EventEmitter(); const rl = createInterface({ input: process.stdin, output: process.stdout -}); +} as any); const user = { _id: "stdin", @@ -26,6 +27,12 @@ const user = { (globalThis as unknown as any).rl = rl; rl.setPrompt("> "); rl.prompt(); +setPrompt(); + +function setPrompt() { + const color = new CosmicColor(user.color); + rl.setPrompt(`\x1b[38;2;${color.r};${color.g};${color.b}m> `); +} rl.on("line", async line => { if (line == "stop" || line == "exit") process.exit(); @@ -94,6 +101,7 @@ setInterval(async () => { b.on("color", msg => { if (typeof msg.color !== "string" || typeof msg.id !== "string") return; user.color = msg.color; + setPrompt(); }); b.on("sendchat", msg => { diff --git a/src/util/time.ts b/src/util/time.ts index 7c7a907..0f4888f 100644 --- a/src/util/time.ts +++ b/src/util/time.ts @@ -1,22 +1,32 @@ -export function getHHMMSS() { - const now = Date.now(); +export function getTime(t = Date.now(), twelveHour = true) { + const now = t; const s = now / 1000; const m = s / 60; const h = m / 60; - const hh = Math.floor(h % 12) + const hours = Math.floor(h % (twelveHour ? 12 : 24)) .toString() .padStart(2, "0"); - const mm = Math.floor(m % 60) + const minutes = Math.floor(m % 60) .toString() .padStart(2, "0"); - const ss = Math.floor(s % 60) + const seconds = Math.floor(s % 60) .toString() .padStart(2, "0"); - const ms = Math.floor(now % 1000) + const milliseconds = Math.floor(now % 1000) .toString() .padStart(3, "0"); - return `${hh}:${mm}:${ss}.${ms}`; + return { + hours, + minutes, + seconds, + milliseconds + }; +} + +export function getHHMMSS(t = Date.now(), twelveHour = true) { + const { hours, minutes, seconds, milliseconds } = getTime(t, twelveHour); + return `${hours}:${minutes}:${seconds}.${milliseconds}`; } diff --git a/src/util/types.d.ts b/src/util/types.d.ts index 2dd06a0..cc27ceb 100644 --- a/src/util/types.d.ts +++ b/src/util/types.d.ts @@ -21,7 +21,7 @@ interface IContextProps { type TCommandCallback = (props: IContextProps) => Promise; -interface CountComponent { +interface ICountComponent { count: number; } @@ -54,7 +54,7 @@ interface IFish extends IObject { } interface IPokemon extends IObject { - id: number; + id: string; pokeID: number; objtype: "pokemon"; emoji?: string; @@ -78,6 +78,7 @@ type TPokemonSack = JsonArray & IPokemon[]; interface IInventory { id: number; balance: number; + location: string; items: TInventoryItems; fishSack: TFishSack; @@ -90,7 +91,7 @@ interface IBack extends Record { m: T; } -interface Backs extends Record> { +interface IBacks extends Record> { color: { m: "color"; };