fix: bigbot template (#2535)

* chore: fix script extension

* chore: remove extention

* fix: at least it run can now

* chore: fix dev script

* refactor: change to use env file for config

* chore: update readme
This commit is contained in:
Jonathan Ho
2022-10-20 22:44:34 +08:00
committed by GitHub
parent 783da60779
commit cedf75f28e
21 changed files with 1298 additions and 308 deletions

View File

@@ -1,4 +1,86 @@
# For Prisma use. Remember to remove the `[` and `]`
DATABASE_URL=postgres://[username]:[password]@[host]:[port]/[db]
# For Prisma use. Remember to remove the [ and ]
DATABASE_URL=postgres:#[username]:[password]@[host]:[port]/[db]
# For other configs, update src/configs.ts
# For other configs, update src/configs.ts
# General Configurations
# Whether or not this process is a local development version. Change to false for the main production bot. */
# SETUP-DD-TEMP: Change this to false in your server for production bot. Keep true in local testing.
DEVELOPMENT=true
# The server id where you develop/test the bot. */
# SETUP-DD-TEMP: Change the id to a server where you develop the bot privately.
DEV_SERVER_ID=
# The discord bot token, without the BOT prefix. */
# SETUP-DD-TEMP: Add the bot token here.
DISCORD_TOKEN=
# Bot Configurations
# The secret passcode that the bot code (event handler) is listening for. This is used to prevent someone else from trying to send malicious messages to your bot. */
# SETUP-DD-TEMP: Add a secret passcode here.
EVENT_HANDLER_AUTHORIZATION=server
# The host where the event handler will run. Must follow https:#nodejs.org/api/net.html#serverlistenoptions-callback. */
# SETUP-DD-TEMP: Set the event handler's host here.
EVENT_HANDLER_HOST=localhost
# The port where the event handler is listening for events. */
# SETUP-DD-TEMP: Set the desired port where events will be sent to be processed.
EVENT_HANDLER_PORT=8081
# The full webhook url where the bot can send errors to alert you that the bot is missing translations. */
# SETUP-DD-TEMP: Set a full discord webhook url here.
MISSING_TRANSLATION_WEBHOOK=
# The full webhook url where the bot can send errors to alert you that the bot is throwing errors. */
# SETUP-DD-TEMP: Set a full discord webhook url here.
BUGS_ERRORS_REPORT_WEBHOOK=
# Rest Proxy Configurations
# The authorization code that the REST proxy will check for to make sure the requests are coming from you. */
# SETUP-DD-TEMP: Add a secret passcode here.
REST_AUTHORIZATION=secret
# The host where the REST proxy will run. Must follow https:#nodejs.org/api/net.html#serverlistenoptions-callback. */
# SETUP-DD-TEMP: Set the REST proxy's host here.
REST_HOST=localhost
# The port that will run the REST proxy. */
# SETUP-DD-TEMP: Choose the port number here that will be used for the REST proxy.
REST_PORT=8000
# Gateway Proxy Configurations
# The amount of shards to start. Useful with multiple servers where each server is handling a portion of your bot. */
# SETUP-DD-TEMP: To start all bots, leave it as undefined. Specify he number of shards this process should handle.
TOTAL_SHARDS=
# The amount of shards to start per worker. */
# SETUP-DD-TEMP: Choose how many shards to start per worker. If you are not sure just stick to 16.
SHARDS_PER_WORKER=16
# The total amount of workers to start. Generally this should be equal to the number of cores your server has. */
# SETUP-DD-TEMP: Choose how many workers to start up. If you are not sure, check how many cores your server has.
TOTAL_WORKERS=4
# The secret passcode that the gateway is listening for. This is used to prevent someone else from trying to send malicious messages to your bot. */
# SETUP-DD-TEMP: Add a secret passcode here.
GATEWAY_AUTHORIZATION=
# The host where the gateway will run. Must follow https:#nodejs.org/api/net.html#serverlistenoptions-callback. */
# SETUP-DD-TEMP: Set the gateways's host here.
GATEWAY_HOST=localhost
# The port where the gateway will run. This is where the bot will send its messages to the gateway. */
# SETUP-DD-TEMP: Set the gateways's port here.
GATEWAY_PORT=8080
# Database Configurations
# These INFLUX configs are only if you wish to enable analytics. */
# SETUP-DD-TEMP: This is optional. If you want to build analytics, add influxdb here.
INFLUX_BUCKET=
INFLUX_ORG=
INFLUX_TOKEN=
INFLUX_URL=

31
template/bigbot/.swcrc Normal file
View File

@@ -0,0 +1,31 @@
{
"minify": true,
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true,
"dynamicImport": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2022",
"keepClassNames": true,
"loose": true,
"minify": {
"compress": {
"unused": true
},
"mangle": true
}
},
"module": {
"type": "es6",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
},
"sourceMaps": "inline"
}

