diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 812d296..0eb7ab4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,4 +15,4 @@ jobs: run: | docker stop ${{ github.event.repository.name }} || true docker rm ${{ github.event.repository.name }} || true - docker run -d -e "TOKEN=${{ secrets.TOKEN }}" --restart always -p 2020:2020 --name ${{ github.event.repository.name }} ${{ github.repository }} + docker run -d -e "TOKEN=${{ secrets.TOKEN }}" -e "ENVIRONMENT=${{ secrets.ENVIRONMENT }}" --restart always -p 2020:2020 --name ${{ github.event.repository.name }} ${{ github.repository }} diff --git a/environment.d.ts b/environment.d.ts new file mode 100644 index 0000000..acb02d3 --- /dev/null +++ b/environment.d.ts @@ -0,0 +1,12 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + TOKEN: string; + DEVTOKEN: string; + PORT: string; + ENVIRONMENT: 'prod' | 'dev'; + } + } +} + +export {}; diff --git a/package-lock.json b/package-lock.json index 82b21de..44c8ba6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,12 @@ "dependencies": { "@types/cors": "^2.8.13", "@types/express": "^4.17.15", + "@types/glob": "^8.0.0", "cors": "^2.8.5", "discord.js": "^14.7.1", "dotenv": "^16.0.3", "express": "^4.18.2", + "glob": "^8.0.3", "typescript": "^4.9.4" } }, @@ -150,11 +152,25 @@ "@types/range-parser": "*" } }, + "node_modules/@types/glob": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", + "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "node_modules/@types/node": { "version": "18.11.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", @@ -204,6 +220,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -227,6 +248,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -483,6 +512,11 @@ "node": ">= 0.6" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -501,6 +535,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -568,6 +620,15 @@ } ] }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -642,6 +703,17 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -682,6 +754,14 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1005,6 +1085,11 @@ "node": ">= 0.8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", @@ -1135,11 +1220,25 @@ "@types/range-parser": "*" } }, + "@types/glob": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", + "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "@types/node": { "version": "18.11.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", @@ -1186,6 +1285,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1205,6 +1309,14 @@ "unpipe": "1.0.0" } }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1403,6 +1515,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1418,6 +1535,18 @@ "has-symbols": "^1.0.3" } }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1456,6 +1585,15 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1509,6 +1647,14 @@ "mime-db": "1.52.0" } }, + "minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1537,6 +1683,14 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1759,6 +1913,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", diff --git a/package.json b/package.json index 046a3c6..bdeb399 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "start": "tsc && node .", "build": "tsc", "serve": "node .", + "register": "tsc && node ./dist/register.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -22,10 +23,12 @@ "dependencies": { "@types/cors": "^2.8.13", "@types/express": "^4.17.15", + "@types/glob": "^8.0.0", "cors": "^2.8.5", "discord.js": "^14.7.1", "dotenv": "^16.0.3", "express": "^4.18.2", + "glob": "^8.0.3", "typescript": "^4.9.4" } } diff --git a/src/cfg.json b/src/cfg.json index ce6199d..e7159df 100644 --- a/src/cfg.json +++ b/src/cfg.json @@ -2,5 +2,8 @@ "api": { "fallbackPort": 2020, "memberCountGuildId": "447075692664979466" + }, + "dsc": { + "slashCmdTestGuildId": "860916258282012692" } } diff --git a/src/commands/ping.ts b/src/commands/ping.ts new file mode 100644 index 0000000..611857e --- /dev/null +++ b/src/commands/ping.ts @@ -0,0 +1,9 @@ +import { Command } from '../structures/Command'; + +export default new Command({ + name: 'ping', + description: 'pongs back!', + run: async ({ interaction }) => { + interaction.followUp('pong'); + }, +}); diff --git a/src/commands/pomoc.ts b/src/commands/pomoc.ts new file mode 100644 index 0000000..d49a75e --- /dev/null +++ b/src/commands/pomoc.ts @@ -0,0 +1,9 @@ +import { Command } from '../structures/Command'; + +export default new Command({ + name: 'pomoc', + description: 'pokazuje pomocne informacje', + run: async ({ interaction }) => { + interaction.followUp('tu będzie komenda z pomocami!'); + }, +}); diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts new file mode 100644 index 0000000..0164418 --- /dev/null +++ b/src/events/guildMemberAdd.ts @@ -0,0 +1,6 @@ +import { Event } from '../structures/Event'; +import { members } from '..'; + +export default new Event('guildMemberAdd', () => { + members.incrementCurrent(); +}); diff --git a/src/events/guildMemberRemove.ts b/src/events/guildMemberRemove.ts new file mode 100644 index 0000000..209146b --- /dev/null +++ b/src/events/guildMemberRemove.ts @@ -0,0 +1,6 @@ +import { Event } from '../structures/Event'; +import { members } from '..'; + +export default new Event('guildMemberRemove', () => { + members.decrementCurrent(); +}); diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts new file mode 100644 index 0000000..b59ba1b --- /dev/null +++ b/src/events/interactionCreate.ts @@ -0,0 +1,18 @@ +import { CommandInteractionOptionResolver } from 'discord.js'; +import { client } from '..'; +import { Event } from '../structures/Event'; +import { BotInteraction } from '../typings/Command'; + +export default new Event('interactionCreate', async (interaction) => { + if (interaction.isCommand()) { + await interaction.deferReply(); + const command = client.commands.get(interaction.commandName); + if (!command) + return interaction.followUp("This command doesn't exist. Dork."); + command.run({ + args: interaction.options as CommandInteractionOptionResolver, + client, + interaction: interaction as BotInteraction, + }); + } +}); diff --git a/src/events/ready.ts b/src/events/ready.ts new file mode 100644 index 0000000..9a2dcb2 --- /dev/null +++ b/src/events/ready.ts @@ -0,0 +1,23 @@ +import { client, config, members } from '..'; +import { Event } from '../structures/Event'; + +export default new Event('ready', () => { + console.log(`BOT: Now logged in as ${client.user?.tag}.`); + + // memberCount reporting - for the API + // --------------------------------------- + // NOTE: for this to work, bot needs to be + // member of Gractwo Main Discord Server + members.setCurrent( + client.guilds.resolve(config.api.memberCountGuildId)?.memberCount || null + ); + let intervalSeconds = 5; + setInterval(() => { + if (members.previous != members.current) { + console.log( + `API/BOT: Gractwo memberCount is ${`now ${members.current}` || 'unset'}` + ); + } + members.setPrevious(members.current); + }, intervalSeconds * 1000); +}); diff --git a/src/index.ts b/src/index.ts index 5c99786..68e8002 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,52 +1,28 @@ -import DiscordJS from 'discord.js'; -// import fs from 'fs'; +import { BotClient } from './structures/Client'; +import { MemberCount } from './structures/MemberCount'; import express from 'express'; -import dotenv from 'dotenv'; import cors from 'cors'; import config from './cfg.json'; -dotenv.config(); +require('dotenv').config(); + +const client = new BotClient(); +client.boot(); -const client = new DiscordJS.Client({ - intents: [ - DiscordJS.GatewayIntentBits.Guilds, - DiscordJS.GatewayIntentBits.GuildMembers, - ], -}); const app = express(); app.use(cors()); -const port = process.env.PORT || config.api.fallbackPort; -let memberCount: number | null, previousCount: number | null; +const members = new MemberCount(); +let port = process.env.PORT || config.api.fallbackPort; app.get('/', (req, res) => { res.sendStatus(200); }); app.get('/members', (req, res) => { res - .status(memberCount ? 200 : 500) - .send(memberCount ? memberCount.toString() : 'Server Error'); + .status(members.current ? 200 : 500) + .send(members.current ? members.current.toString() : 'Server Error'); }); - -client.on('guildMemberAdd', () => { - if (memberCount) memberCount++; -}); -client.on('guildMemberRemove', () => { - if (memberCount) memberCount--; -}); -client.on('ready', () => { - console.log(`BOT: Now logged in as ${client.user?.tag}.`); - memberCount = - client.guilds.resolve(config.api.memberCountGuildId)?.memberCount || null; - setInterval(() => { - if (previousCount != memberCount) { - console.log( - `API/BOT: Gractwo memberCount is ${`now ${memberCount}` || 'unset'}.` - ); - } - previousCount = memberCount; - }, 5 * 1000); -}); - app.listen(port, () => { console.log(`API: Now listening on :${port}.`); }); -client.login(process.env.TOKEN); + +export { client, app, config, members }; diff --git a/src/structures/Client.ts b/src/structures/Client.ts new file mode 100644 index 0000000..2b54a10 --- /dev/null +++ b/src/structures/Client.ts @@ -0,0 +1,84 @@ +import { + ApplicationCommandDataResolvable, + Client, + ClientEvents, + Collection, + GatewayIntentBits, +} from 'discord.js'; +import { CommandType } from '../typings/Command'; +import glob from 'glob'; +import { promisify } from 'util'; +import { RegisterCommandsOptions } from '../typings/Client'; +import { Event } from './Event'; +import config from '../cfg.json'; + +// makes file search lib a promise +const globPromise = promisify(glob); + +class BotClient extends Client { + commands: Collection = new Collection(); + + constructor() { + super({ + intents: 32767, // all intents! + }); + } + + boot() { + this.registerModules(); + this.login( + // if prod, use main bot token + process.env.ENVIRONMENT == 'prod' + ? process.env.TOKEN + : process.env.DEVTOKEN + ); + } + + async importFile(filePath: string) { + return (await import(filePath))?.default; + } + + async registerCommands({ commands, guildId }: RegisterCommandsOptions) { + if (guildId) { + // register in guild + this.guilds.cache.get(guildId)?.commands.set(commands); + } else { + // register globally + this.application?.commands.set(commands); + } + } + + async registerModules() { + const slashCommands: ApplicationCommandDataResolvable[] = []; + const commandFiles = await globPromise( + `${__dirname}/../commands/*{.ts,.,js}` + ); + commandFiles.forEach(async (filePath) => { + const command: CommandType = await this.importFile(filePath); + if (!command.name) return; + this.commands.set(command.name, command); + slashCommands.push(command); + }); + + this.on('ready', () => { + this.registerCommands({ + commands: slashCommands, + guildId: config.dsc.slashCmdTestGuildId, + }); + // if prod, register global commands + if (process.env.ENVIRONMENT == 'prod') { + this.registerCommands({ + commands: slashCommands, + }); + } + }); + + const eventFiles = await globPromise(`${__dirname}/../events/*{.ts,.js}`); + eventFiles.forEach(async (filePath) => { + const event: Event = await this.importFile(filePath); + this.on(event.event, event.run); + }); + } +} + +export { BotClient }; diff --git a/src/structures/Command.ts b/src/structures/Command.ts new file mode 100644 index 0000000..c071da7 --- /dev/null +++ b/src/structures/Command.ts @@ -0,0 +1,9 @@ +import { CommandType } from '../typings/Command'; + +class Command { + constructor(commandOptions: CommandType) { + Object.assign(this, commandOptions); + } +} + +export { Command }; diff --git a/src/structures/Event.ts b/src/structures/Event.ts new file mode 100644 index 0000000..7f2690a --- /dev/null +++ b/src/structures/Event.ts @@ -0,0 +1,10 @@ +import { ClientEvents } from 'discord.js'; + +class Event { + constructor( + public event: Key, + public run: (...args: ClientEvents[Key]) => any + ) {} +} + +export { Event }; diff --git a/src/structures/MemberCount.ts b/src/structures/MemberCount.ts new file mode 100644 index 0000000..e0cf1d7 --- /dev/null +++ b/src/structures/MemberCount.ts @@ -0,0 +1,22 @@ +class MemberCount { + current: number | null = null; + previous: number | null = null; + // "previous" is only used for reporting checks + // inside the ready.ts event + + incrementCurrent() { + if (this.current) this.current++; + } + decrementCurrent() { + if (this.current) this.current--; + } + setCurrent(input: number | null) { + this.current = input; + } + + setPrevious(input: number | null) { + this.previous = input; + } +} + +export { MemberCount }; diff --git a/src/typings/Client.ts b/src/typings/Client.ts new file mode 100644 index 0000000..bc18c84 --- /dev/null +++ b/src/typings/Client.ts @@ -0,0 +1,7 @@ +import { ApplicationCommandDataResolvable } from 'discord.js'; + +interface RegisterCommandsOptions { + guildId?: string; + commands: ApplicationCommandDataResolvable[]; +} +export { RegisterCommandsOptions }; diff --git a/src/typings/Command.ts b/src/typings/Command.ts new file mode 100644 index 0000000..bfac51d --- /dev/null +++ b/src/typings/Command.ts @@ -0,0 +1,27 @@ +import { + ChatInputApplicationCommandData, + CommandInteraction, + CommandInteractionOptionResolver, + GuildMember, + PermissionResolvable, +} from 'discord.js'; +import { BotClient } from '../structures/Client'; + +interface BotInteraction extends CommandInteraction { + member: GuildMember; +} + +interface RunOptions { + client: BotClient; + interaction: BotInteraction; + args: CommandInteractionOptionResolver; +} + +type RunFunction = (options: RunOptions) => any; + +type CommandType = { + userPermissions?: PermissionResolvable[]; + run: RunFunction; +} & ChatInputApplicationCommandData; + +export { CommandType, BotInteraction };