feat(examples): Migrate example bots to discordeno v19 (#3647)

* Migrate beginner and minimal bot to discordeno v19

* Add .swcrc and fix minimal yarn.lock

* update .gitignore files

* Update nodejs template

Discordeno.js (DD v13) -> DD v19 "raw"

Currently the permission checking is not working correctly

* Fix permission issue

* Rename the templates

* remove unused indents

* Rename starter to beginner

So now it is minimal (main branch) -> beginner

* Really small refactor & eslint fixes (bigbot template)

This is to make my life less miserable at a later time

* mark rabbitMQ plugins as binary files

git seems to be treating them as text

* Add v19 bigbot rest

* Add gateway code

and rabbitmq_message_deduplication v0.6.2 plugin

* fix yarn messy semevr version for @types/amqplib

* clear channel con amqp connection close

* Add bot code for bigbot v19

missing prisma setup, collector setup & language setup

* Add localization

The "command versioning" system works the same say as before, but instead of a command version the code updates the commands every time they change based on the SHA1 of the commands

* Use file relative paths instead of cwd relative paths

* Fix todos

* revert autocomplete tests

* Revert "Add localization"

This reverts commit 2b1da8d2cd.

* move env assertion to config.ts

* Add shard ping to /ping

* fix small issue

* Update readme files

* use Date.now() for the bigbot REST ping

* Remove bigbot v16 code

* Add docker (compose) setup to bigbot template

* remove healthchecks from rest & gateway

* Update dependencies of examples

* Apply readme(s) suggestions from code review

Hopefully i haven't missed any related to markdown files

Co-authored-by: LTS20050703 <lts20050703@gmail.com>

* Apply code suggestions from code review

Co-authored-by: LTS20050703 <lts20050703@gmail.com>

---------

Co-authored-by: LTS20050703 <lts20050703@gmail.com>
This commit is contained in:
Fleny
2024-07-07 10:40:43 -05:00
committed by GitHub
co-authored by LTS20050703
parent aecc55d150
commit 769d50ff93
168 changed files with 11846 additions and 3854 deletions
-1
View File
@@ -1,2 +1 @@
BOT_TOKEN=''
DEV_GUILD_ID=''
+32
View File
@@ -0,0 +1,32 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# build
dist
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
+24
View File
@@ -0,0 +1,24 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true,
"dynamicImport": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2022",
"keepClassNames": true,
"loose": true
},
"module": {
"type": "es6",
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
}
}
+10 -8
View File
@@ -1,18 +1,20 @@
# Beginner Bot Template
This template is designed for the beginner developer to start coding discord bots.
This template is designed for beginners to start coding discord bots.
Make sure to install the latest version when you use it.
This template includes caching (using `dd-cache-proxy`) and support for slash subcommands.
This template also includes a /ping command to show the bot latency
## Setup
- [Click here](https://github.com/discordeno/template/generate) to make your own copy.
- Delete all the template folders except the beginner folder.
- Move all files from this folder to the root of the project.
- You may encounter an issue with README.md file but force move the files to the root of the project.
- Rename the .env.example file to .env OR create a new .env file and copy the example file code to this new file.
- Download the source
- Install the dependencies using `yarn`
- Copy the .env.example file and rename it to .env
- Fill out the .env file
## Run Bot
- deno run -A mod.ts
- run `yarn` to install the dependencies
- run `yarn build` to build the source
- run `node dist/register-commands.js` to register the slash commands
- run `yarn start` to run the bot
-35
View File
@@ -1,35 +0,0 @@
import { configs } from './configs.ts.js'
import type { BotWithCache, BotWithHelpersPlugin } from './deps.ts.js'
import {
Collection,
createBot,
enableCachePlugin,
enableCacheSweepers,
enableHelpersPlugin,
enablePermissionsPlugin,
GatewayIntents,
} from './deps.ts.js'
import type { Command } from './src/types/commands.ts.js'
// MAKE THE BASIC BOT OBJECT
const bot = createBot({
token: configs.token,
botId: configs.botId,
intents: GatewayIntents.Guilds,
events: {},
})
// ENABLE ALL THE PLUGINS THAT WILL HELP MAKE IT EASIER TO CODE YOUR BOT
enableHelpersPlugin(bot)
enableCachePlugin(bot)
enableCacheSweepers(bot as BotWithCache)
enablePermissionsPlugin(bot as BotWithCache)
export interface BotClient extends BotWithCache<BotWithHelpersPlugin> {
commands: Collection<string, Command>
}
// THIS IS THE BOT YOU WANT TO USE EVERYWHERE IN YOUR CODE! IT HAS EVERYTHING BUILT INTO IT!
export const Bot = bot as BotClient
// PREPARE COMMANDS HOLDER
Bot.commands = new Collection()
-19
View File
@@ -1,19 +0,0 @@
import { dotEnvConfig } from './deps.ts.js'
// Get the .env file that the user should have created, and get the token
const env = dotEnvConfig({ export: true, path: './.env' })
const token = env.BOT_TOKEN || ''
export interface Config {
token: string
botId: bigint
}
export const configs = {
/** Get token from ENV variable */
token,
/** Get the BotId from the token */
botId: BigInt(atob(token.split('.')[0])),
/** The server id where you develop your bot and want dev commands created. */
devGuildId: BigInt(env.DEV_GUILD_ID!),
}
-9
View File
@@ -1,9 +0,0 @@
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'
// Terminal Colors!
export * from 'https://deno.land/std@0.117.0/fmt/colors.ts'
// Get data from .env files
export { config as dotEnvConfig } from 'https://deno.land/x/dotenv@v3.1.0/mod.ts'
// Database, thx Tri!
export { decode as KwikDecode, encode as KwikEncode, Kwik } from 'https://deno.land/x/kwik@v1.3.1/mod.ts'
-25
View File
@@ -1,25 +0,0 @@
import { startBot } from './deps.ts.js'
import log from './src/utils/logger.ts.js'
import { fileLoader, importDirectory } from './src/utils/loader.ts.js'
import { updateApplicationCommands } from './src/utils/updateCommands.ts.js'
// setup db
import './src/database/mod.ts.js'
import { Bot } from './bot.ts.js'
log.info('Starting bot...')
// Forces deno to read all the files which will fill the commands/inhibitors cache etc.
await Promise.all(
[
'./src/commands',
'./src/events',
// "./src/tasks",
].map((path) => importDirectory(Deno.realPathSync(path))),
)
await fileLoader()
// UPDATES YOUR COMMANDS TO LATEST COMMANDS
await updateApplicationCommands()
// STARTS THE CONNECTION TO DISCORD
await startBot(Bot)
+27
View File
@@ -0,0 +1,27 @@
{
"name": "dd-beginner-bot",
"version": "1.0.0",
"description": "An example bot for beginner developers to start coding discord bots.",
"main": "dist/index.js",
"type": "module",
"license": "ISC",
"private": true,
"packageManager": "yarn@4.0.2",
"scripts": {
"start": "node dist/index.js",
"build": "swc src --strip-leading-paths --delete-dir-on-start --out-dir dist",
"setup-dd": ""
},
"dependencies": {
"@discordeno/bot": "19.0.0-next.92bf166",
"chalk": "^5.3.0",
"dd-cache-proxy": "^2.1.1",
"dotenv": "^16.4.5"
},
"devDependencies": {
"@swc/cli": "^0.3.12",
"@swc/core": "^1.6.3",
"@types/node": "^20.14.6",
"typescript": "^5.5.2"
}
}
+32
View File
@@ -0,0 +1,32 @@
import { Intents, createBot } from '@discordeno/bot'
import { createProxyCache } from 'dd-cache-proxy'
import { configs } from './config.js'
export const bot = createProxyCache(
createBot({
token: configs.token,
intents: Intents.Guilds,
}),
{
desiredProps: {
guilds: ['id', 'name'],
},
cacheInMemory: {
guilds: true,
default: false,
},
},
)
// Setup desired proprieties
bot.transformers.desiredProperties.interaction.id = true
bot.transformers.desiredProperties.interaction.type = true
bot.transformers.desiredProperties.interaction.data = true
bot.transformers.desiredProperties.interaction.user = true
bot.transformers.desiredProperties.interaction.token = true
bot.transformers.desiredProperties.interaction.guildId = true
bot.transformers.desiredProperties.guild.id = true
bot.transformers.desiredProperties.guild.name = true
bot.transformers.desiredProperties.user.username = true
+26
View File
@@ -0,0 +1,26 @@
import { Collection, type ApplicationCommandOption, type ApplicationCommandTypes, type Interaction } from '@discordeno/bot'
export const commands = new Collection<string, Command>()
export function createCommand(command: Command): void {
commands.set(command.name, command)
}
export interface Command {
name: string
description: string
usage?: string[]
options?: ApplicationCommandOption[]
type: ApplicationCommandTypes
/** Defaults to `Guild` */
scope?: 'Global' | 'Guild'
execute: (interaction: Interaction) => unknown
subcommands?: Array<SubCommandGroup | SubCommand>
}
export type SubCommand = Omit<Command, 'subcommands'>
export interface SubCommandGroup {
name: string
subCommands: SubCommand[]
}
-6
View File
@@ -1,6 +0,0 @@
import { Bot } from '../../bot.ts.js'
import type { Command } from '../types/commands.ts.js'
export function createCommand(command: Command) {
Bot.commands.set(command.name, command)
}
+7 -10
View File
@@ -1,18 +1,15 @@
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js'
import { snowflakeToTimestamp } from '../utils/helpers.ts.js'
import { createCommand } from './mod.ts.js'
import { ApplicationCommandTypes, snowflakeToTimestamp } from '@discordeno/bot'
import { createCommand } from '../commands.js'
import { humanizeMilliseconds } from '../utils/helpers.js'
createCommand({
name: 'ping',
description: 'Ping the Bot!',
type: ApplicationCommandTypes.ChatInput,
execute: async (Bot, interaction) => {
scope: 'Global',
async execute(interaction) {
const ping = Date.now() - snowflakeToTimestamp(interaction.id)
await Bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
content: `🏓 Pong! ${ping}ms`,
},
})
await interaction.respond(`🏓 Pong! Ping ${ping}ms (${humanizeMilliseconds(ping)})`)
},
})
+12
View File
@@ -0,0 +1,12 @@
const token = process.env.BOT_TOKEN
if (!token) throw new Error('Missing BOT_TOKEN environment variable')
export const configs: Config = {
/** Get token from ENV variable */
token,
}
export interface Config {
token: string
}
-32
View File
@@ -1,32 +0,0 @@
import { Kwik, KwikDecode, KwikEncode } from '../../deps.ts.js'
import { logger } from '../utils/logger.ts.js'
const log = logger({ name: 'DB Manager' })
log.info('Initializing Database')
const kwik = new Kwik()
// Add BigInt Support
kwik.msgpackExtensionCodec.register({
type: 0,
encode: (object: unknown): Uint8Array | null => {
if (typeof object === 'bigint') {
if (object <= Number.MAX_SAFE_INTEGER && object >= Number.MIN_SAFE_INTEGER) {
return KwikEncode(parseInt(object.toString(), 10), {})
} else {
return KwikEncode(object.toString(), {})
}
} else {
return null
}
},
decode: (data: Uint8Array) => {
return BigInt(KwikDecode(data, {}) as string)
},
})
// Initialize the Database
await kwik.init()
log.info('Database Initialized!')
@@ -0,0 +1,4 @@
import { bot } from '../bot.js'
import { updateGuildCommands } from '../utils/helpers.js'
bot.events.guildCreate = async (guild) => await updateGuildCommands(bot, guild)
@@ -1,14 +1,94 @@
import { Bot } from '../../bot.ts.js'
import { InteractionTypes } from '../../deps.ts.js'
import log from '../utils/logger.ts.js'
import { ApplicationCommandOptionTypes, hasProperty, type Guild } from '@discordeno/bot'
import chalk from 'chalk'
import { bot } from '../bot.js'
import { commands } from '../commands.js'
import { getGuildFromId, isSubCommand, isSubCommandGroup } from '../utils/helpers.js'
import { createLogger } from '../utils/logger.js'
Bot.events.interactionCreate = (_, interaction) => {
if (!interaction.data) return
const logger = createLogger({ name: 'Event: InteractionCreate' })
switch (interaction.type) {
case InteractionTypes.ApplicationCommand:
log.info(`[Application Command] ${interaction.data.name} command executed.`)
Bot.commands.get(interaction.data.name!)?.execute(Bot, interaction)
break
bot.events.interactionCreate = async (interaction) => {
if (!interaction.data || !interaction.id) return
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(interaction.guildId).catch((err) => {
logger.error(err)
})
if (guildOrVoid) {
guild = guildOrVoid
guildName = guild.name
}
}
logger.info(
`[Command: ${chalk.bgYellow.black(interaction.data.name)} - ${chalk.bgBlack.white(`Trigger`)}] by @${interaction.user.username} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
)
let command = commands.get(interaction.data.name)
if (!command) {
logger.warn(
`[Command: ${chalk.bgYellow.black(interaction.data.name)} - ${chalk.bgBlack.yellow(`Not Found`)}] by @${interaction.user.username} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
)
return
}
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)
}
if (optionType === ApplicationCommandOptionTypes.SubCommand) {
// 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
}
}
try {
if (!command) throw new Error('Not command could be found')
await command.execute(interaction)
logger.info(
`[Command: ${chalk.bgYellow.black(interaction.data.name)} - ${chalk.bgBlack.green(`Success`)}] by @${interaction.user.username} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
)
} catch (err) {
logger.error(
`[Command: ${chalk.bgYellow.black(interaction.data.name)} - ${chalk.bgBlack.red(`Error`)}] by @${interaction.user.username} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
)
if (typeof err !== 'object' || !err || !hasProperty(err, 'message') || err.message === 'Not command could be found') return
logger.error(err)
}
}
+18 -12
View File
@@ -1,16 +1,22 @@
import { Bot } from '../../bot.ts.js'
import log from '../utils/logger.ts.js'
import { ActivityTypes } from '@discordeno/bot'
import { bot } from '../bot.js'
import { createLogger } from '../utils/logger.js'
Bot.events.ready = (_, payload) => {
log.info(`[READY] Shard ID ${payload.shardId} of ${Bot.gateway.lastShardId + 1} shards is ready!`)
const logger = createLogger({ name: 'Event: Ready' })
if (payload.shardId === Bot.gateway.lastShardId) {
botFullyReady()
}
}
bot.events.ready = async ({ shardId }) => {
logger.info('Bot Ready')
// This function lets you run custom code when all your bot's shards are online.
function botFullyReady() {
// DO STUFF YOU WANT HERE ONCE BOT IS FULLY ONLINE.
log.info('[READY] Bot is fully online.')
await bot.gateway.editShardStatus(shardId, {
status: 'online',
activities: [
{
name: 'Discordeno is the Best Lib',
type: ActivityTypes.Game,
timestamps: {
start: Date.now(),
},
},
],
})
}
+15
View File
@@ -0,0 +1,15 @@
import 'dotenv/config'
import { bot } from './bot.js'
import importDirectory from './utils/loader.js'
import logger from './utils/logger.js'
logger.info('Starting bot...')
logger.info('Loading commands...')
await importDirectory('./dist/commands')
logger.info('Loading events...')
await importDirectory('./dist/events')
await bot.start()
@@ -0,0 +1,7 @@
import 'dotenv/config'
import { bot } from './bot.js'
import { updateCommands } from './utils/helpers.js'
bot.logger.info('Updating commands...')
await updateCommands()
-17
View File
@@ -1,17 +0,0 @@
import type { BotClient } from '../../bot.ts.js'
import type { ApplicationCommandOption, ApplicationCommandTypes, Interaction } from '../../deps.ts.js'
export interface Command {
/** The name of this command. */
name: string
/** What does this command do? */
description: string
/** The type of command this is. */
type: ApplicationCommandTypes
/** Whether or not this command is for the dev server only. */
devOnly?: boolean
/** The options for this command */
options?: ApplicationCommandOption[]
/** This will be executed when the command is run. */
execute: (bot: BotClient, interaction: Interaction) => unknown
}
-3
View File
@@ -1,3 +0,0 @@
// This file will export all of the types in this directory.
export * from './commands.ts.js'
+100 -2
View File
@@ -1,3 +1,101 @@
export function snowflakeToTimestamp(id: bigint) {
return Number(id / 4194304n + 1420070400000n)
import { hasProperty, type Bot, type CreateApplicationCommand, type Guild } from '@discordeno/bot'
import { bot } from '../bot.js'
import { commands, type SubCommand, type SubCommandGroup } from '../commands.js'
import { createLogger } from './logger.js'
const logger = createLogger({ name: 'Helpers' })
/** This function will update all commands, or the defined scope */
export async function updateCommands(scope?: 'Guild' | 'Global'): Promise<void> {
const globalCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
for (const command of commands.values()) {
if (command.scope === 'Guild') {
perGuildCommands.push({
name: command.name,
description: command.description,
type: command.type,
options: command.options ? command.options : undefined,
})
} else {
globalCommands.push({
name: command.name,
description: command.description,
type: command.type,
options: command.options ? command.options : undefined,
})
}
}
if (globalCommands.length && (scope === 'Global' || scope === undefined)) {
logger.info('Updating Global Commands, changes should apply in short...')
await bot.helpers.upsertGlobalApplicationCommands(globalCommands).catch(logger.error)
}
if (perGuildCommands.length && (scope === 'Guild' || scope === undefined)) {
await Promise.all(
bot.cache.guilds.memory.map(async (guild: Guild) => {
await bot.helpers.upsertGuildApplicationCommands(guild.id, perGuildCommands)
}),
)
}
}
/** Update commands for a guild */
export async function updateGuildCommands(bot: Bot, guild: Guild): Promise<void> {
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
for (const command of commands.values()) {
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 bot.helpers.upsertGuildApplicationCommands(guild.id, perGuildCommands)
}
}
export async function getGuildFromId(guildId: bigint): Promise<Guild> {
const cached = await bot.cache.guilds.get(guildId)
if (cached) return cached
return await bot.helpers.getGuild(guildId)
}
export function humanizeMilliseconds(milliseconds: number): string {
// 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')
}
type MakeRequired<TObj, TKey extends keyof TObj> = TObj & {
[Key in TKey]-?: TObj[Key]
}
+10 -35
View File
@@ -1,40 +1,15 @@
import log from './logger.ts.js'
import { readdir } from 'node:fs/promises'
import logger from './logger.js'
// Very important to make sure files are reloaded properly
let uniqueFilePathCounter = 0
let paths: string[] = []
export default async function importDirectory(folder: string): Promise<void> {
const files = await readdir(folder, { recursive: true })
/** This function allows reading all files in a folder. Useful for loading/reloading commands, monitors etc */
export async function importDirectory(path: string) {
path = path.replaceAll('\\', '/')
const files = Deno.readDirSync(Deno.realPathSync(path))
const folder = path.substring(path.indexOf('/src/') + 5)
for (const filename of files) {
if (!filename.endsWith('.js')) continue
if (!folder.includes('/')) log.info(`Loading ${folder}...`)
for (const file of files) {
if (!file.name) continue
const currentPath = `${path}/${file.name}`
if (file.isFile) {
if (!currentPath.endsWith('.ts')) continue
paths.push(
`import "${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf('/'))}/${currentPath.substring(
currentPath.indexOf('src/'),
)}#${uniqueFilePathCounter}";`,
)
continue
}
await importDirectory(currentPath)
// Using `file://` and `process.cwd()` to avoid weird issues with relative paths and/or Windows
await import(`file://${process.cwd()}/${folder}/${filename}`).catch((x) =>
logger.fatal(`Cannot import file (${folder}/${filename}) for reason:`, x),
)
}
uniqueFilePathCounter++
}
/** Imports all everything in fileloader.ts */
export async function fileLoader() {
await Deno.writeTextFile('fileloader.ts', paths.join('\n').replaceAll('\\', '/'))
await import(`${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf('/'))}/fileloader.ts#${uniqueFilePathCounter}`)
paths = []
}
+28 -24
View File
@@ -1,5 +1,5 @@
// deno-lint-ignore-file no-explicit-any
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js'
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import chalk from 'chalk'
export enum LogLevels {
Debug,
@@ -19,21 +19,15 @@ const prefixes = new Map<LogLevels, string>([
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)))],
[LogLevels.Debug, chalk.gray],
[LogLevels.Info, chalk.cyan],
[LogLevels.Warn, chalk.yellow],
[LogLevels.Error, (str: string) => chalk.red(str)],
[LogLevels.Fatal, (str: string) => chalk.red.bold.italic(str)],
])
export function logger({
logLevel = LogLevels.Info,
name,
}: {
logLevel?: LogLevels
name?: string
} = {}) {
function log(level: LogLevels, ...args: any[]) {
export function createLogger({ logLevel = LogLevels.Info, name }: { logLevel?: LogLevels; name?: string } = {}): Logger {
function log(level: LogLevels, ...args: any[]): void {
if (level < logLevel) return
let color = colorFunctions.get(level)
@@ -42,7 +36,7 @@ export function logger({
const date = new Date()
const log = [
`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`,
color(prefixes.get(level) || 'DEBUG'),
color(prefixes.get(level) ?? 'DEBUG'),
name ? `${name} >` : '>',
...args,
]
@@ -63,27 +57,27 @@ export function logger({
}
}
function setLevel(level: LogLevels) {
function setLevel(level: LogLevels): void {
logLevel = level
}
function debug(...args: any[]) {
function debug(...args: any[]): void {
log(LogLevels.Debug, ...args)
}
function info(...args: any[]) {
function info(...args: any[]): void {
log(LogLevels.Info, ...args)
}
function warn(...args: any[]) {
function warn(...args: any[]): void {
log(LogLevels.Warn, ...args)
}
function error(...args: any[]) {
function error(...args: any[]): void {
log(LogLevels.Error, ...args)
}
function fatal(...args: any[]) {
function fatal(...args: any[]): void {
log(LogLevels.Fatal, ...args)
}
@@ -98,5 +92,15 @@ export function logger({
}
}
export const log = logger({ name: 'Main' })
export default log
export const logger = createLogger({ name: 'Main' })
export default logger
export interface Logger {
log: (level: LogLevels, ...args: any[]) => void
debug: (...args: any[]) => void
info: (...args: any[]) => void
warn: (...args: any[]) => void
error: (...args: any[]) => void
fatal: (...args: any[]) => void
setLevel: (level: LogLevels) => void
}
@@ -1,19 +0,0 @@
import { Bot } from '../../bot.ts.js'
import { configs } from '../../configs.ts.js'
export async function updateApplicationCommands() {
await Bot.helpers.upsertGlobalApplicationCommands(
Bot.commands
// ONLY GLOBAL COMMANDS
.filter((command) => !command.devOnly)
.array(),
)
await Bot.helpers.upsertGuildApplicationCommands(
configs.devGuildId,
Bot.commands
// ONLY GLOBAL COMMANDS
.filter((command) => !!command.devOnly)
.array(),
)
}
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"moduleResolution": "node",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"strict": true,
"incremental": true
}
}
File diff suppressed because it is too large Load Diff