Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
Hri7566 | bb4b41c440 | |
Hri7566 | add6749a51 | |
Hri7566 | bbce118370 | |
Hri7566 | 6a21aa0cbd | |
Hri7566 | 41d2330f28 | |
Hri7566 | 6a4021d5ae | |
Hri7566 | cdaba3c951 | |
Hri7566 | 99ccca2412 | |
Hri7566 | 6db28fd2bc | |
Hri7566 | 35ba9ea0cc |
|
@ -1,7 +1,11 @@
|
||||||
desiredUser:
|
desiredUser:
|
||||||
name: "🟇 𝙎𝙪𝙥𝙚𝙧 Cosmic"
|
#name: "🟇 𝙎𝙪𝙥𝙚𝙧 Cosmic"
|
||||||
|
name: "🟇 Cosmic ⚝ *help"
|
||||||
color: "#1d0054"
|
color: "#1d0054"
|
||||||
agents:
|
agents:
|
||||||
|
#wss://smnmpp.hri7566.info:8443:
|
||||||
|
# - id: "lobby"
|
||||||
wss://mppclone.com:
|
wss://mppclone.com:
|
||||||
- id: "✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧"
|
#- id: "✧𝓓𝓔𝓥 𝓡𝓸𝓸𝓶✧"
|
||||||
- id: "test/awkward"
|
#- id: "Hree7566"
|
||||||
|
- id: "{midi-test}"
|
||||||
|
|
|
@ -3,3 +3,5 @@ prefixes:
|
||||||
spaced: false
|
spaced: false
|
||||||
- id: cosmic
|
- id: cosmic
|
||||||
spaced: true
|
spaced: true
|
||||||
|
- id: c
|
||||||
|
spaced: false
|
||||||
|
|
|
@ -4,6 +4,7 @@ NONE:
|
||||||
- cosmic.commandGroup.general
|
- cosmic.commandGroup.general
|
||||||
- cosmic.command.inventory
|
- cosmic.command.inventory
|
||||||
- cosmic.command.balance
|
- cosmic.command.balance
|
||||||
|
- cosmic.command.bake
|
||||||
- cosmic.command.magic8ball
|
- cosmic.command.magic8ball
|
||||||
- cosmic.command.color
|
- cosmic.command.color
|
||||||
- cosmic.command.id
|
- cosmic.command.id
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
debug: false
|
debug: true
|
||||||
enableConsole: false
|
enableConsole: true
|
||||||
enableMPP: true
|
enableMPP: true
|
||||||
enableDiscord: true
|
enableDiscord: false
|
||||||
enableSwitchChat: false
|
enableSwitchChat: false
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "supercosmic",
|
"name": "supercosmic",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"module": "src/index.ts",
|
"module": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bun-types": "latest"
|
"bun-types": "^1.0.16"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.5.2",
|
"@prisma/client": "^5.5.2",
|
||||||
"@t3-oss/env-core": "^0.7.1",
|
"@t3-oss/env-core": "^0.7.1",
|
||||||
"discord.js": "^14.14.1",
|
"discord.js": "^14.14.1",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"hyperimport": "^0.1.0",
|
"hyperimport": "^0.1.0",
|
||||||
"mathjs": "^11.11.2",
|
"mathjs": "^11.11.2",
|
||||||
"mpp-client-net": "^1.1.3",
|
"mpp-client-net": "^1.1.3",
|
||||||
"mpp-client-xt": "^1.3.1",
|
"mpp-client-xt": "^1.3.1",
|
||||||
"prisma": "^5.4.2",
|
"prisma": "^5.4.2",
|
||||||
"switchchat": "^3.2.1",
|
"switchchat": "^3.2.1",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.2",
|
||||||
"yaml": "^2.3.3",
|
"yaml": "^2.3.3",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,3 +34,8 @@ enum Role {
|
||||||
ADMINISTRATOR
|
ADMINISTRATOR
|
||||||
OWNER
|
OWNER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model KeyValueStore {
|
||||||
|
id Int @id @unique @default(1)
|
||||||
|
data Json @default("{}")
|
||||||
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ export type BaseCommandMessage<T = unknown> = Omit<
|
||||||
|
|
||||||
export class CommandHandler {
|
export class CommandHandler {
|
||||||
public static commandGroups = new Array<CommandGroup>();
|
public static commandGroups = new Array<CommandGroup>();
|
||||||
public static prefixes = new Array<Prefix>();
|
public static prefixes = new Set<Prefix>();
|
||||||
|
|
||||||
public static logger = new Logger("Command Handler");
|
public static logger = new Logger("Command Handler");
|
||||||
|
|
||||||
|
@ -172,5 +172,12 @@ export class CommandHandler {
|
||||||
|
|
||||||
// Add prefixes
|
// Add prefixes
|
||||||
for (const prefix of prefixConfig.prefixes) {
|
for (const prefix of prefixConfig.prefixes) {
|
||||||
CommandHandler.prefixes.push(prefix);
|
CommandHandler.prefixes.add(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store commands for hot reload
|
||||||
|
declare global {
|
||||||
|
var commandHandler: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.commandHandler ??= CommandHandler;
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { formatBalance } from "../../../economy/Balance";
|
||||||
|
import { startBaking } from "../../../economy/baking";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
export const bake = new Command(
|
||||||
|
"bake",
|
||||||
|
["bake", "b", "startbaking", "bakecake", "oven"],
|
||||||
|
"bozo's oven",
|
||||||
|
"bake",
|
||||||
|
(msg, agent) => {
|
||||||
|
const agentId = globalThis.serviceLoader.getAgentId(agent);
|
||||||
|
const message = startBaking(msg.user.id, agentId);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
);
|
|
@ -8,6 +8,6 @@ export const balance = new Command(
|
||||||
"balance",
|
"balance",
|
||||||
msg => {
|
msg => {
|
||||||
const bal = msg.inventory.balance;
|
const bal = msg.inventory.balance;
|
||||||
return `Balance: ${formatBalance(bal)}`;
|
return `Your balance: ${formatBalance(bal)}`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { KekklefruitTree } from "../../../economy/kekkle";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
export const grow = new Command(
|
||||||
|
"grow",
|
||||||
|
["grow"],
|
||||||
|
"grow bozo's kekklefruit (forcefully)",
|
||||||
|
"grow [number]",
|
||||||
|
async msg => {
|
||||||
|
let num: number;
|
||||||
|
|
||||||
|
if (msg.argv[1]) {
|
||||||
|
num = parseInt(msg.argv[1]);
|
||||||
|
if (isNaN(num)) return `Need number bozo`;
|
||||||
|
} else {
|
||||||
|
num = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
await KekklefruitTree.growFruit(num);
|
||||||
|
|
||||||
|
return `You grew ${num} fruit.`;
|
||||||
|
}
|
||||||
|
);
|
|
@ -1,4 +1,5 @@
|
||||||
import { Item, StackableItem } from "../../../economy/Item";
|
import { readItems } from "../../../data/inventory";
|
||||||
|
import { CakeItem, Item, StackableItem } from "../../../economy/Item";
|
||||||
import { Command } from "../../Command";
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
export const inventory = new Command(
|
export const inventory = new Command(
|
||||||
|
@ -6,15 +7,18 @@ export const inventory = new Command(
|
||||||
["inventory", "inv"],
|
["inventory", "inv"],
|
||||||
"get bozo's inventory",
|
"get bozo's inventory",
|
||||||
"inventory",
|
"inventory",
|
||||||
msg => {
|
async msg => {
|
||||||
const items = msg.inventory.items as unknown as Item[];
|
const items = await readItems(msg.p._id);
|
||||||
|
if (!items) return `Items: (none)`;
|
||||||
|
|
||||||
|
console.log(typeof items, items);
|
||||||
|
|
||||||
const list = items
|
const list = items
|
||||||
.map(
|
.map(
|
||||||
i =>
|
i =>
|
||||||
`${i.name}${
|
`${(i as CakeItem).emoji ? (i as CakeItem).emoji : ""}${i.name}${(i as StackableItem).count
|
||||||
(i as StackableItem).count
|
? " " + `(x${(i as StackableItem).count})`
|
||||||
? " " + (i as StackableItem).count
|
: ""
|
||||||
: ""
|
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
.join(" | ");
|
.join(" | ");
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { JsonArray, JsonValue } from "@prisma/client/runtime/library";
|
||||||
|
import { KekklefruitTree } from "../../../economy/kekkle";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
import { addItem, updateInventory } from "../../../data/inventory";
|
||||||
|
|
||||||
|
export const pick = new Command(
|
||||||
|
"pick",
|
||||||
|
["pick"],
|
||||||
|
"bozo will pick fruit off the kekklefruit tree",
|
||||||
|
"pick",
|
||||||
|
async msg => {
|
||||||
|
const fruit = await KekklefruitTree.pickFruit();
|
||||||
|
|
||||||
|
if (!fruit)
|
||||||
|
return `There are not enough fruit on the kekklefruit tree.`;
|
||||||
|
|
||||||
|
addItem(msg.p._id, fruit);
|
||||||
|
return `(insert random boring message about ${fruit.name} here)`;
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { KekklefruitTree } from "../../../economy/kekkle";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
export const tree = new Command(
|
||||||
|
"tree",
|
||||||
|
["tree"],
|
||||||
|
"bozo will get the amount of fruit on the kekklefruit tree",
|
||||||
|
"tree",
|
||||||
|
async msg => {
|
||||||
|
return `There are ${KekklefruitTree.getFruitCount()} kekklefruit on the tree.`;
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { readItems, subtractItem } from "../../../data/inventory";
|
||||||
|
import { FoodItem } from "../../../economy/Item";
|
||||||
|
import { TEatBhv, eatBhv } from "../../../economy/eatBhv";
|
||||||
|
import { Logger } from "../../../util/Logger";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
const logger = new Logger("eat");
|
||||||
|
|
||||||
|
export const eat = new Command(
|
||||||
|
"eat",
|
||||||
|
["eat"],
|
||||||
|
"bozo hungy",
|
||||||
|
"eat",
|
||||||
|
async (msg, agent) => {
|
||||||
|
if (!msg.argv[1]) return "What do you want to eat?";
|
||||||
|
const itemFuzzy = msg.argv[1].toLowerCase();
|
||||||
|
const items = await readItems(msg.user.id);
|
||||||
|
if (!items) return "You have nothing to eat.";
|
||||||
|
|
||||||
|
let what = items.find(item => {
|
||||||
|
if (item.name.toLowerCase().includes(itemFuzzy)) return true;
|
||||||
|
}) as FoodItem | undefined;
|
||||||
|
|
||||||
|
if (!what) return `You don't have any "${itemFuzzy}" to eat.`;
|
||||||
|
if (!what.consumable) return `You can't eat the ${what.name}.`;
|
||||||
|
if (!what.edible) return `The ${what.name} is not edible.`;
|
||||||
|
|
||||||
|
// TODO cause eat behavior
|
||||||
|
let bhv: TEatBhv | undefined;
|
||||||
|
|
||||||
|
if (what.id.includes("cake")) {
|
||||||
|
// Find regular cake behavior
|
||||||
|
bhv = eatBhv.get("cake");
|
||||||
|
} else {
|
||||||
|
// Find eat bhv for item ID
|
||||||
|
bhv = eatBhv.get(what.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bhv) return `You don't know how to eat ${what.name}.`;
|
||||||
|
const reply = await bhv(msg, agent, what);
|
||||||
|
|
||||||
|
if (reply.consumed) {
|
||||||
|
const worked = await subtractItem(msg.user.id, what);
|
||||||
|
if (!worked) logger.warn(`Failed to subtract item ${what.name} from user ${msg.user.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.output;
|
||||||
|
//return `You ate the ${what.name}. (not really)`;
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Client from "mpp-client-net";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
export const crown = new Command(
|
||||||
|
"crown",
|
||||||
|
["crown"],
|
||||||
|
"get bozo hat",
|
||||||
|
"crown",
|
||||||
|
(msg, agent) => {
|
||||||
|
if (agent.platform !== "mpp") return;
|
||||||
|
(agent.client as Client).sendArray([{
|
||||||
|
m: "chown",
|
||||||
|
id: msg.p.platformId
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
);
|
|
@ -1,4 +1,4 @@
|
||||||
import type { MPPAgent } from "../../../services/mpp";
|
import type { MPPNetAgent } from "../../../services/mppnet";
|
||||||
import { Command } from "../../Command";
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
export const cursor = new Command(
|
export const cursor = new Command(
|
||||||
|
@ -10,7 +10,7 @@ export const cursor = new Command(
|
||||||
if (agent.platform !== "mpp") return;
|
if (agent.platform !== "mpp") return;
|
||||||
if (!msg.argv[1]) return "Specify a mode.";
|
if (!msg.argv[1]) return "Specify a mode.";
|
||||||
|
|
||||||
const cursor = (agent as MPPAgent).cursor;
|
const cursor = (agent as MPPNetAgent).cursor;
|
||||||
cursor.props.currentAnimation = msg.argv[1];
|
cursor.props.currentAnimation = msg.argv[1];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { deleteInventory } from "../../../data/inventory";
|
||||||
|
import { Command } from "../../Command";
|
||||||
|
|
||||||
|
export const delinv = new Command(
|
||||||
|
"delinv",
|
||||||
|
["delinv"],
|
||||||
|
"delete a bozo's inventory",
|
||||||
|
"delinv [id]",
|
||||||
|
async (msg) => {
|
||||||
|
let userId = msg.argv[1] ? msg.argv[1] : msg.p._id;
|
||||||
|
await deleteInventory(userId);
|
||||||
|
return `Inventory of \`${userId}\` deleted.`
|
||||||
|
}
|
||||||
|
);
|
|
@ -6,7 +6,7 @@ export const id = new Command(
|
||||||
"get your id bozo",
|
"get your id bozo",
|
||||||
"id",
|
"id",
|
||||||
(msg, agent) => {
|
(msg, agent) => {
|
||||||
if (agent.platform == "mpp") {
|
if (agent.platform === "mpp") {
|
||||||
return `ID: \`${
|
return `ID: \`${
|
||||||
(msg.originalMessage as any).p._id
|
(msg.originalMessage as any).p._id
|
||||||
}\` Cosmic ID: \`${msg.p._id}\``;
|
}\` Cosmic ID: \`${msg.p._id}\``;
|
||||||
|
|
|
@ -6,10 +6,12 @@ export const memory = new Command(
|
||||||
"get the memory bozo",
|
"get the memory bozo",
|
||||||
"memory",
|
"memory",
|
||||||
() => {
|
() => {
|
||||||
return `${(process.memoryUsage().heapUsed / 1000 / 1000).toFixed(
|
const mem = process.memoryUsage();
|
||||||
2
|
|
||||||
)} MB used / ${(process.memoryUsage().heapTotal / 1000 / 1000).toFixed(
|
return `${(mem.heapUsed / 1000 / 1000).toFixed(2)} MB / ${(
|
||||||
2
|
mem.heapTotal /
|
||||||
)} MB total`;
|
1000 /
|
||||||
|
1000
|
||||||
|
).toFixed(2)} MB / ${(mem.rss / 1000 / 1000).toFixed(2)} MB`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,9 +18,7 @@ export const permissions = new Command(
|
||||||
if (msg.argv[1]) role = fuzzyFindRoleByDisplayName(msg.argv[1]);
|
if (msg.argv[1]) role = fuzzyFindRoleByDisplayName(msg.argv[1]);
|
||||||
|
|
||||||
if (role) {
|
if (role) {
|
||||||
return `Permissions for role "${
|
return `Permissions for role \`${role.displayName}\`: \`${role.permissions.join("\` | \`")}\``;
|
||||||
role.displayName
|
|
||||||
}": ${role.permissions.join(" | ")}`;
|
|
||||||
} else {
|
} else {
|
||||||
return `No role found.`;
|
return `No role found.`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { CommandGroup } from "./CommandGroup";
|
import { CommandGroup } from "./CommandGroup";
|
||||||
import { CommandHandler } from "./CommandHandler";
|
|
||||||
import { about } from "./commands/general/about";
|
import { about } from "./commands/general/about";
|
||||||
import { help } from "./commands/general/help";
|
import { help } from "./commands/general/help";
|
||||||
import { id } from "./commands/utility/id";
|
import { id } from "./commands/utility/id";
|
||||||
|
@ -16,20 +15,27 @@ import { uptime } from "./commands/utility/uptime";
|
||||||
import { balance } from "./commands/economy/balance";
|
import { balance } from "./commands/economy/balance";
|
||||||
import { permissions } from "./commands/utility/permissions";
|
import { permissions } from "./commands/utility/permissions";
|
||||||
import { branch } from "./commands/utility/branch";
|
import { branch } from "./commands/utility/branch";
|
||||||
|
import { tree } from "./commands/economy/tree";
|
||||||
|
import { pick } from "./commands/economy/pick";
|
||||||
|
import { grow } from "./commands/economy/grow";
|
||||||
|
import { delinv } from "./commands/utility/delinv";
|
||||||
|
import { eat } from "./commands/fun/eat";
|
||||||
|
import { bake } from "./commands/economy/bake";
|
||||||
|
import { crown } from "./commands/utility/crown";
|
||||||
|
|
||||||
export function loadCommands() {
|
export function loadCommands() {
|
||||||
// cringe
|
// cringe
|
||||||
const general = new CommandGroup("general", "⭐ General");
|
const general = new CommandGroup("general", "⭐ General");
|
||||||
general.addCommands([help, about]);
|
general.addCommands([help, about]);
|
||||||
CommandHandler.addCommandGroup(general);
|
globalThis.commandHandler.addCommandGroup(general);
|
||||||
|
|
||||||
const economy = new CommandGroup("economy", "💸 Economy");
|
const economy = new CommandGroup("economy", "💸 Economy");
|
||||||
economy.addCommands([inventory, balance]);
|
economy.addCommands([inventory, balance, tree, pick, grow, bake]);
|
||||||
CommandHandler.addCommandGroup(economy);
|
globalThis.commandHandler.addCommandGroup(economy);
|
||||||
|
|
||||||
const fun = new CommandGroup("fun", "✨ Fun");
|
const fun = new CommandGroup("fun", "✨ Fun");
|
||||||
fun.addCommands([magic8ball]);
|
fun.addCommands([magic8ball, eat]);
|
||||||
CommandHandler.addCommandGroup(fun);
|
globalThis.commandHandler.addCommandGroup(fun);
|
||||||
|
|
||||||
const utility = new CommandGroup("utility", "🔨 Utility");
|
const utility = new CommandGroup("utility", "🔨 Utility");
|
||||||
utility.addCommands([
|
utility.addCommands([
|
||||||
|
@ -43,7 +49,11 @@ export function loadCommands() {
|
||||||
ic,
|
ic,
|
||||||
uptime,
|
uptime,
|
||||||
permissions,
|
permissions,
|
||||||
branch
|
branch,
|
||||||
|
delinv,
|
||||||
|
crown
|
||||||
]);
|
]);
|
||||||
CommandHandler.addCommandGroup(utility);
|
globalThis.commandHandler.addCommandGroup(utility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { CommandGroup };
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Inventory } from "@prisma/client";
|
import { Inventory } from "@prisma/client";
|
||||||
import { prisma } from "./prisma";
|
import { prisma } from "./prisma";
|
||||||
|
import { JsonArray } from "@prisma/client/runtime/library";
|
||||||
|
import { Item, StackableItem } from "../economy/Item";
|
||||||
|
|
||||||
export async function createInventory(data: Omit<Inventory, "id">) {
|
export async function createInventory(data: Omit<Inventory, "id">) {
|
||||||
await prisma.inventory.create({
|
await prisma.inventory.create({
|
||||||
|
@ -14,13 +16,118 @@ export async function readInventory(userId: Inventory["userId"]) {
|
||||||
return await prisma.inventory.findUnique({ where: { userId: userId } });
|
return await prisma.inventory.findUnique({ where: { userId: userId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function readItems(userId: Inventory["userId"]) {
|
||||||
|
const inv = await readInventory(userId);
|
||||||
|
if (!inv) return null;
|
||||||
|
|
||||||
|
console.log("bruh", inv.items, "end bruh");
|
||||||
|
|
||||||
|
// prisma why? pick one!!!
|
||||||
|
if (typeof inv.items !== "string") return inv.items as unknown as Item[];
|
||||||
|
return JSON.parse(inv.items) as Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collapseInventory(inventoryData: Item[]) {
|
||||||
|
let newItems: Item[] = [];
|
||||||
|
|
||||||
|
oldLoop:
|
||||||
|
for (let i of inventoryData) {
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
newLoop:
|
||||||
|
for (let j of newItems) {
|
||||||
|
if (i.id === j.id) {
|
||||||
|
// Merge
|
||||||
|
if (
|
||||||
|
typeof (i as StackableItem).count === "number" &&
|
||||||
|
typeof (j as StackableItem).count === "number"
|
||||||
|
) {
|
||||||
|
(i as StackableItem).count += (j as StackableItem).count;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break newLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add
|
||||||
|
if (!found) newItems.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < inventoryData.length; i++) {
|
||||||
|
if (i <= 0) continue;
|
||||||
|
|
||||||
|
if (inventoryData[i].id === inventoryData[i - 1].id) {
|
||||||
|
if (
|
||||||
|
typeof (inventoryData[i - 1] as StackableItem).count ===
|
||||||
|
"number" &&
|
||||||
|
typeof (inventoryData[i] as StackableItem).count === "number"
|
||||||
|
) {
|
||||||
|
(inventoryData[i - 1] as StackableItem).count += (
|
||||||
|
inventoryData[i] as StackableItem
|
||||||
|
).count;
|
||||||
|
inventoryData.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateInventory(data: Omit<Inventory, "id">) {
|
export async function updateInventory(data: Omit<Inventory, "id">) {
|
||||||
|
collapseInventory(data.items as unknown as Item[]);
|
||||||
return await prisma.inventory.update({
|
return await prisma.inventory.update({
|
||||||
where: { userId: data.userId },
|
where: { userId: data.userId },
|
||||||
data: {}
|
data: {
|
||||||
|
balance: data.balance,
|
||||||
|
items: data.items as JsonArray
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteInventory(userId: Inventory["userId"]) {
|
export async function deleteInventory(userId: Inventory["userId"]) {
|
||||||
return await prisma.inventory.delete({ where: { userId } });
|
return await prisma.inventory.delete({ where: { userId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addItem<T extends Item>(
|
||||||
|
userId: Inventory["userId"],
|
||||||
|
item: T
|
||||||
|
) {
|
||||||
|
let inventory = await readInventory(userId);
|
||||||
|
if (!inventory) return false;
|
||||||
|
|
||||||
|
(inventory.items as unknown as Item[]).push(item);
|
||||||
|
collapseInventory(inventory.items as unknown as Item[]);
|
||||||
|
|
||||||
|
await updateInventory(inventory);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function subtractItem<T extends Item>(
|
||||||
|
userId: Inventory["userId"],
|
||||||
|
item: T
|
||||||
|
) {
|
||||||
|
let inventory = await readInventory(userId);
|
||||||
|
if (!inventory) return false;
|
||||||
|
|
||||||
|
if ((item as unknown as StackableItem).count) {
|
||||||
|
const it = (inventory.items as unknown as Item[]).find(
|
||||||
|
it => it.id == item.id
|
||||||
|
);
|
||||||
|
if (!it) return false;
|
||||||
|
(it as StackableItem).count--;
|
||||||
|
} else {
|
||||||
|
const it = (inventory.items as unknown as Item[]).find(
|
||||||
|
it => it.id == item.id
|
||||||
|
);
|
||||||
|
if (!it) return false;
|
||||||
|
(inventory.items as unknown as Item[]).splice(
|
||||||
|
(inventory.items as unknown as Item[]).indexOf(it),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateInventory(inventory);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,51 @@
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { JsonObject } from "@prisma/client/runtime/library";
|
||||||
|
|
||||||
export const prisma = new PrismaClient();
|
declare global {
|
||||||
|
var prisma: PrismaClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.prisma ??= new PrismaClient();
|
||||||
|
export const prisma = globalThis.prisma;
|
||||||
|
|
||||||
|
export async function set(key: string, value: any) {
|
||||||
|
const store = await globalThis.prisma.keyValueStore.findUnique({
|
||||||
|
where: { id: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!store) {
|
||||||
|
// throw new Error("Unable to access key-value store.");
|
||||||
|
await prisma.keyValueStore.create({
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = store.data as JsonObject;
|
||||||
|
data[key] = value;
|
||||||
|
|
||||||
|
await globalThis.prisma.keyValueStore.update({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { data: data }
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get<T = unknown>(key: string) {
|
||||||
|
const store = await globalThis.prisma.keyValueStore.findUnique({
|
||||||
|
where: { id: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!store) {
|
||||||
|
// throw new Error("Unable to access key-value store.");
|
||||||
|
await globalThis.prisma.keyValueStore.create({
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = store.data as JsonObject;
|
||||||
|
return data[key] as T;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { readInventory, updateInventory } from "../data/inventory";
|
||||||
import { loadConfig } from "../util/config";
|
import { loadConfig } from "../util/config";
|
||||||
|
|
||||||
export const balanceConfig = loadConfig("config/balance.yml", {
|
export const balanceConfig = loadConfig("config/balance.yml", {
|
||||||
|
@ -16,3 +17,17 @@ export function formatBalance(
|
||||||
if (after) return `${balance.toFixed(cutoff)}${symbol}`;
|
if (after) return `${balance.toFixed(cutoff)}${symbol}`;
|
||||||
else return `${symbol}${balance.toFixed(cutoff)}`;
|
else return `${symbol}${balance.toFixed(cutoff)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getBalance(id: string) {
|
||||||
|
const inventory = await readInventory(id);
|
||||||
|
if (!inventory) return balanceConfig.defaultBalance;
|
||||||
|
return inventory.balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setBalance(id: string, balance: number) {
|
||||||
|
const inventory = await readInventory(id);
|
||||||
|
if (!inventory) return false;
|
||||||
|
inventory.balance = balance;
|
||||||
|
await updateInventory(inventory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -6,3 +6,22 @@ export interface Item {
|
||||||
export interface StackableItem extends Item {
|
export interface StackableItem extends Item {
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConsumableItem extends Item {
|
||||||
|
consumable: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FoodItem extends ConsumableItem {
|
||||||
|
edible: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CakeItem extends FoodItem {
|
||||||
|
emoji: string;
|
||||||
|
icing: string;
|
||||||
|
filling: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShopItem extends Item {
|
||||||
|
buyValue: number;
|
||||||
|
sellValue: number;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { type CakeItem } from "../Item";
|
||||||
|
|
||||||
|
export const cakes: CakeItem[] = [
|
||||||
|
{
|
||||||
|
id: "cake",
|
||||||
|
name: "Chocolate Cake",
|
||||||
|
emoji: "🎂",
|
||||||
|
icing: "chocolate",
|
||||||
|
filling: "chocolate",
|
||||||
|
edible: true,
|
||||||
|
consumable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cake",
|
||||||
|
name: "Vanilla Cake",
|
||||||
|
emoji: "🎂",
|
||||||
|
icing: "vanilla",
|
||||||
|
filling: "vanilla",
|
||||||
|
edible: true,
|
||||||
|
consumable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cake",
|
||||||
|
name: "Strawberry Cake",
|
||||||
|
emoji: "🍓",
|
||||||
|
icing: "strawberry",
|
||||||
|
filling: "strawberry",
|
||||||
|
edible: true,
|
||||||
|
consumable: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { User } from "@prisma/client";
|
||||||
|
import { cakes } from "./cakes";
|
||||||
|
import { ServiceAgent } from "../../services/ServiceAgent";
|
||||||
|
import { StackableItem } from "../Item";
|
||||||
|
import { Logger } from "../../util/Logger";
|
||||||
|
import { addItem } from "../../data/inventory";
|
||||||
|
import { readUser } from "../../data/user";
|
||||||
|
|
||||||
|
export interface BakingUser {
|
||||||
|
id: User["id"];
|
||||||
|
client_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bakingUsers = new Array<BakingUser>();
|
||||||
|
|
||||||
|
export function getRandomCake() {
|
||||||
|
return cakes[Math.floor(Math.random() * cakes.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBaking(userId: User["id"]) {
|
||||||
|
return bakingUsers.find(u => u.id === userId) !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alreadyBakingAnswers = [
|
||||||
|
"You are already baking, dummy.",
|
||||||
|
"Though it seems you don't care, you are already baking a cake, and you can't start baking another one.",
|
||||||
|
"Baking is an art that requires patience, which you do not have. In other words, you are already baking, dummy.",
|
||||||
|
"You shouldn't be baking any more cakes than one at a time.",
|
||||||
|
"You happen to be baking already, and you would need another oven to bake another cake, and there is only one oven in your house.",
|
||||||
|
"Since you don't seem to get it, baking can not be started again while you are already baking.",
|
||||||
|
"You are already baking.",
|
||||||
|
"Baking is something you are already doing.",
|
||||||
|
"Baking should be something you do once in a while, not constantly.",
|
||||||
|
"You are currently baking a cake, which means the oven is already in use, so you can't start baking another one."
|
||||||
|
];
|
||||||
|
|
||||||
|
export function startBaking(userId: User["id"], clientId: string) {
|
||||||
|
// Already baking?
|
||||||
|
if (isBaking(userId)) return alreadyBakingAnswers[Math.floor(Math.random() * alreadyBakingAnswers.length)];
|
||||||
|
|
||||||
|
// Add to baking list
|
||||||
|
bakingUsers.push({
|
||||||
|
id: userId,
|
||||||
|
client_id: clientId
|
||||||
|
});
|
||||||
|
|
||||||
|
return "You started baking a cake.";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopBaking(userId: User["id"]) {
|
||||||
|
if (!isBaking(userId)) return "You are not baking.";
|
||||||
|
bakingUsers.splice(bakingUsers.findIndex(u => u.id === userId), 1);
|
||||||
|
return "You stopped baking a cake.";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomBaker() {
|
||||||
|
return bakingUsers[Math.floor(Math.random() * bakingUsers.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClient(clientId: string) {
|
||||||
|
const agent = globalThis.serviceLoader.getAgent(0) as ServiceAgent<unknown>;
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = new Logger("baking");
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
const r = Math.random();
|
||||||
|
|
||||||
|
//logger.debug(`Baking check: ${r}`);
|
||||||
|
|
||||||
|
if (r < 0.03) {
|
||||||
|
// Get a random baker and send them a cake and a message stating they finished baking
|
||||||
|
const baker = getRandomBaker();
|
||||||
|
if (!baker) return;
|
||||||
|
const client = getClient(baker.client_id);
|
||||||
|
bakingUsers.splice(bakingUsers.findIndex(u => u.id === baker.id), 1);
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
const cake = getRandomCake();
|
||||||
|
const user = await readUser(baker.id);
|
||||||
|
|
||||||
|
if (!user) return void client.emit("send chat", "Something terrible happened when I tried to give someone a cake");
|
||||||
|
|
||||||
|
client.emit("send chat", `@${user.platformId} finished baking and got ${cake.name}${(cake as unknown as StackableItem).count
|
||||||
|
? " " + `(x${(cake as unknown as StackableItem).count})` : ""}.`);
|
||||||
|
|
||||||
|
|
||||||
|
// Add cake to inventory
|
||||||
|
await addItem(baker.id, cake);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
|
@ -0,0 +1,35 @@
|
||||||
|
import type { CommandMessage } from "../commands/CommandHandler";
|
||||||
|
import type { ServiceAgent } from "../services/ServiceAgent";
|
||||||
|
import { formatBalance, getBalance, setBalance } from "./Balance";
|
||||||
|
import type { CakeItem, Item } from "./Item";
|
||||||
|
|
||||||
|
export interface IEatReply {
|
||||||
|
output?: string;
|
||||||
|
consumed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TEatBhv = (msg: CommandMessage, agent: ServiceAgent<unknown>, item: Item) => Promise<IEatReply>;
|
||||||
|
|
||||||
|
export const eatBhv = new Map<Item["id"], TEatBhv>();
|
||||||
|
|
||||||
|
eatBhv.set("kekklefruit", async (msg, agent) => {
|
||||||
|
return {
|
||||||
|
output: "kek eat bhv test",
|
||||||
|
consumed: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
eatBhv.set("cake", async (msg, agent, item) => {
|
||||||
|
const cake = item as CakeItem;
|
||||||
|
const balance = await getBalance(msg.user.id);
|
||||||
|
|
||||||
|
if (typeof balance !== "number") return { output: "Somehow, you don't have a wallet to put stuff in, so you can't eat cake.", consumed: false };
|
||||||
|
|
||||||
|
const money = Math.floor(Math.random() * 500);
|
||||||
|
await setBalance(msg.user.id, balance + money);
|
||||||
|
|
||||||
|
return {
|
||||||
|
output: `You ate ${item.name} and your stomach turned it into ${formatBalance(money)}.`,
|
||||||
|
consumed: true
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Item } from "./Item";
|
||||||
|
|
||||||
|
const items = new Map<string, Item>();
|
||||||
|
|
||||||
|
export function getItem(key: string) {
|
||||||
|
return items.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setItem(key: string, item: Item) {
|
||||||
|
return items.set(key, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteItem(key: string) {
|
||||||
|
return items.delete(key);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { get, set } from "../data/prisma";
|
||||||
|
import { Logger } from "../util/Logger";
|
||||||
|
import type { FoodItem, StackableItem } from "./Item";
|
||||||
|
|
||||||
|
export class KekklefruitTree {
|
||||||
|
protected static fruit: number = 0;
|
||||||
|
public static logger = new Logger("Kekklefruit Tree");
|
||||||
|
|
||||||
|
public static async saveFruit() {
|
||||||
|
return set("kekklefruit-tree", this.fruit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async loadFruit() {
|
||||||
|
let fruit = await get<number>("kekklefruit-tree");
|
||||||
|
let save = false;
|
||||||
|
|
||||||
|
if (!fruit) {
|
||||||
|
fruit = 0;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fruit = fruit;
|
||||||
|
if (save) this.saveFruit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async growFruit(amount: number = 1) {
|
||||||
|
this.fruit += amount;
|
||||||
|
await this.saveFruit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getFruitCount() {
|
||||||
|
return this.fruit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async pickFruit() {
|
||||||
|
if (this.fruit > 0) {
|
||||||
|
this.fruit--;
|
||||||
|
await this.saveFruit();
|
||||||
|
return this.randomFruit();
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static randomFruit() {
|
||||||
|
return {
|
||||||
|
id: "kekklefruit",
|
||||||
|
name: "Kekklefruit",
|
||||||
|
consumable: true,
|
||||||
|
edible: true,
|
||||||
|
count: 1
|
||||||
|
} as FoodItem & StackableItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await KekklefruitTree.loadFruit();
|
|
@ -1,12 +1,54 @@
|
||||||
import { loadCommands } from "./commands";
|
import { loadCommands, type CommandGroup } from "./commands";
|
||||||
|
import { CommandHandler } from "./commands/CommandHandler";
|
||||||
import { loadRoleConfig } from "./permissions";
|
import { loadRoleConfig } from "./permissions";
|
||||||
import { ServiceLoader } from "./services";
|
import { ServiceLoader } from "./services";
|
||||||
|
import { ConsoleAgent } from "./services/console";
|
||||||
import { printStartupASCII } from "./util/ascii";
|
import { printStartupASCII } from "./util/ascii";
|
||||||
|
|
||||||
printStartupASCII();
|
// Hot reload persistence
|
||||||
loadRoleConfig();
|
declare global {
|
||||||
loadCommands();
|
var loaded: boolean;
|
||||||
ServiceLoader.loadServices();
|
var serviceLoader: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.loaded ??= false;
|
||||||
|
globalThis.serviceLoader ??= ServiceLoader;
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
printStartupASCII();
|
||||||
|
loadRoleConfig();
|
||||||
|
loadCommands();
|
||||||
|
globalThis.serviceLoader.loadServices();
|
||||||
|
|
||||||
|
globalThis.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
console.log("Reloading...");
|
||||||
|
|
||||||
|
// Reload commands
|
||||||
|
globalThis.commandHandler.commandGroups = new Array<CommandGroup>();
|
||||||
|
loadCommands();
|
||||||
|
|
||||||
|
// Reload services
|
||||||
|
globalThis.serviceLoader.unloadServices();
|
||||||
|
globalThis.serviceLoader.loadServices();
|
||||||
|
|
||||||
|
// Set console prompt
|
||||||
|
globalThis.serviceLoader.agents.forEach(agent => {
|
||||||
|
if (agent.platform === "console")
|
||||||
|
(agent as ConsoleAgent).client.prompt();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for hot reload
|
||||||
|
if (!globalThis.loaded) {
|
||||||
|
load();
|
||||||
|
} else {
|
||||||
|
console.clear();
|
||||||
|
console.log("Hot reload triggered");
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
export function scopedEval(code: string) {
|
export function scopedEval(code: string) {
|
||||||
return eval(code);
|
return eval(code);
|
||||||
|
|
|
@ -9,6 +9,8 @@ export const defaultConfig = {
|
||||||
|
|
||||||
"cosmic.command.inventory",
|
"cosmic.command.inventory",
|
||||||
"cosmic.command.balance",
|
"cosmic.command.balance",
|
||||||
|
"cosmic.command.bake",
|
||||||
|
"cosmic.command.eat",
|
||||||
|
|
||||||
"cosmic.command.magic8ball",
|
"cosmic.command.magic8ball",
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ export function handlePermission(node1: string, node2: string) {
|
||||||
|
|
||||||
// Check nodes in order
|
// Check nodes in order
|
||||||
for (let i = 0; i < hierarchy1.length; i++) {
|
for (let i = 0; i < hierarchy1.length; i++) {
|
||||||
if (hierarchy1[i] == hierarchy2[i]) {
|
if (hierarchy1[i] === hierarchy2[i]) {
|
||||||
// Last node?
|
// Last node?
|
||||||
if (i == hierarchy1.length - 1 || i == hierarchy2.length - 1) {
|
if (i === hierarchy1.length - 1 || i === hierarchy2.length - 1) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -33,8 +33,8 @@ export function handlePermission(node1: string, node2: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wildcard?
|
// Wildcard?
|
||||||
if (hierarchy1[i] == "*") return true;
|
if (hierarchy1[i] === "*") return true;
|
||||||
if (hierarchy2[i] == "*") return true;
|
if (hierarchy2[i] === "*") return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,12 @@ export function hasPermission(role: Role, permission: string) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const roles = new Map<Role, TRole>();
|
declare global {
|
||||||
|
var roles: Map<Role, TRole>;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.roles ??= new Map<Role, TRole>();
|
||||||
|
export const roles = globalThis.roles;
|
||||||
|
|
||||||
export type TRole = {
|
export type TRole = {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { BaseCommandMessage } from "../../commands/CommandHandler";
|
||||||
import { readUser, updateUser } from "../../data/user";
|
import { readUser, updateUser } from "../../data/user";
|
||||||
import { CosmicColor } from "../../util/CosmicColor";
|
import { CosmicColor } from "../../util/CosmicColor";
|
||||||
import { ServiceAgent } from "../ServiceAgent";
|
import { ServiceAgent } from "../ServiceAgent";
|
||||||
import { MPPAgent } from "../mpp";
|
import { MPPNetAgent } from "../mppnet";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
|
||||||
export interface ChatMessage<T = unknown> {
|
export interface ChatMessage<T = unknown> {
|
||||||
m: "a";
|
m: "a";
|
||||||
|
@ -22,7 +23,7 @@ export interface ChatMessage<T = unknown> {
|
||||||
|
|
||||||
function onChildMessage(msg: ChatMessage) {
|
function onChildMessage(msg: ChatMessage) {
|
||||||
const consoleAgent = ServiceLoader.agents.find(
|
const consoleAgent = ServiceLoader.agents.find(
|
||||||
ag => ag.platform == "console"
|
ag => ag.platform === "console"
|
||||||
) as ConsoleAgent | undefined;
|
) as ConsoleAgent | undefined;
|
||||||
|
|
||||||
if (!consoleAgent) return;
|
if (!consoleAgent) return;
|
||||||
|
@ -34,7 +35,7 @@ function onChildMessage(msg: ChatMessage) {
|
||||||
|
|
||||||
function onConsoleMessage(text: string) {
|
function onConsoleMessage(text: string) {
|
||||||
const consoleAgent = ServiceLoader.agents.find(
|
const consoleAgent = ServiceLoader.agents.find(
|
||||||
ag => ag.platform == "console"
|
ag => ag.platform === "console"
|
||||||
) as ConsoleAgent | undefined;
|
) as ConsoleAgent | undefined;
|
||||||
|
|
||||||
if (!consoleAgent) return;
|
if (!consoleAgent) return;
|
||||||
|
@ -55,7 +56,7 @@ export class MicroHandler {
|
||||||
case "commands":
|
case "commands":
|
||||||
case "cmds":
|
case "cmds":
|
||||||
default:
|
default:
|
||||||
return "Microcommands: /help | /js <expr> | /exit | /list | /view <index> | /unview | /admin+ <id> | /admin- <id> | /owner+ <id>";
|
return "Microcommands: /help | /js <expr> | /exit | /list | /view <index> | /unview | /admin+ <id> | /admin- <id> | /owner+ <id> | /owner- <id>";
|
||||||
break;
|
break;
|
||||||
case "js":
|
case "js":
|
||||||
case "eval":
|
case "eval":
|
||||||
|
@ -77,11 +78,10 @@ export class MicroHandler {
|
||||||
|
|
||||||
for (let i in ServiceLoader.agents) {
|
for (let i in ServiceLoader.agents) {
|
||||||
const agent2 = ServiceLoader.agents[i];
|
const agent2 = ServiceLoader.agents[i];
|
||||||
if (agent2.platform == "mpp") {
|
if (agent2.platform === "mpp") {
|
||||||
agent.emit(
|
agent.emit(
|
||||||
"log",
|
"log",
|
||||||
`${i} - ${agent2.platform} - ${
|
`${i} - ${agent2.platform} - ${(agent2 as MPPNetAgent).desiredChannel
|
||||||
(agent2 as MPPAgent).desiredChannel
|
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,7 +101,7 @@ export class MicroHandler {
|
||||||
let walkie = agent as ConsoleAgent;
|
let walkie = agent as ConsoleAgent;
|
||||||
let talky = ServiceLoader.agents[index];
|
let talky = ServiceLoader.agents[index];
|
||||||
|
|
||||||
if (index == ServiceLoader.agents.indexOf(walkie))
|
if (index === ServiceLoader.agents.indexOf(walkie))
|
||||||
return "Why would you want to chat with yourself?";
|
return "Why would you want to chat with yourself?";
|
||||||
|
|
||||||
// Remove old listeners
|
// Remove old listeners
|
||||||
|
@ -150,11 +150,10 @@ export class MicroHandler {
|
||||||
if (conAg.viewAgent.platform !== "mpp")
|
if (conAg.viewAgent.platform !== "mpp")
|
||||||
return "The view agent is not on MPP.";
|
return "The view agent is not on MPP.";
|
||||||
|
|
||||||
const ppl = (conAg.viewAgent as MPPAgent).client.ppl;
|
const ppl = (conAg.viewAgent as MPPNetAgent).client.ppl;
|
||||||
return `MPP Users: ${Object.values(ppl).map(
|
return `MPP Users: ${Object.values(ppl).map(
|
||||||
p =>
|
p =>
|
||||||
`\n - ${p._id} (user) / ${p.id} (part): ${p.name} (${
|
`\n - ${p._id} (user) / ${p.id} (part): ${p.name} (${p.color
|
||||||
p.color
|
|
||||||
}, ${new CosmicColor(p.color).getName()})`
|
}, ${new CosmicColor(p.color).getName()})`
|
||||||
)}`;
|
)}`;
|
||||||
break;
|
break;
|
||||||
|
@ -206,6 +205,22 @@ export class MicroHandler {
|
||||||
6
|
6
|
||||||
)}...] an owner`;
|
)}...] an owner`;
|
||||||
break;
|
break;
|
||||||
|
case "owner-":
|
||||||
|
const userId4 = command.argv
|
||||||
|
.slice(1, command.argv.length)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
let user4 = await readUser(userId4);
|
||||||
|
if (!user4) return "No such user.";
|
||||||
|
|
||||||
|
user4.role = Role.NONE;
|
||||||
|
await updateUser(user4);
|
||||||
|
|
||||||
|
return `Made user "${user4.name}" [${user4.platformId.substring(
|
||||||
|
0,
|
||||||
|
6
|
||||||
|
)}] a normal user.`;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,14 @@ export class ConsoleAgent extends ServiceAgent<readline.ReadLine> {
|
||||||
this.started = true;
|
this.started = true;
|
||||||
this.client.setPrompt("> ");
|
this.client.setPrompt("> ");
|
||||||
this.client.prompt(true);
|
this.client.prompt(true);
|
||||||
|
(globalThis as any).readline = this.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop() {
|
public stop() {
|
||||||
if (!this.started) return;
|
if (!this.started) return;
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.client.setPrompt("");
|
this.client.close();
|
||||||
|
delete (globalThis as any).readline;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bindEventListeners(): void {
|
protected bindEventListeners(): void {
|
||||||
|
@ -65,7 +67,10 @@ export class ConsoleAgent extends ServiceAgent<readline.ReadLine> {
|
||||||
if (text.startsWith("/")) {
|
if (text.startsWith("/")) {
|
||||||
out = await MicroHandler.handleMicroCommand(message, this);
|
out = await MicroHandler.handleMicroCommand(message, this);
|
||||||
} else {
|
} else {
|
||||||
out = await CommandHandler.handleCommand(message, this);
|
out = await globalThis.commandHandler.handleCommand(
|
||||||
|
message,
|
||||||
|
this
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out) {
|
if (out) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ export class DiscordAgent extends ServiceAgent<Discord.Client> {
|
||||||
|
|
||||||
let args = msg.content.split(" ");
|
let args = msg.content.split(" ");
|
||||||
|
|
||||||
const str = await CommandHandler.handleCommand(
|
const str = await globalThis.commandHandler.handleCommand(
|
||||||
{
|
{
|
||||||
m: "command",
|
m: "command",
|
||||||
a: msg.content,
|
a: msg.content,
|
||||||
|
@ -74,7 +74,7 @@ export class DiscordAgent extends ServiceAgent<Discord.Client> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (str) {
|
if (str) {
|
||||||
if (typeof str == "string") {
|
if (typeof str === "string") {
|
||||||
const channel = await this.client.channels.fetch(
|
const channel = await this.client.channels.fetch(
|
||||||
msg.channelId
|
msg.channelId
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MPPAgent } from "./mpp";
|
import { MPPNetAgent } from "./mppnet";
|
||||||
import env from "../util/env";
|
import env from "../util/env";
|
||||||
import { ServiceAgent } from "./ServiceAgent";
|
import { ServiceAgent } from "./ServiceAgent";
|
||||||
import { loadConfig } from "../util/config";
|
import { loadConfig } from "../util/config";
|
||||||
|
@ -50,18 +50,22 @@ export class ServiceLoader {
|
||||||
this.agents.push(agent);
|
this.agents.push(agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getAgent(index: number) {
|
||||||
|
return this.agents[index];
|
||||||
|
}
|
||||||
|
|
||||||
public static loadServices() {
|
public static loadServices() {
|
||||||
if (config.enableMPP) {
|
if (config.enableMPP) {
|
||||||
for (const uri of Object.keys(mppConfig.agents)) {
|
for (const uri of Object.keys(mppConfig.agents)) {
|
||||||
for (const channel of mppConfig.agents[uri]) {
|
for (const channel of mppConfig.agents[uri]) {
|
||||||
const mppAgent = new MPPAgent(
|
const mppAgent = new MPPNetAgent(
|
||||||
uri,
|
uri,
|
||||||
channel.id,
|
channel.id,
|
||||||
channel.overrideName
|
channel.overrideName
|
||||||
? {
|
? {
|
||||||
name: channel.overrideName,
|
name: channel.overrideName,
|
||||||
color: mppConfig.desiredUser.color
|
color: mppConfig.desiredUser.color
|
||||||
}
|
}
|
||||||
: mppConfig.desiredUser,
|
: mppConfig.desiredUser,
|
||||||
env.MPPNET_TOKEN,
|
env.MPPNET_TOKEN,
|
||||||
config.debug
|
config.debug
|
||||||
|
@ -96,4 +100,15 @@ export class ServiceLoader {
|
||||||
this.addAgent(consoleAgent);
|
this.addAgent(consoleAgent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static unloadServices() {
|
||||||
|
for (const agent of this.agents) {
|
||||||
|
agent.stop();
|
||||||
|
this.agents.splice(this.agents.indexOf(agent), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getAgentId(agent: ServiceAgent<unknown>) {
|
||||||
|
return this.agents.indexOf(agent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MPPAgent } from ".";
|
import { MPPNetAgent } from ".";
|
||||||
|
|
||||||
interface Vector2 {
|
interface Vector2 {
|
||||||
x: number;
|
x: number;
|
||||||
|
@ -20,12 +20,22 @@ interface CursorProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Cursor {
|
export class Cursor {
|
||||||
|
public static animations = new Map<string, (cursor: Cursor) => void>();
|
||||||
|
|
||||||
public visible: boolean = true;
|
public visible: boolean = true;
|
||||||
public displayInterval: NodeJS.Timeout;
|
public displayInterval: NodeJS.Timeout;
|
||||||
public updateInterval: NodeJS.Timeout;
|
public updateInterval: NodeJS.Timeout;
|
||||||
|
|
||||||
|
public animationLoop = [
|
||||||
|
"bounce",
|
||||||
|
"bounce2",
|
||||||
|
"constrained",
|
||||||
|
"lemniscate",
|
||||||
|
"test",
|
||||||
|
];
|
||||||
|
|
||||||
public props: CursorProps = {
|
public props: CursorProps = {
|
||||||
currentAnimation: "lemniscate",
|
currentAnimation: this.animationLoop[0],
|
||||||
position: {
|
position: {
|
||||||
x: 50,
|
x: 50,
|
||||||
y: 50
|
y: 50
|
||||||
|
@ -50,7 +60,7 @@ export class Cursor {
|
||||||
following: ""
|
following: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(public agent: MPPAgent) {
|
constructor(public agent: MPPNetAgent) {
|
||||||
this.displayInterval = setInterval(() => {
|
this.displayInterval = setInterval(() => {
|
||||||
if (
|
if (
|
||||||
this.props.oldPosition.x !== this.props.position.x ||
|
this.props.oldPosition.x !== this.props.position.x ||
|
||||||
|
@ -66,6 +76,13 @@ export class Cursor {
|
||||||
}
|
}
|
||||||
}, 1000 / 20);
|
}, 1000 / 20);
|
||||||
|
|
||||||
|
let animationIndex = 0;
|
||||||
|
const animationInterval = setInterval(() => {
|
||||||
|
animationIndex++;
|
||||||
|
if (animationIndex >= this.animationLoop.length) animationIndex = 0;
|
||||||
|
this.props.currentAnimation = this.animationLoop[animationIndex];
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
this.updateInterval = setInterval(() => {
|
this.updateInterval = setInterval(() => {
|
||||||
switch (this.props.currentAnimation) {
|
switch (this.props.currentAnimation) {
|
||||||
case "bounce":
|
case "bounce":
|
||||||
|
@ -88,6 +105,11 @@ export class Cursor {
|
||||||
this.props.velocity.y +=
|
this.props.velocity.y +=
|
||||||
this.props.acceleration.y * this.props.dt;
|
this.props.acceleration.y * this.props.dt;
|
||||||
|
|
||||||
|
if (this.props.velocity.x > 50) this.props.velocity.x = 50;
|
||||||
|
if (this.props.velocity.x < -50) this.props.velocity.x = -50;
|
||||||
|
if (this.props.velocity.y > 50) this.props.velocity.y = 50;
|
||||||
|
if (this.props.velocity.y < -50) this.props.velocity.y = -50;
|
||||||
|
|
||||||
this.props.position.x +=
|
this.props.position.x +=
|
||||||
this.props.velocity.x * this.props.dt;
|
this.props.velocity.x * this.props.dt;
|
||||||
this.props.position.y +=
|
this.props.position.y +=
|
||||||
|
@ -135,7 +157,7 @@ export class Cursor {
|
||||||
|
|
||||||
if (this.props.position.y < 75) {
|
if (this.props.position.y < 75) {
|
||||||
this.props.acceleration.y =
|
this.props.acceleration.y =
|
||||||
Math.random() * 100 - 50 - this.props.gravity;
|
((Math.random() * 50) - 25) - this.props.gravity;
|
||||||
} else {
|
} else {
|
||||||
this.props.acceleration.y = -(Math.random() * 50);
|
this.props.acceleration.y = -(Math.random() * 50);
|
||||||
}
|
}
|
||||||
|
@ -239,7 +261,7 @@ export class Cursor {
|
||||||
50;
|
50;
|
||||||
this.props.position.y =
|
this.props.position.y =
|
||||||
Math.sin(this.props.angles[0] * (Math.PI / 180) * 3) *
|
Math.sin(this.props.angles[0] * (Math.PI / 180) * 3) *
|
||||||
10 +
|
10 +
|
||||||
50;
|
50;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -254,7 +276,7 @@ export class Cursor {
|
||||||
50;
|
50;
|
||||||
this.props.position.y =
|
this.props.position.y =
|
||||||
Math.sin(this.props.angles[0] * (Math.PI / 180) * 2) *
|
Math.sin(this.props.angles[0] * (Math.PI / 180) * 2) *
|
||||||
10 +
|
10 +
|
||||||
50;
|
50;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -296,7 +318,7 @@ export class Cursor {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, 1000 / 60);
|
}, 1500 / 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
public show() {
|
public show() {
|
|
@ -5,7 +5,7 @@ import { Cursor } from "./Cursor";
|
||||||
import { ChatMessage } from "../console/MicroHandler";
|
import { ChatMessage } from "../console/MicroHandler";
|
||||||
import { help as helpCommand } from "../../commands/commands/general/help";
|
import { help as helpCommand } from "../../commands/commands/general/help";
|
||||||
|
|
||||||
export class MPPAgent extends ServiceAgent<Client> {
|
export class MPPNetAgent extends ServiceAgent<Client> {
|
||||||
public cursor: Cursor;
|
public cursor: Cursor;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -18,6 +18,7 @@ export class MPPAgent extends ServiceAgent<Client> {
|
||||||
const cl = new Client(uri, token);
|
const cl = new Client(uri, token);
|
||||||
super("mpp", cl);
|
super("mpp", cl);
|
||||||
this.cursor = new Cursor(this);
|
this.cursor = new Cursor(this);
|
||||||
|
this.cursor.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
|
@ -33,11 +34,14 @@ export class MPPAgent extends ServiceAgent<Client> {
|
||||||
|
|
||||||
this.client.on("hi", msg => {
|
this.client.on("hi", msg => {
|
||||||
this.client.setChannel(this.desiredChannel);
|
this.client.setChannel(this.desiredChannel);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.on("ch", msg => {
|
||||||
this.fixUser();
|
this.fixUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client.on("t", msg => {
|
this.client.on("t", msg => {
|
||||||
this.fixUser();
|
// this.fixUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client.on("a", async msg => {
|
this.client.on("a", async msg => {
|
||||||
|
@ -61,7 +65,7 @@ export class MPPAgent extends ServiceAgent<Client> {
|
||||||
let args = msg.a.split(" ");
|
let args = msg.a.split(" ");
|
||||||
|
|
||||||
// Run command and get output
|
// Run command and get output
|
||||||
const str = await CommandHandler.handleCommand(
|
const str = await globalThis.commandHandler.handleCommand(
|
||||||
{
|
{
|
||||||
m: "command",
|
m: "command",
|
||||||
a: msg.a,
|
a: msg.a,
|
||||||
|
@ -80,7 +84,7 @@ export class MPPAgent extends ServiceAgent<Client> {
|
||||||
|
|
||||||
// Send message in chat
|
// Send message in chat
|
||||||
if (str) {
|
if (str) {
|
||||||
if (typeof str == "string") {
|
if (typeof str === "string") {
|
||||||
if (str.includes("\n")) {
|
if (str.includes("\n")) {
|
||||||
let sp = str.split("\n");
|
let sp = str.split("\n");
|
||||||
|
|
|
@ -80,9 +80,9 @@ export class CosmicColor {
|
||||||
let g = (~~this.g || 0).toString(16);
|
let g = (~~this.g || 0).toString(16);
|
||||||
let b = (~~this.b || 0).toString(16);
|
let b = (~~this.b || 0).toString(16);
|
||||||
|
|
||||||
if (r.length == 1) r = "0" + r;
|
if (r.length === 1) r = "0" + r;
|
||||||
if (g.length == 1) g = "0" + g;
|
if (g.length === 1) g = "0" + g;
|
||||||
if (b.length == 1) b = "0" + b;
|
if (b.length === 1) b = "0" + b;
|
||||||
|
|
||||||
return "#" + r + g + b;
|
return "#" + r + g + b;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ export function padNum(
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
private static log(method: string, ...args: any[]) {
|
private static log(method: string, ...args: any[]) {
|
||||||
|
process.stdout.write(`\x1b[2K\r`);
|
||||||
|
|
||||||
(console as unknown as Record<string, (..._args: any[]) => any>)[
|
(console as unknown as Record<string, (..._args: any[]) => any>)[
|
||||||
method
|
method
|
||||||
](
|
](
|
||||||
|
@ -24,6 +26,10 @@ export class Logger {
|
||||||
unimportant(this.getHHMMSSMS()),
|
unimportant(this.getHHMMSSMS()),
|
||||||
...args
|
...args
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ((globalThis as any).readline) {
|
||||||
|
(globalThis as any).readline.prompt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getHHMMSSMS() {
|
public static getHHMMSSMS() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { parse as parsePath } from "path/posix";
|
||||||
* @returns Parsed YAML config
|
* @returns Parsed YAML config
|
||||||
*/
|
*/
|
||||||
export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
||||||
|
console.time(`Loading config ${configPath}`);
|
||||||
// Config exists?
|
// Config exists?
|
||||||
if (existsSync(configPath)) {
|
if (existsSync(configPath)) {
|
||||||
// Load config
|
// Load config
|
||||||
|
@ -30,12 +31,12 @@ export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
||||||
obj2: Record<string, unknown>
|
obj2: Record<string, unknown>
|
||||||
) {
|
) {
|
||||||
for (const key of Object.keys(obj2)) {
|
for (const key of Object.keys(obj2)) {
|
||||||
if (typeof obj[key] == "undefined") {
|
if (typeof obj[key] === "undefined") {
|
||||||
obj[key] = obj2[key];
|
obj[key] = obj2[key];
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof obj[key] == "object" && !Array.isArray(obj[key])) {
|
if (typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
||||||
mix(
|
mix(
|
||||||
obj[key] as Record<string, unknown>,
|
obj[key] as Record<string, unknown>,
|
||||||
obj2[key] as Record<string, unknown>
|
obj2[key] as Record<string, unknown>
|
||||||
|
@ -45,7 +46,7 @@ export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply any missing default values
|
// Apply any missing default values
|
||||||
mix(config, defRecord);
|
// mix(config, defRecord);
|
||||||
|
|
||||||
// Save config if modified
|
// Save config if modified
|
||||||
if (changed) writeConfig(configPath, config);
|
if (changed) writeConfig(configPath, config);
|
||||||
|
@ -54,6 +55,7 @@ export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
||||||
} else {
|
} else {
|
||||||
// Write default config to disk and use that
|
// Write default config to disk and use that
|
||||||
writeConfig(configPath, defaultConfig);
|
writeConfig(configPath, defaultConfig);
|
||||||
|
|
||||||
return defaultConfig as T;
|
return defaultConfig as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { collapseInventory } from "../../src/data/inventory";
|
||||||
|
import { StackableItem } from "../../src/economy/Item";
|
||||||
|
|
||||||
|
test("Collapse inventory", () => {
|
||||||
|
let sampleData: StackableItem[] = [
|
||||||
|
{
|
||||||
|
id: "test_item",
|
||||||
|
name: "Test Item",
|
||||||
|
count: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test_item",
|
||||||
|
name: "Test Item",
|
||||||
|
count: 15
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
collapseInventory(sampleData);
|
||||||
|
expect(sampleData[0].count).toBe(25);
|
||||||
|
expect(sampleData[1]).toBe(undefined);
|
||||||
|
console.log(sampleData);
|
||||||
|
});
|
Loading…
Reference in New Issue