architecture besides db setup, say+roll

This commit is contained in:
stjet
2024-07-22 09:19:12 +00:00
commit f8780249ce
12 changed files with 783 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.env
node_modules
*.js

2
commands/error.ts Normal file
View File

@@ -0,0 +1,2 @@
export class BotError extends Error {};

78
commands/index.ts Normal file
View File

@@ -0,0 +1,78 @@
import { EmbedBuilder } from "discord.js";
import type { ChatInputCommandInteraction } from "discord.js";
import { BotError } from "./error";
import say from "./say";
import roll from "./roll";
export interface CommandData {
name: string;
description: string;
ephemeral: boolean;
admin_only: boolean;
run: (interaction: ChatInputCommandInteraction) => Promise<any>;
//
};
const commands: CommandData[] = [say, roll];
//todo: look from config. also, have a config
function is_admin(interaction: ChatInputCommandInteraction): boolean {
//
//placeholder
return true;
}
export default async function run(interaction: ChatInputCommandInteraction) {
const name = interaction.commandName;
//help command is "auto-generated"
if (name === "help") {
//max of 25 fields per embed, so if too many commands, this section needs a rewrite
let embeds = [];
let help_embed = new EmbedBuilder();
help_embed.setTitle("Help");
for (const c of commands.filter((c) => !c.admin_only)) {
help_embed.addFields([{
name: `/${c.name}`,
value: c.description,
}]);
}
embeds.push(help_embed);
if (is_admin(interaction)) {
let admin_help_embed = new EmbedBuilder();
admin_help_embed.setTitle("Help (admin only)");
for (const c of commands.filter((c) => c.admin_only)) {
admin_help_embed.addFields([{
name: `/${c.name}`,
value: c.description,
}]);
}
embeds.push(admin_help_embed);
}
return await interaction.reply({ embeds, ephemeral: true });
}
const found = commands.find((c) => c.name === name);
try {
//admin stuff should be ideally handled by register.ts, but this is a fallback
if (found.admin_only && !is_admin(interaction)) throw new BotError("Admin permission needed to run that command");
await found.run(interaction);
} catch (e) {
if (e instanceof BotError) {
//send error message to that channel
if (interaction.deferred) {
return await interaction.editReply(String(e));
} else {
return await interaction.reply({ content: String(e), ephemeral: found.ephemeral });
}
} else {
//an actual error
//console.log(e);
throw e; //crash it
}
}
}

39
commands/roll.ts Normal file
View File

@@ -0,0 +1,39 @@
import type { ChatInputCommandInteraction } from "discord.js";
import { randomInt } from "crypto";
import type { CommandData } from "./index";
import { BotError } from "./error";
const MAX_DICE: number = 100;
const MAX_FACES: number = 9999;
async function run(interaction: ChatInputCommandInteraction) {
await interaction.deferReply(); //do these options.get things need to be await?? kinda stupid
const options = interaction.options;
const dice_num: number = (await options.get("dice_num")).value as number;
const dice_faces: number = (await options.get("dice_faces")).value as number;
const show_calc: boolean = ((await options.get("show_calc"))?.value ?? true) as boolean;
const include_zero: boolean = ((await options.get("include_zero"))?.value ?? false) as boolean;
if (dice_num < 1) throw new BotError("Must roll at least 1 dice, obviously");
//semi-arbitrary limits, discord messages can be max 2000 chars long
if (dice_num > MAX_DICE) throw new BotError(`Max of ${MAX_DICE} dice`);
if (dice_faces > 9999) throw new BotError(`Max of ${MAX_FACES} faces`);
let rolls: number[] = [];
for (let i = 0; i < dice_num; i++) {
rolls.push(randomInt(include_zero ? 0 : 1, dice_faces + 1));
}
const result: number = rolls.reduce((accum, roll) => accum + roll, 0);
return await interaction.editReply(`Result: **${result}** ${ show_calc ? `(${ rolls.join(" + ")} = ${result})` : "" }`);
}
const data: CommandData = {
name: "roll",
description: "Roll dice",
ephemeral: false,
admin_only: false,
run,
//
};
export default data;

