mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
BREAKING: node migration + major improvements and style changes (#2783)
* Setup turborepo (#2610) * chore: BREAKING move to monorepo structure * chore: setup turborepo * setup more eslint and tsconfig (#2612) * fix packages setting lcoation * add dist to ignore * style * fix: .vscode set prettier and remove deno configs (#2611) * fix: rewrite to process.env * chore: add node types * style: run eslint --fix * fix: tests import ext * chore: fix formatter * chore: add build script * chore: remove site from turborepo * chore: move to seperate packages * chore: seperate util * chore: rename to index * fix: utils * chore: bump typescript * fix: in process of fixing rest * fix: logger * style * chore: fix turbo script * fix: types * fix: types * fix: shard * fix: gateway * fixing: bot * fix: at least it can run now * chore: fix turbo script * chore: move plugins * chore: add type and utils export * chore: working bot type and cache plugin * Add git hooks (#2618) * CI: Setup ci (#2669) * ci: fix test * ci: fix typo * fix: turbo script * fixes: yarn and linter errors in embeds pkg * chore: fix yarn gitignore * Node migration - devcontainer (#2672) * ✨ - feat: devcontainer -> node * 👷♂ - ci: devcontainer - add tabnine, prettier * fix: ignore .env and debug.ts * fix: couple of linter errors * fix: linter error * fix: gateway linter errors * style: update style * style: fix bot style * fix: type * ci: move all old workflow * chore: close #2619 * chore: close #2671 * test: add mocha * chore: add typescript plugin * test: add mocha * test: add test to utils * test/ci: update ci and coverage * chore: change script naming * ci: update include test * test: add coverage * ci: fix cache * ci: fix ci and codecov * Discordeno Documentation (#2673) * Add git hooks * Add documentation generatation * Change Documentation Engine * Add documentation * Remove autogenerated docs * combine lint staged action into one * style: fix Co-authored-by: H01001000 <heiheiho000@gmail.com> * chore: new package client * test: add rest test * ci: enable codecov * fix: type * test: add test to all packages * ci: add release to gh per commit * fix: ci syntax * fix: package version * fix: publish script * fix: remove private from gateway * ci: add filter for changes * fix: ci syntex * ci: try fix path filter * ci: try fix path filter * test: add test * ci: fix string and boolean * fix: package and ci * chore: fix turbo type cache * ci: also publish to npm * ci: change to public * fix: not publish to npm * fix: dependencies * chore: fix fmt script * fix: better rest typecheck Closes #2621 * fix: run yarn install * feat: add transformers to rest * feat: add helpers to rest * test: move bot utils test * reverse change to release.yml * chore: add clean build * refactor: discordeno * chore: add import type * chore: remove bot * fix: change deps from bot to dd * chore: update yarn lock * test: temp remove test from logger * refactor: remove transformers in helpers/channel * type: close #2622 * ci(fix): explicitly define coverage file * refactor: remove transformers in helpers/emoji * type: fix discord guild type * feat: DiscordEditAutomoderationRule type * fix: remove unused type * feat: DiscordCreateGuildEmoji DiscordModifyGuildEmoji & DiscordModifyCHannel types * feat: DiscordCreateChannel DiscordBuDeleteMessages DiscordCreateMessage DiscordEditMessage * feat: DiscordCreateScheduledEvent EditScheduledEvent DiscordCreateInvite * fix: types for guild stuff for rest * feat: thread discord types for rest * feat: channel rest types * feat: member rest types * feat: more discord rest types * fix: type errors * fix: docs bot param name should be rest * type: fix type error * ci(fix): codecov * chore(client) :add export transformer * fix: verifySignature * test: fix types * test: add test prevent #2683 #2678 * fix: export transformer twice * ci: reuse cache * test: add test:unit-noTextCoverage * feat: add transform and constant package * fix: half fix #2683 #2688 * fix: #2688 fix all transformer.spec.ts * fix: transformer name * ci: update style * fix: dependencies * fix: naming * fix: yarn lock * fix: remove validations from helpers. Closes #2700 * fix: rest routes as a constants pkg * fix: esm import with .js * test: add exception case * feat: adding transformer * chore: change script name * chore: add path fixing to coverage file * fix: camelize tuple bug * fix: reverse transformers * fix: transformers folder to camel * fix: transformers as an object * fix: linter error * ci: run test with deno close #2701 * fix: test depends on build * fix: deno import node:crypto * fix: rest improvements * fix: channel type * fix: export user * chore: move to unit dir * test: moving bench * fix: remove transformers from rest package * fix: move toggle transformers to bot pkg * fix: move out gw helpers to gw package * test: add rest e2e test * fix: syntex * ci: add discord token * fix: ci not passing secret * test: add role test * ci: fix secret inherit * fix: role helpers transformer * test: increase timeout to 10s * fix: member helpers transformer * test: add member test * fix: name and type * fix: guild ban * test: fix test * fix: test add await * test: skip some test * test: increase timeout * chore: add transformer to import map * fix: test * test: add why-is-node-still-running * test: add debug hook * ci: add timeout incase any async running * test: try fix * test: turn on rest debug * fix: if undefined * test: reduce timeout * fix: queue not running after some request * fix: increase remaining after request without ratelimit header * fix: partial webhook * fix: fetch hooks not working if debug defined later * fix: nickname null to undefined * test: finish adding webhook test * refactor(test): move rest to utils * fix: add await * fix: sticker * feat: add message embed component transformer * fix: test not done * fix: arg type to bigString * fix: sendMessage * fix: add allowedMentions, interactionResponse * test: add emoji e2e test * test: add guild e2e test * refactor(test): remove extra rest call * fix: create emoji BINARY_TYPE_INVALID_DATA_URI * fix: create guild rate limit * fix: automode rule helpers and test * fix: test run in only * test: add some queue bucket test * test: remove empty test * refactor(test): use new guild * test: use new guild for other test * fix: guild not defind * reactor(test): remove duplicated creat channel * test: increase timeout to 30s * test: add thread test * fix: more transformers * fix: gateway helpers use transformers * fix: types belong in types pkg * fix: helpers use transformers * BREAKING: v19 rewrite to node + major improvements (#2703) * fix: move all to old folder * fix: cleanup types * fix: more cleanup * fix: more base cleanup * fix: token dotenv * fix: add base transformer * fix: partial error handling * fix: handle 429 rate limit * test(rest): fix unit test * fix(script): transform extension * test: fix error and buffer * feat: camelizer util * fix: cleanup * fix: rate limit queues and headers processing * fix: rest exports * fix: no more transformers * fix: queue header null bug * fix: add gateway package base * fix: lint error * fix: add prettier file * fix: prettier is default fmtr * fix: fmt shard file * fix: fmt * fix: types issue * fix: remove unused consts * fix: all import issues * fix: import error * fix: import ending with .js * fix: remove transformers package * Fix eslint (#2710) * fix: typing of button component label to be optional (#2708) * feat: add guild_connections to role tags and toggles (#2706) Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * Add lint checks and autofixing workflow (#2702) * Add lint checks and autofixing workflow * Update lint.yml * Fix: use yarn instead npm * fix: add ts to eslint_extensions * fix: update dir * fix: lint.yml format Co-authored-by: Jonathan Ho <heiheiho000@gmail.com> * fix: unused deps * ci: fix e2e test not running * chore: run yarn install * test: this should run test * feat: getCHannel * feat: createEMji helper * feat: collection class in util package * fix: gateway bugs * why on earth is this needed change * fix: cleanup docs on collection * Emoji rest methods (node-migration-clean) (#2713) * feat: all emoji rest methods * Fix code style issues with ESLint Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: stuff * fix: bot types * fix: remove logs * fix: camelize all gateway payloads * fix: remove todos * fix: start deris * fix: lint/ts errors except shard file * thats 1 way to fix type errors * yes (#2714) Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * feat: rest channel helpers * Update release.yml * Update release.yml * ci: add build filter * ci: fail fast false * fix: complete webhook related helpers * fix: follow announcement helper * add forum helper * add stage helpers * add thread related helpers * alphabetize * cleanup webhook routes * automod helpers * scheduled events * integrations helpers * invite stuff incomplete * dm channel and avatar url * chore: move ts-node into package * fix: importing esm * fix: tris message helpers * test(rest): add simplifyUrl test * test(rest): add checkRateLimits test * test(rest): add processRateLimitedPaths test * test(rest): fix missing beforeEach * test(utils): enable old test * interaction helpers * perf(utils): optimize snake to camel case conversion (#2717) * perf(utils): optimize snake to camel case conversion * fmt * wont change much but still faster * actually this was a stupid idea * shh * Fix(client): Fix typings. (#2716) Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * test: fix rest and utils test (#2723) * test(utils): fix cant import collection * test(rest): await expect * fix(utils): deno compactability * test(utils): typing * fix(utils): add return type * Add rest helpers for templates (#2727) * Add rest helpers for templates * Fix code style issues with ESLint Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: remove frozenAt * fix: cleanup templates * ci: release to npm (#2725) * ci: release to npm * Update release.yml * 🐛 - fix: types - slashcommands - add nsfw prop (#2731) * feat(rest helpers): Add template and member helpers. (#2728) * feat(rest helpers): Add template and member helpers. * format code * feat(rest): add template routes * fix(rest): routes and runMethod * fix(rest): try to fix most of type Co-authored-by: H01001000 <heiheiho000@gmail.com> Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * ci: fix release (#2732) * fix: rest type errors * fix: v19 begin * fix: yarn lock * chore: fix deps and script (#2733) * fix: bug camelizer deleting letters * fix: falsy token check * fix: add frozenat check for queue * fix: max stack trace error do to infinite loop * fix: type error * test(rest): fix missing import (#2734) Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * fix: file paths for imports * fix: lastShardId should default to 0 * fix: use isomorphic ws * test(rest): fix "TypeError: [Function] is not a thenable" (#2736) * test(utils): add utils tests (#2737) * test(utils): add urltobase64 test * test(utils): add token test * test(utils): fix missing import buffer * test(utils): add casting test * test(utils): add casting test * test(utils): fix use correct function * chore: make eslint error if missing .js extension (#2735) * chore: eslint error if missing .js extension * chore * lint: fix missing .js error Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * fix: readme runtime list (#2739) * Bot pkg (#2740) * fix: bot pkg test * Fix code style issues with ESLint * Update Guild.ts * Update Guild.ts * Fix code style issues with ESLint Co-authored-by: Lint Action <lint-action@samuelmeuli.com> Co-authored-by: Jonathan Ho <heiheiho000@gmail.com> * Update tsdoc.json (#2741) Updates tsdoc.json to reflect the current packages. * Interaction types - remove member,channel,role from value type (#2743) https://discord.com/channels/785384884197392384/1067265182776176690/1068189883073572924 * Add missing types (#2742) * Revert "fix: use isomorphic ws" (#2744) This reverts commitad306b0d0a. * fix: interaction requests that sent without full url * fix: lint issues * fix: remote gateway test file * fix: interaction response bug with body being invalid (#2746) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: interaction followup type (#2747) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: e2e exit bug (#2748) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: rest sending attachments files (#2749) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: guild and role methods (#2751) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * ci: add bot package (#2752) * ci: add bot package * ci: fix test * e2e test stuff (#2754) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint * fix: dont send heartbeat if socket is not open * fix: remove logs * fox: remove more logs * fix some bugs in role tests * Switch to after hook style * hoti's speed snaker * auto convert objects for discord * Fix code style issues with ESLint * fix: remove dup imports * fix: i hate linters * speeder --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * fix: delete guilds test (#2758) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint * fix: dont send heartbeat if socket is not open * fix: remove logs * fox: remove more logs * fix some bugs in role tests * Switch to after hook style * hoti's speed snaker * auto convert objects for discord * Fix code style issues with ESLint * fix: remove dup imports * fix: i hate linters * speeder * fix: tests delete guilds * Fix code style issues with ESLint --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * chore: fix deps (#2757) * ci/test: fix bot pkg e2e test (#2759) * chore: fix script and update import map (#2761) * chore: fix version script * chore: update import map * chore: fix ws import map * chore: fix deno import map * test: fix * fix: more rest e2e tests (#2763) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint * fix: dont send heartbeat if socket is not open * fix: remove logs * fox: remove more logs * fix some bugs in role tests * Switch to after hook style * hoti's speed snaker * auto convert objects for discord * Fix code style issues with ESLint * fix: remove dup imports * fix: i hate linters * speeder * fix: tests delete guilds * Fix code style issues with ESLint * fix: easier to provide custom intents in bot * fix: shutdown bot after test * fix: add getGuild * fix: multiple guild delete attempts * fix: add emoji e2e tests * fix: remaining old e2e rest tests * Fix code style issues with ESLint --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * test: add gateway integration test (#2756) * test: add gateway integration test * test(gateway): fix connection test * test(gateway): add heartbeat test * ci: add integration test * fix: add uWebSockets.js * ci: add timeout * test(utils): remove old test * Revert "test(utils): remove old test" This reverts commit04fb6dd4b5. * test(gateway): fix uws server * test(gateway): fix type * chore: update codecov flag * test(gateway): remove dev code --------- Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> * Update release.yml (#2768) * fix: bot logger (#2769) * fix: readme runtime list * Fix code style issues with ESLint * node 18 * fix: websocket import type * fix: body for interaction requests * fix: perma fix for type error in ci * fix: followupmessage option type * fix: e2e tests exit bug * fix: color console logger * fix: image file sending * Fix code style issues with ESLint * guild and role methods * Fix code style issues with ESLint * fix: dont send heartbeat if socket is not open * fix: remove logs * fox: remove more logs * fix some bugs in role tests * Switch to after hook style * hoti's speed snaker * auto convert objects for discord * Fix code style issues with ESLint * fix: remove dup imports * fix: i hate linters * speeder * fix: tests delete guilds * Fix code style issues with ESLint * fix: easier to provide custom intents in bot * fix: shutdown bot after test * fix: add getGuild * fix: multiple guild delete attempts * fix: add emoji e2e tests * fix: remaining old e2e rest tests * Fix code style issues with ESLint * fix: add bot.logger * fix: make logger name capital --------- Co-authored-by: Lint Action <lint-action@samuelmeuli.com> * chore: update readme (#2772) * chore: add coverage per pkg * chore: add npm version * chore: add test status * chore: fix ci * chore: fix ci * ci: fix ci needs * chore: add only push event * style: remove import logger * 📚 - docs: fix README package links (#2773) * test(all): add test importing index (#2774) * test(all): add test importing index * chore: remove old benchmark dir * chore: disable coverage status fail * test(client): add import test with try catch * test(rest): fix narrow import scope * test(utils): add test (#2764) * test(utils): remove old test * test(utils): add color test * test(utils): fix import mocha * test(utils): fix test type error * test(utils): remove dev code * fix(utils): bucket not export all function * test(utils): add some test for bucket * fix(utils): close #2775 * test(utils): add test for permissions.ts * test(utils): fix missing mocha import * fix(utils): better fix for #2775 * feat: addReaction & addReactions * feat: connectToVoice * fix: linters issues * fix: remove aliases and add createGuildFromTemplate * feat: deleteMessages * fix: reaction related helpers * mfa level * voie states editing * image urls * fix: typos * get message typeguards * fix: more helpers * fix: remaining helpers * fix: add logs to gateway manager * fix: rest resolve sends status and body * fix: lots of errors * fix: client errors * fix: remove old pkg * snaker * fix: broken util import for image url * fix: cleanup shard and circular deps * fix: remove ThreadChannel from GuildChannel * fix: generate interaction usage * fix: more bugs * fix: use node:events to import * fix(rest): add interface RestRequestRejection (#2782) * fix: remove invalid todo * fix: timeout bug --------- Co-authored-by: Skillz <skillz@discord.gg/ddeno> Co-authored-by: Jonathan Ho <heiheiho000@gmail.com> Co-authored-by: deepsarda <92147339+deepsarda@users.noreply.github.com> Co-authored-by: Awesome Stickz <awesome@stickz.dev> Co-authored-by: Lint Action <lint-action@samuelmeuli.com> Co-authored-by: Lars_und_so <46791248+Larsundso@users.noreply.github.com> Co-authored-by: ITOH <to@itoh.at> Co-authored-by: Yaikava <83710104+Yaikava@users.noreply.github.com> Co-authored-by: Andreas Fink <mail@afink.dev> --------- Co-authored-by: Jonathan Ho <48591478+H01001000@users.noreply.github.com> Co-authored-by: H01001000 <heiheiho000@gmail.com> Co-authored-by: deepsarda <92147339+deepsarda@users.noreply.github.com> Co-authored-by: Yaikava <83710104+Yaikava@users.noreply.github.com> Co-authored-by: Skillz <skillz@discord.gg/ddeno> Co-authored-by: Awesome Stickz <awesome@stickz.dev> Co-authored-by: Lint Action <lint-action@samuelmeuli.com> Co-authored-by: Lars_und_so <46791248+Larsundso@users.noreply.github.com> Co-authored-by: ITOH <to@itoh.at> Co-authored-by: Andreas Fink <mail@afink.dev>
This commit is contained in:
co-authored by
H01001000
Lint Action
Skillz
deepsarda
Awesome Stickz
Lars_und_so
ITOH
Yaikava
Andreas Fink
Jonathan Ho
parent
1a717ddc7d
commit
2714e1e7d1
@@ -0,0 +1 @@
|
||||
BOT_TOKEN=''
|
||||
@@ -0,0 +1,13 @@
|
||||
# Minimal Bot Template
|
||||
|
||||
Just the minimum to get a working bot using interactions.
|
||||
|
||||
Make sure to install the latest version when you use it.
|
||||
|
||||
## Setup
|
||||
|
||||
Just rename `.env.example` to `.env` and fill it with your bot token.
|
||||
|
||||
## Run Bot
|
||||
|
||||
- deno run -A mod.ts
|
||||
@@ -0,0 +1,5 @@
|
||||
import { dotEnvConfig } from './deps.ts.js';
|
||||
|
||||
dotEnvConfig({ export: true });
|
||||
export const BOT_TOKEN = process.env.BOT_TOKEN || "";
|
||||
export const BOT_ID = BigInt(atob(BOT_TOKEN.split(".")[0]));
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/mod.ts";
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/plugins/mod.ts";
|
||||
export { config as dotEnvConfig } from "https://deno.land/x/dotenv@v3.1.0/mod.ts";
|
||||
export * from "https://deno.land/std@0.117.0/fmt/colors.ts";
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
ActivityTypes,
|
||||
createBot,
|
||||
enableCachePlugin,
|
||||
enableCacheSweepers,
|
||||
fastFileLoader,
|
||||
GatewayIntents,
|
||||
startBot,
|
||||
} from './deps.ts.js';
|
||||
import { BOT_ID, BOT_TOKEN } from './configs.ts.js';
|
||||
import { logger } from './src/utils/logger.ts.js';
|
||||
import { events } from './src/events/mod.ts.js';
|
||||
import { updateCommands } from './src/utils/helpers.ts.js';
|
||||
|
||||
const log = logger({ name: "Main" });
|
||||
|
||||
log.info("Starting Bot, this might take a while...");
|
||||
|
||||
const paths = ["./src/events", "./src/commands"];
|
||||
await fastFileLoader(paths).catch((err) => {
|
||||
log.fatal(`Unable to Import ${paths}`);
|
||||
log.fatal(err);
|
||||
Deno.exit(1);
|
||||
});
|
||||
|
||||
export const bot = enableCachePlugin(
|
||||
createBot({
|
||||
token: BOT_TOKEN,
|
||||
botId: BOT_ID,
|
||||
intents: GatewayIntents.Guilds,
|
||||
events,
|
||||
}),
|
||||
);
|
||||
|
||||
// @ts-nocheck: no-updated-depencdencies
|
||||
enableCacheSweepers(bot);
|
||||
|
||||
bot.gateway.manager.createShardOptions.makePresence = (shardId: number) => {
|
||||
return {
|
||||
shardId,
|
||||
status: "online",
|
||||
activities: [
|
||||
{
|
||||
name: "Discordeno is the Best Lib",
|
||||
type: ActivityTypes.Game,
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
await startBot(bot);
|
||||
|
||||
await updateCommands(bot);
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { ApplicationCommandOption, ApplicationCommandTypes, Bot, Interaction } from '../../deps.ts.js';
|
||||
import { Collection } from '../../deps.ts.js';
|
||||
|
||||
export type subCommand = Omit<Command, "subcommands">;
|
||||
export interface subCommandGroup {
|
||||
name: string;
|
||||
subCommands: subCommand[];
|
||||
}
|
||||
export interface Command {
|
||||
name: string;
|
||||
description: string;
|
||||
usage?: string[];
|
||||
options?: ApplicationCommandOption[];
|
||||
type: ApplicationCommandTypes;
|
||||
/** Defaults to `Guild` */
|
||||
scope?: "Global" | "Guild";
|
||||
execute: (bot: Bot, interaction: Interaction) => unknown;
|
||||
subcommands?: Array<subCommandGroup | subCommand>;
|
||||
}
|
||||
|
||||
export const commands = new Collection<string, Command>();
|
||||
|
||||
export function createCommand(command: Command) {
|
||||
commands.set(command.name, command);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js';
|
||||
import { humanizeMilliseconds, snowflakeToTimestamp } from '../utils/helpers.ts.js';
|
||||
import { createCommand } from './mod.ts.js';
|
||||
|
||||
createCommand({
|
||||
name: "ping",
|
||||
description: "Ping the Bot!",
|
||||
type: ApplicationCommandTypes.ChatInput,
|
||||
scope: "Global",
|
||||
execute: async (bot, interaction) => {
|
||||
const ping = Date.now() - snowflakeToTimestamp(interaction.id);
|
||||
await bot.helpers.sendInteractionResponse(
|
||||
interaction.id,
|
||||
interaction.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `🏓 Pong! Ping ${ping}ms (${humanizeMilliseconds(ping)})`,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import { events } from './mod.ts.js';
|
||||
import { updateGuildCommands } from '../utils/helpers.ts.js';
|
||||
|
||||
events.guildCreate = async (bot, guild) => await updateGuildCommands(bot, guild);
|
||||
@@ -0,0 +1,249 @@
|
||||
import type {
|
||||
BotWithCache,
|
||||
Guild} from '../../deps.ts.js';
|
||||
import {
|
||||
ApplicationCommandOptionTypes,
|
||||
bgBlack,
|
||||
bgYellow,
|
||||
black,
|
||||
green,
|
||||
red,
|
||||
white,
|
||||
yellow,
|
||||
} from '../../deps.ts.js';
|
||||
import { events } from './mod.ts.js';
|
||||
import { logger } from '../utils/logger.ts.js';
|
||||
import { getGuildFromId, isSubCommand, isSubCommandGroup } from '../utils/helpers.ts.js';
|
||||
import type { Command} from '../commands/mod.ts.js';
|
||||
import { commands } from '../commands/mod.ts.js';
|
||||
|
||||
const log = logger({ name: "Event: InteractionCreate" });
|
||||
|
||||
events.interactionCreate = async (rawBot, interaction) => {
|
||||
const bot = rawBot as BotWithCache;
|
||||
|
||||
if (interaction.data && interaction.id) {
|
||||
let guildName = "Direct Message";
|
||||
let guild = {} as Guild;
|
||||
|
||||
// Set guild, if there was an error getting the guild, then just say it was a DM. (What else are we going to do?)
|
||||
if (interaction.guildId) {
|
||||
const guildOrVoid = await getGuildFromId(bot, interaction.guildId).catch(
|
||||
(err) => {
|
||||
log.error(err);
|
||||
},
|
||||
);
|
||||
if (guildOrVoid) {
|
||||
guild = guildOrVoid;
|
||||
guildName = guild.name;
|
||||
}
|
||||
}
|
||||
|
||||
log.info(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(white(`Trigger`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
|
||||
let command: undefined | Command = interaction.data.name ? commands.get(interaction.data.name) : undefined;
|
||||
let commandName = command?.name;
|
||||
|
||||
if (command !== undefined) {
|
||||
if (interaction.data.name) {
|
||||
if (interaction.data.options?.[0]) {
|
||||
const optionType = interaction.data.options[0].type;
|
||||
|
||||
if (optionType === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!command.subcommands) return;
|
||||
|
||||
// Try to find the subcommand group
|
||||
const subCommandGroup = command.subcommands?.find(
|
||||
(command) => command.name == interaction.data?.options?.[0].name,
|
||||
);
|
||||
if (!subCommandGroup) return;
|
||||
|
||||
if (isSubCommand(subCommandGroup)) return;
|
||||
|
||||
// Get name of the command which we are looking for
|
||||
const targetCmdName = interaction.data.options?.[0].options?.[0].name ||
|
||||
interaction.data.options?.[0].options?.[0].name;
|
||||
if (!targetCmdName) return;
|
||||
|
||||
// Try to find the command
|
||||
command = subCommandGroup.subCommands.find((c) => c.name === targetCmdName);
|
||||
|
||||
commandName += ` ${subCommandGroup.name} ${command?.name}`;
|
||||
|
||||
// Normal
|
||||
}
|
||||
|
||||
if (optionType === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!command?.subcommands) return;
|
||||
|
||||
// Try to find the command
|
||||
const found = command.subcommands.find((command) => command.name == interaction.data?.options?.[0].name);
|
||||
if (!found) return;
|
||||
|
||||
if (isSubCommandGroup(found)) return;
|
||||
|
||||
command = found;
|
||||
commandName += ` ${command?.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (command) {
|
||||
command.execute(rawBot, interaction);
|
||||
log.info(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(green(`Success`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
} else {
|
||||
throw "";
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(red(`Error`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
err.length ? log.error(err) : undefined;
|
||||
}
|
||||
} else {
|
||||
log.warn(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(yellow(`Not Found`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
// Handle subcommands
|
||||
let cmdName = cmd.name;
|
||||
|
||||
// Group
|
||||
if (interaction.data?.options?.[0].type === DiscordApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!cmd.subcommands) return;
|
||||
|
||||
// Try to find the subcommand group
|
||||
const subCmdGroup = cmd.subcommands?.find((cmd) => cmd.name == interaction?.data?.options?.[0].name);
|
||||
if (!subCmdGroup) return;
|
||||
|
||||
if (isSubCommand(subCmdGroup)) return;
|
||||
|
||||
// Get name of the command which we are looking for
|
||||
const targetCmdName =
|
||||
interaction.data.options?.[0].options?.[0].name || interaction.data.options?.[0].options?.[0].name;
|
||||
if (!targetCmdName) return;
|
||||
|
||||
// Try to find the command
|
||||
cmd = subCmdGroup.subCommands.find((c) => c.name === targetCmdName);
|
||||
|
||||
cmdName += ` ${subCmdGroup.name} ${cmd?.name}`;
|
||||
|
||||
// Normal
|
||||
} else if (interaction.data?.options?.[0].type === DiscordApplicationCommandOptionTypes.SubCommand) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!cmd.subcommands) return;
|
||||
|
||||
// Try to find the command
|
||||
const found = cmd.subcommands.find((cmd) => cmd.name == interaction.data?.options?.[0].name);
|
||||
if (!found) return;
|
||||
|
||||
if (isSubCommandGroup(found)) return;
|
||||
|
||||
cmd = found;
|
||||
cmdName += ` ${cmd?.name}`;
|
||||
}
|
||||
if (!cmd) return;
|
||||
|
||||
// Get options
|
||||
const options =
|
||||
interaction.data?.options?.[0].type === DiscordApplicationCommandOptionTypes.SubCommandGroup
|
||||
? interaction.data?.options?.[0].options?.[0].options
|
||||
: interaction.data?.options?.[0].type === DiscordApplicationCommandOptionTypes.SubCommand
|
||||
? interaction.data?.options[0].options
|
||||
: interaction.data?.options;
|
||||
|
||||
// Prepare info for logs
|
||||
const user = member || interaction.user;
|
||||
const guild = interaction.guildId
|
||||
? await customCacheHandlers.get("guilds", snowflakeToBigint(interaction.guildId))
|
||||
: undefined;
|
||||
|
||||
// Log cmd trigger
|
||||
logSlashCommand(cmdName, user, guild, "trigger");
|
||||
|
||||
// Check inhibitors
|
||||
for await (const inhibitor of bot.inhibitors.values()) {
|
||||
const inhibited = await inhibitor(cmd, interaction, member);
|
||||
if (inhibited) {
|
||||
// Log cmd inhibition
|
||||
logSlashCommand(cmdName, user, guild, "inhibited");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if command has execute
|
||||
if (!cmd.execute) {
|
||||
logger.error(`Command ${cmdName} is missing execute.`);
|
||||
sendBasicResponse(data.id, data.token, "This command is not configured to be executed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Command context
|
||||
const cmdCtx = createCommandCtx(interaction, undefined, undefined, options, member);
|
||||
|
||||
// Execute command
|
||||
let err;
|
||||
try {
|
||||
if (cmd.longExecution)
|
||||
// Thinking....
|
||||
await sendInteractionResponse(interaction.id, interaction.token, {
|
||||
type: DiscordInteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
});
|
||||
|
||||
await cmd.execute(cmdCtx);
|
||||
} catch (error) {
|
||||
err = true;
|
||||
|
||||
// Log command fail
|
||||
logSlashCommand(cmdName, user, guild, "failed", error);
|
||||
|
||||
// Log the error
|
||||
logger.error(`Error ${cmdName}`, error);
|
||||
|
||||
// Send error message to user
|
||||
sendInteractionResponse(interaction.id, interaction.token, {
|
||||
type: DiscordInteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
embeds: [
|
||||
createSimpleEmbed("error")
|
||||
.setDescription("Unexpected Error")
|
||||
.setFooter("This error has been reported to the developers of this bot."),
|
||||
],
|
||||
},
|
||||
}).catch(logger.error);
|
||||
}
|
||||
|
||||
// Log success
|
||||
if (!err) logSlashCommand(cmdName, user, guild, "success");
|
||||
},
|
||||
});
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { EventHandlers } from '../../deps.ts.js';
|
||||
|
||||
export const events: Partial<EventHandlers> = {};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { events } from './mod.ts.js';
|
||||
import { logger } from '../utils/logger.ts.js';
|
||||
|
||||
const log = logger({ name: "Event: Ready" });
|
||||
|
||||
events.ready = () => {
|
||||
log.info("Bot Ready");
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
import type {
|
||||
Bot,
|
||||
BotWithCache,
|
||||
CreateApplicationCommand,
|
||||
Guild,
|
||||
MakeRequired} from '../../deps.ts.js';
|
||||
import {
|
||||
getGuild,
|
||||
hasProperty,
|
||||
upsertGuildApplicationCommands,
|
||||
} from '../../deps.ts.js';
|
||||
import { logger } from './logger.ts.js';
|
||||
import type { subCommand, subCommandGroup } from '../commands/mod.ts.js';
|
||||
import { commands } from '../commands/mod.ts.js';
|
||||
|
||||
const log = logger({ name: "Helpers" });
|
||||
|
||||
/** This function will update all commands, or the defined scope */
|
||||
export async function updateCommands(bot: BotWithCache, scope?: "Guild" | "Global") {
|
||||
const globalCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
|
||||
for (const command of commands.values()) {
|
||||
if (command.scope) {
|
||||
if (command.scope === "Guild") {
|
||||
perGuildCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
} else if (command.scope === "Global") {
|
||||
globalCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
perGuildCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (globalCommands.length && (scope === "Global" || scope === undefined)) {
|
||||
log.info("Updating Global Commands, changes should apply in short...");
|
||||
await bot.helpers.upsertGlobalApplicationCommands(globalCommands).catch(log.error);
|
||||
}
|
||||
|
||||
if (perGuildCommands.length && (scope === "Guild" || scope === undefined)) {
|
||||
await bot.guilds.forEach(async (guild: Guild) => {
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Update commands for a guild */
|
||||
export async function updateGuildCommands(bot: Bot, guild: Guild) {
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
|
||||
for (const command of commands.values()) {
|
||||
if (command.scope) {
|
||||
if (command.scope === "Guild") {
|
||||
perGuildCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (perGuildCommands.length) {
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGuildFromId(bot: BotWithCache, guildId: bigint): Promise<Guild> {
|
||||
let returnValue: Guild = {} as Guild;
|
||||
|
||||
if (guildId !== 0n) {
|
||||
if (bot.guilds.get(guildId)) {
|
||||
returnValue = bot.guilds.get(guildId) as Guild;
|
||||
}
|
||||
|
||||
await getGuild(bot, guildId).then((guild) => {
|
||||
if (guild) bot.guilds.set(guildId, guild);
|
||||
if (guild) returnValue = guild;
|
||||
});
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
export function snowflakeToTimestamp(id: bigint) {
|
||||
return Number(id / 4194304n + 1420070400000n);
|
||||
}
|
||||
|
||||
export function humanizeMilliseconds(milliseconds: number) {
|
||||
// Gets ms into seconds
|
||||
const time = milliseconds / 1000;
|
||||
if (time < 1) return "1s";
|
||||
|
||||
const days = Math.floor(time / 86400);
|
||||
const hours = Math.floor((time % 86400) / 3600);
|
||||
const minutes = Math.floor(((time % 86400) % 3600) / 60);
|
||||
const seconds = Math.floor(((time % 86400) % 3600) % 60);
|
||||
|
||||
const dayString = days ? `${days}d ` : "";
|
||||
const hourString = hours ? `${hours}h ` : "";
|
||||
const minuteString = minutes ? `${minutes}m ` : "";
|
||||
const secondString = seconds ? `${seconds}s ` : "";
|
||||
|
||||
return `${dayString}${hourString}${minuteString}${secondString}`;
|
||||
}
|
||||
|
||||
export function isSubCommand(data: subCommand | subCommandGroup): data is subCommand {
|
||||
return !hasProperty(data, "subCommands");
|
||||
}
|
||||
|
||||
export function isSubCommandGroup(data: subCommand | subCommandGroup): data is subCommandGroup {
|
||||
return hasProperty(data, "subCommands");
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js';
|
||||
|
||||
export enum LogLevels {
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal,
|
||||
}
|
||||
|
||||
const prefixes = new Map<LogLevels, string>([
|
||||
[LogLevels.Debug, "DEBUG"],
|
||||
[LogLevels.Info, "INFO"],
|
||||
[LogLevels.Warn, "WARN"],
|
||||
[LogLevels.Error, "ERROR"],
|
||||
[LogLevels.Fatal, "FATAL"],
|
||||
]);
|
||||
|
||||
const noColor: (str: string) => string = (msg) => msg;
|
||||
const colorFunctions = new Map<LogLevels, (str: string) => string>([
|
||||
[LogLevels.Debug, gray],
|
||||
[LogLevels.Info, cyan],
|
||||
[LogLevels.Warn, yellow],
|
||||
[LogLevels.Error, (str: string) => red(str)],
|
||||
[LogLevels.Fatal, (str: string) => red(bold(italic(str)))],
|
||||
]);
|
||||
|
||||
export function logger({
|
||||
logLevel = LogLevels.Info,
|
||||
name,
|
||||
}: {
|
||||
logLevel?: LogLevels;
|
||||
name?: string;
|
||||
} = {}) {
|
||||
function log(level: LogLevels, ...args: any[]) {
|
||||
if (level < logLevel) return;
|
||||
|
||||
let color = colorFunctions.get(level);
|
||||
if (!color) color = noColor;
|
||||
|
||||
const date = new Date();
|
||||
const log = [
|
||||
`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`,
|
||||
color(prefixes.get(level) || "DEBUG"),
|
||||
name ? `${name} >` : ">",
|
||||
...args,
|
||||
];
|
||||
|
||||
switch (level) {
|
||||
case LogLevels.Debug:
|
||||
return console.debug(...log);
|
||||
case LogLevels.Info:
|
||||
return console.info(...log);
|
||||
case LogLevels.Warn:
|
||||
return console.warn(...log);
|
||||
case LogLevels.Error:
|
||||
return console.error(...log);
|
||||
case LogLevels.Fatal:
|
||||
return console.error(...log);
|
||||
default:
|
||||
return console.log(...log);
|
||||
}
|
||||
}
|
||||
|
||||
function setLevel(level: LogLevels) {
|
||||
logLevel = level;
|
||||
}
|
||||
|
||||
function debug(...args: any[]) {
|
||||
log(LogLevels.Debug, ...args);
|
||||
}
|
||||
|
||||
function info(...args: any[]) {
|
||||
log(LogLevels.Info, ...args);
|
||||
}
|
||||
|
||||
function warn(...args: any[]) {
|
||||
log(LogLevels.Warn, ...args);
|
||||
}
|
||||
|
||||
function error(...args: any[]) {
|
||||
log(LogLevels.Error, ...args);
|
||||
}
|
||||
|
||||
function fatal(...args: any[]) {
|
||||
log(LogLevels.Fatal, ...args);
|
||||
}
|
||||
|
||||
return {
|
||||
log,
|
||||
setLevel,
|
||||
debug,
|
||||
info,
|
||||
warn,
|
||||
error,
|
||||
fatal,
|
||||
};
|
||||
}
|
||||
|
||||
export const log = logger();
|
||||
Reference in New Issue
Block a user