View File

@@ -17,15 +17,11 @@ Make sure to install the latest version when you use it.
To start your bot, you will need to start a few processes. The instructions below will use `node` but you can use
something like `pm2` to help keep your processes alive.
Please compile everything first with `tsc`.
Please compile everything first with `npm run build`.
- Start REST
- `ts-node src/rest/index.ts`
- `npm run startr`
- Start Gateway
- `ts-node src/gateway/index.ts`
- `npm run startg`
- Start Bot
- `ts-node src/bot/index.ts`
## Improvements
- Change configs.ts file to use an .env file.
- `npm run startb`

View File

@@ -0,0 +1,6 @@
{
"watch": "./src/**/*.ts",
"ext ": "env,ts",
"signal": "SIGKILL",
"exec": "npm run build && node --experimental-specifier-resolution=node"
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,39 +2,41 @@
"name": "dd-big-bot",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon -e ts --exec 'npm run start'",
"start": "node --no-warnings dist/index.js",
"devbg": "npx prisma generate && tsc --watch",
"fmt": "prettier -w ./src",
"dg": "ts-node src/gateway/index.ts",
"dr": "ts-node src/rest/index.js",
"db": "ts-node src/bot/index.ts",
"tsc": "tsc",
"tscw": "tsc --watch",
"devr": "node dist/rest/index",
"devg": "node dist/gateway/index",
"devb": "node dist/bot/index"
"devg": "nodemon --ignore ./src/bot/**/* --ignore ./src/rest/**/* --ignore ./dist/**/* -e ts dist/gateway/index.js",
"devr": "nodemon --ignore ./src/bot/**/* --ignore ./src/gateway/**/* --ignore ./dist/**/* -e ts dist/rest/index.js",
"devb": "nodemon --ignore ./src/rest/**/* --ignore ./src/gateway/**/* --ignore ./dist/**/* -e ts dist/bot/index.js",
"type": "tsc --noEmit",
"build": "swc src --out-dir dist",
"startr": "node --experimental-specifier-resolution=node dist/rest/index.js",
"startg": "node --experimental-specifier-resolution=node dist/gateway/index.js",
"startb": "node --experimental-specifier-resolution=node dist/bot/index.js"
},
"dependencies": {
"@influxdata/influxdb-client": "^1.29.0",
"@prisma/client": "^3.15.2",
"colorette": "^2.0.19",
"discordeno": "^16.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.1",
"fastify": "^4.8.1",
"nanoid": "^4.0.0",
"ts-node": "^10.9.1",
"tslib": "^2.3.1",
"node-fetch": "^3.2.10",
"web-worker": "^1.2.0"
},
"devDependencies": {
"@swc/cli": "^0.1.57",
"@swc/core": "^1.3.9",
"@types/express": "^4.17.13",
"@types/node": "^17.0.23",
"@types/ws": "^8.5.3",
"nodemon": "^2.0.15",
"prettier": "2.6.2",
"prisma": "^4.2.1",
"ts-node": "^10.9.1",
"typescript": "^4.6.3"
},
"prettier": {
@@ -45,4 +47,4 @@
"semi": true,
"printWidth": 120
}
}
}

View File

@@ -1,5 +1,9 @@
import { InfluxDB } from "@influxdata/influxdb-client";
import { INFLUX_BUCKET, INFLUX_ORG, INFLUX_TOKEN, INFLUX_URL } from "./configs.js";
const INFLUX_BUCKET = process.env.INFLUX_BUCKET as string
const INFLUX_ORG = process.env.INFLUX_ORG as string
const INFLUX_TOKEN = process.env.INFLUX_TOKEN as string
const INFLUX_URL = process.env.INFLUX_URL as string
export const influxDB = INFLUX_URL && INFLUX_TOKEN ? new InfluxDB({ url: INFLUX_URL, token: INFLUX_TOKEN }) : undefined;
export const Influx = influxDB?.getWriteApi(INFLUX_ORG, INFLUX_BUCKET);

View File