35
commands/say.ts Normal file
View File

@@ -0,0 +1,35 @@
import type { ChatInputCommandInteraction } from "discord.js";
import type { CommandData } from "./index";
import { BotError } from "./error";
import { is_text_channel } from "../guards";
async function run(interaction: ChatInputCommandInteraction) {
const options = interaction.options;
const text: string = (await options.get("text")).value as string; //100% this is a string
const channel = (await options.get("channel")).channel;
//screw threads, news, announcements and shit, at least for now
if (is_text_channel(channel)) {
try {
await channel.send(text);
} catch (e) {
console.log(e);
throw new BotError("Couldn't send message");
}
return await interaction.reply({ content: "Sent", ephemeral: true });
} else {
throw new BotError("Must be guild text channel"); //I don't think DM channels are valid to pass in here so no worries there, probably
}
}
const data: CommandData = {
name: "say",
description: "Have the bot say something in a channel",
ephemeral: true,
admin_only: true,
run,
//
};
export default data;

12
db.ts Normal file
View File

@@ -0,0 +1,12 @@
import { MongoClient } from "mongodb";
//figure out the options and whatnot later
const client = new MongoClient(process.env.MONGO_CONNECTION_STRING);
let store, users, income;
client.connect().then(() => {
console.log("Connected to the database");
//
});

7
guards.ts Normal file
View File

@@ -0,0 +1,7 @@
import type { TextChannel } from "discord.js";
import { ChannelType } from "discord.js";
export function is_text_channel(channel: any): channel is TextChannel {
return channel.type === ChannelType.GuildText;
}

24
index.ts Normal file
View File

@@ -0,0 +1,24 @@
import { Client, BaseInteraction } from "discord.js";
import { config } from "dotenv";
//import db from "./db";
import run from "./commands";
config();
const client = new Client({ intents: [] });
client.on("ready", async () => {
console.log(`Logged in as ${client.user.tag}`);
//
});
client.on("interactionCreate", async (interaction: BaseInteraction) => {
//
if (interaction.isChatInputCommand()) {
return await run(interaction);
}
});
setTimeout(() => client.login(process.env.DISCORD_TOKEN), 2000);

486
package-lock.json generated Normal file
View File

