Compare commits
No commits in common. "919e2ff7ad2633c1fb031ef65c71f3a00a482e92" and "5cc71cfdbbffcac6685cd67e409f1ac8e0d48d4b" have entirely different histories.
919e2ff7ad
...
5cc71cfdbb
|
@ -1,5 +1,3 @@
|
||||||
<img src="./fish_icon.png" width="64" alt="Crucian Carp" />
|
|
||||||
|
|
||||||
# fishing-api
|
# fishing-api
|
||||||
|
|
||||||
This is a rewrite of Brandon Lockaby's fishing bot for Multiplayer Piano.
|
This is a rewrite of Brandon Lockaby's fishing bot for Multiplayer Piano.
|
||||||
|
@ -18,16 +16,14 @@ Copy the default `.env` file:
|
||||||
cp .env.template .env
|
cp .env.template .env
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit that file to match your environment. Keep in mind that a connection token is required for all fishing service clients. These clients are for connecting to the actual service (such as Discord or MPP) and bridges the connection to the backend API. This way, the execution related to fishing itself is focused in its own process, instead of travelling between sending messages and processing fishing-related computations.
|
Edit that file to match your environment, then install packages:
|
||||||
|
|
||||||
Next, install the project's dependencies from npm:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
bunx prisma db push
|
bunx prisma db push
|
||||||
```
|
```
|
||||||
|
|
||||||
Run both the HTTP API and the clients for various services separately with these commands:
|
Run both the http server and the clients for various services separately with these commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun . # Main http server
|
bun . # Main http server
|
||||||
|
|
|
@ -2,3 +2,7 @@
|
||||||
channel:
|
channel:
|
||||||
id: "✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧"
|
id: "✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧"
|
||||||
allowColorChanging: true
|
allowColorChanging: true
|
||||||
|
- uri: wss://mppclone.com:8443
|
||||||
|
channel:
|
||||||
|
id: "test/fishing"
|
||||||
|
allowColorChanging: true
|
||||||
|
|
BIN
fish_icon.png
BIN
fish_icon.png
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
16
package.json
16
package.json
|
@ -8,22 +8,22 @@
|
||||||
"start-discord": "bun src/discord/index.ts"
|
"start-discord": "bun src/discord/index.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.1.9"
|
"@types/bun": "^1.1.6"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.19.1",
|
"@prisma/client": "^5.16.1",
|
||||||
"@trpc/client": "next",
|
"@trpc/client": "next",
|
||||||
"@trpc/server": "next",
|
"@trpc/server": "next",
|
||||||
"@types/node": "^20.16.5",
|
"@types/node": "^20.14.10",
|
||||||
"cli-markdown": "^3.4.0",
|
"cli-markdown": "^3.4.0",
|
||||||
"discord.js": "^14.16.2",
|
"discord.js": "^14.15.3",
|
||||||
"mpp-client-net": "^1.2.3",
|
"mpp-client-net": "^1.2.0",
|
||||||
"prisma": "^5.19.1",
|
"prisma": "^5.16.1",
|
||||||
"trpc-bun-adapter": "^1.1.2",
|
"trpc-bun-adapter": "^1.1.1",
|
||||||
"yaml": "^2.5.1",
|
"yaml": "^2.4.5",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import { fid } from "./util/fid";
|
||||||
import { chance } from "./util/chance";
|
import { chance } from "./util/chance";
|
||||||
import { info } from "./general/info";
|
import { info } from "./general/info";
|
||||||
import { burger } from "./util/burger";
|
import { burger } from "./util/burger";
|
||||||
import { daily } from "./pokemon/daily";
|
|
||||||
// import { give } from "./inventory/give";
|
// import { give } from "./inventory/give";
|
||||||
|
|
||||||
interface ICommandGroup {
|
interface ICommandGroup {
|
||||||
|
@ -55,23 +54,15 @@ commandGroups.push(fishingGroup);
|
||||||
const inventoryGroup: ICommandGroup = {
|
const inventoryGroup: ICommandGroup = {
|
||||||
id: "inventory",
|
id: "inventory",
|
||||||
displayName: "Inventory",
|
displayName: "Inventory",
|
||||||
commands: [inventory, sack, pokemon, take, eat, yeet, burger /* give */]
|
commands: [inventory, take, eat, sack, pokemon, yeet, burger /* give */]
|
||||||
};
|
};
|
||||||
|
|
||||||
commandGroups.push(inventoryGroup);
|
commandGroups.push(inventoryGroup);
|
||||||
|
|
||||||
const pokemonGroup: ICommandGroup = {
|
|
||||||
id: "pokemon",
|
|
||||||
displayName: "Pokémon",
|
|
||||||
commands: [daily, pokedex]
|
|
||||||
};
|
|
||||||
|
|
||||||
commandGroups.push(pokemonGroup);
|
|
||||||
|
|
||||||
const utilGroup: ICommandGroup = {
|
const utilGroup: ICommandGroup = {
|
||||||
id: "util",
|
id: "util",
|
||||||
displayName: "Utility",
|
displayName: "Utility",
|
||||||
commands: [data, setcolor, memory, autofish, fid, chance]
|
commands: [data, setcolor, memory, autofish, pokedex, fid, chance]
|
||||||
};
|
};
|
||||||
|
|
||||||
commandGroups.push(utilGroup);
|
commandGroups.push(utilGroup);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Command from "@server/commands/Command";
|
import Command from "@server/commands/Command";
|
||||||
import { logger } from "@server/commands/handler";
|
|
||||||
import { getInventory, updateInventory } from "@server/data/inventory";
|
import { getInventory, updateInventory } from "@server/data/inventory";
|
||||||
import { removeItem } from "@server/items";
|
|
||||||
import { itemBehaviorMap, runBehavior } from "@server/items/behavior";
|
import { itemBehaviorMap, runBehavior } from "@server/items/behavior";
|
||||||
|
|
||||||
export const eat = new Command(
|
export const eat = new Command(
|
||||||
|
@ -64,9 +62,45 @@ export const eat = new Command(
|
||||||
|
|
||||||
if (shouldRemove) {
|
if (shouldRemove) {
|
||||||
if (foundObject.objtype == "fish") {
|
if (foundObject.objtype == "fish") {
|
||||||
removeItem(inventory.fishSack, foundObject);
|
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;
|
||||||
|
}
|
||||||
} else if (foundObject.objtype == "item") {
|
} else if (foundObject.objtype == "item") {
|
||||||
removeItem(inventory.items, foundObject);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateInventory(inventory);
|
await updateInventory(inventory);
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
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
|
|
||||||
);
|
|
|
@ -1,16 +1,15 @@
|
||||||
import type { User } from "@prisma/client";
|
|
||||||
import prisma from "./prisma";
|
import prisma from "./prisma";
|
||||||
|
|
||||||
export async function createInventory(inventory: Partial<IInventory>) {
|
export async function createInventory(inventory: Partial<IInventory>) {
|
||||||
return (await prisma.inventory.create({
|
return await prisma.inventory.create({
|
||||||
data: inventory
|
data: inventory
|
||||||
})) as IInventory;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInventory(id: IInventory["id"]) {
|
export async function getInventory(id: IInventory["id"]) {
|
||||||
return (await prisma.inventory.findUnique({
|
return await prisma.inventory.findUnique({
|
||||||
where: { id }
|
where: { id }
|
||||||
})) as IInventory;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateInventory(inventory: Partial<IInventory>) {
|
export async function updateInventory(inventory: Partial<IInventory>) {
|
||||||
|
|
|
@ -108,11 +108,7 @@ export function hasFishTime(
|
||||||
|
|
||||||
export function getSizeString(cm: number) {
|
export function getSizeString(cm: number) {
|
||||||
const size =
|
const size =
|
||||||
cm < 5
|
cm < 30
|
||||||
? "microscopic"
|
|
||||||
: cm < 10
|
|
||||||
? "tiny"
|
|
||||||
: cm < 30
|
|
||||||
? "small"
|
? "small"
|
||||||
: cm < 60
|
: cm < 60
|
||||||
? "medium-sized"
|
? "medium-sized"
|
||||||
|
|
|
@ -23,26 +23,3 @@ export function addItem(arr: IObject[], item: IObject) {
|
||||||
(arr[i].count as number) += inc;
|
(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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
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
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,8 +8,3 @@ const logger = new Logger("Pokédex");
|
||||||
export function getPokemonByID(id: number) {
|
export function getPokemonByID(id: number) {
|
||||||
return pokedex.find(p => p.pokeID == id);
|
return pokedex.find(p => p.pokeID == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomPokemon() {
|
|
||||||
const r = Math.floor(Math.random() * pokedex.length);
|
|
||||||
return pokedex[r];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { Logger } from "@util/Logger";
|
import { Logger } from "@util/Logger";
|
||||||
import { createInterface } from "readline";
|
import { createInterface } from "readline";
|
||||||
import { EventEmitter } from "node:events";
|
import { EventEmitter } from "events";
|
||||||
import gettRPC from "@util/api/trpc";
|
import gettRPC from "@util/api/trpc";
|
||||||
import { startAutorestart } from "@util/autorestart";
|
import { startAutorestart } from "@util/autorestart";
|
||||||
import { CosmicColor } from "@util/CosmicColor";
|
|
||||||
|
|
||||||
const trpc = gettRPC(process.env.CLI_FISHING_TOKEN as string);
|
const trpc = gettRPC(process.env.CLI_FISHING_TOKEN as string);
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ const b = new EventEmitter();
|
||||||
const rl = createInterface({
|
const rl = createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout
|
||||||
} as any);
|
});
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
_id: "stdin",
|
_id: "stdin",
|
||||||
|
@ -27,12 +26,6 @@ const user = {
|
||||||
(globalThis as unknown as any).rl = rl;
|
(globalThis as unknown as any).rl = rl;
|
||||||
rl.setPrompt("> ");
|
rl.setPrompt("> ");
|
||||||
rl.prompt();
|
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 => {
|
rl.on("line", async line => {
|
||||||
if (line == "stop" || line == "exit") process.exit();
|
if (line == "stop" || line == "exit") process.exit();
|
||||||
|
@ -101,7 +94,6 @@ setInterval(async () => {
|
||||||
b.on("color", msg => {
|
b.on("color", msg => {
|
||||||
if (typeof msg.color !== "string" || typeof msg.id !== "string") return;
|
if (typeof msg.color !== "string" || typeof msg.id !== "string") return;
|
||||||
user.color = msg.color;
|
user.color = msg.color;
|
||||||
setPrompt();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
b.on("sendchat", msg => {
|
b.on("sendchat", msg => {
|
||||||
|
|
|
@ -1,32 +1,22 @@
|
||||||
export function getTime(t = Date.now(), twelveHour = true) {
|
export function getHHMMSS() {
|
||||||
const now = t;
|
const now = Date.now();
|
||||||
|
|
||||||
const s = now / 1000;
|
const s = now / 1000;
|
||||||
const m = s / 60;
|
const m = s / 60;
|
||||||
const h = m / 60;
|
const h = m / 60;
|
||||||
|
|
||||||
const hours = Math.floor(h % (twelveHour ? 12 : 24))
|
const hh = Math.floor(h % 12)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, "0");
|
.padStart(2, "0");
|
||||||
const minutes = Math.floor(m % 60)
|
const mm = Math.floor(m % 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, "0");
|
.padStart(2, "0");
|
||||||
const seconds = Math.floor(s % 60)
|
const ss = Math.floor(s % 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, "0");
|
.padStart(2, "0");
|
||||||
const milliseconds = Math.floor(now % 1000)
|
const ms = Math.floor(now % 1000)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(3, "0");
|
.padStart(3, "0");
|
||||||
|
|
||||||
return {
|
return `${hh}:${mm}:${ss}.${ms}`;
|
||||||
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}`;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface IContextProps {
|
||||||
|
|
||||||
type TCommandCallback<User> = (props: IContextProps) => Promise<string | void>;
|
type TCommandCallback<User> = (props: IContextProps) => Promise<string | void>;
|
||||||
|
|
||||||
interface ICountComponent {
|
interface CountComponent {
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ interface IFish extends IObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPokemon extends IObject {
|
interface IPokemon extends IObject {
|
||||||
id: string;
|
id: number;
|
||||||
pokeID: number;
|
pokeID: number;
|
||||||
objtype: "pokemon";
|
objtype: "pokemon";
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
|
@ -78,7 +78,6 @@ type TPokemonSack = JsonArray & IPokemon[];
|
||||||
interface IInventory {
|
interface IInventory {
|
||||||
id: number;
|
id: number;
|
||||||
balance: number;
|
balance: number;
|
||||||
location: string;
|
|
||||||
|
|
||||||
items: TInventoryItems;
|
items: TInventoryItems;
|
||||||
fishSack: TFishSack;
|
fishSack: TFishSack;
|
||||||
|
@ -91,7 +90,7 @@ interface IBack<T extends string | unknown> extends Record<string, unknown> {
|
||||||
m: T;
|
m: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IBacks extends Record<string, IBack<unknown>> {
|
interface Backs extends Record<string, IBack<unknown>> {
|
||||||
color: {
|
color: {
|
||||||
m: "color";
|
m: "color";
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue