Implement user and inventory, back messages, and color changing, add pokedex
This commit is contained in:
parent
11967df2ae
commit
29676fb502
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -21,7 +21,7 @@ model User {
|
||||||
|
|
||||||
model Inventory {
|
model Inventory {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
balance Int
|
balance Int @default(0)
|
||||||
|
|
||||||
items Json @default("[]")
|
items Json @default("[]")
|
||||||
fishSack Json @default("[]")
|
fishSack Json @default("[]")
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Logger } from "@util/Logger";
|
||||||
|
import { argv } from "bun";
|
||||||
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
import YAML from "yaml";
|
||||||
|
|
||||||
|
const logger = new Logger("JSON Converter");
|
||||||
|
|
||||||
|
const inFile = argv[2];
|
||||||
|
const outFile = argv[3];
|
||||||
|
|
||||||
|
if (typeof inFile !== "string" || typeof outFile !== "string") {
|
||||||
|
logger.error(`Usage: <infile> <outfile>`);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existsSync(inFile)) {
|
||||||
|
logger.error("Input file not found");
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Reading JSON...");
|
||||||
|
|
||||||
|
let data: unknown;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jdata = readFileSync(inFile).toString();
|
||||||
|
data = JSON.parse(jdata);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err);
|
||||||
|
logger.error("JSON read error");
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Writing file...");
|
||||||
|
writeFileSync(outFile, YAML.stringify(data));
|
||||||
|
logger.info("Done.");
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { getBacks, flushBacks } from "@server/backs";
|
||||||
import { handleCommand } from "@server/commands/handler";
|
import { handleCommand } from "@server/commands/handler";
|
||||||
import { prefixes } from "@server/commands/prefixes";
|
import { prefixes } from "@server/commands/prefixes";
|
||||||
import { checkToken } from "@server/data/token";
|
import { checkToken, tokenToID } from "@server/data/token";
|
||||||
import { TRPCError, initTRPC } from "@trpc/server";
|
import { TRPCError, initTRPC } from "@trpc/server";
|
||||||
import { Logger } from "@util/Logger";
|
import { Logger } from "@util/Logger";
|
||||||
import type { CreateBunContextOptions } from "trpc-bun-adapter";
|
import type { CreateBunContextOptions } from "trpc-bun-adapter";
|
||||||
|
@ -8,11 +9,21 @@ import { z } from "zod";
|
||||||
|
|
||||||
const logger = new Logger("tRPC");
|
const logger = new Logger("tRPC");
|
||||||
|
|
||||||
|
interface FishingContext {
|
||||||
|
isAuthed: boolean;
|
||||||
|
req: Request;
|
||||||
|
token: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuthedFishingContext extends FishingContext {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const createContext = async (opts: CreateBunContextOptions) => {
|
export const createContext = async (opts: CreateBunContextOptions) => {
|
||||||
return {
|
return {
|
||||||
isAuthed: false,
|
isAuthed: false,
|
||||||
req: opts.req
|
req: opts.req
|
||||||
};
|
} as FishingContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||||
|
@ -25,14 +36,15 @@ export const privateProcedure = publicProcedure.use(async opts => {
|
||||||
const { ctx } = opts;
|
const { ctx } = opts;
|
||||||
const { req } = ctx;
|
const { req } = ctx;
|
||||||
|
|
||||||
const token = req.headers.get("authorization");
|
opts.ctx.token = req.headers.get("authorization");
|
||||||
if (!token) throw new TRPCError({ code: "UNAUTHORIZED" });
|
if (!opts.ctx.token) throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
opts.ctx.isAuthed = await checkToken(opts.ctx.token);
|
||||||
opts.ctx.isAuthed = await checkToken(token);
|
|
||||||
|
|
||||||
if (!ctx.isAuthed) throw new TRPCError({ code: "UNAUTHORIZED" });
|
if (!ctx.isAuthed) throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
|
||||||
return opts.next(opts);
|
return opts.next({
|
||||||
|
ctx: opts.ctx as AuthedFishingContext
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
|
@ -54,11 +66,21 @@ export const appRouter = router({
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async opts => {
|
.query(async opts => {
|
||||||
|
const id = tokenToID(opts.ctx.token);
|
||||||
const { command, args, prefix, user } = opts.input;
|
const { command, args, prefix, user } = opts.input;
|
||||||
const out = await handleCommand(command, args, prefix, user);
|
const out = await handleCommand(id, command, args, prefix, user);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
backs: privateProcedure.query(async opts => {
|
||||||
|
const id = tokenToID(opts.ctx.token);
|
||||||
|
|
||||||
|
const backs = getBacks<{}>(id);
|
||||||
|
flushBacks(id);
|
||||||
|
|
||||||
|
return backs;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
export const backs: Record<string, IBack<unknown>[]> = {};
|
||||||
|
|
||||||
|
export function flushBacks<T>(id: string) {
|
||||||
|
backs[id] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBack<T>(id: string, back: IBack<T>) {
|
||||||
|
if (!backs[id]) backs[id] = [];
|
||||||
|
backs[id].push(back);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasBack<T>(id: string, back: IBack<T>) {
|
||||||
|
if (!backs[id]) return false;
|
||||||
|
if (backs[id].includes(back)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBacks<T>(id: string) {
|
||||||
|
if (!backs[id]) return [];
|
||||||
|
return backs[id] as T;
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { User } from "@prisma/client";
|
||||||
|
|
||||||
export class Command {
|
export class Command {
|
||||||
constructor(
|
constructor(
|
||||||
public id: string,
|
public id: string,
|
||||||
|
@ -5,7 +7,7 @@ export class Command {
|
||||||
public description: string,
|
public description: string,
|
||||||
public usage: string,
|
public usage: string,
|
||||||
public permissionNode: string,
|
public permissionNode: string,
|
||||||
public callback: TCommandCallback,
|
public callback: TCommandCallback<User>,
|
||||||
public visible: boolean = true
|
public visible: boolean = true
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ export const fish = new Command(
|
||||||
"Send your LURE into a water for catching fish",
|
"Send your LURE into a water for catching fish",
|
||||||
"fish",
|
"fish",
|
||||||
"command.fishing.fish",
|
"command.fishing.fish",
|
||||||
async () => {
|
async ({ id, command, args, prefix, part, user }) => {
|
||||||
return "There is no fishing yet, please come back later when I write the code for it";
|
return "There is no fishing yet, please come back later when I write the code for it";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,17 +18,30 @@ export const help = new Command(
|
||||||
"cammands",
|
"cammands",
|
||||||
"cummunds"
|
"cummunds"
|
||||||
],
|
],
|
||||||
"Help command",
|
"Get command list or command usage",
|
||||||
"help [command]",
|
"help [command]",
|
||||||
"command.general.help",
|
"command.general.help",
|
||||||
async (command, args, prefix, user) => {
|
async ({ id, command, args, prefix, part, user }) => {
|
||||||
return `${commandGroups
|
if (!args[0]) {
|
||||||
.map(
|
return `${commandGroups
|
||||||
group =>
|
.map(
|
||||||
`${group.displayName}: ${group.commands
|
group =>
|
||||||
.map(cmd => (cmd.visible ? cmd.aliases[0] : "<hidden>"))
|
`${group.displayName}: ${group.commands
|
||||||
.join(", ")}`
|
.map(cmd =>
|
||||||
)
|
cmd.visible ? cmd.aliases[0] : "<hidden>"
|
||||||
.join("\n")}`;
|
)
|
||||||
|
.join(", ")}`
|
||||||
|
)
|
||||||
|
.join("\n")}`;
|
||||||
|
} else {
|
||||||
|
const commands = commandGroups.flatMap(group => group.commands);
|
||||||
|
|
||||||
|
const foundCommand = commands.find(cmd =>
|
||||||
|
cmd.aliases.includes(args[0])
|
||||||
|
);
|
||||||
|
if (!foundCommand) return `Command "${args[0]}" not found.`;
|
||||||
|
|
||||||
|
return `Description: ${foundCommand.description} | Usage: ${foundCommand.usage}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { Command } from "../Command";
|
import type { Command } from "../Command";
|
||||||
import { fish } from "./fishing/fish";
|
import { fish } from "./fishing/fish";
|
||||||
import { help } from "./general/help";
|
import { help } from "./general/help";
|
||||||
|
import { setcolor } from "./util/setcolor";
|
||||||
import { data } from "./util/data";
|
import { data } from "./util/data";
|
||||||
|
|
||||||
interface ICommandGroup {
|
interface ICommandGroup {
|
||||||
|
@ -30,7 +31,7 @@ commandGroups.push(fishing);
|
||||||
const util: ICommandGroup = {
|
const util: ICommandGroup = {
|
||||||
id: "util",
|
id: "util",
|
||||||
displayName: "Utility",
|
displayName: "Utility",
|
||||||
commands: [data]
|
commands: [data, setcolor]
|
||||||
};
|
};
|
||||||
|
|
||||||
commandGroups.push(util);
|
commandGroups.push(util);
|
||||||
|
|
|
@ -6,7 +6,7 @@ export const data = new Command(
|
||||||
"Data command",
|
"Data command",
|
||||||
"data",
|
"data",
|
||||||
"command.util.data",
|
"command.util.data",
|
||||||
async (command, args, prefix, user) => {
|
async props => {
|
||||||
return JSON.stringify({ command, args, prefix, user });
|
return JSON.stringify(props);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { addBack } from "@server/backs";
|
||||||
|
import Command from "@server/commands/Command";
|
||||||
|
|
||||||
|
export const setcolor = new Command(
|
||||||
|
"setcolor",
|
||||||
|
["setcolor"],
|
||||||
|
"Set own user color",
|
||||||
|
"setcolor <color>",
|
||||||
|
"command.util.setcolor",
|
||||||
|
async ({ id, command, args, prefix, part, user }) => {
|
||||||
|
if (typeof args[0] !== "string") return "Please provide a color.";
|
||||||
|
|
||||||
|
addBack(id, {
|
||||||
|
m: "color",
|
||||||
|
id: part.id,
|
||||||
|
color: args[0]
|
||||||
|
});
|
||||||
|
|
||||||
|
return "Attempting to set color.";
|
||||||
|
}
|
||||||
|
);
|
|
@ -1,14 +1,17 @@
|
||||||
import { Logger } from "@util/Logger";
|
import { Logger } from "@util/Logger";
|
||||||
import type Command from "./Command";
|
import type Command from "./Command";
|
||||||
import { commandGroups } from "./groups";
|
import { commandGroups } from "./groups";
|
||||||
|
import { createUser, getUser } from "@server/data/user";
|
||||||
|
import { createInventory, getInventory } from "@server/data/inventory";
|
||||||
|
|
||||||
export const logger = new Logger("Command Handler");
|
export const logger = new Logger("Command Handler");
|
||||||
|
|
||||||
export async function handleCommand(
|
export async function handleCommand(
|
||||||
|
id: string,
|
||||||
command: string,
|
command: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
prefix: string,
|
prefix: string,
|
||||||
user: IUser
|
part: IPart
|
||||||
): Promise<ICommandResponse | void> {
|
): Promise<ICommandResponse | void> {
|
||||||
let foundCommand: Command | undefined;
|
let foundCommand: Command | undefined;
|
||||||
|
|
||||||
|
@ -22,21 +25,42 @@ export async function handleCommand(
|
||||||
|
|
||||||
if (!foundCommand) return;
|
if (!foundCommand) return;
|
||||||
|
|
||||||
|
let user = await getUser(part.id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
const inventory = await createInventory({});
|
||||||
|
|
||||||
|
user = await createUser({
|
||||||
|
id: part.id,
|
||||||
|
name: part.name,
|
||||||
|
color: part.color,
|
||||||
|
inventoryId: inventory.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let inventory = await getInventory(user.inventoryId);
|
||||||
|
|
||||||
|
if (!inventory) inventory = await createInventory({ id: user.inventoryId });
|
||||||
|
|
||||||
// TODO Check user's (or their groups') permissions against command permission node
|
// TODO Check user's (or their groups') permissions against command permission node
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await foundCommand.callback(
|
const response = await foundCommand.callback({
|
||||||
|
id,
|
||||||
command,
|
command,
|
||||||
args,
|
args,
|
||||||
prefix,
|
prefix,
|
||||||
|
part,
|
||||||
user
|
user
|
||||||
);
|
});
|
||||||
|
|
||||||
if (response) return { response };
|
if (response) return { response };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response:
|
response:
|
||||||
"An error has occurred, but no fish were lost. If you are the fishing bot owner, check the error logs for details."
|
"An error has occurred, but no fish were lost. If you are the fishing bot owner, check the server's error logs for details."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import prisma from "./prisma";
|
||||||
|
|
||||||
|
export async function createInventory(inventory: Partial<IInventory>) {
|
||||||
|
return await prisma.inventory.create({
|
||||||
|
data: inventory
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getInventory(id: IInventory["id"]) {
|
||||||
|
return await prisma.inventory.findUnique({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateInventory(inventory: Partial<IInventory>) {
|
||||||
|
return await prisma.inventory.update({
|
||||||
|
where: { id: inventory.id },
|
||||||
|
data: inventory
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteInventory(id: IInventory["id"]) {
|
||||||
|
return await prisma.inventory.delete({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import prisma from "./prisma";
|
import prisma from "./prisma";
|
||||||
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
export async function createToken() {
|
export async function createToken() {
|
||||||
const randomToken = crypto.randomUUID();
|
const randomToken = crypto.randomUUID();
|
||||||
|
@ -29,3 +30,10 @@ export async function checkToken(token: string) {
|
||||||
export async function getAllTokens() {
|
export async function getAllTokens() {
|
||||||
return await prisma.authToken.findMany();
|
return await prisma.authToken.findMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tokenToID(token: string) {
|
||||||
|
const hash = createHash("sha-256");
|
||||||
|
hash.update("ID");
|
||||||
|
hash.update(token);
|
||||||
|
return hash.digest("hex").substring(0, 24);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import type { User } from "@prisma/client";
|
||||||
|
import prisma from "./prisma";
|
||||||
|
|
||||||
|
export async function createUser(user: User) {
|
||||||
|
return await prisma.user.create({
|
||||||
|
data: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUser(id: string) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(user: Partial<User> & { id: User["id"] }) {
|
||||||
|
return await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id
|
||||||
|
},
|
||||||
|
data: user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUser(id: string) {
|
||||||
|
return await prisma.user.delete({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import Client from "mpp-client-net";
|
import Client from "mpp-client-net";
|
||||||
import { Logger } from "@util/Logger";
|
import { Logger } from "@util/Logger";
|
||||||
import trpc from "@client/api/trpc";
|
import trpc from "@client/api/trpc";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
export interface MPPNetBotConfig {
|
export interface MPPNetBotConfig {
|
||||||
uri: string;
|
uri: string;
|
||||||
|
@ -13,7 +14,7 @@ export interface MPPNetBotConfig {
|
||||||
|
|
||||||
export class MPPNetBot {
|
export class MPPNetBot {
|
||||||
public client: Client;
|
public client: Client;
|
||||||
|
public b = new EventEmitter();
|
||||||
public logger: Logger;
|
public logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -78,7 +79,8 @@ export class MPPNetBot {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!command) return;
|
if (!command) return;
|
||||||
if (command.response) this.sendChat(command.response);
|
if (command.response)
|
||||||
|
this.sendChat(command.response, (msg as any).id);
|
||||||
});
|
});
|
||||||
|
|
||||||
(this.client as unknown as any).on(
|
(this.client as unknown as any).on(
|
||||||
|
@ -143,24 +145,52 @@ export class MPPNetBot {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!command) return;
|
if (!command) return;
|
||||||
if (command.response) this.sendChat(command.response);
|
if (command.response) this.sendChat(command.response, msg.id);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const backs = (await trpc.backs.query()) as IBack<unknown>[];
|
||||||
|
if (backs.length > 0) {
|
||||||
|
this.logger.debug(backs);
|
||||||
|
for (const back of backs) {
|
||||||
|
if (typeof back.m !== "string") return;
|
||||||
|
this.b.emit(back.m, back);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, 1000 / 20);
|
||||||
|
|
||||||
|
this.b.on("color", msg => {
|
||||||
|
if (typeof msg.color !== "string" || typeof msg.id !== "string")
|
||||||
|
return;
|
||||||
|
this.client.sendArray([
|
||||||
|
{
|
||||||
|
m: "setcolor",
|
||||||
|
_id: msg.id,
|
||||||
|
color: msg.color
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendChat(text: string) {
|
public sendChat(text: string, reply?: string) {
|
||||||
let lines = text.split("\n");
|
let lines = text.split("\n");
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.length <= 510) {
|
if (line.length <= 510) {
|
||||||
this.client.sendArray([
|
(this.client as any).sendArray([
|
||||||
{
|
{
|
||||||
m: "a",
|
m: "a",
|
||||||
message: `\u034f${line
|
message: `\u034f${line
|
||||||
.split("\t")
|
.split("\t")
|
||||||
.join("")
|
.join("")
|
||||||
.split("\r")
|
.split("\r")
|
||||||
.join("")}`
|
.join("")}`,
|
||||||
|
reply_to: reply
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
interface IUser {
|
interface IPart {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
@ -8,9 +8,60 @@ interface ICommandResponse {
|
||||||
response: string;
|
response: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TCommandCallback = (
|
type TCommandCallback<User> = (props: {
|
||||||
command: string,
|
id: string;
|
||||||
args: string[],
|
command: string;
|
||||||
prefix: string,
|
args: string[];
|
||||||
user: IUser
|
prefix: string;
|
||||||
) => Promise<string | void>;
|
part: IPart;
|
||||||
|
user: User;
|
||||||
|
}) => Promise<string | void>;
|
||||||
|
|
||||||
|
interface CountComponent {
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFish extends JsonValue {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
size: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPokemon extends JsonValue {
|
||||||
|
id: number;
|
||||||
|
name: {
|
||||||
|
english: string;
|
||||||
|
japanese: string;
|
||||||
|
chinese: string;
|
||||||
|
french: string;
|
||||||
|
};
|
||||||
|
type: string[];
|
||||||
|
base: {
|
||||||
|
HP: number;
|
||||||
|
Attack: number;
|
||||||
|
Defense: number;
|
||||||
|
"Sp. Attack": number;
|
||||||
|
"Sp. Defense": number;
|
||||||
|
Speed: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type TFishSack = JsonArray & IFish[];
|
||||||
|
type TPokemonSack = JsonArray & IPokemon[];
|
||||||
|
|
||||||
|
interface IInventory {
|
||||||
|
id: number;
|
||||||
|
balance: number;
|
||||||
|
fishSack: TFishSack;
|
||||||
|
pokemon: TPokemonSack;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBack<T extends string | unknown> extends Record<string, unknown> {
|
||||||
|
m: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Backs extends Record<string, IBack<unknown>> {
|
||||||
|
color: {
|
||||||
|
m: "color";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {
|
||||||
|
createInventory,
|
||||||
|
deleteInventory,
|
||||||
|
getInventory
|
||||||
|
} from "@server/data/inventory";
|
||||||
|
import { test, expect } from "bun:test";
|
||||||
|
|
||||||
|
test("Inventory can be created, read, updated and deleted", async () => {
|
||||||
|
const inventory = await createInventory({});
|
||||||
|
expect(inventory.id).toBeNumber();
|
||||||
|
|
||||||
|
await deleteInventory(inventory.id);
|
||||||
|
|
||||||
|
const badInventory = await getInventory(inventory.id);
|
||||||
|
expect(badInventory).toBeNull();
|
||||||
|
});
|
|
@ -1,4 +1,9 @@
|
||||||
import { checkToken, createToken, deleteToken } from "@server/data/token";
|
import {
|
||||||
|
checkToken,
|
||||||
|
createToken,
|
||||||
|
deleteToken,
|
||||||
|
tokenToID
|
||||||
|
} from "@server/data/token";
|
||||||
import { test, expect } from "bun:test";
|
import { test, expect } from "bun:test";
|
||||||
|
|
||||||
test("Token can be created and deleted", async () => {
|
test("Token can be created and deleted", async () => {
|
||||||
|
@ -26,3 +31,14 @@ test("Token can be invalidated", async () => {
|
||||||
const checked = await checkToken(token);
|
const checked = await checkToken(token);
|
||||||
expect(checked).toBeFalsy();
|
expect(checked).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Token can be digested into ID", async () => {
|
||||||
|
const token = await createToken();
|
||||||
|
expect(token).toBeString();
|
||||||
|
|
||||||
|
const id = tokenToID(token);
|
||||||
|
expect(id).toBeString();
|
||||||
|
expect(id).toHaveLength(24);
|
||||||
|
|
||||||
|
await deleteToken(token);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { User } from "@prisma/client";
|
||||||
|
import { createInventory } from "@server/data/inventory";
|
||||||
|
import { createUser, getUser, updateUser, deleteUser } from "@server/data/user";
|
||||||
|
import { test, expect } from "bun:test";
|
||||||
|
|
||||||
|
test("User can be created, read, updated, and deleted", async () => {
|
||||||
|
const inventory = await createInventory({});
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
id: "test",
|
||||||
|
name: "test",
|
||||||
|
color: "#8d3f50",
|
||||||
|
inventoryId: inventory.id
|
||||||
|
};
|
||||||
|
|
||||||
|
await createUser(data);
|
||||||
|
const user = await getUser(data.id);
|
||||||
|
|
||||||
|
expect(user).toBeDefined();
|
||||||
|
expect(user?.id).toBeString();
|
||||||
|
expect(user?.name).toBeString();
|
||||||
|
|
||||||
|
await updateUser({
|
||||||
|
id: data.id,
|
||||||
|
name: "hi"
|
||||||
|
});
|
||||||
|
|
||||||
|
const user2 = await getUser(data.id);
|
||||||
|
|
||||||
|
expect(user2).toBeDefined();
|
||||||
|
expect(user2?.name).toBeString();
|
||||||
|
|
||||||
|
await deleteUser((user as User).id);
|
||||||
|
});
|
Loading…
Reference in New Issue