@@ -0,0 +1,486 @@
{
"name": "arvalddos-bot",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "arvalddos-bot",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"mongodb": "^6.8.0"
},
"devDependencies": {
"typescript": "^5.5.3"
}
},
"node_modules/@discordjs/builders": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.8.2.tgz",
"integrity": "sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/formatters": "^0.4.0",
"@discordjs/util": "^1.1.0",
"@sapphire/shapeshift": "^3.9.7",
"discord-api-types": "0.37.83",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.4.0.tgz",
"integrity": "sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "0.37.83"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.3.0.tgz",
"integrity": "sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.2",
"undici": "6.13.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz",
"integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/util": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.0.tgz",
"integrity": "sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz",
"integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.3.0",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83",
"tslib": "^2.6.2",
"ws": "^8.16.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz",
"integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@mongodb-js/saslprep": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz",
"integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==",
"license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz",
"integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz",
"integrity": "sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v16"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@types/node": {
"version": "20.14.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
"integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
"license": "MIT"
},
"node_modules/@types/whatwg-url": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
"license": "MIT",
"dependencies": {
"@types/webidl-conversions": "*"
}
},
"node_modules/@types/ws": {
"version": "8.5.11",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz",
"integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.4.tgz",
"integrity": "sha512-ZL62PFXEIeGUI8btfJ5S8Flc286eU1ZUSjwyFQtIGXfRUDPZKO+CDJMYb1R71LjGWRZ4n202O+a6FGjsgTw58g==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/bson": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz",
"integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.20.1"
}
},
"node_modules/discord-api-types": {
"version": "0.37.83",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz",
"integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==",
"license": "MIT"
},
"node_modules/discord.js": {
"version": "14.15.3",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.15.3.tgz",
"integrity": "sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/builders": "^1.8.2",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.4.0",
"@discordjs/rest": "^2.3.0",
"@discordjs/util": "^1.1.0",
"@discordjs/ws": "^1.1.1",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "0.37.83",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"tslib": "2.6.2",
"undici": "6.13.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"license": "MIT"
},
"node_modules/magic-bytes.js": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==",
"license": "MIT"
},
"node_modules/memory-pager": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
"license": "MIT"
},
"node_modules/mongodb": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz",
"integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==",
"license": "Apache-2.0",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.5",
"bson": "^6.7.0",
"mongodb-connection-string-url": "^3.0.0"
},
"engines": {
"node": ">=16.20.1"
},
"peerDependencies": {
"@aws-sdk/credential-providers": "^3.188.0",
"@mongodb-js/zstd": "^1.1.0",
"gcp-metadata": "^5.2.0",
"kerberos": "^2.0.1",
"mongodb-client-encryption": ">=6.0.0 <7",
"snappy": "^7.2.2",
"socks": "^2.7.1"
},
"peerDependenciesMeta": {
"@aws-sdk/credential-providers": {
"optional": true
},
"@mongodb-js/zstd": {
"optional": true
},
"gcp-metadata": {
"optional": true
},
"kerberos": {
"optional": true
},
"mongodb-client-encryption": {
"optional": true
},
"snappy": {
"optional": true
},
"socks": {
"optional": true
}
}
},
"node_modules/mongodb-connection-string-url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
"integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
"license": "Apache-2.0",
"dependencies": {
"@types/whatwg-url": "^11.0.2",
"whatwg-url": "^13.0.0"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
"license": "MIT",
"dependencies": {
"memory-pager": "^1.0.2"
}
},
"node_modules/tr46": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/ts-mixer": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz",
"integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==",
"license": "MIT",
"engines": {
"node": ">=18.0"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"license": "MIT"
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/whatwg-url": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
"integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
"license": "MIT",
"dependencies": {
"tr46": "^4.1.1",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=16"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "arvalddos-bot",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"compile": "tsc -p .",
"register": "npm run compile && node register.js",
"start": "npm run compile && node index.js"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"mongodb": "^6.8.0"
},
"devDependencies": {
"typescript": "^5.5.3"
}
}

63
register.ts Normal file
View File

@@ -0,0 +1,63 @@
import { REST, Routes } from "discord.js";
import { config } from "dotenv";
config();
//description in two places is annoying but length limits are different (100 vs 1024 [but sum of all chars in embed cannot be >6000])
const commands = [
{
name: "help",
description: "Get a list of commands for this bot",
},
{
name: "say",
description: "Have bot say something in a channel (admin only)",
options: [
{
type: 3,
name: "text",
description: "What the bot should say",
required: true,
},
{
type: 7,
name: "channel",
description: "What guild text channel the bot should say the text in",
required: true,
},
],
},
{
name: "roll",
description: "Roll dice",
options: [
{
type: 4,
name: "dice_num",
description: "Amount of dice to roll",
required: true,
},
{
type: 4,
name: "dice_faces",
description: "Max value of each dice",
required: true,
},
{
type: 5,
name: "show_calc",
description: "Show the calculations and each dice roll (default: true)",
required: false,
},
{
type: 5,
name: "include_zero",
description: "Make it possible for the dice to roll 0 (default: false)",
required: false,
},
],
},
];
(new REST().setToken(process.env.DISCORD_TOKEN)).put(Routes.applicationCommands(process.env.CLIENT_ID), { body: commands }).then(() => console.log("Finished reloading slash commands"));

13
tsconfig.json Normal file
View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2020",
"module": "node16",
"moduleResolution": "node16",
"typeRoots": ["./node_modules/@types"],
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"lib": ["ES2020"],
"exclude": ["node_modules"]
}