From dbdbf16bfe0f6b9d33ea23996de3805694fe791a Mon Sep 17 00:00:00 2001 From: ytshih Date: Sun, 13 Oct 2024 14:50:19 +0000 Subject: [PATCH] initial commit --- .gitignore | 131 ++++ Dockerfile | 10 + classes/command.ts | 23 + classes/extendedclient.ts | 22 + commands/giveaway/giveaway.ts | 152 ++++ commands/images/image.ts | 221 ++++++ commands/roles/autorole.ts | 90 +++ commands/tests/test.ts | 10 + commands/utils/help.ts | 29 + commands/utils/ping.ts | 19 + commands/utils/set.ts | 104 +++ config.ts | 51 ++ events/handle-auto-roles.ts | 101 +++ events/handle-commands.ts | 31 + events/handle-giveaway.ts | 88 +++ events/handle-react-image.ts | 40 + functions/react-preprocess.ts | 3 + index.ts | 97 +++ init/load-commands.ts | 25 + init/ready-dm.ts | 11 + init/register-commands.ts | 26 + init/set-nickname.ts | 22 + k8s/deploy.amane-backend.yaml | 64 ++ logger.ts | 45 ++ models/Alias.ts | 21 + models/AutoroleMsg.ts | 22 + models/GiveawayMsg.ts | 22 + models/Guild.ts | 23 + models/Image.ts | 14 + models/Token.ts | 18 + mongo.ts | 45 ++ package-lock.json | 1312 +++++++++++++++++++++++++++++++++ package.json | 34 + tsconfig.json | 109 +++ 34 files changed, 3035 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 classes/command.ts create mode 100644 classes/extendedclient.ts create mode 100644 commands/giveaway/giveaway.ts create mode 100644 commands/images/image.ts create mode 100644 commands/roles/autorole.ts create mode 100644 commands/tests/test.ts create mode 100644 commands/utils/help.ts create mode 100644 commands/utils/ping.ts create mode 100644 commands/utils/set.ts create mode 100644 config.ts create mode 100644 events/handle-auto-roles.ts create mode 100644 events/handle-commands.ts create mode 100644 events/handle-giveaway.ts create mode 100644 events/handle-react-image.ts create mode 100644 functions/react-preprocess.ts create mode 100644 index.ts create mode 100644 init/load-commands.ts create mode 100644 init/ready-dm.ts create mode 100644 init/register-commands.ts create mode 100644 init/set-nickname.ts create mode 100644 k8s/deploy.amane-backend.yaml create mode 100644 logger.ts create mode 100644 models/Alias.ts create mode 100644 models/AutoroleMsg.ts create mode 100644 models/GiveawayMsg.ts create mode 100644 models/Guild.ts create mode 100644 models/Image.ts create mode 100644 models/Token.ts create mode 100644 mongo.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..745264f --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..710b6ba --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:22.4-slim AS build +WORKDIR /work +COPY . /work +RUN npm ci && npm run build + +FROM gcr.io/distroless/nodejs22-debian12 +WORKDIR /work +COPY --from=build /work /work + +CMD ["index.js"] diff --git a/classes/command.ts b/classes/command.ts new file mode 100644 index 0000000..de014d2 --- /dev/null +++ b/classes/command.ts @@ -0,0 +1,23 @@ +import { + SlashCommandBuilder, + SlashCommandSubcommandsOnlyBuilder, + CommandInteraction +} from 'discord.js'; + +export interface Component{ + execute(interaction: unknown): Promise; + build(): unknown; +}; + +export abstract class Command implements Component{ + abstract get name(): string; + abstract get description(): string; + abstract execute(interaction: CommandInteraction): Promise; + build(): + SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder | + Omit{ + return new SlashCommandBuilder() + .setName(this.name) + .setDescription(this.description); + } +}; \ No newline at end of file diff --git a/classes/extendedclient.ts b/classes/extendedclient.ts new file mode 100644 index 0000000..36e1af3 --- /dev/null +++ b/classes/extendedclient.ts @@ -0,0 +1,22 @@ +import { + Client, + Collection, + ClientOptions, +} from 'discord.js'; + +import {Command} from './command' + +export function isExtendedClient(client: Client): client is ExtendedClient{ + return (client as ExtendedClient).commands !== undefined; +} + +export class ExtendedClient extends Client{ + public commands: Collection; + constructor( + opts: ClientOptions, + cmds = new Collection() + ){ + super(opts); + this.commands = cmds; + } +}; \ No newline at end of file diff --git a/commands/giveaway/giveaway.ts b/commands/giveaway/giveaway.ts new file mode 100644 index 0000000..4de29ea --- /dev/null +++ b/commands/giveaway/giveaway.ts @@ -0,0 +1,152 @@ +import discord, { + CommandInteraction, + CommandInteractionOptionResolver, + SlashCommandBuilder, + SlashCommandRoleOption, + SlashCommandStringOption, + SlashCommandIntegerOption, + SlashCommandSubcommandBuilder, + EmbedBuilder, + InteractionResponse, + SlashCommandSubcommandsOnlyBuilder, +} from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {Command} from '../../classes/command'; +import {logger} from '../../logger'; +import {config} from '../../config'; +import {Guild, guildModel} from '../../models/Guild'; +import {GiveawayMsg, giveawayMsgModel} from '../../models/GiveawayMsg'; + +type CIOR = CommandInteractionOptionResolver; + +async function add(interaction: CommandInteraction): Promise{ + const message: string | null = + (interaction.options as CIOR).getString('message'); + if(!message) + throw Error('message not exist'); + const embed = new EmbedBuilder() + .setTitle('New Giveaway') + .setDescription(`@everyone\n${message}`); + + if(!interaction.guildId) + throw Error('guildId not exist'); + if(!interaction.channelId) + throw Error('channelId not exist'); + + let msg: discord.Message = + await (await interaction.reply({embeds: [embed]})).fetch(); + await msg.react(config.giveaway.emoji); + if(!interaction.guild || !interaction.guild.id) + throw Error('guild not exist'); + const guild: HydratedDocument | null = + await guildModel.findOneAndUpdate( + {id: interaction.guild.id}, + {$set: {name: interaction.guild.name}}, + {upsert: true, new: true} + ); + if(!guild) + throw Error('guild not exist'); + const giveawayMsg: HydratedDocument | null = + await giveawayMsgModel.findOneAndUpdate( + {guild: guild, messageId: msg.id, authorId: interaction.user.id}, + {}, {upsert: true, new: true} + ); + if(!giveawayMsg) + throw Error('giveawayMsg not exist'); + + await msg.edit({embeds: [new EmbedBuilder() + .setColor(Math.floor(Math.random()*16777215)) + .setTitle('Giveaway') + .setDescription(message) + .setFooter({text: `id: ${msg.id}`}) + ]}); +} + +async function pick(interaction: CommandInteraction): Promise{ + const messageId: string | null = + (interaction.options as CIOR).getString('message'); + if(!messageId) + throw Error('message not exist'); + const num: number | null = + (interaction.options as CIOR).getInteger('number'); + if(!num) + throw Error('number not exist'); + + const dbGiveawayMsg: HydratedDocument | null = + await giveawayMsgModel.findOneAndDelete({ + messageId: messageId, authorId: interaction.user.id + }); + if(!dbGiveawayMsg){ + await interaction.reply({ + content: logger.log(`Giveaway didn't exist or the author didn't match.`), + ephemeral: true + }); + return; + } + for(let i = dbGiveawayMsg.userIds.length - 1; i > 0; i--){ + let j = Math.floor(Math.random() * (i + 1)); + [dbGiveawayMsg.userIds[i], dbGiveawayMsg.userIds[j]] + = [dbGiveawayMsg.userIds[j], dbGiveawayMsg.userIds[i]]; + } + let content: string = '### Giveaway Result\n'; + for(let i = 0; i < num; i++) + content += `1. ${await interaction.client.users.fetch(dbGiveawayMsg.userIds[i])}\n`; + content += `will obtain the prize for giveaway, id: ${dbGiveawayMsg.messageId}`; + await interaction.reply({content: content}); +} + +class Giveaway extends Command{ + get name(){return 'giveaway';} + get description(){return 'Setup a Giveaway message.';} + async execute(interaction: CommandInteraction): Promise{ + try{ + const subcommand: string | null = + (interaction.options as CIOR).getSubcommand(); + switch(subcommand){ + case 'add': + await add(interaction); break; + case 'pick': + await pick(interaction); break; + default: + throw Error('subcommand not exist'); + } + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "/giveaway", ${message}`); + await interaction.reply({content: `While executing "/giveaway", ${message}`}); + } + } + override build(): SlashCommandSubcommandsOnlyBuilder{ + return new SlashCommandBuilder() + .setName(this.name) + .setDescription(this.description) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('add') + .setDescription('Construct a giveaway') + .addStringOption((option: SlashCommandStringOption) => option + .setName('message') + .setDescription('The message displayed with the giveaway message.') + .setRequired(true) + ) + ) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('pick') + .setDescription('Random pick a number of user in a giveaway') + .addStringOption((option: SlashCommandStringOption) => option + .setName('message') + .setDescription('The message id of the giveaway (Shown in the footer)') + .setRequired(true) + ) + .addIntegerOption((option: SlashCommandIntegerOption) => option + .setName('number') + .setDescription('The number of member to choose.') + .setRequired(true) + ) + ) + } +}; + +export const command = new Giveaway(); diff --git a/commands/images/image.ts b/commands/images/image.ts new file mode 100644 index 0000000..6123c81 --- /dev/null +++ b/commands/images/image.ts @@ -0,0 +1,221 @@ +import discord, { + CommandInteraction, + CommandInteractionOptionResolver, + SlashCommandBuilder, + SlashCommandStringOption, + SlashCommandAttachmentOption, + SlashCommandSubcommandBuilder, + EmbedBuilder, + InteractionResponse, + SlashCommandSubcommandsOnlyBuilder, + Attachment +} from 'discord.js'; +import { HydratedDocument } from 'mongoose'; +import fs from 'fs'; +import path from 'path'; +import axios from 'axios'; +import { Client } from 'minio'; + +import { Command } from '../../classes/command'; +import { logger } from '../../logger'; +import { config } from '../../config'; +import { reactPreprocess } from '../../functions/react-preprocess'; +import { Image, imageModel } from '../../models/Image'; +import { Alias, aliasModel } from '../../models/Alias'; +import { Guild, guildModel } from '../../models/Guild'; +import { Token, tokenModel } from '../../models/Token'; + +type CIOR = CommandInteractionOptionResolver; + +const client = new Client(config.minio.client); + +async function download(url: string, file: string): Promise{ + const saveFile = await axios({ + url: url, method: 'GET', responseType: 'arraybuffer' + }); + if(!await client.bucketExists(config.minio.bucket)) + await client.makeBucket(config.minio.bucket); + + await client.putObject(config.minio.bucket, file, saveFile.data); +} + +async function upload(interaction: CommandInteraction): Promise{ + if(!interaction.guildId) + throw Error('guildId not exist'); + const file: discord.Attachment | null = + (interaction.options as CIOR).getAttachment('file'); + if(!file) + throw Error('file not exist'); + const fileExt: string = path.extname(file.name); + + const image: HydratedDocument | null = + await (new imageModel({extension: fileExt})).save(); + if(!image) + throw Error('image not exist'); + await download(file.url, `${String(image._id)}${fileExt}`); + + const publicUrl: string = `${config.httpServer.external.url}/${String(image._id)}${fileExt}`; + await interaction.reply({ + content: logger.log(`Image saved with id: ${image._id}.`), + }); + if(interaction.channel) + await interaction.channel.send({content: publicUrl}); +} + +async function link(interaction: CommandInteraction): Promise{ + if(!interaction.guildId) + throw Error('guildId not exist'); + let aliasText: string | null = + (interaction.options as CIOR).getString('alias'); + if(!aliasText) + throw Error('aliasText not exist'); + const imageId: string | null = + (interaction.options as CIOR).getString('image'); + if(!imageId) + throw Error('imageId not exist'); + + aliasText = reactPreprocess(aliasText); + if(!interaction.guild || !interaction.guild.id || !interaction.guild.name) + throw Error('guild not exist'); + const guild: HydratedDocument | null = + await guildModel.findOneAndUpdate( + {id: interaction.guild.id}, + {$set: {name: interaction.guild.name}}, + {new: true, upsert: true} + ); + if(!guild) + throw Error('guild not exist'); + const image: HydratedDocument | null = + await imageModel.findById(imageId); + if(!image) + throw Error('image not exist'); + const alias: HydratedDocument | null = + await aliasModel.findOneAndUpdate( + {guild: guild, text: aliasText, images: {$nin: image}}, + {$push: {images: image}}, + {new: true, upsert: true} + ); + if(!alias) + throw Error('alias update failed'); + + await interaction.reply({ + content: logger.log(`Alias ${aliasText} has linked to image id: ${imageId}.`) + }); +} + +async function unlink(interaction: CommandInteraction): Promise{ + if(!interaction.guildId) + throw Error('guildId not exist'); + let aliasText: string | null = + (interaction.options as CIOR).getString('alias'); + if(!aliasText) + throw Error('aliasText not exist'); + aliasText = reactPreprocess(aliasText); + if(!interaction.guild || !interaction.guild.id) + throw Error('guild not exist'); + const guild: HydratedDocument | null = + await guildModel.findOne({id: interaction.guild.id}); + if(!guild) + throw Error('guild not exist'); + const res = await aliasModel.deleteOne( + {guild: guild, text: aliasText} + ); + if(!res) + logger.warning('alias not exist'); + + await interaction.reply({ + content: logger.log(`All the image have been unlinked to alias ${aliasText}.`) + }); +} + +async function list(interaction: CommandInteraction): Promise{ + const token: string = String(Math.floor(Math.random() * 100000000)); + if(!interaction.guild || !interaction.guild.id) + throw Error('guild not exist'); + await tokenModel.create({ + token: token, + guildId: interaction.guild.id, + exp: Date.now() + config.tokenTimeLimit + }); + const embed = new EmbedBuilder() + .setColor(0x1f1e33) + .setTitle('Autoreact Image List') + .setDescription('experimental function') + .addFields({ + name: 'One Time Link:', + value: `[open in browser](https://amane.konchin.com/auth?token=${token})` + }); + await interaction.reply({embeds: [embed], ephemeral: true}); +} + +class ImageCmd extends Command{ + get name(){return "image";} + get description(){return "Modify the reacting image database.";} + async execute(interaction: CommandInteraction): Promise{ + try{ + const subcommand: string | null = + (interaction.options as CIOR).getSubcommand(); + switch(subcommand){ + case 'upload': + await upload(interaction); break; + case 'link': + await link(interaction); break; + case 'unlink': + await unlink(interaction); break; + case 'list': + await list(interaction); break; + default: + throw Error('subcommand not exist'); + } + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "/image", ${message}`); + await interaction.reply({content: `While executing "/image", ${message}`}); + } + } + override build(): SlashCommandSubcommandsOnlyBuilder{ + return new SlashCommandBuilder() + .setName(this.name) + .setDescription(this.description) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('upload') + .setDescription('Upload an image.') + .addAttachmentOption((option: SlashCommandAttachmentOption) => option + .setName('file') + .setDescription('The image file that you want to upload.') + .setRequired(true) + ) + ) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('link') + .setDescription('Link an alias to an image') + .addStringOption((option: SlashCommandStringOption) => option + .setName('alias') + .setDescription('The alias to be linked.') + .setRequired(true) + ) + .addStringOption((option: SlashCommandStringOption) => option + .setName('image') + .setDescription('The image to be linked, as id.') + .setRequired(true) + ) + ) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('unlink') + .setDescription('Unlink an alias to an image') + .addStringOption((option: SlashCommandStringOption) => option + .setName('alias') + .setDescription('The alias to be unlinked.') + .setRequired(true) + ) + ) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('list') + .setDescription('Show the image list') + ) + } +}; + +export const command = new ImageCmd(); diff --git a/commands/roles/autorole.ts b/commands/roles/autorole.ts new file mode 100644 index 0000000..a3aecbb --- /dev/null +++ b/commands/roles/autorole.ts @@ -0,0 +1,90 @@ +import discord, { + CommandInteraction, + CommandInteractionOptionResolver, + SlashCommandBuilder, + SlashCommandRoleOption, + SlashCommandStringOption, + InteractionResponse, +} from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {Command} from '../../classes/command'; +import {logger} from '../../logger'; +import {AutoroleMsg, autoroleMsgModel} from '../../models/AutoroleMsg'; +import {Guild, guildModel} from '../../models/Guild'; + +type CIOR = CommandInteractionOptionResolver; + +class Autorole extends Command{ + get name(){return "autorole";} + get description(){return "Setup a autorole message.";} + async execute(interaction: CommandInteraction): Promise{ + try{ + let role = (interaction.options as CIOR).getRole('role'); + if(!role) + throw Error('role not exist'); + const title: string | null = + (interaction.options as CIOR).getString('title'); + if(!title) + throw Error('title not exist'); + const message: string | null = + (interaction.options as CIOR).getString('message'); + if(!message) + throw Error('message not exist'); + + if(!interaction.guild || !interaction.guild.id || !interaction.guild.name) + throw Error('guildId not exist'); + if(!interaction.channelId) + throw Error('channelId not exist'); + let content: string = `>> **Auto Role: ${title}** <<\n`; + content += '*React to give yourself a role.*\n\n'; + content += `${message}`; + let msg: discord.Message = + await (await interaction.reply({content: content})).fetch(); + const guild: HydratedDocument | null = + await guildModel.findOneAndUpdate( + {id: interaction.guild.id}, + {$set: {name: interaction.guild.name}}, + {upsert: true, new: true} + ); + if(!guild) + throw Error('guild not exist'); + const autoroleMsg: HydratedDocument | null = + await autoroleMsgModel.findOneAndUpdate( + {guild: guild, messageId: msg.id, roleId: role.id}, + {}, {upsert: true, new: true} + ); + if(!autoroleMsg) + throw Error('autoroleMsg not exist'); + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "/autorole", ${message}`); + await interaction.reply({content: `While executing "/autorole", ${message}`}); + } + } + override build(): SlashCommandBuilder | + Omit{ + return new SlashCommandBuilder() + .setName(this.name) + .setDescription(this.description) + .addRoleOption((option: SlashCommandRoleOption) => option + .setName('role') + .setDescription('The role that autorole gives when reacted.') + .setRequired(true) + ) + .addStringOption((option: SlashCommandStringOption) => option + .setName('title') + .setDescription('The title displayed with the autorole message.') + .setRequired(true) + ) + .addStringOption((option: SlashCommandStringOption) => option + .setName('message') + .setDescription('The additional message displayed with the autorole message.') + .setRequired(true) + ) + } +}; + +export const command = new Autorole(); diff --git a/commands/tests/test.ts b/commands/tests/test.ts new file mode 100644 index 0000000..dc10bb7 --- /dev/null +++ b/commands/tests/test.ts @@ -0,0 +1,10 @@ +import {CommandInteraction} from 'discord.js'; +import {Command} from '../../classes/command'; + +class Test extends Command{ + get name(){return "test";} + get description(){return "Testing some features.";} + async execute(interaction: CommandInteraction): Promise{} +}; + +// export const command = new Test(); \ No newline at end of file diff --git a/commands/utils/help.ts b/commands/utils/help.ts new file mode 100644 index 0000000..f110318 --- /dev/null +++ b/commands/utils/help.ts @@ -0,0 +1,29 @@ +import {CommandInteraction, EmbedBuilder} from 'discord.js'; + +import {Command} from '../../classes/command'; +import {logger} from '../../logger'; +import {config} from '../../config'; + +class Help extends Command{ + get name(){return "help";} + get description(){return "Help messages.";} + async execute(interaction: CommandInteraction): Promise{ + const embed = new EmbedBuilder() + .setTitle("Help message") + .setDescription(`Report issues on [this page](${config.urls.issue})`) + .setColor(0x1f1e33) + .setAuthor({ + name: 'Ian Shih (konchin)', + url: config.urls.author, + iconURL: config.urls.icon + }) + .setFields( + {name: 'Read the documentation', value: config.urls.help}, + {name: 'Read the source code', value: config.urls.git} + ); + await interaction.reply({embeds: [embed], ephemeral: true}); + logger.log(`Command: help`); + } +}; + +export const command = new Help(); diff --git a/commands/utils/ping.ts b/commands/utils/ping.ts new file mode 100644 index 0000000..7559712 --- /dev/null +++ b/commands/utils/ping.ts @@ -0,0 +1,19 @@ +import {CommandInteraction} from 'discord.js'; +import {Command} from '../../classes/command'; + +class Ping extends Command{ + get name(){return "ping";} + get description(){return "Reply with the RTT of this bot.";} + async execute(interaction: CommandInteraction): Promise{ + const sent = await interaction.reply({ + content: "Pinging...", + ephemeral: true, + fetchReply: true, + }); + await interaction.editReply(`Roundtrip latency: ${ + sent.createdTimestamp - interaction.createdTimestamp + }ms`); + } +}; + +export const command = new Ping(); diff --git a/commands/utils/set.ts b/commands/utils/set.ts new file mode 100644 index 0000000..da3ef64 --- /dev/null +++ b/commands/utils/set.ts @@ -0,0 +1,104 @@ +import discord, { + CommandInteraction, + CommandInteractionOptionResolver, + SlashCommandBuilder, + SlashCommandSubcommandBuilder, + SlashCommandStringOption, + SlashCommandChannelOption, + InteractionResponse, + SlashCommandSubcommandsOnlyBuilder, +} from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {Command} from '../../classes/command'; +import {logger} from '../../logger'; +import {Guild, guildModel} from '../../models/Guild'; + +type CIOR = CommandInteractionOptionResolver; + +async function log(interaction: CommandInteraction): Promise{ + const feature: string | null = + (interaction.options as CIOR).getString('feature'); + if(!feature) + throw Error('feature not exist'); + const logChannel = (interaction.options as CIOR).getChannel('channel'); + if(!logChannel || !('send' in logChannel)) + throw Error('logChannel not exist or illegal'); + if(!interaction.guild || !interaction.guild.id || !interaction.guild.name) + throw Error('guild not exist'); + let guild: HydratedDocument | null = null; + switch(feature){ + case 'giveaway': + await guildModel.findOneAndUpdate( + {id: interaction.guild.id}, + {$set: { + name: interaction.guild.name, + giveawayLogChannelId: logChannel.id + }}, + {new: true, upsert: true} + ); + break; + case 'autorole': + await guildModel.findOneAndUpdate( + {id: interaction.guild.id}, + {$set: { + name: interaction.guild.name, + autoroleLogChannelId: logChannel.id + }}, + {new: true, upsert: true} + ); + break; + } + await interaction.reply({ + content: logger.log(`The log channel of ${feature} has been set to ${logChannel}`) + }); + ; +} + +class SetVariables extends Command{ + get name(){return "set";} + get description(){return "Set guild variables.";} + async execute(interaction: CommandInteraction): Promise{ + try{ + const subcommand: string = + (interaction.options as CIOR).getSubcommand(); + switch(subcommand){ + case 'log': + await log(interaction); break; + default: + throw Error('subcommand not exist'); + } + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "/set", ${message}`); + await interaction.reply({content: `While executing "/set", ${message}`}); + } + } + override build(): SlashCommandSubcommandsOnlyBuilder{ + return new SlashCommandBuilder() + .setName(this.name) + .setDescription(this.description) + .addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand + .setName('log') + .setDescription('Set log of features.') + .addStringOption((option: SlashCommandStringOption) => option + .setName('feature') + .setDescription('Specify the to be set feature.') + .setRequired(true) + .addChoices( + {name: 'giveaway', value: 'giveaway'}, + {name: 'autorole', value: 'autorole'}, + ) + ) + .addChannelOption((option: SlashCommandChannelOption) => option + .setName('channel') + .setDescription('The channel that log send to.') + .setRequired(true) + ) + ) + } +}; + +export const command = new SetVariables(); diff --git a/config.ts b/config.ts new file mode 100644 index 0000000..fd13a89 --- /dev/null +++ b/config.ts @@ -0,0 +1,51 @@ +import dotenv from 'dotenv'; + +dotenv.config(); + +export const config = { + token: process.env.DC_TOKEN!, + clientId: process.env.DC_CLIENTID!, + adminId: process.env.ADMIN_ID ?? '', + nickname: '谷風 天音', + data: '.', + urls: { + author: 'https://hello.konchin.com', + icon: 'https://secure.gravatar.com/avatar/c35f2cb664f366e3e3365b9c22216834?d=identicon&s=512', + help: 'https://md.konchin.com/s/u7qUK4oY4', + git: 'https://git.konchin.com/discord-bot/Tanikaze-Amane', + issue: 'https://git.konchin.com/discord-bot/Tanikaze-Amane/issues' + }, + giveaway: { + emoji: '✋', + }, + logger: { + logFile: 'test.log', + }, + httpServer: { + local: { + port: process.env.PORT ?? 8082, + }, + external: { + url: 'https://amane.konchin.com/img' + }, + }, + mongodb: { + host: process.env.MONGODB_HOST ?? '127.0.0.1', + port: process.env.MONGODB_PORT ?? 27017, + db: process.env.MONGODB_DB ?? 'amanev2', + user: process.env.MONGODB_USER ?? 'amane', + pass: process.env.MONGODB_PASS!, + reset: process.env.DB_RESET ?? 'false', + }, + tokenTimeLimit: 60 * 1000, + minio: { + bucket: 'amane-img', + client: { + endPoint: process.env.MINIO_ENDPOINT ?? 'minio.konchin.com', + useSSL: (process.env.MINIO_USESSL == 'true') ?? false, + port: (process.env.MINIO_PORT as unknown as number) ?? 9000, + accessKey: process.env.MINIO_ACCESSKEY ?? '', + secretKey: process.env.MINIO_SECRETKEY ?? '', + } + } +}; diff --git a/events/handle-auto-roles.ts b/events/handle-auto-roles.ts new file mode 100644 index 0000000..aa7b078 --- /dev/null +++ b/events/handle-auto-roles.ts @@ -0,0 +1,101 @@ +import discord, { + MessageReaction, PartialMessageReaction, + User, PartialUser, +} from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {logger} from '../logger'; +import {config} from '../config'; +import {Guild, guildModel} from '../models/Guild'; +import {AutoroleMsg, autoroleMsgModel} from '../models/AutoroleMsg'; + +function isMessageReaction(reaction: MessageReaction | PartialMessageReaction): reaction is MessageReaction{ + return reaction.partial === false; +} + +function isUser(user: User | PartialUser): user is User{ + return user.partial === false; +} + +async function handleRole( + reaction: MessageReaction | PartialMessageReaction, + user: User | PartialUser, + action: string +): Promise{ + try{ + if(config.clientId === user.id) return; + if(reaction.partial) + reaction = await reaction.fetch(); + if(user.partial) + user = await user.fetch(); + + if(!isMessageReaction(reaction)) + throw Error('type mismatch: reaction.partial'); + if(!isUser(user)) + throw Error('type mismatch: user.partial'); + if(!reaction.emoji) + throw Error('reaction.emoji not exist'); + const dbAutoroleMsg: HydratedDocument | null = + await autoroleMsgModel.findOneAndUpdate( + {messageId: reaction.message.id}, + {$set: {emoji: reaction.emoji.toString()}}, + {new: true} + ); + if(!dbAutoroleMsg || dbAutoroleMsg.emoji !== reaction.emoji.toString()) + return; + if(!reaction.me) + await reaction.react(); + + const dbGuild: HydratedDocument | null = + await guildModel.findById(dbAutoroleMsg.guild._id); + if(!dbGuild) + throw Error('dbGuild not exist'); + + const guild: discord.Guild | null = + await reaction.client.guilds.resolve(dbGuild.id); + if(!guild) + throw Error('guild not exist'); + + const role: discord.Role | null = + await guild.roles.resolve(dbAutoroleMsg.roleId); + if(!role) + throw Error('role not exist'); + + switch(action){ + case 'add': await guild.members.addRole({ + user: user.id, role: role.id + }); break; + case 'remove': await guild.members.removeRole({ + user: user.id, role: role.id + }); break; + } + + logger.log(`${user} has been ${action === 'add'?'given':'removed'} ${role} role.`); + if('autoroleLogChannelId' in dbGuild && dbGuild.autoroleLogChannelId){ + const autoroleChannel: discord.Channel | null = + await guild.channels.resolve(dbGuild.autoroleLogChannelId); + if(autoroleChannel === null || !autoroleChannel.isTextBased()) + throw Error('autoroleChannel not exist or autoroleChannel isn\'t text based'); + await autoroleChannel.send({content: + `${user} has been ${action === 'add'?'given':'removed'} ${role} role.` + }); + } + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "handle-autorole", ${message}`); + } +} + +export async function handleRoleAdd( + reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser +): Promise{ + await handleRole(reaction, user, 'add'); +} + +export async function handleRoleRemove( + reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser +): Promise{ + await handleRole(reaction, user, 'remove'); +} diff --git a/events/handle-commands.ts b/events/handle-commands.ts new file mode 100644 index 0000000..7816a19 --- /dev/null +++ b/events/handle-commands.ts @@ -0,0 +1,31 @@ +import {Interaction} from 'discord.js'; + +import {isExtendedClient} from '../classes/extendedclient'; +import {logger} from '../logger'; + +export async function handleCommands(interaction: Interaction): Promise{ + if(!interaction.isChatInputCommand()) return; + if(interaction.commandName === null) + throw logger.error('interaction.commandName not exist'); + if(!isExtendedClient(interaction.client)) + throw logger.error(`Type Error in function "handleCommands"`); + + const command = interaction.client.commands.get(interaction.commandName); + if(!command) + throw logger.error(`No command matching ${interaction.commandName} was found.`); + + try{ + if('execute' in command) + await command.execute(interaction); + else{ + logger.error(`The command (${interaction.commandName}) is missing a require "execute" function`); + return; + } + }catch(err: unknown){ + if(interaction.replied || interaction.deferred) + await interaction.followUp({content: 'There was an error while executing this command!', ephemeral: true}); + else + await interaction.reply({content: 'There was an error while executing this command!', ephemeral: true}); + throw logger.error(`While handling "${interaction.commandName}, ${err}"`); + } +} \ No newline at end of file diff --git a/events/handle-giveaway.ts b/events/handle-giveaway.ts new file mode 100644 index 0000000..26913de --- /dev/null +++ b/events/handle-giveaway.ts @@ -0,0 +1,88 @@ +import discord, { + MessageReaction, PartialMessageReaction, + User, PartialUser, +} from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {logger} from '../logger'; +import {config} from '../config'; +import {Guild, guildModel} from '../models/Guild'; +import {GiveawayMsg, giveawayMsgModel} from '../models/GiveawayMsg'; + +function isMessageReaction(reaction: MessageReaction | PartialMessageReaction): reaction is MessageReaction{ + return reaction.partial === false; +} + +function isUser(user: User | PartialUser): user is User{ + return user.partial === false; +} + +export async function handleGiveaway( + reaction: MessageReaction | PartialMessageReaction, + user: User | PartialUser +): Promise{ + try{ + // fetch data + if(config.clientId === user.id) return; + if(reaction.partial){ + reaction = await reaction.fetch(); + logger.debug('reaction in handleRole has fetched'); + } + if(user.partial){ + user = await user.fetch(); + logger.debug('user in handleRole has fetched'); + } + if(!isMessageReaction(reaction)) + throw Error('type mismatch: reaction.partial'); + if(!isUser(user)) + throw Error('type mismatch: user.partial'); + if(!reaction.emoji) + throw Error('reaction.emoji not exist'); + if(reaction.emoji.toString() !== config.giveaway.emoji) return; + let dbGiveawayMsg: HydratedDocument | null = + await giveawayMsgModel.findOne( + {messageId: reaction.message.id} + ); + if(!dbGiveawayMsg) return; + if(dbGiveawayMsg.userIds.includes(user.id)){ + logger.info(`${user} has already in giveaway userIds`); + return; + } + const dbGuild: HydratedDocument | null = + await guildModel.findById(dbGiveawayMsg.guild._id); + if(!dbGuild) + throw Error('dbGuild not exist'); + const guild: discord.Guild = await reaction.client.guilds.resolve(dbGuild.id); + if(!guild) + throw Error('guild not exist'); + + // record + dbGiveawayMsg = await giveawayMsgModel.findOneAndUpdate( + {messageId: dbGiveawayMsg.messageId}, + {$push: {userIds: user.id}}, {new: true} + ); + if(!dbGiveawayMsg) + throw Error('dbGiveawayMsg not exist'); + + // notify + logger.log(`${user} has been reacted to giveaway, id: ${dbGiveawayMsg.messageId}.`); + if(!dbGuild.giveawayLogChannelId){ + const author: discord.User | null = + await reaction.client.users.fetch(dbGiveawayMsg.authorId); + if(!author) + throw Error(`author ${author} not exist`); + await author.send({content: `user ${user} has reacted to giveaway, id: ${dbGiveawayMsg.messageId}`}); + }else{ + const logChannel: discord.Channel | null = + await guild.channels.resolve(dbGuild.giveawayLogChannelId); + if(!logChannel || !logChannel.isTextBased()) + throw Error('logChannel not exist or logChannel isn\'t text based'); + await logChannel.send({content: `user ${user} has reacted to giveaway, id: ${dbGiveawayMsg.messageId}`}); + } + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "handleGiveaway", ${message}`); + } +} diff --git a/events/handle-react-image.ts b/events/handle-react-image.ts new file mode 100644 index 0000000..e753f59 --- /dev/null +++ b/events/handle-react-image.ts @@ -0,0 +1,40 @@ +import discord from 'discord.js'; +import {HydratedDocument} from 'mongoose'; + +import {logger} from '../logger'; +import {config} from '../config'; +import {reactPreprocess} from '../functions/react-preprocess'; +import {Alias, aliasModel} from '../models/Alias'; +import {Guild, guildModel} from '../models/Guild'; +import {Image, imageModel} from '../models/Image'; + +export async function handleReactImage(message: discord.Message): Promise{ + try{ + if(!message.guild || !message.guild.id || !message.content) return; + const guild: HydratedDocument | null = + await guildModel.findOne({id: message.guild.id}); + if(!guild) return; // not in guild + const alias: HydratedDocument | null = + await aliasModel.findOne( + {guild: guild, text: reactPreprocess(message.content)} + ); + if(!alias) return; // alias not exist + if(!alias.images.length) return; // alias not linked + + const chosenImage: Image = + alias.images[Math.floor(Math.random() * alias.images.length)]; + const image: HydratedDocument | null = + await imageModel.findById(chosenImage._id); + if(!image) + throw Error('image not exist'); + + await message.channel.send({ + content: `${config.httpServer.external.url}/${image._id}${image.extension}` + }); + }catch(err: unknown){ + let errMsg; + if(err instanceof Error) errMsg = err.message; + else errMsg = String(errMsg); + logger.error(`While executing "handleReactImage", ${errMsg}`); + } +} diff --git a/functions/react-preprocess.ts b/functions/react-preprocess.ts new file mode 100644 index 0000000..d94ceab --- /dev/null +++ b/functions/react-preprocess.ts @@ -0,0 +1,3 @@ +export function reactPreprocess(str: string){ + return str.toLowerCase().replaceAll(' ', ''); +} \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..8ddbe04 --- /dev/null +++ b/index.ts @@ -0,0 +1,97 @@ +import {Events, GatewayIntentBits, Partials, ActivityType} from 'discord.js'; +import {createServer} from 'http'; + +// Global config +import {config} from './config'; + +// Classes +import {ExtendedClient} from './classes/extendedclient' + +// Initialization functions +import {setNickname} from './init/set-nickname'; +import {loadCommands} from './init/load-commands'; +import {registerCommands} from './init/register-commands'; +import {sendReadyDM} from './init/ready-dm'; + +// Event-handling functions +import {handleCommands} from './events/handle-commands'; +import {handleGiveaway} from './events/handle-giveaway'; +import {handleRoleAdd, handleRoleRemove} from './events/handle-auto-roles'; +import {handleReactImage} from './events/handle-react-image'; + +// Sub-services +import {logger} from './logger'; +import {runMongo} from './mongo'; + +const client = new ExtendedClient({ + intents:[ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildModeration, + GatewayIntentBits.GuildEmojisAndStickers, + GatewayIntentBits.GuildIntegrations, + GatewayIntentBits.GuildWebhooks, + GatewayIntentBits.GuildInvites, + GatewayIntentBits.GuildVoiceStates, + GatewayIntentBits.GuildPresences, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildMessageReactions, + GatewayIntentBits.GuildMessageTyping, + GatewayIntentBits.DirectMessages, + GatewayIntentBits.DirectMessageReactions, + GatewayIntentBits.DirectMessageTyping, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildScheduledEvents, + GatewayIntentBits.AutoModerationConfiguration, + GatewayIntentBits.AutoModerationExecution, + ], + partials:[ + Partials.User, + Partials.Channel, + Partials.GuildMember, + Partials.Message, + Partials.Reaction, + Partials.GuildScheduledEvent, + Partials.ThreadMember, + ] +}); + +client.login(config.token); + +// Event handling +client.on(Events.InteractionCreate, handleCommands); +client.on(Events.MessageReactionAdd, handleGiveaway); +client.on(Events.MessageReactionAdd, handleRoleAdd); +client.on(Events.MessageReactionRemove, handleRoleRemove); +client.on(Events.MessageCreate, handleReactImage); + +// Initialization +client.once(Events.ClientReady, async c => { + logger.info(`Logged in as ${c.user.tag}`); + + if(client.user){ + client.user.setPresence({ + activities: [{ + name: '天使☆騒々 RE-BOOT!', + type: ActivityType.Playing, + }], + status: 'online' + }); + logger.info('Set status done'); + } + + await setNickname(client); + logger.info(`Set nickname as ${config.nickname}`); + + const commands = await loadCommands(client); + logger.info(`${commands.length} commands loaded.`); + + const regCmdCnt = await registerCommands(commands); + logger.info(`${regCmdCnt} commands registered.`); + + await runMongo(); + logger.info(`Database ready`); + + logger.info(`Ready!`); + await sendReadyDM(client, config.adminId); +}); diff --git a/init/load-commands.ts b/init/load-commands.ts new file mode 100644 index 0000000..bc907ff --- /dev/null +++ b/init/load-commands.ts @@ -0,0 +1,25 @@ +import path from 'path'; +import {readdirSync} from 'fs'; + +import {ExtendedClient} from '../classes/extendedclient'; +import {logger} from '../logger'; + +export async function loadCommands(client: ExtendedClient): Promise>{ + const foldersPath = path.join(__dirname, '../commands'); + const commandFolders = readdirSync(foldersPath); + const commands: Array = []; + for(const folder of commandFolders){ + const commandsPath = path.join(foldersPath, folder); + const commandsFiles = readdirSync(commandsPath).filter(file => file.endsWith('.ts')); + for(const file of commandsFiles){ + const filePath = path.join(commandsPath, file); + const data = await import(filePath); + if(data.command !== undefined){ + client.commands.set(data.command.name, data.command); + commands.push(data.command.build().toJSON()); + }else + logger.warning(`The command at ${filePath} is missing required properties.`); + } + } + return commands; +} \ No newline at end of file diff --git a/init/ready-dm.ts b/init/ready-dm.ts new file mode 100644 index 0000000..801a757 --- /dev/null +++ b/init/ready-dm.ts @@ -0,0 +1,11 @@ +import {logger} from '../logger'; +import {ExtendedClient} from '../classes/extendedclient'; + +export async function sendReadyDM(client: ExtendedClient, adminId: string): Promise{ + try{ + await (await client.users.fetch(adminId)).send(`service up at ${new Date()}`); + logger.log('Service up message sent'); + }catch(err: unknown){ + logger.warning('sendReadyDM failed.'); + } +} \ No newline at end of file diff --git a/init/register-commands.ts b/init/register-commands.ts new file mode 100644 index 0000000..88fca98 --- /dev/null +++ b/init/register-commands.ts @@ -0,0 +1,26 @@ +import {REST, Routes} from 'discord.js'; + +import {config} from '../config'; +import {logger} from '../logger'; + +function isArray(data: unknown): data is Array{ + return (data as Array).length !== undefined; +} + +export async function registerCommands(commands: Array): Promise{ + const rest = new REST().setToken(config.token); + try{ + const data = await rest.put( + Routes.applicationCommands(config.clientId), + {body: commands}, + ); + if(!isArray(data)) throw Error('Type error'); + return data.length; + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing "registerCommands", ${message}`); + return -1; + } +} \ No newline at end of file diff --git a/init/set-nickname.ts b/init/set-nickname.ts new file mode 100644 index 0000000..bf08098 --- /dev/null +++ b/init/set-nickname.ts @@ -0,0 +1,22 @@ +import {Guild} from 'discord.js'; + +import {ExtendedClient} from '../classes/extendedclient'; +import {config} from '../config'; +import {logger} from '../logger'; + +export async function setNickname(client: ExtendedClient): Promise{ + await client.guilds.cache.forEach(async (guild: Guild): Promise => { + try{ + // console.log(guild.members); + const self = await guild.members.fetch({user: config.clientId, force: true}); + if(!self) throw Error('self not exist'); + await self.setNickname(config.nickname); + logger.log(`Nickname had changed in guild: ${guild.name}`); + }catch(err: unknown){ + let message; + if(err instanceof Error) message = err.message; + else message = String(message); + logger.error(`While executing setNickname, ${message}`); + } + }); +} diff --git a/k8s/deploy.amane-backend.yaml b/k8s/deploy.amane-backend.yaml new file mode 100644 index 0000000..891706b --- /dev/null +++ b/k8s/deploy.amane-backend.yaml @@ -0,0 +1,64 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: amane-dcbot + namespace: amane-tanikaze + labels: + app: amane-dcbot +spec: + replicas: 1 + selector: + matchLabels: + app: amane-dcbot + template: + metadata: + labels: + app: amane-dcbot + spec: + containers: + - name: dcbot + image: 'gitea.konchin.com/services/amane-tanikaze-dcbot:latest' + env: + - name: DC_TOKEN + valueFrom: + secretKeyRef: + name: amane-dcbot + key: DC_TOKEN + - name: DC_CLIENTID + valueFrom: + secretKeyRef: + name: amane-dcbot + key: DC_CLIENTID + - name: ADMIN_ID + valueFrom: + secretKeyRef: + name: amane-dcbot + key: ADMIN_ID + - name: MONGODB_HOST + valueFrom: + secretKeyRef: + name: amane-dcbot + key: MONGODB_HOST + - name: MONGODB_USER + valueFrom: + secretKeyRef: + name: amane-dcbot + key: MONGODB_USER + - name: MONGODB_PASS + valueFrom: + secretKeyRef: + name: amane-dcbot + key: MONGODB_PASS + - name: MINIO_ACCESSKEY + valueFrom: + secretKeyRef: + name: amane-dcbot + key: MINIO_ACCESSKEY + - name: MINIO_SECRETKEY + valueFrom: + secretKeyRef: + name: amane-dcbot + key: MINIO_SECRETKEY + imagePullSecrets: + - name: regcred diff --git a/logger.ts b/logger.ts new file mode 100644 index 0000000..8c7f76b --- /dev/null +++ b/logger.ts @@ -0,0 +1,45 @@ +import {appendFileSync} from 'fs'; +import moment from 'moment-timezone'; + +import {config} from './config'; + +enum LogLevel{ + ERROR = 'ERROR', + WARNING = 'WARNING', + DEBUG = 'DEBUG', + LOG = 'LOG', + INFO = 'INFO', +} + +class Logger{ + constructor(readonly logFile?: string){ + this.debug('logger initialized'); + } + private currentTime(): string{ + return '[' + moment().tz('Asia/Taipei').format('YYYY/MM/DD hh:mm:ss') + ']'; + } + private writeLog(content: string, logLevel: LogLevel): void{ + const line = `${this.currentTime()} ${logLevel}: ${content}`; + console.log(line); + if(this.logFile !== undefined){ + appendFileSync(this.logFile, line + '\n'); + } + } + error(content: string): Error{ + this.writeLog(content, LogLevel.ERROR); return Error(content); + } + warning(content: string): string{ + this.writeLog(content, LogLevel.WARNING); return content; + } + debug(content: string): string{ + this.writeLog(content, LogLevel.DEBUG); return content; + } + log(content: string): string{ + this.writeLog(content, LogLevel.LOG); return content; + } + info(content: string): string{ + this.writeLog(content, LogLevel.INFO); return content; + } +} + +export const logger = new Logger(`${config.data}/${config.logger.logFile}`); \ No newline at end of file diff --git a/models/Alias.ts b/models/Alias.ts new file mode 100644 index 0000000..9760b7a --- /dev/null +++ b/models/Alias.ts @@ -0,0 +1,21 @@ +import {Schema, Types, model} from 'mongoose'; + +import {Image} from './Image'; +import {Guild} from './Guild'; + +export interface Alias{ + _id: Types.ObjectId; + guild: Guild; + text: string; + images: Image[]; +} + +const aliasSchema = new Schema({ + guild: {type: Schema.Types.ObjectId, ref: 'Guild', required: true}, + text: {type: String, required: true}, + images: {type: [{type: Schema.Types.ObjectId, ref: 'Image'}], default: []}, +}); + +export const aliasModel = model( + 'aliasModel', aliasSchema +); diff --git a/models/AutoroleMsg.ts b/models/AutoroleMsg.ts new file mode 100644 index 0000000..2f349c7 --- /dev/null +++ b/models/AutoroleMsg.ts @@ -0,0 +1,22 @@ +import {Schema, Types, model} from 'mongoose'; + +import {Guild} from './Guild'; + +export interface AutoroleMsg{ + _id: Types.ObjectId; + guild: Guild; + messageId: string; + emoji: string; + roleId: string; +} + +const autoroleMsgSchema = new Schema({ + guild: {type: Schema.Types.ObjectId, required: true}, + messageId: {type: String, required: true}, + emoji: {type: String, required: false}, + roleId: {type: String, required: true}, +}); + +export const autoroleMsgModel = model( + 'autoroleMsgModel', autoroleMsgSchema +); diff --git a/models/GiveawayMsg.ts b/models/GiveawayMsg.ts new file mode 100644 index 0000000..5c8b9f0 --- /dev/null +++ b/models/GiveawayMsg.ts @@ -0,0 +1,22 @@ +import {Schema, Types, model} from 'mongoose'; + +import {Guild} from './Guild'; + +export interface GiveawayMsg{ + _id: Types.ObjectId; + guild: Guild; + messageId: string; + authorId: string; + userIds: string[]; +} + +const giveawayMsgSchema = new Schema({ + guild: {type: Schema.Types.ObjectId, required: true}, + messageId: {type: String, required: true}, + authorId: {type: String, required: true}, + userIds: {type: [{type: String}], default: []} +}); + +export const giveawayMsgModel = model( + 'giveawayMsgModel', giveawayMsgSchema +); diff --git a/models/Guild.ts b/models/Guild.ts new file mode 100644 index 0000000..a4360b1 --- /dev/null +++ b/models/Guild.ts @@ -0,0 +1,23 @@ +import {Schema, Types, model} from 'mongoose'; + +import {GiveawayMsg} from './GiveawayMsg'; +import {AutoroleMsg} from './AutoroleMsg'; + +export interface Guild{ + _id: Types.ObjectId; + id: string; + name: string; + giveawayLogChannelId: string; + autoroleLogChannelId: string; +} + +const guildSchema = new Schema({ + id: {type: String, required: true}, + name: {type: String, required: true}, + giveawayLogChannelId: {type: String, required: false}, + autoroleLogChannelId: {type: String, required: false}, +}); + +export const guildModel = model( + 'guildModel', guildSchema +); diff --git a/models/Image.ts b/models/Image.ts new file mode 100644 index 0000000..427a35f --- /dev/null +++ b/models/Image.ts @@ -0,0 +1,14 @@ +import {Schema, Types, model} from 'mongoose'; + +export interface Image{ + _id: Types.ObjectId; // act as random generated filename + extension: string; +} + +const imageSchema = new Schema({ + extension: {type: String, required: true} +}); + +export const imageModel = model( + 'imageModel', imageSchema +); diff --git a/models/Token.ts b/models/Token.ts new file mode 100644 index 0000000..625051c --- /dev/null +++ b/models/Token.ts @@ -0,0 +1,18 @@ +import {Schema, Types, model} from 'mongoose'; + +export interface Token{ + _id: Types.ObjectId; + token: string; + guildId: string; + exp: number; +}; + +const tokenSchema = new Schema({ + token: {type: String, required: true}, + guildId: {type: String, required: true}, + exp: {type: Number, required: true} +}); + +export const tokenModel = model( + 'tokenModel', tokenSchema +); diff --git a/mongo.ts b/mongo.ts new file mode 100644 index 0000000..d658eb6 --- /dev/null +++ b/mongo.ts @@ -0,0 +1,45 @@ +import mongoose from 'mongoose'; + +import {config} from './config'; +import {guildModel} from './models/Guild'; +import {autoroleMsgModel} from './models/AutoroleMsg'; +import {giveawayMsgModel} from './models/GiveawayMsg'; +import {imageModel} from './models/Image'; +import {aliasModel} from './models/Alias'; + +async function resetMongo(): Promise { + try{ + await guildModel.deleteMany({}); + await autoroleMsgModel.deleteMany({}); + await giveawayMsgModel.deleteMany({}); + await imageModel.deleteMany({}); + await aliasModel.deleteMany({}); + }catch(err: unknown){ + throw new Error(`MongoDB reset failed. ${err}`); + } +} + +async function initializeMongo(): Promise { + try{ + if(config.mongodb.reset === 'true') await resetMongo(); + }catch(err: unknown){ + throw new Error(`MongoDB initialize failed. ${err}`); + } +} + +export async function runMongo(): Promise { + try { + mongoose.set('strictQuery', false); + const auth: string = `${config.mongodb.user}:${config.mongodb.pass}`; + const server: string = `${config.mongodb.host}:${config.mongodb.port}` + const uri: string = `mongodb://${auth}@${server}/${config.mongodb.db}`; + await mongoose.connect(uri); + } catch(err: unknown) { + throw new Error(`MongoDB connection failed. ${err}`); + } + try { + await initializeMongo(); + } catch(err: unknown) { + throw new Error(`Initialize MongoDB data failed. ${err}`); + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a6237cc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1312 @@ +{ + "name": "tanikaze-amane", + "version": "1.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tanikaze-amane", + "version": "1.0.1", + "license": "ISC", + "dependencies": { + "@types/node": "^20.3.2", + "axios": "^1.6.2", + "discord.js": "^14.11.0", + "dotenv": "^16.3.1", + "fs": "^0.0.1-security", + "minio": "^8.0.1", + "moment-timezone": "^0.5.43", + "mongodb": "^5.6.0", + "mongoose": "^8.0.1", + "path": "^0.12.7", + "typescript": "^5.1.3" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", + "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", + "dependencies": { + "@discordjs/formatters": "^0.3.3", + "@discordjs/util": "^1.0.2", + "@sapphire/shapeshift": "^3.9.3", + "discord-api-types": "0.37.61", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", + "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", + "dependencies": { + "discord-api-types": "0.37.61" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.1.0.tgz", + "integrity": "sha512-5gFWFkZX2JCFSRzs8ltx8bWmyVi0wPMk6pBa9KGIQSDPMmrP+uOrZ9j9HOwvmVWGe+LmZ5Bov0jMnQd6/jVReg==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.5.1", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "magic-bytes.js": "^1.5.0", + "tslib": "^2.6.2", + "undici": "5.27.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/util": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", + "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", + "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.9", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "tslib": "^2.6.2", + "ws": "^8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.3.tgz", + "integrity": "sha512-WzKJSwDYloSkHoBbE8rkRW8UNKJiSRJ/P8NqJ5iVq7U2Yr/kriIBx2hW+wj2Z5e5EnXL1hgYomgaFsdK6b+zqQ==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz", + "integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", + "optional": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "license": "MIT" + }, + "node_modules/bson": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", + "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", + "engines": { + "node": ">=14.20.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/discord.js": { + "version": "14.14.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", + "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.3.3", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@discordjs/ws": "^1.0.2", + "@sapphire/snowflake": "3.5.1", + "@types/ws": "8.5.9", + "discord-api-types": "0.37.61", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "2.6.2", + "undici": "5.27.2", + "ws": "8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/magic-bytes.js": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.5.0.tgz", + "integrity": "sha512-wJkXvutRbNWcc37tt5j1HyOK1nosspdh3dj6LUYYAvF6JYNqs53IfRvK9oEpcwiDA1NdoIi64yAMfdivPeVAyw==" + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minio": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.1.tgz", + "integrity": "sha512-FzDO6yGnqLtm8sp3mXafWtiRUOslJSSg/aI0v9YbN5vjw5KLoODKAROCyi766NIvTSxcfHBrbhCSGk1A+MOzDg==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.4", + "block-stream2": "^2.1.0", + "browser-or-node": "^2.1.1", + "buffer-crc32": "^1.0.0", + "eventemitter3": "^5.0.1", + "fast-xml-parser": "^4.2.2", + "ipaddr.js": "^2.0.1", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "query-string": "^7.1.3", + "stream-json": "^1.8.0", + "through2": "^4.0.2", + "web-encoding": "^1.1.5", + "xml2js": "^0.5.0" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mongodb": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.1.tgz", + "integrity": "sha512-NBGA8AfJxGPeB12F73xXwozt8ZpeIPmCUeWRwl9xejozTXFes/3zaep9zhzs1B/nKKsw4P3I4iPfXl3K7s6g+Q==", + "dependencies": { + "bson": "^5.5.0", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=14.20.1" + }, + "optionalDependencies": { + "@mongodb-js/saslprep": "^1.1.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.0.0", + "kerberos": "^1.0.0 || ^2.0.0", + "mongodb-client-encryption": ">=2.3.0 <3", + "snappy": "^7.2.2" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.1.tgz", + "integrity": "sha512-O3TJrtLCt4H1eGf2HoHGcnOcCTWloQkpmIP3hA9olybX3OX2KUjdIIq135HD5paGjZEDJYKn9fw4eH5N477zqQ==", + "dependencies": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.2.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", + "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^2.6.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause" + }, + "node_modules/stream-json": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", + "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", + "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "license": "MIT", + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, + "node_modules/web-encoding/node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..262f5a9 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "tanikaze-amane", + "version": "1.0.1", + "main": "index.ts", + "scripts": { + "test": "ts-node index.ts", + "build": "npx tsc --build", + "clean": "npx tsc --build --clean" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/konchinshih/Tanikaze-Amane.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/konchinshih/Tanikaze-Amane/issues" + }, + "homepage": "https://github.com/konchinshih/Tanikaze-Amane#readme", + "dependencies": { + "@types/node": "^20.3.2", + "axios": "^1.6.2", + "discord.js": "^14.11.0", + "dotenv": "^16.3.1", + "fs": "^0.0.1-security", + "minio": "^8.0.1", + "moment-timezone": "^0.5.43", + "mongodb": "^5.6.0", + "mongoose": "^8.0.1", + "path": "^0.12.7", + "typescript": "^5.1.3" + }, + "description": "" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..755c1e1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonJS", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}