@@ -1,10 +1,13 @@
import { Bot, Collection, createBot, createRestManager } from "discordeno";
import enableHelpersPlugin from "discordeno/helpers-plugin";
import { createLogger } from "discordeno/logger";
import { DISCORD_TOKEN, INTENTS, REST_AUTHORIZATION, REST_URL } from "../configs.js";
import { setupEventHandlers } from "./events/mod.js";
import { MessageCollector } from "./utils/collectors.js";
import { customizeInternals } from "./utils/internals/mod.js";
import { INTENTS, REST_URL } from "../configs";
import { setupEventHandlers } from "./events/mod";
import { MessageCollector } from "./utils/collectors";
import { customizeInternals } from "./utils/internals/mod";
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string
const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string
export const bot = enableHelpersPlugin(
customizeBot(

View File

@@ -10,11 +10,12 @@ import {
InteractionResponseTypes,
Member,
Role,
User,
User
} from "discordeno";
import { bot, BotWithCustomProps } from "../../bot.js";
import COMMANDS from "../../commands/mod.js";
import { getLanguage, loadLanguage, serverLanguages, translate, translationKeys } from "../../languages/translate.js";
import { InteractionWithCustomProps } from "../../typings/discordeno.js";
import { Command, ConvertArgumentDefinitionsToArgs } from "../../utils/slash/createCommand.js";
function logCommand(
@@ -22,11 +23,10 @@ function logCommand(
type: "Failure" | "Success" | "Trigger" | "Slowmode" | "Missing" | "Inhibit",
commandName: string,
) {
const command = `[COMMAND: ${bgYellow(black(commandName || "Unknown"))} - ${
bgBlack(
["Failure", "Slowmode", "Missing"].includes(type) ? red(type) : type === "Success" ? green(type) : white(type),
)
}]`;
const command = `[COMMAND: ${bgYellow(black(commandName || "Unknown"))} - ${bgBlack(
["Failure", "Slowmode", "Missing"].includes(type) ? red(type) : type === "Success" ? green(type) : white(type),
)
}]`;
const user = bgGreen(
black(`${info.user.username}#${info.user.discriminator.toString().padStart(4, "0")}(${info.id})`),
@@ -36,7 +36,7 @@ function logCommand(
bot.logger.info(`${command} by ${user} in ${guild} with MessageID: ${info.id}`);
}
export async function executeSlashCommand(bot: BotWithCustomProps, interaction: Interaction) {
export async function executeSlashCommand(bot: BotWithCustomProps, interaction: InteractionWithCustomProps) {
const data = interaction.data;
const name = data?.name as keyof typeof COMMANDS;
@@ -89,7 +89,7 @@ export async function executeSlashCommand(bot: BotWithCustomProps, interaction:
}
/** Runs the inhibitors to see if a command is allowed to run. */
export async function commandAllowed(interaction: Interaction, command: Command<any>) {
export async function commandAllowed(interaction: InteractionWithCustomProps, command: Command<any>) {
// CHECK WHETHER THE USER/GUILD IS VIP
if (command.vipOnly) {
// SETUP-DD-TEMP: Check if this server/user is a vip.
@@ -149,21 +149,21 @@ function convertOptionValue(
option: InteractionDataOption,
translateOptions?: Record<string, string>,
): [
string,
(
| { user: User; member: Member }
| Role
| {
id: bigint;
name: string;
type: ChannelTypes;
permissions: bigint;
}
| boolean
| string
| number
),
] {
string,
(
| { user: User; member: Member }
| Role
| {
id: bigint;
name: string;
type: ChannelTypes;
permissions: bigint;
}
| boolean
| string
| number
),
] {
// THE OPTION IS A CHANNEL
if (option.type === ApplicationCommandOptionTypes.Channel) {
const channel = interaction.data?.resolved?.channels?.get(BigInt(option.value as string));
@@ -269,7 +269,7 @@ export function optionParser(
[translateOptions?.[interaction.data.options[0].name] ?? interaction.data.options[0].name]: {
[
translateOptions?.[interaction.data.options[0]!.options![0]!.name] ??
interaction.data.options[0]!.options![0]!.name
interaction.data.options[0]!.options![0]!.name
]: convertedOptions,
},
};

View File

@@ -1,5 +1,6 @@
import { InteractionTypes, MessageComponentTypes } from "discordeno";
import { bot } from "../../bot.js";
import { InteractionWithCustomProps } from "../../typings/discordeno.js";
import { executeButtonClick } from "./button.js";
import { executeSlashCommand } from "./command.js";
import { executeModalSubmit } from "./modal.js";
@@ -7,7 +8,7 @@ import { executeModalSubmit } from "./modal.js";
export function setInteractionCreateEvent() {
bot.events.interactionCreate = async function (_, interaction) {
if (interaction.type === InteractionTypes.ApplicationCommand) {
await executeSlashCommand(bot, interaction);
await executeSlashCommand(bot, interaction as InteractionWithCustomProps);
} else if (interaction.type === InteractionTypes.MessageComponent) {
if (!interaction.data) return;

View File

@@ -1,6 +1,6 @@
import { setInteractionCreateEvent } from "./interactions/mod.js";
import { setMessageCreateEvent } from "./messages/create.js";
import { setRawEvent } from "./raw.js";
import { setInteractionCreateEvent } from "./interactions/mod";
import { setMessageCreateEvent } from "./messages/create";
import { setRawEvent } from "./raw";
export function setupEventHandlers() {
setInteractionCreateEvent();

View File

@@ -1,18 +1,20 @@
import { DiscordGatewayPayload } from "discordeno";
import Embeds from "discordeno/embeds";
// ReferenceError: publishMessage is not defined
// import Embeds from "discordeno/embeds";
import dotenv from 'dotenv';
import express from "express";
import {
BOT_ID,
BUGS_ERRORS_REPORT_WEBHOOK,
DEVELOPMENT,
EVENT_HANDLER_AUTHORIZATION,
EVENT_HANDLER_PORT,
EVENT_HANDLER_URL,
} from "../configs.js";
import { BOT_ID, EVENT_HANDLER_URL } from "../configs.js";
import { bot } from "./bot.js";
import { updateDevCommands } from "./utils/slash/updateCommands.js";
import { webhookURLToIDAndToken } from "./utils/webhook.js";
dotenv.config()
const BUGS_ERRORS_REPORT_WEBHOOK = process.env.BUGS_ERRORS_REPORT_WEBHOOK as string
const DEVELOPMENT = process.env.DEVELOPMENT as string
const EVENT_HANDLER_AUTHORIZATION = process.env.EVENT_HANDLER_AUTHORIZATION as string
const EVENT_HANDLER_PORT = process.env.EVENT_HANDLER_PORT as string
process
.on("unhandledRejection", (error) => {
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
@@ -28,6 +30,8 @@ process
if (!error) return;
// ReferenceError: publishMessage is not defined
/*
const embeds = new Embeds()
.setDescription(["```js", error, "```"].join(`\n`))
.setTimestamp()
@@ -35,6 +39,7 @@ process
// SEND ERROR TO THE LOG CHANNEL ON THE DEV SERVER
return bot.helpers.sendWebhookMessage(bot.transformers.snowflake(id), token, { embeds }).catch(console.error);
*/
})
.on("uncaughtException", async (error) => {
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
@@ -50,13 +55,14 @@ process
if (!error) process.exit(1);
/*
const embeds = new Embeds()
.setDescription(["```js", error.stack, "```"].join(`\n`))
.setTimestamp()
.setFooter("Unhandled Exception Error Occurred");
// SEND ERROR TO THE LOG CHANNEL ON THE DEV SERVER
await bot.helpers.sendWebhookMessage(bot.transformers.snowflake(id), token, { embeds }).catch(console.error);
// SEND ERROR TO THE LOG CHANNEL ON THE DEV SERVER
await bot.helpers.sendWebhookMessage(bot.transformers.snowflake(id), token, { embeds }).catch(console.error);
*/
process.exit(1);
});

View File

@@ -1,10 +1,11 @@
import Embeds from "discordeno/embeds";
import { MISSING_TRANSLATION_WEBHOOK } from "../../configs.js";
import { bot } from "../bot.js";
import { webhookURLToIDAndToken } from "../utils/webhook.js";
import english from "./english.js";
import languages from "./languages.js";
const MISSING_TRANSLATION_WEBHOOK = process.env.MISSING_TRANSLATION_WEBHOOK as string;
/** This should hold the language names per guild id. <guildId, language> */
export const serverLanguages = new Map<bigint, keyof typeof languages>();

View File

@@ -1,13 +1,10 @@
// This file allows you to tell typescript about any additions you have made to the internal discordeno objects.
import { InteractionResponse, Message } from "discordeno";
import { Interaction, InteractionResponse, Message } from "discordeno";
declare module "discordeno" {
// We want to add something to the Interaction object.
interface Interaction {
// Normally, to send a response you would have to do something like bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: "text here" } })
// But with this reply method we added, it is as simple as interaction.reply("text here").
// Feel free to delete these comments once you have understood the concept.
/** Send a reply to an interaction. */
reply(response: InteractionResponse | string): Promise<Message | undefined>;
}
}
export interface InteractionWithCustomProps extends Interaction {
// Normally, to send a response you would have to do something like bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: "text here" } })
// But with this reply method we added, it is as simple as interaction.reply("text here").
// Feel free to delete these comments once you have understood the concept.
/** Send a reply to an interaction. */
reply(response: InteractionResponse | string): Promise<Message | undefined>;
}

View File

@@ -7,10 +7,11 @@ import {
Member,
PermissionStrings,
Role,
User,
User
} from "discordeno";
import english from "../../languages/english.js";
import { translationKeys } from "../../languages/translate.js";
import { InteractionWithCustomProps } from "../../typings/discordeno.js";
import { PermissionLevelHandlers } from "./permLevels.js";
export function createCommand<T extends readonly ArgumentDefinition[]>(command: Command<T>) {
@@ -156,79 +157,79 @@ export type ConvertArgumentDefinitionsToArgs<T extends readonly ArgumentDefiniti
UnionToIntersection<
{
[P in keyof T]: T[P] extends StringOptionalArgumentDefinition<infer N> // STRING
? {
// @ts-ignore TODO: fix this some day
[_ in getName<N>]?: T[P]["choices"] extends readonly { name: string; value: string }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: string;
}
: T[P] extends StringArgumentDefinition<infer N> ? {
// @ts-ignore TODO: fix this some day
[_ in getName<N>]: T[P]["choices"] extends readonly { name: string; value: string }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: string;
}
// INTEGER
: T[P] extends IntegerOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?: T[P]["choices"] extends readonly { name: string; value: number }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: number;
}
: T[P] extends IntegerArgumentDefinition<infer N> ? {
[_ in getName<N>]: T[P]["choices"] extends readonly { name: string; value: number }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: number;
}
// BOOLEAN
: T[P] extends BooleanOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: boolean }
: T[P] extends BooleanArgumentDefinition<infer N> ? { [_ in getName<N>]: boolean }
// USER
: T[P] extends UserOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?: {
user: User;
member: Member;
};
}
: T[P] extends UserArgumentDefinition<infer N> ? {
[_ in getName<N>]: {
user: User;
member: Member;
};
}
// CHANNEL
: T[P] extends ChannelOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Channel }
: T[P] extends ChannelArgumentDefinition<infer N> ? { [_ in getName<N>]: Channel }
// ROLE
: T[P] extends RoleOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Role }
: T[P] extends RoleArgumentDefinition<infer N> ? { [_ in getName<N>]: Role }
// MENTIONABLE
: T[P] extends MentionableOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?:
| Role
| {
user: User;
member: Member;
};
}
: T[P] extends MentionableArgumentDefinition<infer N> ? {
[_ in getName<N>]:
| Role
| {
user: User;
member: Member;
};
}
// SUBCOMMAND
: T[P] extends SubcommandArgumentDefinition<infer N> ? {
[_ in getName<N>]?: T[P]["options"] extends readonly ArgumentDefinition[] // @ts-ignore somehow this check does not work
? ConvertArgumentDefinitionsToArgs<T[P]["options"]>
: {};
}
// SUBCOMMANDGROUP
: T[P] extends SubcommandGroupArgumentDefinition<infer N> ? {
[_ in getName<N>]?: ConvertArgumentDefinitionsToArgs<T[P]["options"]>;
}
: never;
? {
// @ts-ignore TODO: fix this some day
[_ in getName<N>]?: T[P]["choices"] extends readonly { name: string; value: string }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: string;
}
: T[P] extends StringArgumentDefinition<infer N> ? {
// @ts-ignore TODO: fix this some day
[_ in getName<N>]: T[P]["choices"] extends readonly { name: string; value: string }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: string;
}
// INTEGER
: T[P] extends IntegerOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?: T[P]["choices"] extends readonly { name: string; value: number }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: number;
}
: T[P] extends IntegerArgumentDefinition<infer N> ? {
[_ in getName<N>]: T[P]["choices"] extends readonly { name: string; value: number }[] // @ts-ignore
? T[P]["choices"][number]["value"]
: number;
}
// BOOLEAN
: T[P] extends BooleanOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: boolean }
: T[P] extends BooleanArgumentDefinition<infer N> ? { [_ in getName<N>]: boolean }
// USER
: T[P] extends UserOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?: {
user: User;
member: Member;
};
}
: T[P] extends UserArgumentDefinition<infer N> ? {
[_ in getName<N>]: {
user: User;
member: Member;
};
}
// CHANNEL
: T[P] extends ChannelOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Channel }
: T[P] extends ChannelArgumentDefinition<infer N> ? { [_ in getName<N>]: Channel }
// ROLE
: T[P] extends RoleOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Role }
: T[P] extends RoleArgumentDefinition<infer N> ? { [_ in getName<N>]: Role }
// MENTIONABLE
: T[P] extends MentionableOptionalArgumentDefinition<infer N> ? {
[_ in getName<N>]?:
| Role
| {
user: User;
member: Member;
};
}
: T[P] extends MentionableArgumentDefinition<infer N> ? {
[_ in getName<N>]:
| Role
| {
user: User;
member: Member;
};
}
// SUBCOMMAND
: T[P] extends SubcommandArgumentDefinition<infer N> ? {
[_ in getName<N>]?: T[P]["options"] extends readonly ArgumentDefinition[] // @ts-ignore somehow this check does not work
? ConvertArgumentDefinitionsToArgs<T[P]["options"]>
: {};
}
// SUBCOMMANDGROUP
: T[P] extends SubcommandGroupArgumentDefinition<infer N> ? {
[_ in getName<N>]?: ConvertArgumentDefinitionsToArgs<T[P]["options"]>;
}
: never;
}[number]
>
>;
@@ -244,7 +245,7 @@ export interface Command<T extends readonly ArgumentDefinition[]> {
/** The options for the command, used for both slash and message commands. */
// options?: ApplicationCommandOption[];
options?: T;
execute: (bot: Bot, data: Interaction, args: ConvertArgumentDefinitionsToArgs<T>) => unknown;
execute: (bot: Bot, data: InteractionWithCustomProps, args: ConvertArgumentDefinitionsToArgs<T>) => unknown;
subcommands?: Record<string, Omit<Command<any>, "subcommands"> & { group?: string }>;
/** Whether the command should have a cooldown */
cooldown?: {
@@ -272,8 +273,8 @@ export interface Command<T extends readonly ArgumentDefinition[]> {
acknowledge?: boolean;
permissionLevels?:
| (keyof typeof PermissionLevelHandlers)[]
| ((data: Interaction, command: Command<T>) => boolean | Promise<boolean>);
| (keyof typeof PermissionLevelHandlers)[]
| ((data: Interaction, command: Command<T>) => boolean | Promise<boolean>);
botServerPermissions?: PermissionStrings[];
botChannelPermissions?: PermissionStrings[];
userServerPermissions?: PermissionStrings[];

View File

@@ -1,11 +1,12 @@
import { ApplicationCommandOption, ApplicationCommandTypes, Bot } from "discordeno";
import { DEV_SERVER_ID } from "../../../configs.js";
import { prisma } from "../../../prisma.js";
import { bot } from "../../bot.js";
import COMMANDS from "../../commands/mod.js";
import { serverLanguages, translate } from "../../languages/translate.js";
import { ArgumentDefinition } from "./createCommand.js";
const DEV_SERVER_ID = process.env.DEV_SERVER_ID as string;
export async function updateDevCommands(bot: Bot) {
const cmds = Object.entries(COMMANDS)
// ONLY DEV COMMANDS

View File

@@ -1,54 +1,12 @@
import { getBotIdFromToken, Intents } from "discordeno";
// General Configurations
/** Whether or not this process is a local development version. Change to false for the main production bot. */
// SETUP-DD-TEMP: Change this to false in your server for production bot. Keep true in local testing.
export const DEVELOPMENT = true;
/** The server id where you develop/test the bot. */
// SETUP-DD-TEMP: Change the id to a server where you develop the bot privately.
export const DEV_SERVER_ID: string = "";
/** The discord bot token, without the BOT prefix. */
// SETUP-DD-TEMP: Add the bot token here.
export const DISCORD_TOKEN = "";
import dotenv from 'dotenv';
dotenv.config()
/** The bot id, derived from the bot token. */
export const BOT_ID = getBotIdFromToken(DISCORD_TOKEN);
// Bot Configurations
/** The secret passcode that the bot code (event handler) is listening for. This is used to prevent someone else from trying to send malicious messages to your bot. */
// SETUP-DD-TEMP: Add a secret passcode here.
export const EVENT_HANDLER_AUTHORIZATION = "";
/** The port where the event handler is listening for events. */
// SETUP-DD-TEMP: Set the desired port where events will be sent to be processed.
export const EVENT_HANDLER_PORT = 8081;
/** The url where the bot code(event handler) will run. This is where the gateway will send its messages to. */
// SETUP-DD-TEMP: Set the bot's url here.
export const EVENT_HANDLER_URL = `http://localhost:${EVENT_HANDLER_PORT}`;
/** The full webhook url where the bot can send errors to alert you that the bot is missing translations. */
// SETUP-DD-TEMP: Set a full discord webhook url here.
export const MISSING_TRANSLATION_WEBHOOK = "";
/** The full webhook url where the bot can send errors to alert you that the bot is throwing errors. */
// SETUP-DD-TEMP: Set a full discord webhook url here.
export const BUGS_ERRORS_REPORT_WEBHOOK = "";
// Rest Proxy Configurations
/** The authorization code that the REST proxy will check for to make sure the requests are coming from you. */
// SETUP-DD-TEMP: Add a secret passcode here.
export const REST_AUTHORIZATION = "";
/** The port that will run the REST proxy. */
// SETUP-DD-TEMP: Choose the port number here that will be used for the REST proxy.
export const REST_PORT = 8000;
/** The url where requests will be sent to from the bot. */
// SETUP-DD-TEMP: Provide the url where you will host your REST proxy. If it is on the same server as others, you can use localhost but if it is on a separate server you should change this entirely.
export const REST_URL = `http://localhost:${REST_PORT}`;
export const BOT_ID = getBotIdFromToken(process.env.DISCORD_TOKEN as string);
export const EVENT_HANDLER_URL = `http://${process.env.EVENT_HANDLER_HOST}:${process.env.EVENT_HANDLER_PORT}`;
export const REST_URL = `http://${process.env.REST_HOST}:${process.env.REST_PORT}`;
export const GATEWAY_URL = `http://${process.env.GATEWAY_HOST}:${process.env.GATEWAY_PORT}`;
// Gateway Proxy Configurations
/** The gateway intents you would like to use. */
@@ -69,40 +27,3 @@ export const INTENTS: Intents =
Intents.GuildVoiceStates |
Intents.GuildWebhooks |
Intents.Guilds;
/** The amount of shards to start. Useful with multiple servers where each server is handling a portion of your bot. */
// SETUP-DD-TEMP: To start all bots, leave it as undefined. Specify he number of shards this process should handle.
export const TOTAL_SHARDS: number | undefined = undefined;
/** The amount of shards to start per worker. */
// SETUP-DD-TEMP: Choose how many shards to start per worker. If you are not sure just stick to 16.
export const SHARDS_PER_WORKER: number = 16;
/** The total amount of workers to start. Generally this should be equal to the number of cores your server has. */
// SETUP-DD-TEMP: Choose how many workers to start up. If you are not sure, check how many cores your server has.
export const TOTAL_WORKERS: number = 4;
/** The secret passcode that the gateway is listening for. This is used to prevent someone else from trying to send malicious messages to your bot. */
// SETUP-DD-TEMP: Add a secret passcode here.
export const GATEWAY_AUTHORIZATION = "";
/** The host where the gateway will run. Must follow https://nodejs.org/api/net.html#serverlistenoptions-callback. */
// SETUP-DD-TEMP: Set the gateways's host here.
export const GATEWAY_HOST = "localhost";
/** The port where the gateway will run. This is where the bot will send its messages to the gateway. */
// SETUP-DD-TEMP: Set the gateways's port here.
export const GATEWAY_PORT: number = 8080;
/** The url where the gateway will run. This is where the bot will send its messages to the gateway. */
// SETUP-DD-TEMP: Set the gateways's url here.
export const GATEWAY_URL = `${GATEWAY_HOST}:${GATEWAY_PORT}`;
// Database Configurations
/** These INFLUX configs are only if you wish to enable analytics. */
// SETUP-DD-TEMP: This is optional. If you want to build analytics, add influxdb here.
export const INFLUX_BUCKET = "";
export const INFLUX_ORG = "";
export const INFLUX_TOKEN = "";
export const INFLUX_URL = "";

View File

@@ -3,21 +3,21 @@ import { createLogger } from "discordeno/logger";
import fastify from "fastify";
import { nanoid } from "nanoid";
import { Worker } from "worker_threads";
import {
DISCORD_TOKEN,
EVENT_HANDLER_AUTHORIZATION,
EVENT_HANDLER_URL,
GATEWAY_AUTHORIZATION,
GATEWAY_HOST,
GATEWAY_PORT,
INTENTS,
REST_AUTHORIZATION,
REST_URL,
SHARDS_PER_WORKER,
TOTAL_SHARDS,
TOTAL_WORKERS,
} from "../configs.js";
import { WorkerCreateData, WorkerGetShardInfo, WorkerMessage, WorkerShardInfo, WorkerShardPayload } from "./worker.js";
import { EVENT_HANDLER_URL, INTENTS, REST_URL } from "../configs.js";
import { WorkerCreateData, WorkerGetShardInfo, WorkerMessage, WorkerShardInfo, WorkerShardPayload } from "./worker";
import dotenv from "dotenv";
dotenv.config()
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string
const EVENT_HANDLER_AUTHORIZATION = process.env.EVENT_HANDLER_AUTHORIZATION as string
const GATEWAY_AUTHORIZATION = process.env.GATEWAY_AUTHORIZATION as string
const GATEWAY_HOST = process.env.GATEWAY_HOST as string
const GATEWAY_PORT = Number(process.env.GATEWAY_PORT as string)
const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string
const SHARDS_PER_WORKER = Number(process.env.SHARDS_PER_WORKER as string)
const TOTAL_SHARDS = process.env.TOTAL_SHARDS ? Number(process.env.TOTAL_SHARDS) : undefined
const TOTAL_WORKERS = Number(process.env.TOTAL_WORKERS as string)
async function main() {
const log = createLogger({ name: "[MANAGER]" });
@@ -45,7 +45,7 @@ async function main() {
shardsPerWorker: SHARDS_PER_WORKER,
totalWorkers: TOTAL_WORKERS,
handleDiscordPayload: () => {},
handleDiscordPayload: () => { },
tellWorkerToIdentify: async (_gateway, workerId, shardId, _bucketId) => {
log.info("TELL TO IDENTIFY", { workerId, shardId, _bucketId });
@@ -81,7 +81,7 @@ async function main() {
workerId,
};
const worker = new Worker("./worker.js", {
const worker = new Worker("./dist/gateway/worker.js", {
workerData,
});

View File

@@ -6,11 +6,12 @@ import {
GatewayEventNames,
Shard,
ShardSocketRequest,
ShardState,
ShardState
} from "discordeno";
import { createLogger } from "discordeno/logger";
import fetch from "node-fetch";
import { parentPort, workerData } from "worker_threads";
import { ManagerMessage } from "./index.js";
import { ManagerMessage } from "./index";
if (!parentPort) {
throw new Error("Parent port is null");

View File

@@ -1,9 +1,15 @@
import { Point } from "@influxdata/influxdb-client";
import { BASE_URL, createRestManager } from "discordeno";
import express, { Request, Response } from "express";
import { Influx } from "../analytics";
import { REST_URL } from '../configs';
import { Influx } from "../analytics.js";
import { DISCORD_TOKEN, REST_AUTHORIZATION, REST_PORT, REST_URL } from "../configs.js";
import dotenv from "dotenv";
dotenv.config()
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string
const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string
const REST_PORT = process.env.REST_PORT as string
const rest = createRestManager({
token: DISCORD_TOKEN,
@@ -55,6 +61,7 @@ if (Influx) {
}, 30000);
}
//@ts-ignore
rest.convertRestError = (errorStack, data) => {
if (!data) return { message: errorStack.message };
return { ...data, message: errorStack.message };

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "es2020",
"module": "CommonJS",
"module": "es2020",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "./dist",
@@ -10,7 +10,6 @@
"importHelpers": true,
"allowUnusedLabels": false,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noUncheckedIndexedAccess": true,
@@ -20,7 +19,15 @@
"useUnknownInCatchVariables": false,
"allowUnreachableCode": false,
"skipLibCheck": true,
"moduleResolution": "node"
"moduleResolution": "node",
},
"include": ["./src/**/*", ".env"]
}
"include": [
"./src/**/*",
".env"
],
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node",
"swc": true
}
}