mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
cleanup cleanup cleanup on isle dd (#2792)
* cleanup cleanup cleanup on isle dd * fix: rest manager import in test
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ""
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug** A clear and concise description of what the bug is.
|
||||
@@ -11,10 +11,10 @@ assignees: ""
|
||||
**To Reproduce** Write a small mod.ts example to replicate the behavior.
|
||||
|
||||
```ts
|
||||
import { createBot, startBot } from "https://deno.land/x/discordeno/mod.ts";
|
||||
import { createBot, startBot } from 'https://deno.land/x/discordeno/mod.ts'
|
||||
|
||||
const token = "DO NOT PUT TOKEN HERE!!!";
|
||||
const botId = BigInt(atob(TOKEN.split(".")[0]));
|
||||
const token = 'DO NOT PUT TOKEN HERE!!!'
|
||||
const botId = BigInt(atob(TOKEN.split('.')[0]))
|
||||
|
||||
const bot = createBot({
|
||||
token,
|
||||
@@ -23,9 +23,9 @@ const bot = createBot({
|
||||
// ADD EVENTS NEEDED TO SHOW THE BUG HERE
|
||||
},
|
||||
intents: 0, // ADD INTENTS NEEDED HERE FOR YOUR TEST IF NECESSARY
|
||||
});
|
||||
})
|
||||
|
||||
await startBot(bot);
|
||||
await startBot(bot)
|
||||
```
|
||||
|
||||
**Expected behavior** A clear and concise description of what you expected to happen.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
title: ''
|
||||
labels: feat
|
||||
assignees: ""
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# This configuration file was automatically generated by Gitpod.
|
||||
# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
|
||||
# and commit this file to your remote git repository to share the goodness with others.
|
||||
|
||||
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
|
||||
|
||||
tasks:
|
||||
- init: yarn install && yarn run build && yarn run lint && yarn run build:doc
|
||||
command: yarn run dev
|
||||
|
||||
|
||||
+77
-78
@@ -1,64 +1,66 @@
|
||||
await import(`https://raw.githubusercontent.com/discordeno/discordeno/benchies/benchmarksResult/data.js`);
|
||||
const commitSha = await Deno.readTextFile("./sha");
|
||||
const results = JSON.parse(await Deno.readTextFile("./data.json"));
|
||||
await import(`https://raw.githubusercontent.com/discordeno/discordeno/benchies/benchmarksResult/data.js`)
|
||||
const commitSha = await Deno.readTextFile('./sha')
|
||||
const results = JSON.parse(await Deno.readTextFile('./data.json'))
|
||||
|
||||
interface BenchmarksData {
|
||||
commit: {
|
||||
author: { email: string; name: string; username: string };
|
||||
committer: { email: string; name: string; username: string };
|
||||
distinct: boolean;
|
||||
id: string;
|
||||
message: string;
|
||||
timestamp: string;
|
||||
tree_id: string;
|
||||
url: string;
|
||||
};
|
||||
date: number;
|
||||
tool: string;
|
||||
benches: Array<{ name: string; value: number; unit: string; range: string }>;
|
||||
author: { email: string; name: string; username: string }
|
||||
committer: { email: string; name: string; username: string }
|
||||
distinct: boolean
|
||||
id: string
|
||||
message: string
|
||||
timestamp: string
|
||||
tree_id: string
|
||||
url: string
|
||||
}
|
||||
date: number
|
||||
tool: string
|
||||
benches: Array<{ name: string; value: number; unit: string; range: string }>
|
||||
}
|
||||
|
||||
interface CompareTable {
|
||||
[index: string]: {
|
||||
current: { name: string; value: number; unit: string; range: string } | {
|
||||
name?: string;
|
||||
value?: number;
|
||||
unit?: string;
|
||||
range?: string;
|
||||
};
|
||||
previous: { name: string; value: number; unit: string; range: string } | {
|
||||
name?: string;
|
||||
value?: number;
|
||||
unit?: string;
|
||||
range?: string;
|
||||
};
|
||||
};
|
||||
current:
|
||||
| { name: string; value: number; unit: string; range: string }
|
||||
| {
|
||||
name?: string
|
||||
value?: number
|
||||
unit?: string
|
||||
range?: string
|
||||
}
|
||||
previous:
|
||||
| { name: string; value: number; unit: string; range: string }
|
||||
| {
|
||||
name?: string
|
||||
value?: number
|
||||
unit?: string
|
||||
range?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const benchmarks = results.entries.Benchmark.slice(-2) as BenchmarksData[];
|
||||
const latestHeadBenchmarks = benchmarks.length === 2 ? benchmarks[1] : benchmarks[0];
|
||||
const lastHeadBenchmarks = benchmarks.length === 2 ? benchmarks[0] : undefined;
|
||||
const benchmarks = results.entries.Benchmark.slice(-2) as BenchmarksData[]
|
||||
const latestHeadBenchmarks = benchmarks.length === 2 ? benchmarks[1] : benchmarks[0]
|
||||
const lastHeadBenchmarks = benchmarks.length === 2 ? benchmarks[0] : undefined
|
||||
// @ts-expect-error
|
||||
const latestBaseBenchmarks = JSON.parse(JSON.stringify(window.BENCHMARK_DATA.entries.Benchmark)).slice(
|
||||
-1,
|
||||
)[0] as BenchmarksData;
|
||||
const latestBaseBenchmarks = JSON.parse(JSON.stringify(window.BENCHMARK_DATA.entries.Benchmark)).slice(-1)[0] as BenchmarksData
|
||||
|
||||
const compareWithHead: CompareTable = {};
|
||||
const compareWithBase: CompareTable = {};
|
||||
const compareWithHead: CompareTable = {}
|
||||
const compareWithBase: CompareTable = {}
|
||||
|
||||
if (lastHeadBenchmarks) {
|
||||
for (const benchmark of lastHeadBenchmarks.benches) {
|
||||
compareWithHead[benchmark.name] = {
|
||||
previous: benchmark,
|
||||
current: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const benchmark of latestBaseBenchmarks.benches) {
|
||||
compareWithBase[benchmark.name] = {
|
||||
previous: benchmark,
|
||||
current: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const benchmark of latestHeadBenchmarks.benches) {
|
||||
compareWithBase[benchmark.name] = {
|
||||
@@ -66,51 +68,48 @@ for (const benchmark of latestHeadBenchmarks.benches) {
|
||||
previous: {},
|
||||
...compareWithBase[benchmark.name],
|
||||
current: benchmark,
|
||||
};
|
||||
}
|
||||
compareWithHead[benchmark.name] = {
|
||||
// @ts-expect-error
|
||||
previous: {},
|
||||
...compareWithHead[benchmark.name],
|
||||
current: benchmark,
|
||||
};
|
||||
}
|
||||
|
||||
let message = "";
|
||||
|
||||
const compareTableInfo = [{ name: "last head", commit: lastHeadBenchmarks ? lastHeadBenchmarks.commit.id : "" }, {
|
||||
name: "base",
|
||||
commit: latestBaseBenchmarks.commit.id,
|
||||
}];
|
||||
for (const benchmarkType of ["Performance", "Memory"]) {
|
||||
message += `# ${benchmarkType} Benchmark\n\n`;
|
||||
for (const [index, compare] of [compareWithHead, compareWithBase].entries()) {
|
||||
message += `## Compared with ${compareTableInfo[index].name}\n`;
|
||||
message += "<details><summary>Detail results of benchmarks</summary>\n\n";
|
||||
message += `| Benchmark suite | Current: ${latestHeadBenchmarks.commit.id} | Previous: ${
|
||||
compareTableInfo[index].commit
|
||||
} | Ratio |\n | -| -| -| -|\n`;
|
||||
for (
|
||||
const field of Object.keys(compare).filter((key) =>
|
||||
benchmarkType === "Performance" ? !key.startsWith("[Cache Plugin]") : key.startsWith("[Cache Plugin]")
|
||||
)
|
||||
) {
|
||||
message += `| \`${field}\` | ${compare[field].current.value ? `\`${compare[field].current.value}\`` : ""} ${
|
||||
compare[field].current.unit ?? ""
|
||||
} ${compare[field].current.range ? `(\`${compare[field].current.range ?? ""}\`)` : ""} | ${
|
||||
compare[field].previous.value ? `\`${compare[field].previous.value}\`` : ""
|
||||
} ${compare[field].previous.unit ?? ""} ${
|
||||
compare[field].previous.range ? `(\`${compare[field].previous.range ?? ""}\`)` : ""
|
||||
} | ${
|
||||
compare[field].previous.value && compare[field].current.value
|
||||
? `\`${
|
||||
// @ts-expect-error
|
||||
Math.round((parseFloat(compare[field].previous.value) / parseFloat(compare[field].current.value)) * 100) /
|
||||
100}\``
|
||||
: ""
|
||||
} |\n`;
|
||||
}
|
||||
message += "</details>\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
console.log(message.replaceAll("`", "\\`"));
|
||||
let message = ''
|
||||
|
||||
const compareTableInfo = [
|
||||
{ name: 'last head', commit: lastHeadBenchmarks ? lastHeadBenchmarks.commit.id : '' },
|
||||
{
|
||||
name: 'base',
|
||||
commit: latestBaseBenchmarks.commit.id,
|
||||
},
|
||||
]
|
||||
for (const benchmarkType of ['Performance', 'Memory']) {
|
||||
message += `# ${benchmarkType} Benchmark\n\n`
|
||||
for (const [index, compare] of [compareWithHead, compareWithBase].entries()) {
|
||||
message += `## Compared with ${compareTableInfo[index].name}\n`
|
||||
message += '<details><summary>Detail results of benchmarks</summary>\n\n'
|
||||
message += `| Benchmark suite | Current: ${latestHeadBenchmarks.commit.id} | Previous: ${compareTableInfo[index].commit} | Ratio |\n | -| -| -| -|\n`
|
||||
for (const field of Object.keys(compare).filter((key) =>
|
||||
benchmarkType === 'Performance' ? !key.startsWith('[Cache Plugin]') : key.startsWith('[Cache Plugin]'),
|
||||
)) {
|
||||
message += `| \`${field}\` | ${compare[field].current.value ? `\`${compare[field].current.value}\`` : ''} ${
|
||||
compare[field].current.unit ?? ''
|
||||
} ${compare[field].current.range ? `(\`${compare[field].current.range ?? ''}\`)` : ''} | ${
|
||||
compare[field].previous.value ? `\`${compare[field].previous.value}\`` : ''
|
||||
} ${compare[field].previous.unit ?? ''} ${compare[field].previous.range ? `(\`${compare[field].previous.range ?? ''}\`)` : ''} | ${
|
||||
compare[field].previous.value && compare[field].current.value
|
||||
? `\`${
|
||||
// @ts-expect-error
|
||||
Math.round((parseFloat(compare[field].previous.value) / parseFloat(compare[field].current.value)) * 100) / 100
|
||||
}\``
|
||||
: ''
|
||||
} |\n`
|
||||
}
|
||||
message += '</details>\n\n'
|
||||
}
|
||||
}
|
||||
|
||||
console.log(message.replaceAll('`', '\\`'))
|
||||
|
||||
+18
-16
@@ -1,28 +1,30 @@
|
||||
import { memoryBenchmarks } from "https://raw.githubusercontent.com/discordeno/benchmarks/main/index.ts";
|
||||
import { createBot } from '../mod.ts.js';
|
||||
import { enableCachePlugin } from '../plugins/mod.ts.js';
|
||||
import { memoryBenchmarks } from 'https://raw.githubusercontent.com/discordeno/benchmarks/main/index.ts'
|
||||
import { createBot } from '../mod.ts.js'
|
||||
import { enableCachePlugin } from '../plugins/mod.ts.js'
|
||||
|
||||
const results = await memoryBenchmarks(() =>
|
||||
enableCachePlugin(createBot({
|
||||
token: " ",
|
||||
botId: 0n,
|
||||
}))
|
||||
);
|
||||
enableCachePlugin(
|
||||
createBot({
|
||||
token: ' ',
|
||||
botId: 0n,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const output: Array<{
|
||||
name: string;
|
||||
value: number;
|
||||
range: string;
|
||||
unit: string;
|
||||
}> = JSON.parse(await Deno.readTextFile("output.txt"));
|
||||
name: string
|
||||
value: number
|
||||
range: string
|
||||
unit: string
|
||||
}> = JSON.parse(await Deno.readTextFile('output.txt'))
|
||||
|
||||
for (const resultKey of Object.keys(results.Cached) as Array<keyof typeof results.Cached>) {
|
||||
output.push({
|
||||
name: `[Cache Plugin] ${resultKey.toString()}`,
|
||||
value: results.Cached[resultKey].value,
|
||||
range: `${results.Cached[resultKey].min} … ${results.Cached[resultKey].max}`,
|
||||
unit: "MB",
|
||||
});
|
||||
unit: 'MB',
|
||||
})
|
||||
}
|
||||
|
||||
Deno.writeTextFile("output.txt", JSON.stringify(output, undefined, 2));
|
||||
Deno.writeTextFile('output.txt', JSON.stringify(output, undefined, 2))
|
||||
|
||||
+19
-19
@@ -1,31 +1,31 @@
|
||||
// Just a constant sysbench sorce to compare against
|
||||
const baselineSysbenchScore = 2000;
|
||||
let sysbenchScore = 2000;
|
||||
const baselineSysbenchScore = 2000
|
||||
let sysbenchScore = 2000
|
||||
|
||||
try {
|
||||
const { stdout } = await Deno.spawn("sysbench", { args: ["cpu", "run"] });
|
||||
const textout = new TextDecoder().decode(stdout);
|
||||
sysbenchScore = parseFloat(textout.match(/\s+events per second:\s+(.+)/)![1]);
|
||||
const { stdout } = await Deno.spawn('sysbench', { args: ['cpu', 'run'] })
|
||||
const textout = new TextDecoder().decode(stdout)
|
||||
sysbenchScore = parseFloat(textout.match(/\s+events per second:\s+(.+)/)![1])
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
|
||||
const output = await Deno.readTextFile("output.txt");
|
||||
const lines = output.split(/\r?\n/g);
|
||||
const output = await Deno.readTextFile('output.txt')
|
||||
const lines = output.split(/\r?\n/g)
|
||||
|
||||
const ret = [];
|
||||
const ret = []
|
||||
|
||||
const unitMultiplier = {
|
||||
"s": 1000 * 1000 * 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
"ms": 1000 * 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
"µs": 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
"ns": 1 * (sysbenchScore / baselineSysbenchScore),
|
||||
"ps": 0.1 * (sysbenchScore / baselineSysbenchScore),
|
||||
};
|
||||
s: 1000 * 1000 * 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
ms: 1000 * 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
µs: 1000 * (sysbenchScore / baselineSysbenchScore),
|
||||
ns: 1 * (sysbenchScore / baselineSysbenchScore),
|
||||
ps: 0.1 * (sysbenchScore / baselineSysbenchScore),
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^(.+)\s+([0-9.]+) (.s)\/iter\s+\((.+) (.s) … (.+) (.s)\)(.+)$/);
|
||||
if (m === null) continue;
|
||||
const m = line.match(/^(.+)\s+([0-9.]+) (.s)\/iter\s+\((.+) (.s) … (.+) (.s)\)(.+)$/)
|
||||
if (m === null) continue
|
||||
|
||||
ret.push({
|
||||
name: m[1].trim(),
|
||||
@@ -33,8 +33,8 @@ for (const line of lines) {
|
||||
range: `${Math.round(parseFloat(m[4]) * unitMultiplier[m[5] as keyof typeof unitMultiplier] * 100) / 100} … ${
|
||||
Math.round(parseFloat(m[6]) * unitMultiplier[m[7] as keyof typeof unitMultiplier] * 100) / 100
|
||||
}`,
|
||||
unit: "ns/iter",
|
||||
});
|
||||
unit: 'ns/iter',
|
||||
})
|
||||
}
|
||||
|
||||
await Deno.writeTextFile("output.txt", JSON.stringify(ret, undefined, 2));
|
||||
await Deno.writeTextFile('output.txt', JSON.stringify(ret, undefined, 2))
|
||||
|
||||
+12
-14
@@ -1,7 +1,5 @@
|
||||
import { configs } from './configs.ts.js';
|
||||
import type {
|
||||
BotWithCache,
|
||||
BotWithHelpersPlugin} from './deps.ts.js';
|
||||
import { configs } from './configs.ts.js'
|
||||
import type { BotWithCache, BotWithHelpersPlugin } from './deps.ts.js'
|
||||
import {
|
||||
Collection,
|
||||
createBot,
|
||||
@@ -10,8 +8,8 @@ import {
|
||||
enableHelpersPlugin,
|
||||
enablePermissionsPlugin,
|
||||
GatewayIntents,
|
||||
} from './deps.ts.js';
|
||||
import type { Command } from './src/types/commands.ts.js';
|
||||
} from './deps.ts.js'
|
||||
import type { Command } from './src/types/commands.ts.js'
|
||||
|
||||
// MAKE THE BASIC BOT OBJECT
|
||||
const bot = createBot({
|
||||
@@ -19,19 +17,19 @@ const bot = createBot({
|
||||
botId: configs.botId,
|
||||
intents: GatewayIntents.Guilds,
|
||||
events: {},
|
||||
});
|
||||
})
|
||||
|
||||
// ENABLE ALL THE PLUGINS THAT WILL HELP MAKE IT EASIER TO CODE YOUR BOT
|
||||
enableHelpersPlugin(bot);
|
||||
enableCachePlugin(bot);
|
||||
enableCacheSweepers(bot as BotWithCache);
|
||||
enablePermissionsPlugin(bot as BotWithCache);
|
||||
enableHelpersPlugin(bot)
|
||||
enableCachePlugin(bot)
|
||||
enableCacheSweepers(bot as BotWithCache)
|
||||
enablePermissionsPlugin(bot as BotWithCache)
|
||||
|
||||
export interface BotClient extends BotWithCache<BotWithHelpersPlugin> {
|
||||
commands: Collection<string, Command>;
|
||||
commands: Collection<string, Command>
|
||||
}
|
||||
|
||||
// THIS IS THE BOT YOU WANT TO USE EVERYWHERE IN YOUR CODE! IT HAS EVERYTHING BUILT INTO IT!
|
||||
export const Bot = bot as BotClient;
|
||||
export const Bot = bot as BotClient
|
||||
// PREPARE COMMANDS HOLDER
|
||||
Bot.commands = new Collection();
|
||||
Bot.commands = new Collection()
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { dotEnvConfig } from './deps.ts.js';
|
||||
import { dotEnvConfig } from './deps.ts.js'
|
||||
|
||||
// Get the .env file that the user should have created, and get the token
|
||||
const env = dotEnvConfig({ export: true, path: "./.env" });
|
||||
const token = env.BOT_TOKEN || "";
|
||||
const env = dotEnvConfig({ export: true, path: './.env' })
|
||||
const token = env.BOT_TOKEN || ''
|
||||
|
||||
export interface Config {
|
||||
token: string;
|
||||
botId: bigint;
|
||||
token: string
|
||||
botId: bigint
|
||||
}
|
||||
|
||||
export const configs = {
|
||||
/** Get token from ENV variable */
|
||||
token,
|
||||
/** Get the BotId from the token */
|
||||
botId: BigInt(atob(token.split(".")[0])),
|
||||
botId: BigInt(atob(token.split('.')[0])),
|
||||
/** The server id where you develop your bot and want dev commands created. */
|
||||
devGuildId: BigInt(env.DEV_GUILD_ID!),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/mod.ts";
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/plugins/mod.ts";
|
||||
export * from 'https://deno.land/x/discordeno@17.0.0/mod.ts'
|
||||
export * from 'https://deno.land/x/discordeno@17.0.0/plugins/mod.ts'
|
||||
|
||||
// Terminal Colors!
|
||||
export * from "https://deno.land/std@0.117.0/fmt/colors.ts";
|
||||
export * from 'https://deno.land/std@0.117.0/fmt/colors.ts'
|
||||
// Get data from .env files
|
||||
export { config as dotEnvConfig } from "https://deno.land/x/dotenv@v3.1.0/mod.ts";
|
||||
export { config as dotEnvConfig } from 'https://deno.land/x/dotenv@v3.1.0/mod.ts'
|
||||
// Database, thx Tri!
|
||||
export { decode as KwikDecode, encode as KwikEncode, Kwik } from "https://deno.land/x/kwik@v1.3.1/mod.ts";
|
||||
export { decode as KwikDecode, encode as KwikEncode, Kwik } from 'https://deno.land/x/kwik@v1.3.1/mod.ts'
|
||||
|
||||
+13
-13
@@ -1,25 +1,25 @@
|
||||
import { startBot } from './deps.ts.js';
|
||||
import log from './src/utils/logger.ts.js';
|
||||
import { fileLoader, importDirectory } from './src/utils/loader.ts.js';
|
||||
import { updateApplicationCommands } from './src/utils/updateCommands.ts.js';
|
||||
import { startBot } from './deps.ts.js'
|
||||
import log from './src/utils/logger.ts.js'
|
||||
import { fileLoader, importDirectory } from './src/utils/loader.ts.js'
|
||||
import { updateApplicationCommands } from './src/utils/updateCommands.ts.js'
|
||||
// setup db
|
||||
import './src/database/mod.ts.js';
|
||||
import { Bot } from './bot.ts.js';
|
||||
import './src/database/mod.ts.js'
|
||||
import { Bot } from './bot.ts.js'
|
||||
|
||||
log.info("Starting bot...");
|
||||
log.info('Starting bot...')
|
||||
|
||||
// Forces deno to read all the files which will fill the commands/inhibitors cache etc.
|
||||
await Promise.all(
|
||||
[
|
||||
"./src/commands",
|
||||
"./src/events",
|
||||
'./src/commands',
|
||||
'./src/events',
|
||||
// "./src/tasks",
|
||||
].map((path) => importDirectory(Deno.realPathSync(path))),
|
||||
);
|
||||
await fileLoader();
|
||||
)
|
||||
await fileLoader()
|
||||
|
||||
// UPDATES YOUR COMMANDS TO LATEST COMMANDS
|
||||
await updateApplicationCommands();
|
||||
await updateApplicationCommands()
|
||||
|
||||
// STARTS THE CONNECTION TO DISCORD
|
||||
await startBot(Bot);
|
||||
await startBot(Bot)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Bot } from '../../bot.ts.js';
|
||||
import type { Command } from '../types/commands.ts.js';
|
||||
import { Bot } from '../../bot.ts.js'
|
||||
import type { Command } from '../types/commands.ts.js'
|
||||
|
||||
export function createCommand(command: Command) {
|
||||
Bot.commands.set(command.name, command);
|
||||
Bot.commands.set(command.name, command)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js';
|
||||
import { snowflakeToTimestamp } from '../utils/helpers.ts.js';
|
||||
import { createCommand } from './mod.ts.js';
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js'
|
||||
import { snowflakeToTimestamp } from '../utils/helpers.ts.js'
|
||||
import { createCommand } from './mod.ts.js'
|
||||
|
||||
createCommand({
|
||||
name: "ping",
|
||||
description: "Ping the Bot!",
|
||||
name: 'ping',
|
||||
description: 'Ping the Bot!',
|
||||
type: ApplicationCommandTypes.ChatInput,
|
||||
execute: async (Bot, interaction) => {
|
||||
const ping = Date.now() - snowflakeToTimestamp(interaction.id);
|
||||
await Bot.helpers.sendInteractionResponse(
|
||||
interaction.id,
|
||||
interaction.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `🏓 Pong! ${ping}ms`,
|
||||
},
|
||||
const ping = Date.now() - snowflakeToTimestamp(interaction.id)
|
||||
await Bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `🏓 Pong! ${ping}ms`,
|
||||
},
|
||||
);
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { Kwik, KwikDecode, KwikEncode } from '../../deps.ts.js';
|
||||
import { logger } from '../utils/logger.ts.js';
|
||||
import { Kwik, KwikDecode, KwikEncode } from '../../deps.ts.js'
|
||||
import { logger } from '../utils/logger.ts.js'
|
||||
|
||||
const log = logger({ name: "DB Manager" });
|
||||
const log = logger({ name: 'DB Manager' })
|
||||
|
||||
log.info("Initializing Database");
|
||||
log.info('Initializing Database')
|
||||
|
||||
const kwik = new Kwik();
|
||||
const kwik = new Kwik()
|
||||
|
||||
// Add BigInt Support
|
||||
kwik.msgpackExtensionCodec.register({
|
||||
type: 0,
|
||||
encode: (object: unknown): Uint8Array | null => {
|
||||
if (typeof object === "bigint") {
|
||||
if (typeof object === 'bigint') {
|
||||
if (object <= Number.MAX_SAFE_INTEGER && object >= Number.MIN_SAFE_INTEGER) {
|
||||
return KwikEncode(parseInt(object.toString(), 10), {});
|
||||
return KwikEncode(parseInt(object.toString(), 10), {})
|
||||
} else {
|
||||
return KwikEncode(object.toString(), {});
|
||||
return KwikEncode(object.toString(), {})
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
},
|
||||
decode: (data: Uint8Array) => {
|
||||
return BigInt(KwikDecode(data, {}) as string);
|
||||
return BigInt(KwikDecode(data, {}) as string)
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
// Initialize the Database
|
||||
await kwik.init();
|
||||
await kwik.init()
|
||||
|
||||
log.info("Database Initialized!");
|
||||
log.info('Database Initialized!')
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Bot } from '../../bot.ts.js';
|
||||
import { InteractionTypes } from '../../deps.ts.js';
|
||||
import log from '../utils/logger.ts.js';
|
||||
import { Bot } from '../../bot.ts.js'
|
||||
import { InteractionTypes } from '../../deps.ts.js'
|
||||
import log from '../utils/logger.ts.js'
|
||||
|
||||
Bot.events.interactionCreate = (_, interaction) => {
|
||||
if (!interaction.data) return;
|
||||
if (!interaction.data) return
|
||||
|
||||
switch (interaction.type) {
|
||||
case InteractionTypes.ApplicationCommand:
|
||||
log.info(
|
||||
`[Application Command] ${interaction.data.name} command executed.`,
|
||||
);
|
||||
Bot.commands.get(interaction.data.name!)?.execute(Bot, interaction);
|
||||
break;
|
||||
log.info(`[Application Command] ${interaction.data.name} command executed.`)
|
||||
Bot.commands.get(interaction.data.name!)?.execute(Bot, interaction)
|
||||
break
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Bot } from '../../bot.ts.js';
|
||||
import log from '../utils/logger.ts.js';
|
||||
import { Bot } from '../../bot.ts.js'
|
||||
import log from '../utils/logger.ts.js'
|
||||
|
||||
Bot.events.ready = (_, payload) => {
|
||||
log.info(`[READY] Shard ID ${payload.shardId} of ${Bot.gateway.lastShardId + 1} shards is ready!`);
|
||||
log.info(`[READY] Shard ID ${payload.shardId} of ${Bot.gateway.lastShardId + 1} shards is ready!`)
|
||||
|
||||
if (payload.shardId === Bot.gateway.lastShardId) {
|
||||
botFullyReady();
|
||||
botFullyReady()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This function lets you run custom code when all your bot's shards are online.
|
||||
function botFullyReady() {
|
||||
// DO STUFF YOU WANT HERE ONCE BOT IS FULLY ONLINE.
|
||||
log.info("[READY] Bot is fully online.");
|
||||
log.info('[READY] Bot is fully online.')
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { BotClient } from '../../bot.ts.js';
|
||||
import type { ApplicationCommandOption, ApplicationCommandTypes, Interaction } from '../../deps.ts.js';
|
||||
import type { BotClient } from '../../bot.ts.js'
|
||||
import type { ApplicationCommandOption, ApplicationCommandTypes, Interaction } from '../../deps.ts.js'
|
||||
|
||||
export interface Command {
|
||||
/** The name of this command. */
|
||||
name: string;
|
||||
name: string
|
||||
/** What does this command do? */
|
||||
description: string;
|
||||
description: string
|
||||
/** The type of command this is. */
|
||||
type: ApplicationCommandTypes;
|
||||
type: ApplicationCommandTypes
|
||||
/** Whether or not this command is for the dev server only. */
|
||||
devOnly?: boolean;
|
||||
devOnly?: boolean
|
||||
/** The options for this command */
|
||||
options?: ApplicationCommandOption[];
|
||||
options?: ApplicationCommandOption[]
|
||||
/** This will be executed when the command is run. */
|
||||
execute: (bot: BotClient, interaction: Interaction) => unknown;
|
||||
execute: (bot: BotClient, interaction: Interaction) => unknown
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file will export all of the types in this directory.
|
||||
|
||||
export * from './commands.ts.js';
|
||||
export * from './commands.ts.js'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export function snowflakeToTimestamp(id: bigint) {
|
||||
return Number(id / 4194304n + 1420070400000n);
|
||||
return Number(id / 4194304n + 1420070400000n)
|
||||
}
|
||||
|
||||
@@ -1,47 +1,40 @@
|
||||
import log from './logger.ts.js';
|
||||
import log from './logger.ts.js'
|
||||
|
||||
// Very important to make sure files are reloaded properly
|
||||
let uniqueFilePathCounter = 0;
|
||||
let paths: string[] = [];
|
||||
let uniqueFilePathCounter = 0
|
||||
let paths: string[] = []
|
||||
|
||||
/** This function allows reading all files in a folder. Useful for loading/reloading commands, monitors etc */
|
||||
export async function importDirectory(path: string) {
|
||||
path = path.replaceAll("\\", "/");
|
||||
const files = Deno.readDirSync(Deno.realPathSync(path));
|
||||
const folder = path.substring(path.indexOf("/src/") + 5);
|
||||
path = path.replaceAll('\\', '/')
|
||||
const files = Deno.readDirSync(Deno.realPathSync(path))
|
||||
const folder = path.substring(path.indexOf('/src/') + 5)
|
||||
|
||||
if (!folder.includes("/")) log.info(`Loading ${folder}...`);
|
||||
if (!folder.includes('/')) log.info(`Loading ${folder}...`)
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.name) continue;
|
||||
if (!file.name) continue
|
||||
|
||||
const currentPath = `${path}/${file.name}`;
|
||||
const currentPath = `${path}/${file.name}`
|
||||
if (file.isFile) {
|
||||
if (!currentPath.endsWith(".ts")) continue;
|
||||
if (!currentPath.endsWith('.ts')) continue
|
||||
paths.push(
|
||||
`import "${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf("/"))}/${
|
||||
currentPath.substring(
|
||||
currentPath.indexOf("src/"),
|
||||
)
|
||||
}#${uniqueFilePathCounter}";`,
|
||||
);
|
||||
continue;
|
||||
`import "${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf('/'))}/${currentPath.substring(
|
||||
currentPath.indexOf('src/'),
|
||||
)}#${uniqueFilePathCounter}";`,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
await importDirectory(currentPath);
|
||||
await importDirectory(currentPath)
|
||||
}
|
||||
|
||||
uniqueFilePathCounter++;
|
||||
uniqueFilePathCounter++
|
||||
}
|
||||
|
||||
/** Imports all everything in fileloader.ts */
|
||||
export async function fileLoader() {
|
||||
await Deno.writeTextFile(
|
||||
"fileloader.ts",
|
||||
paths.join("\n").replaceAll("\\", "/"),
|
||||
);
|
||||
await import(
|
||||
`${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf("/"))}/fileloader.ts#${uniqueFilePathCounter}`
|
||||
);
|
||||
paths = [];
|
||||
await Deno.writeTextFile('fileloader.ts', paths.join('\n').replaceAll('\\', '/'))
|
||||
await import(`${Deno.mainModule.substring(0, Deno.mainModule.lastIndexOf('/'))}/fileloader.ts#${uniqueFilePathCounter}`)
|
||||
paths = []
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js';
|
||||
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js'
|
||||
|
||||
export enum LogLevels {
|
||||
Debug,
|
||||
@@ -10,81 +10,81 @@ export enum LogLevels {
|
||||
}
|
||||
|
||||
const prefixes = new Map<LogLevels, string>([
|
||||
[LogLevels.Debug, "DEBUG"],
|
||||
[LogLevels.Info, "INFO"],
|
||||
[LogLevels.Warn, "WARN"],
|
||||
[LogLevels.Error, "ERROR"],
|
||||
[LogLevels.Fatal, "FATAL"],
|
||||
]);
|
||||
[LogLevels.Debug, 'DEBUG'],
|
||||
[LogLevels.Info, 'INFO'],
|
||||
[LogLevels.Warn, 'WARN'],
|
||||
[LogLevels.Error, 'ERROR'],
|
||||
[LogLevels.Fatal, 'FATAL'],
|
||||
])
|
||||
|
||||
const noColor: (str: string) => string = (msg) => msg;
|
||||
const noColor: (str: string) => string = (msg) => msg
|
||||
const colorFunctions = new Map<LogLevels, (str: string) => string>([
|
||||
[LogLevels.Debug, gray],
|
||||
[LogLevels.Info, cyan],
|
||||
[LogLevels.Warn, yellow],
|
||||
[LogLevels.Error, (str: string) => red(str)],
|
||||
[LogLevels.Fatal, (str: string) => red(bold(italic(str)))],
|
||||
]);
|
||||
])
|
||||
|
||||
export function logger({
|
||||
logLevel = LogLevels.Info,
|
||||
name,
|
||||
}: {
|
||||
logLevel?: LogLevels;
|
||||
name?: string;
|
||||
logLevel?: LogLevels
|
||||
name?: string
|
||||
} = {}) {
|
||||
function log(level: LogLevels, ...args: any[]) {
|
||||
if (level < logLevel) return;
|
||||
if (level < logLevel) return
|
||||
|
||||
let color = colorFunctions.get(level);
|
||||
if (!color) color = noColor;
|
||||
let color = colorFunctions.get(level)
|
||||
if (!color) color = noColor
|
||||
|
||||
const date = new Date();
|
||||
const date = new Date()
|
||||
const log = [
|
||||
`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`,
|
||||
color(prefixes.get(level) || "DEBUG"),
|
||||
name ? `${name} >` : ">",
|
||||
color(prefixes.get(level) || 'DEBUG'),
|
||||
name ? `${name} >` : '>',
|
||||
...args,
|
||||
];
|
||||
]
|
||||
|
||||
switch (level) {
|
||||
case LogLevels.Debug:
|
||||
return console.debug(...log);
|
||||
return console.debug(...log)
|
||||
case LogLevels.Info:
|
||||
return console.info(...log);
|
||||
return console.info(...log)
|
||||
case LogLevels.Warn:
|
||||
return console.warn(...log);
|
||||
return console.warn(...log)
|
||||
case LogLevels.Error:
|
||||
return console.error(...log);
|
||||
return console.error(...log)
|
||||
case LogLevels.Fatal:
|
||||
return console.error(...log);
|
||||
return console.error(...log)
|
||||
default:
|
||||
return console.log(...log);
|
||||
return console.log(...log)
|
||||
}
|
||||
}
|
||||
|
||||
function setLevel(level: LogLevels) {
|
||||
logLevel = level;
|
||||
logLevel = level
|
||||
}
|
||||
|
||||
function debug(...args: any[]) {
|
||||
log(LogLevels.Debug, ...args);
|
||||
log(LogLevels.Debug, ...args)
|
||||
}
|
||||
|
||||
function info(...args: any[]) {
|
||||
log(LogLevels.Info, ...args);
|
||||
log(LogLevels.Info, ...args)
|
||||
}
|
||||
|
||||
function warn(...args: any[]) {
|
||||
log(LogLevels.Warn, ...args);
|
||||
log(LogLevels.Warn, ...args)
|
||||
}
|
||||
|
||||
function error(...args: any[]) {
|
||||
log(LogLevels.Error, ...args);
|
||||
log(LogLevels.Error, ...args)
|
||||
}
|
||||
|
||||
function fatal(...args: any[]) {
|
||||
log(LogLevels.Fatal, ...args);
|
||||
log(LogLevels.Fatal, ...args)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -95,8 +95,8 @@ export function logger({
|
||||
warn,
|
||||
error,
|
||||
fatal,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const log = logger({ name: "Main" });
|
||||
export default log;
|
||||
export const log = logger({ name: 'Main' })
|
||||
export default log
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Bot } from '../../bot.ts.js';
|
||||
import { configs } from '../../configs.ts.js';
|
||||
import { Bot } from '../../bot.ts.js'
|
||||
import { configs } from '../../configs.ts.js'
|
||||
|
||||
export async function updateApplicationCommands() {
|
||||
await Bot.helpers.upsertGlobalApplicationCommands(
|
||||
@@ -7,7 +7,7 @@ export async function updateApplicationCommands() {
|
||||
// ONLY GLOBAL COMMANDS
|
||||
.filter((command) => !command.devOnly)
|
||||
.array(),
|
||||
);
|
||||
)
|
||||
|
||||
await Bot.helpers.upsertGuildApplicationCommands(
|
||||
configs.devGuildId,
|
||||
@@ -15,5 +15,5 @@ export async function updateApplicationCommands() {
|
||||
// ONLY GLOBAL COMMANDS
|
||||
.filter((command) => !!command.devOnly)
|
||||
.array(),
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { InfluxDB, Point } from "@influxdata/influxdb-client";
|
||||
import type { RestManager } from "discordeno/rest";
|
||||
import { InfluxDB, Point } from '@influxdata/influxdb-client';
|
||||
import type { RestManager } from 'discordeno/rest';
|
||||
|
||||
const INFLUX_ORG = process.env.INFLUX_ORG as string;
|
||||
const INFLUX_BUCKET = process.env.INFLUX_BUCKET as string;
|
||||
@@ -10,46 +10,46 @@ export const influxDB = INFLUX_URL && INFLUX_TOKEN ? new InfluxDB({ url: INFLUX_
|
||||
export const Influx = influxDB?.getWriteApi(INFLUX_ORG, INFLUX_BUCKET);
|
||||
|
||||
export const setupAnalyticsHooks = (rest: RestManager) => {
|
||||
// If influxdb data is provided, enable analytics in this proxy.
|
||||
if (Influx) {
|
||||
rest.fetching = function (options) {
|
||||
Influx?.writePoint(
|
||||
new Point("restEvents")
|
||||
// MARK THE TIME WHEN EVENT ARRIVED
|
||||
.timestamp(new Date())
|
||||
// SET THE GUILD ID
|
||||
.stringField("type", "REQUEST_FETCHING")
|
||||
.tag("method", options.method)
|
||||
.tag("url", options.url)
|
||||
.tag("bucket", options.bucketId ?? "NA"),
|
||||
);
|
||||
};
|
||||
// If influxdb data is provided, enable analytics in this proxy.
|
||||
if (Influx) {
|
||||
rest.fetching = function (options) {
|
||||
Influx?.writePoint(
|
||||
new Point('restEvents')
|
||||
// MARK THE TIME WHEN EVENT ARRIVED
|
||||
.timestamp(new Date())
|
||||
// SET THE GUILD ID
|
||||
.stringField('type', 'REQUEST_FETCHING')
|
||||
.tag('method', options.method)
|
||||
.tag('url', options.url)
|
||||
.tag('bucket', options.bucketId ?? 'NA'),
|
||||
);
|
||||
};
|
||||
|
||||
rest.fetched = function (options, response) {
|
||||
Influx?.writePoint(
|
||||
new Point("restEvents")
|
||||
// MARK THE TIME WHEN EVENT ARRIVED
|
||||
.timestamp(new Date())
|
||||
// SET THE GUILD ID
|
||||
.stringField("type", "REQUEST_FETCHED")
|
||||
.tag("method", options.method)
|
||||
.tag("url", options.url)
|
||||
.tag("bucket", options.bucketId ?? "NA")
|
||||
.intField("status", response.status)
|
||||
.tag("statusText", response.statusText),
|
||||
);
|
||||
};
|
||||
rest.fetched = function (options, response) {
|
||||
Influx?.writePoint(
|
||||
new Point('restEvents')
|
||||
// MARK THE TIME WHEN EVENT ARRIVED
|
||||
.timestamp(new Date())
|
||||
// SET THE GUILD ID
|
||||
.stringField('type', 'REQUEST_FETCHED')
|
||||
.tag('method', options.method)
|
||||
.tag('url', options.url)
|
||||
.tag('bucket', options.bucketId ?? 'NA')
|
||||
.intField('status', response.status)
|
||||
.tag('statusText', response.statusText),
|
||||
);
|
||||
};
|
||||
|
||||
setInterval(() => {
|
||||
console.log(`[Influx - REST] Saving events...`);
|
||||
Influx?.flush()
|
||||
.then(() => {
|
||||
console.log(`[Influx - REST] Saved events!`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(`[Influx - REST] Error saving events!`, error);
|
||||
});
|
||||
// Every 30seconds
|
||||
}, 30000);
|
||||
}
|
||||
setInterval(() => {
|
||||
console.log(`[Influx - REST] Saving events...`);
|
||||
Influx?.flush()
|
||||
.then(() => {
|
||||
console.log(`[Influx - REST] Saved events!`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(`[Influx - REST] Error saving events!`, error);
|
||||
});
|
||||
// Every 30seconds
|
||||
}, 30000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import type { Bot} from "discordeno";
|
||||
import { Collection, createBot, createRestManager } from "discordeno";
|
||||
import enableHelpersPlugin from "discordeno/helpers-plugin";
|
||||
import { createLogger } from "discordeno/logger";
|
||||
import { setupAnalyticsHooks } from "../analytics.js";
|
||||
import { INTENTS, REST_URL } from "../configs.js";
|
||||
import { setupEventHandlers } from "./events/mod.js";
|
||||
import type { MessageCollector } from "./utils/collectors.js";
|
||||
import { customizeInternals } from "./utils/internals/mod.js";
|
||||
import type { Bot } from 'discordeno';
|
||||
import { Collection, createBot, createRestManager } from 'discordeno';
|
||||
import enableHelpersPlugin from 'discordeno/helpers-plugin';
|
||||
import { createLogger } from 'discordeno/logger';
|
||||
import { setupAnalyticsHooks } from '../analytics.js';
|
||||
import { INTENTS, REST_URL } from '../configs.js';
|
||||
import { setupEventHandlers } from './events/mod.js';
|
||||
import type { MessageCollector } from './utils/collectors.js';
|
||||
import { customizeInternals } from './utils/internals/mod.js';
|
||||
|
||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string;
|
||||
const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string;
|
||||
|
||||
export const bot = enableHelpersPlugin(
|
||||
customizeBot(
|
||||
createBot({
|
||||
token: DISCORD_TOKEN,
|
||||
intents: INTENTS,
|
||||
}),
|
||||
),
|
||||
customizeBot(
|
||||
createBot({
|
||||
token: DISCORD_TOKEN,
|
||||
intents: INTENTS,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
/** Add custom props to your `bot` here */
|
||||
// SETUP-DD-TEMP: If you want to add any custom props to `bot` you can do so here. Please make sure to also add them in the type below. As an example, i have added a `logger` property. You can add any useful methods or props you wish to have easily available.
|
||||
function customizeBot<B extends Bot = Bot>(bot: B): BotWithCustomProps {
|
||||
const customized = bot as unknown as BotWithCustomProps;
|
||||
customized.logger = createLogger({ name: "[Bot]" });
|
||||
customized.collectors = {
|
||||
messages: new Collection(),
|
||||
};
|
||||
customized.commandVersions = new Collection();
|
||||
const customized = bot as unknown as BotWithCustomProps;
|
||||
customized.logger = createLogger({ name: '[Bot]' });
|
||||
customized.collectors = {
|
||||
messages: new Collection(),
|
||||
};
|
||||
customized.commandVersions = new Collection();
|
||||
|
||||
return customized;
|
||||
return customized;
|
||||
}
|
||||
|
||||
// SETUP-DD-TEMP: If you want to add any custom props to `bot` you can do so here. Please make sure to also add them in the function above. Run a find all and change this to your Bot's name. For example, if your bot's name is Gamer change BotWithCustomProps to Gamer. This way whenever you need to provide the type for the Bot with your custom props it is your bots name.
|
||||
// Note: ALWAYS edit the function above first before adding the type here.
|
||||
export type BotWithCustomProps<B extends Bot = Bot> = B & {
|
||||
/** A easy to use logger to make clean log messages. */
|
||||
logger: ReturnType<typeof createLogger>;
|
||||
/** Collectors that can be used to get input from users. */
|
||||
collectors: {
|
||||
/** Holds the pending messages collectors that users can respond to. */
|
||||
messages: Collection<bigint, MessageCollector>;
|
||||
};
|
||||
/** The command versions for each guild id. */
|
||||
commandVersions: Collection<bigint, number>;
|
||||
/** A easy to use logger to make clean log messages. */
|
||||
logger: ReturnType<typeof createLogger>;
|
||||
/** Collectors that can be used to get input from users. */
|
||||
collectors: {
|
||||
/** Holds the pending messages collectors that users can respond to. */
|
||||
messages: Collection<bigint, MessageCollector>;
|
||||
};
|
||||
/** The command versions for each guild id. */
|
||||
commandVersions: Collection<bigint, number>;
|
||||
};
|
||||
|
||||
// Example of how to customize internal discordeno stuff easily.
|
||||
@@ -54,9 +54,9 @@ customizeInternals(bot);
|
||||
setupEventHandlers();
|
||||
|
||||
bot.rest = createRestManager({
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
});
|
||||
|
||||
// Add send fetching analytics hook to rest
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { ApplicationCommandOptionTypes } from "discordeno";
|
||||
import { prisma } from "../../prisma.js";
|
||||
import languages from "../languages/languages.js";
|
||||
import { serverLanguages, translate } from "../languages/translate.js";
|
||||
import { createCommand } from "../utils/slash/createCommand.js";
|
||||
import { ApplicationCommandOptionTypes } from 'discordeno';
|
||||
import { prisma } from '../../prisma.js';
|
||||
import languages from '../languages/languages.js';
|
||||
import { serverLanguages, translate } from '../languages/translate.js';
|
||||
import { createCommand } from '../utils/slash/createCommand.js';
|
||||
|
||||
export default createCommand({
|
||||
name: "LANGUAGE_NAME",
|
||||
description: "LANGUAGE_DESCRIPTION",
|
||||
options: [
|
||||
{
|
||||
name: "LANGUAGE_KEY_NAME",
|
||||
description: "LANGUAGE_KEY_DESCRIPTION",
|
||||
type: ApplicationCommandOptionTypes.String,
|
||||
choices: Object.keys(languages).map((key) => ({ name: key, value: key })),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
execute: async function (_, interaction, args) {
|
||||
if (!interaction.guildId) return;
|
||||
name: 'LANGUAGE_NAME',
|
||||
description: 'LANGUAGE_DESCRIPTION',
|
||||
options: [
|
||||
{
|
||||
name: 'LANGUAGE_KEY_NAME',
|
||||
description: 'LANGUAGE_KEY_DESCRIPTION',
|
||||
type: ApplicationCommandOptionTypes.String,
|
||||
choices: Object.keys(languages).map((key) => ({ name: key, value: key })),
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
execute: async function (_, interaction, args) {
|
||||
if (!interaction.guildId) return;
|
||||
|
||||
// Set the new language in cache
|
||||
serverLanguages.set(interaction.guildId, args.name);
|
||||
// Let the user know its been updated.
|
||||
await interaction.reply(translate(interaction.guildId!, "LANGUAGE_UPDATED", args.name));
|
||||
// Update the db
|
||||
return await prisma.guilds.upsert({
|
||||
where: { id: interaction.guildId },
|
||||
create: { language: args.name, id: interaction.guildId },
|
||||
update: { language: args.name },
|
||||
});
|
||||
},
|
||||
// Set the new language in cache
|
||||
serverLanguages.set(interaction.guildId, args.name);
|
||||
// Let the user know its been updated.
|
||||
await interaction.reply(translate(interaction.guildId!, 'LANGUAGE_UPDATED', args.name));
|
||||
// Update the db
|
||||
return await prisma.guilds.upsert({
|
||||
where: { id: interaction.guildId },
|
||||
create: { language: args.name, id: interaction.guildId },
|
||||
update: { language: args.name },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import language from "./language.js";
|
||||
import ping from "./ping.js";
|
||||
import language from './language.js';
|
||||
import ping from './ping.js';
|
||||
|
||||
export const COMMANDS = {
|
||||
language,
|
||||
ping,
|
||||
language,
|
||||
ping,
|
||||
};
|
||||
|
||||
export default COMMANDS;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { translate } from "../languages/translate.js";
|
||||
import { createCommand } from "../utils/slash/createCommand.js";
|
||||
import { translate } from '../languages/translate.js';
|
||||
import { createCommand } from '../utils/slash/createCommand.js';
|
||||
|
||||
export default createCommand({
|
||||
name: "PING_NAME",
|
||||
description: "PING_DESCRIPTION",
|
||||
execute: async function (_, interaction) {
|
||||
return await interaction.reply(
|
||||
translate(interaction.guildId!, "PING_RESPONSE_WITH_TIME", Date.now() - snowflakeToTimestamp(interaction.id)),
|
||||
);
|
||||
},
|
||||
name: 'PING_NAME',
|
||||
description: 'PING_DESCRIPTION',
|
||||
execute: async function (_, interaction) {
|
||||
return await interaction.reply(
|
||||
translate(interaction.guildId!, 'PING_RESPONSE_WITH_TIME', Date.now() - snowflakeToTimestamp(interaction.id)),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: This should be deleted once this is available in the helpers plugin.
|
||||
export function snowflakeToTimestamp(id: bigint) {
|
||||
return Number(id / 4194304n + 1420070400000n);
|
||||
return Number(id / 4194304n + 1420070400000n);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Interaction } from "discordeno";
|
||||
import type { BotWithCustomProps } from "../../bot.js";
|
||||
import type { Interaction } from 'discordeno';
|
||||
import type { BotWithCustomProps } from '../../bot.js';
|
||||
|
||||
export async function executeButtonClick(bot: BotWithCustomProps, interaction: Interaction) {
|
||||
if (!interaction.data) return;
|
||||
if (!interaction.data) return;
|
||||
|
||||
bot.logger.info(
|
||||
`[Button] The ${interaction.data.customId} button was clicked in Guild: ${interaction.guildId} by ${interaction.user.id}.`,
|
||||
);
|
||||
bot.logger.info(
|
||||
`[Button] The ${interaction.data.customId} button was clicked in Guild: ${interaction.guildId} by ${interaction.user.id}.`,
|
||||
);
|
||||
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Insert any functions you wish to run when a user clicks a button.
|
||||
]).catch(console.log);
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Insert any functions you wish to run when a user clicks a button.
|
||||
]).catch(console.log);
|
||||
}
|
||||
|
||||
@@ -1,121 +1,116 @@
|
||||
import { bgBlack, bgGreen, bgMagenta, bgYellow, black, green, red, white } from "colorette";
|
||||
import { bgBlack, bgGreen, bgMagenta, bgYellow, black, green, red, white } from 'colorette';
|
||||
import type {
|
||||
ApplicationCommandOption,
|
||||
Bot,
|
||||
Channel,
|
||||
ChannelTypes,
|
||||
Interaction,
|
||||
InteractionDataOption,
|
||||
Member,
|
||||
Role,
|
||||
User} from "discordeno";
|
||||
import {
|
||||
ApplicationCommandOptionTypes,
|
||||
InteractionResponseTypes
|
||||
} from "discordeno";
|
||||
import type { BotWithCustomProps } from "../../bot.js";
|
||||
import { bot } from "../../bot.js";
|
||||
import COMMANDS from "../../commands/mod.js";
|
||||
import type { translationKeys } from "../../languages/translate.js";
|
||||
import { getLanguage, loadLanguage, serverLanguages, translate } from "../../languages/translate.js";
|
||||
import type { InteractionWithCustomProps } from "../../typings/discordeno.js";
|
||||
import type { Command, ConvertArgumentDefinitionsToArgs } from "../../utils/slash/createCommand.js";
|
||||
ApplicationCommandOption,
|
||||
Bot,
|
||||
Channel,
|
||||
ChannelTypes,
|
||||
Interaction,
|
||||
InteractionDataOption,
|
||||
Member,
|
||||
Role,
|
||||
User,
|
||||
} from 'discordeno';
|
||||
import { ApplicationCommandOptionTypes, InteractionResponseTypes } from 'discordeno';
|
||||
import type { BotWithCustomProps } from '../../bot.js';
|
||||
import { bot } from '../../bot.js';
|
||||
import COMMANDS from '../../commands/mod.js';
|
||||
import type { translationKeys } from '../../languages/translate.js';
|
||||
import { getLanguage, loadLanguage, serverLanguages, translate } from '../../languages/translate.js';
|
||||
import type { InteractionWithCustomProps } from '../../typings/discordeno.js';
|
||||
import type { Command, ConvertArgumentDefinitionsToArgs } from '../../utils/slash/createCommand.js';
|
||||
|
||||
function logCommand(
|
||||
info: Interaction,
|
||||
type: "Failure" | "Success" | "Trigger" | "Slowmode" | "Missing" | "Inhibit",
|
||||
commandName: string,
|
||||
info: Interaction,
|
||||
type: 'Failure' | 'Success' | 'Trigger' | 'Slowmode' | 'Missing' | 'Inhibit',
|
||||
commandName: string,
|
||||
) {
|
||||
const command = `[COMMAND: ${bgYellow(black(commandName || "Unknown"))} - ${
|
||||
bgBlack(
|
||||
["Failure", "Slowmode", "Missing"].includes(type) ? red(type) : type === "Success" ? green(type) : white(type),
|
||||
)
|
||||
}]`;
|
||||
const command = `[COMMAND: ${bgYellow(black(commandName || 'Unknown'))} - ${bgBlack(
|
||||
['Failure', 'Slowmode', 'Missing'].includes(type) ? red(type) : type === 'Success' ? green(type) : white(type),
|
||||
)}]`;
|
||||
|
||||
const user = bgGreen(
|
||||
black(`${info.user.username}#${info.user.discriminator.toString().padStart(4, "0")}(${info.id})`),
|
||||
);
|
||||
const guild = bgMagenta(black(`${info.guildId ? `Guild ID: (${info.guildId})` : "DM"}`));
|
||||
const user = bgGreen(
|
||||
black(`${info.user.username}#${info.user.discriminator.toString().padStart(4, '0')}(${info.id})`),
|
||||
);
|
||||
const guild = bgMagenta(black(`${info.guildId ? `Guild ID: (${info.guildId})` : 'DM'}`));
|
||||
|
||||
bot.logger.info(`${command} by ${user} in ${guild} with MessageID: ${info.id}`);
|
||||
bot.logger.info(`${command} by ${user} in ${guild} with MessageID: ${info.id}`);
|
||||
}
|
||||
|
||||
export async function executeSlashCommand(bot: BotWithCustomProps, interaction: InteractionWithCustomProps) {
|
||||
const data = interaction.data;
|
||||
const name = data?.name as keyof typeof COMMANDS;
|
||||
const data = interaction.data;
|
||||
const name = data?.name as keyof typeof COMMANDS;
|
||||
|
||||
const command: Command<any> | undefined = COMMANDS[name];
|
||||
const command: Command<any> | undefined = COMMANDS[name];
|
||||
|
||||
// Command could not be found
|
||||
if (!command?.execute) {
|
||||
return await interaction
|
||||
.reply(translate(interaction.guildId!, "EXECUTE_COMMAND_NOT_FOUND"))
|
||||
.catch(bot.logger.error);
|
||||
}
|
||||
// Command could not be found
|
||||
if (!command?.execute) {
|
||||
return await interaction
|
||||
.reply(translate(interaction.guildId!, 'EXECUTE_COMMAND_NOT_FOUND'))
|
||||
.catch(bot.logger.error);
|
||||
}
|
||||
|
||||
// HAVE TO CONVERT OUTSIDE OF TRY SO IT CAN BE USED IN CATCH TOO
|
||||
try {
|
||||
logCommand(interaction, "Trigger", name);
|
||||
// HAVE TO CONVERT OUTSIDE OF TRY SO IT CAN BE USED IN CATCH TOO
|
||||
try {
|
||||
logCommand(interaction, 'Trigger', name);
|
||||
|
||||
// Check subcommand permissions and options
|
||||
if (!(await commandAllowed(interaction, command))) return;
|
||||
// Check subcommand permissions and options
|
||||
if (!(await commandAllowed(interaction, command))) return;
|
||||
|
||||
// Load the language for this guild
|
||||
if (interaction.guildId && !serverLanguages.has(interaction.guildId)) {
|
||||
// Todo: make command.execute reply change to editReply after running this
|
||||
// await interaction.reply({
|
||||
// type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
// });
|
||||
await loadLanguage(interaction.guildId);
|
||||
} // Load the language for this guild
|
||||
else if (command.acknowledge) {
|
||||
// Acknowledge the command
|
||||
await interaction.reply({
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
});
|
||||
}
|
||||
// Load the language for this guild
|
||||
if (interaction.guildId && !serverLanguages.has(interaction.guildId)) {
|
||||
// Todo: make command.execute reply change to editReply after running this
|
||||
// await interaction.reply({
|
||||
// type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
// });
|
||||
await loadLanguage(interaction.guildId);
|
||||
} // Load the language for this guild
|
||||
else if (command.acknowledge) {
|
||||
// Acknowledge the command
|
||||
await interaction.reply({
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
});
|
||||
}
|
||||
|
||||
// FIRST GET THE TRANSLATIONS FOR ALL OPTIONS
|
||||
const translatedOptionNames = interaction.guildId && command.options
|
||||
? translateOptionNames(bot, interaction.guildId, command.options)
|
||||
: {};
|
||||
// FIRST GET THE TRANSLATIONS FOR ALL OPTIONS
|
||||
const translatedOptionNames =
|
||||
interaction.guildId && command.options ? translateOptionNames(bot, interaction.guildId, command.options) : {};
|
||||
|
||||
// PARSE THE OPTIONS TO A NICE OBJECT AND TRANSLATE THE KEYS TO ENGLISH
|
||||
const parsedArguments = optionParser(interaction, translatedOptionNames);
|
||||
// PARSE THE OPTIONS TO A NICE OBJECT AND TRANSLATE THE KEYS TO ENGLISH
|
||||
const parsedArguments = optionParser(interaction, translatedOptionNames);
|
||||
|
||||
await command.execute(bot, interaction, parsedArguments as ConvertArgumentDefinitionsToArgs<any>);
|
||||
logCommand(interaction, "Success", name);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
logCommand(interaction, "Failure", name);
|
||||
await command.execute(bot, interaction, parsedArguments as ConvertArgumentDefinitionsToArgs<any>);
|
||||
logCommand(interaction, 'Success', name);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
logCommand(interaction, 'Failure', name);
|
||||
|
||||
try {
|
||||
console.log("try");
|
||||
// try to reply the interaction, becuase we don't know if it replied or deffered
|
||||
return await interaction.reply(translate(interaction.id, "EXECUTE_COMMAND_ERROR"));
|
||||
} catch {
|
||||
console.log("catch");
|
||||
// edit the reply or deffered reply of interaction
|
||||
return await interaction.editReply(translate(interaction.id, "EXECUTE_COMMAND_ERROR")).catch(bot.logger.error);
|
||||
}
|
||||
}
|
||||
try {
|
||||
console.log('try');
|
||||
// try to reply the interaction, becuase we don't know if it replied or deffered
|
||||
return await interaction.reply(translate(interaction.id, 'EXECUTE_COMMAND_ERROR'));
|
||||
} catch {
|
||||
console.log('catch');
|
||||
// edit the reply or deffered reply of interaction
|
||||
return await interaction.editReply(translate(interaction.id, 'EXECUTE_COMMAND_ERROR')).catch(bot.logger.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Runs the inhibitors to see if a command is allowed to run. */
|
||||
export async function commandAllowed(interaction: InteractionWithCustomProps, command: Command<any>) {
|
||||
// CHECK WHETHER THE USER/GUILD IS VIP
|
||||
if (command.vipOnly) {
|
||||
// SETUP-DD-TEMP: Check if this server/user is a vip.
|
||||
const isVIP = true;
|
||||
// CHECK WHETHER THE USER/GUILD IS VIP
|
||||
if (command.vipOnly) {
|
||||
// SETUP-DD-TEMP: Check if this server/user is a vip.
|
||||
const isVIP = true;
|
||||
|
||||
if (!isVIP) {
|
||||
await interaction.reply(translate(interaction.id, "NEED_VIP")).catch(bot.logger.error);
|
||||
if (!isVIP) {
|
||||
await interaction.reply(translate(interaction.id, 'NEED_VIP')).catch(bot.logger.error);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mapped by `language-commandName`
|
||||
@@ -123,189 +118,187 @@ const translatedOptionNamesCache = new Map<string, Record<string, string>>();
|
||||
|
||||
/** Translates all options of the command to an object: translatedOptionName: optionName */
|
||||
export function translateOptionNames(
|
||||
bot: Bot,
|
||||
guildId: bigint,
|
||||
options: ApplicationCommandOption[],
|
||||
commandName?: string,
|
||||
bot: Bot,
|
||||
guildId: bigint,
|
||||
options: ApplicationCommandOption[],
|
||||
commandName?: string,
|
||||
): Record<string, string> {
|
||||
const language = getLanguage(guildId);
|
||||
// RETURN THE ALREADY TRANSLATED OPTIONS WHICH ARE IN CACHE
|
||||
if (commandName && translatedOptionNamesCache.has(`${language}-${commandName}`)) {
|
||||
return translatedOptionNamesCache.get(`${language}-${commandName}`)!;
|
||||
}
|
||||
const language = getLanguage(guildId);
|
||||
// RETURN THE ALREADY TRANSLATED OPTIONS WHICH ARE IN CACHE
|
||||
if (commandName && translatedOptionNamesCache.has(`${language}-${commandName}`)) {
|
||||
return translatedOptionNamesCache.get(`${language}-${commandName}`)!;
|
||||
}
|
||||
|
||||
// TRANSLATE ALL OPTIONS
|
||||
let translated: Record<string, string> = {};
|
||||
for (const option of options) {
|
||||
translated[translate(guildId, option.name as translationKeys).toLowerCase()] = translate(
|
||||
"english",
|
||||
option.name as translationKeys,
|
||||
);
|
||||
if (option.options) {
|
||||
translated = {
|
||||
...translated,
|
||||
...translateOptionNames(bot, guildId, option.options),
|
||||
};
|
||||
}
|
||||
}
|
||||
// TRANSLATE ALL OPTIONS
|
||||
let translated: Record<string, string> = {};
|
||||
for (const option of options) {
|
||||
translated[translate(guildId, option.name as translationKeys).toLowerCase()] = translate(
|
||||
'english',
|
||||
option.name as translationKeys,
|
||||
);
|
||||
if (option.options) {
|
||||
translated = {
|
||||
...translated,
|
||||
...translateOptionNames(bot, guildId, option.options),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// SAVE THE TRANSLATED OPTIONS IN CACHE FOR FASTER ACCESS
|
||||
if (commandName) {
|
||||
translatedOptionNamesCache.set(`${language}-${commandName}`, translated);
|
||||
}
|
||||
// SAVE THE TRANSLATED OPTIONS IN CACHE FOR FASTER ACCESS
|
||||
if (commandName) {
|
||||
translatedOptionNamesCache.set(`${language}-${commandName}`, translated);
|
||||
}
|
||||
|
||||
return translated;
|
||||
return translated;
|
||||
}
|
||||
|
||||
function convertOptionValue(
|
||||
interaction: Interaction,
|
||||
option: InteractionDataOption,
|
||||
translateOptions?: Record<string, string>,
|
||||
interaction: Interaction,
|
||||
option: InteractionDataOption,
|
||||
translateOptions?: Record<string, string>,
|
||||
): [
|
||||
string,
|
||||
(
|
||||
| { user: User; member: Member }
|
||||
| Role
|
||||
| {
|
||||
id: bigint;
|
||||
name: string;
|
||||
type: ChannelTypes;
|
||||
permissions: bigint;
|
||||
}
|
||||
| boolean
|
||||
| string
|
||||
| number
|
||||
),
|
||||
string,
|
||||
(
|
||||
| { user: User; member: Member }
|
||||
| Role
|
||||
| {
|
||||
id: bigint;
|
||||
name: string;
|
||||
type: ChannelTypes;
|
||||
permissions: bigint;
|
||||
}
|
||||
| boolean
|
||||
| string
|
||||
| number
|
||||
),
|
||||
] {
|
||||
// THE OPTION IS A CHANNEL
|
||||
if (option.type === ApplicationCommandOptionTypes.Channel) {
|
||||
const channel = interaction.data?.resolved?.channels?.get(BigInt(option.value as string));
|
||||
// THE OPTION IS A CHANNEL
|
||||
if (option.type === ApplicationCommandOptionTypes.Channel) {
|
||||
const channel = interaction.data?.resolved?.channels?.get(BigInt(option.value as string));
|
||||
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, channel];
|
||||
}
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, channel];
|
||||
}
|
||||
|
||||
// THE OPTION IS A ROLE
|
||||
if (option.type === ApplicationCommandOptionTypes.Role) {
|
||||
const role = interaction.data?.resolved?.roles?.get(BigInt(option.value as string));
|
||||
// THE OPTION IS A ROLE
|
||||
if (option.type === ApplicationCommandOptionTypes.Role) {
|
||||
const role = interaction.data?.resolved?.roles?.get(BigInt(option.value as string));
|
||||
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, role];
|
||||
}
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, role];
|
||||
}
|
||||
|
||||
// THE OPTION IS A USER
|
||||
if (option.type === ApplicationCommandOptionTypes.User) {
|
||||
const user = interaction.data?.resolved?.users?.get(BigInt(option.value as string));
|
||||
const member = interaction.data?.resolved?.members?.get(BigInt(option.value as string));
|
||||
// THE OPTION IS A USER
|
||||
if (option.type === ApplicationCommandOptionTypes.User) {
|
||||
const user = interaction.data?.resolved?.users?.get(BigInt(option.value as string));
|
||||
const member = interaction.data?.resolved?.members?.get(BigInt(option.value as string));
|
||||
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [
|
||||
translateOptions?.[option.name] ?? option.name,
|
||||
{
|
||||
member,
|
||||
user,
|
||||
},
|
||||
];
|
||||
}
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [
|
||||
translateOptions?.[option.name] ?? option.name,
|
||||
{
|
||||
member,
|
||||
user,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// THE OPTION IS A MENTIONABLE
|
||||
if (option.type === ApplicationCommandOptionTypes.Mentionable) {
|
||||
const role = interaction.data?.resolved?.roles?.get(BigInt(option.value as string));
|
||||
const user = interaction.data?.resolved?.users?.get(BigInt(option.value as string));
|
||||
const member = interaction.data?.resolved?.members?.get(BigInt(option.value as string));
|
||||
// THE OPTION IS A MENTIONABLE
|
||||
if (option.type === ApplicationCommandOptionTypes.Mentionable) {
|
||||
const role = interaction.data?.resolved?.roles?.get(BigInt(option.value as string));
|
||||
const user = interaction.data?.resolved?.users?.get(BigInt(option.value as string));
|
||||
const member = interaction.data?.resolved?.members?.get(BigInt(option.value as string));
|
||||
|
||||
const final = user && member ? { user, member } : role;
|
||||
const final = user && member ? { user, member } : role;
|
||||
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, final];
|
||||
}
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
return [translateOptions?.[option.name] ?? option.name, final];
|
||||
}
|
||||
|
||||
// THE REST OF OPTIONS DON'T NEED ANY CONVERTION
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
// @ts-expect-error
|
||||
return [translateOptions?.[option.name] ?? option.name, option.value];
|
||||
// THE REST OF OPTIONS DON'T NEED ANY CONVERTION
|
||||
// SAVE THE ARGUMENT WITH THE CORRECT NAME
|
||||
// @ts-expect-error
|
||||
return [translateOptions?.[option.name] ?? option.name, option.value];
|
||||
}
|
||||
|
||||
/** Parse the options to a nice object.
|
||||
* NOTE: this does not work with subcommands
|
||||
*/
|
||||
export function optionParser(
|
||||
interaction: Interaction,
|
||||
translateOptions?: Record<string, string>,
|
||||
interaction: Interaction,
|
||||
translateOptions?: Record<string, string>,
|
||||
):
|
||||
| InteractionCommandArgs
|
||||
| { [key: string]: InteractionCommandArgs }
|
||||
| { [key: string]: { [key: string]: InteractionCommandArgs } } {
|
||||
// OPTIONS CAN BE UNDEFINED SO WE JUST RETURN AN EMPTY OBJECT
|
||||
if (!interaction.data?.options) return {};
|
||||
| InteractionCommandArgs
|
||||
| { [key: string]: InteractionCommandArgs }
|
||||
| { [key: string]: { [key: string]: InteractionCommandArgs } } {
|
||||
// OPTIONS CAN BE UNDEFINED SO WE JUST RETURN AN EMPTY OBJECT
|
||||
if (!interaction.data?.options) return {};
|
||||
|
||||
// A SUBCOMMAND WAS USED
|
||||
if (interaction.data.options[0]?.type === ApplicationCommandOptionTypes.SubCommand) {
|
||||
const convertedOptions: Record<
|
||||
string,
|
||||
| { user: User; member: Member }
|
||||
| Role
|
||||
| {
|
||||
id: bigint;
|
||||
name: string;
|
||||
type: ChannelTypes;
|
||||
permissions: bigint;
|
||||
}
|
||||
| boolean
|
||||
| string
|
||||
| number
|
||||
> = {};
|
||||
// CONVERT ALL THE OPTIONS
|
||||
for (const option of interaction.data.options[0].options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
// A SUBCOMMAND WAS USED
|
||||
if (interaction.data.options[0]?.type === ApplicationCommandOptionTypes.SubCommand) {
|
||||
const convertedOptions: Record<
|
||||
string,
|
||||
| { user: User; member: Member }
|
||||
| Role
|
||||
| {
|
||||
id: bigint;
|
||||
name: string;
|
||||
type: ChannelTypes;
|
||||
permissions: bigint;
|
||||
}
|
||||
| boolean
|
||||
| string
|
||||
| number
|
||||
> = {};
|
||||
// CONVERT ALL THE OPTIONS
|
||||
for (const option of interaction.data.options[0].options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return {
|
||||
[translateOptions?.[interaction.data.options[0].name] ?? interaction.data.options[0].name]: convertedOptions,
|
||||
};
|
||||
}
|
||||
// @ts-expect-error
|
||||
return {
|
||||
[translateOptions?.[interaction.data.options[0].name] ?? interaction.data.options[0].name]: convertedOptions,
|
||||
};
|
||||
}
|
||||
|
||||
// A SUBCOMMAND GROUP WAS USED
|
||||
if (interaction.data.options[0]?.type === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
const convertedOptions: Record<string, Member | Role | Channel | boolean | string | number> = {};
|
||||
// CONVERT ALL THE OPTIONS
|
||||
for (const option of interaction.data.options[0]?.options![0]?.options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
// @ts-expect-error
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
// A SUBCOMMAND GROUP WAS USED
|
||||
if (interaction.data.options[0]?.type === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
const convertedOptions: Record<string, Member | Role | Channel | boolean | string | number> = {};
|
||||
// CONVERT ALL THE OPTIONS
|
||||
for (const option of interaction.data.options[0]?.options![0]?.options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
// @ts-expect-error
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
return {
|
||||
[translateOptions?.[interaction.data.options[0].name] ?? interaction.data.options[0].name]: {
|
||||
[
|
||||
translateOptions?.[interaction.data.options[0]!.options![0]!.name] ??
|
||||
interaction.data.options[0]!.options![0]!.name
|
||||
]: convertedOptions,
|
||||
},
|
||||
};
|
||||
}
|
||||
// @ts-expect-error
|
||||
return {
|
||||
[translateOptions?.[interaction.data.options[0].name] ?? interaction.data.options[0].name]: {
|
||||
[translateOptions?.[interaction.data.options[0]!.options![0]!.name] ??
|
||||
interaction.data.options[0]!.options![0]!.name]: convertedOptions,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// A NORMAL COMMAND WAS USED
|
||||
const convertedOptions: Record<
|
||||
string,
|
||||
Member | Role | Record<string, Pick<Channel, "id" | "name" | "type" | "permissions">> | boolean | string | number
|
||||
> = {};
|
||||
for (const option of interaction.data.options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
// @ts-expect-error
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
// A NORMAL COMMAND WAS USED
|
||||
const convertedOptions: Record<
|
||||
string,
|
||||
Member | Role | Record<string, Pick<Channel, 'id' | 'name' | 'type' | 'permissions'>> | boolean | string | number
|
||||
> = {};
|
||||
for (const option of interaction.data.options ?? []) {
|
||||
const [name, value] = convertOptionValue(interaction, option, translateOptions);
|
||||
// @ts-expect-error
|
||||
convertedOptions[name] = value;
|
||||
}
|
||||
|
||||
return convertedOptions;
|
||||
return convertedOptions;
|
||||
}
|
||||
|
||||
/** The interaction arguments.
|
||||
* Important the members `deaf` and `mute` properties will always be false.
|
||||
*/
|
||||
export type InteractionCommandArgs = Record<
|
||||
string,
|
||||
Member | Role | Record<string, Pick<Channel, "id" | "name" | "type" | "permissions">> | boolean | string | number
|
||||
string,
|
||||
Member | Role | Record<string, Pick<Channel, 'id' | 'name' | 'type' | 'permissions'>> | boolean | string | number
|
||||
>;
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { InteractionTypes, MessageComponentTypes } from "discordeno";
|
||||
import { bot } from "../../bot.js";
|
||||
import type { InteractionWithCustomProps } from "../../typings/discordeno.js";
|
||||
import { executeButtonClick } from "./button.js";
|
||||
import { executeSlashCommand } from "./command.js";
|
||||
import { executeModalSubmit } from "./modal.js";
|
||||
import { InteractionTypes, MessageComponentTypes } from 'discordeno';
|
||||
import { bot } from '../../bot.js';
|
||||
import type { InteractionWithCustomProps } from '../../typings/discordeno.js';
|
||||
import { executeButtonClick } from './button.js';
|
||||
import { executeSlashCommand } from './command.js';
|
||||
import { executeModalSubmit } from './modal.js';
|
||||
|
||||
export function setInteractionCreateEvent() {
|
||||
bot.events.interactionCreate = async function (_, interaction) {
|
||||
if (interaction.type === InteractionTypes.ApplicationCommand) {
|
||||
await executeSlashCommand(bot, interaction as InteractionWithCustomProps);
|
||||
} else if (interaction.type === InteractionTypes.MessageComponent) {
|
||||
if (!interaction.data) return;
|
||||
bot.events.interactionCreate = async function (_, interaction) {
|
||||
if (interaction.type === InteractionTypes.ApplicationCommand) {
|
||||
await executeSlashCommand(bot, interaction as InteractionWithCustomProps);
|
||||
} else if (interaction.type === InteractionTypes.MessageComponent) {
|
||||
if (!interaction.data) return;
|
||||
|
||||
// THE INTERACTION CAME FROM A BUTTON
|
||||
if (interaction.data.componentType === MessageComponentTypes.Button) {
|
||||
await executeButtonClick(bot, interaction);
|
||||
}
|
||||
} else if (interaction.type === InteractionTypes.ModalSubmit) {
|
||||
await executeModalSubmit(bot, interaction);
|
||||
}
|
||||
};
|
||||
// THE INTERACTION CAME FROM A BUTTON
|
||||
if (interaction.data.componentType === MessageComponentTypes.Button) {
|
||||
await executeButtonClick(bot, interaction);
|
||||
}
|
||||
} else if (interaction.type === InteractionTypes.ModalSubmit) {
|
||||
await executeModalSubmit(bot, interaction);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { Interaction } from "discordeno";
|
||||
import type { BotWithCustomProps } from "../../bot.js";
|
||||
import type { Interaction } from 'discordeno';
|
||||
import type { BotWithCustomProps } from '../../bot.js';
|
||||
|
||||
export async function executeModalSubmit(bot: BotWithCustomProps, interaction: Interaction) {
|
||||
if (!interaction.data) return;
|
||||
if (!interaction.data) return;
|
||||
|
||||
bot.logger.info(
|
||||
`[Modal] The ${
|
||||
interaction.data?.customId || "UNKNWON"
|
||||
} modal was submitted in Guild: ${interaction.guildId} by ${interaction.user.id}.`,
|
||||
);
|
||||
bot.logger.info(
|
||||
`[Modal] The ${interaction.data?.customId || 'UNKNWON'} modal was submitted in Guild: ${interaction.guildId} by ${
|
||||
interaction.user.id
|
||||
}.`,
|
||||
);
|
||||
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Insert any functions you wish to run when a user clicks a button.
|
||||
]).catch(console.log);
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Insert any functions you wish to run when a user clicks a button.
|
||||
]).catch(console.log);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { bot } from "../../bot.js";
|
||||
import { processMessageCollectors } from "../../utils/collectors.js";
|
||||
import { bot } from '../../bot.js';
|
||||
import { processMessageCollectors } from '../../utils/collectors.js';
|
||||
|
||||
export function setMessageCreateEvent() {
|
||||
bot.events.messageCreate = async function (_, message) {
|
||||
processMessageCollectors(message);
|
||||
bot.events.messageCreate = async function (_, message) {
|
||||
processMessageCollectors(message);
|
||||
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Add any functions you want to run on every message here. For example, automoderation filters.
|
||||
]).catch(console.log);
|
||||
};
|
||||
await Promise.allSettled([
|
||||
// SETUP-DD-TEMP: Add any functions you want to run on every message here. For example, automoderation filters.
|
||||
]).catch(console.log);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { setInteractionCreateEvent } from "./interactions/mod.js";
|
||||
import { setMessageCreateEvent } from "./messages/create.js";
|
||||
import { setRawEvent } from "./raw.js";
|
||||
import { setInteractionCreateEvent } from './interactions/mod.js';
|
||||
import { setMessageCreateEvent } from './messages/create.js';
|
||||
import { setRawEvent } from './raw.js';
|
||||
|
||||
export function setupEventHandlers() {
|
||||
setInteractionCreateEvent();
|
||||
setRawEvent();
|
||||
setMessageCreateEvent();
|
||||
setInteractionCreateEvent();
|
||||
setRawEvent();
|
||||
setMessageCreateEvent();
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import type { DiscordUnavailableGuild } from "discordeno";
|
||||
import { prisma } from "../../prisma.js";
|
||||
import { bot } from "../bot.js";
|
||||
import { updateGuildCommands, usesLatestCommandVersion } from "../utils/slash/updateCommands.js";
|
||||
import type { DiscordUnavailableGuild } from 'discordeno';
|
||||
import { prisma } from '../../prisma.js';
|
||||
import { bot } from '../bot.js';
|
||||
import { updateGuildCommands, usesLatestCommandVersion } from '../utils/slash/updateCommands.js';
|
||||
|
||||
/** To prevent updating every guild when a shard goes ready we have to ignore them using this */
|
||||
// export const initialyLoadingGuildIds = new Set<bigint>()
|
||||
|
||||
export function setRawEvent() {
|
||||
bot.events.raw = async function (_, data) {
|
||||
if (data.t === "GUILD_DELETE") {
|
||||
const id = (data.d as DiscordUnavailableGuild).id;
|
||||
bot.events.raw = async function (_, data) {
|
||||
if (data.t === 'GUILD_DELETE') {
|
||||
const id = (data.d as DiscordUnavailableGuild).id;
|
||||
|
||||
return await prisma.commands.delete({ where: { id: bot.transformers.snowflake(id) } });
|
||||
}
|
||||
return await prisma.commands.delete({ where: { id: bot.transformers.snowflake(id) } });
|
||||
}
|
||||
|
||||
const id = bot.transformers.snowflake(
|
||||
(data.t && ["GUILD_UPDATE", "GUILD_CREATE"].includes(data.t)
|
||||
// deno-lint-ignore no-explicit-any
|
||||
? (data.d )?.id
|
||||
// deno-lint-ignore no-explicit-any
|
||||
: (data.d )?.guild_id) ?? "",
|
||||
);
|
||||
const id = bot.transformers.snowflake(
|
||||
(data.t && ['GUILD_UPDATE', 'GUILD_CREATE'].includes(data.t)
|
||||
? // deno-lint-ignore no-explicit-any
|
||||
data.d?.id
|
||||
: // deno-lint-ignore no-explicit-any
|
||||
data.d?.guild_id) ?? '',
|
||||
);
|
||||
|
||||
// The GUILD_CREATE event came from a shard loaded event so ignore it
|
||||
if (["READY", "GUILD_LOADED_DD", null].includes(data.t)) return;
|
||||
// The GUILD_CREATE event came from a shard loaded event so ignore it
|
||||
if (['READY', 'GUILD_LOADED_DD', null].includes(data.t)) return;
|
||||
|
||||
// console.log({ id, v: await usesLatestCommandVersion(id) })
|
||||
// console.log({ id, v: await usesLatestCommandVersion(id) })
|
||||
|
||||
if (!id || (await usesLatestCommandVersion(id))) return;
|
||||
// dev guild
|
||||
if (id === 547046977578336286n) return;
|
||||
if (!id || (await usesLatestCommandVersion(id))) return;
|
||||
// dev guild
|
||||
if (id === 547046977578336286n) return;
|
||||
|
||||
// NEW GUILD AVAILABLE
|
||||
bot.logger.info(`[Slash Setup] Installing Slash commands on Guild ${id} event type: ${data.t}`);
|
||||
await updateGuildCommands(bot, id).catch(bot.logger.error);
|
||||
};
|
||||
// NEW GUILD AVAILABLE
|
||||
bot.logger.info(`[Slash Setup] Installing Slash commands on Guild ${id} event type: ${data.t}`);
|
||||
await updateGuildCommands(bot, id).catch(bot.logger.error);
|
||||
};
|
||||
}
|
||||
|
||||
+115
-119
@@ -1,14 +1,14 @@
|
||||
import dotenv from "dotenv";
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
import type { DiscordGatewayPayload } from "discordeno";
|
||||
import type { DiscordGatewayPayload } from 'discordeno';
|
||||
// ReferenceError: publishMessage is not defined
|
||||
// import Embeds from "discordeno/embeds";
|
||||
import amqplib from "amqplib";
|
||||
import express from "express";
|
||||
import { BOT_ID, EVENT_HANDLER_URL } from "../configs.js";
|
||||
import { bot } from "./bot.js";
|
||||
import { updateDevCommands } from "./utils/slash/updateCommands.js";
|
||||
import { webhookURLToIDAndToken } from "./utils/webhook.js";
|
||||
import amqplib from 'amqplib';
|
||||
import express from 'express';
|
||||
import { BOT_ID, EVENT_HANDLER_URL } from '../configs.js';
|
||||
import { bot } from './bot.js';
|
||||
import { updateDevCommands } from './utils/slash/updateCommands.js';
|
||||
import { webhookURLToIDAndToken } from './utils/webhook.js';
|
||||
dotenv.config();
|
||||
|
||||
const BUGS_ERRORS_REPORT_WEBHOOK = process.env.BUGS_ERRORS_REPORT_WEBHOOK;
|
||||
@@ -16,23 +16,23 @@ const EVENT_HANDLER_AUTHORIZATION = process.env.EVENT_HANDLER_AUTHORIZATION as s
|
||||
const EVENT_HANDLER_PORT = process.env.EVENT_HANDLER_PORT as string;
|
||||
|
||||
process
|
||||
.on("unhandledRejection", (error) => {
|
||||
if (!BUGS_ERRORS_REPORT_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
.on('unhandledRejection', (error) => {
|
||||
if (!BUGS_ERRORS_REPORT_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
|
||||
// DO NOT SEND ERRORS FROM NON PRODUCTION
|
||||
if (BOT_ID !== 270010330782892032n) {
|
||||
return console.error(error);
|
||||
}
|
||||
// DO NOT SEND ERRORS FROM NON PRODUCTION
|
||||
if (BOT_ID !== 270010330782892032n) {
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
// An unhandled error occurred on the bot in production
|
||||
console.error(error ?? `An unhandled rejection error occurred but error was null or undefined`);
|
||||
// An unhandled error occurred on the bot in production
|
||||
console.error(error ?? `An unhandled rejection error occurred but error was null or undefined`);
|
||||
|
||||
if (!error) return;
|
||||
if (!error) return;
|
||||
|
||||
// ReferenceError: publishMessage is not defined
|
||||
/*
|
||||
// ReferenceError: publishMessage is not defined
|
||||
/*
|
||||
const embeds = new Embeds()
|
||||
.setDescription(["```js", error, "```"].join(`\n`))
|
||||
.setTimestamp()
|
||||
@@ -41,23 +41,23 @@ process
|
||||
// SEND ERROR TO THE LOG CHANNEL ON THE DEV SERVER
|
||||
return bot.helpers.sendWebhookMessage(bot.transformers.snowflake(id), token, { embeds }).catch(console.error);
|
||||
*/
|
||||
})
|
||||
.on("uncaughtException", async (error) => {
|
||||
if (!BUGS_ERRORS_REPORT_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
})
|
||||
.on('uncaughtException', async (error) => {
|
||||
if (!BUGS_ERRORS_REPORT_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(BUGS_ERRORS_REPORT_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
|
||||
// DO NOT SEND ERRORS FROM NON PRODUCTION
|
||||
if (BOT_ID !== 270010330782892032n) {
|
||||
return console.error(error);
|
||||
}
|
||||
// DO NOT SEND ERRORS FROM NON PRODUCTION
|
||||
if (BOT_ID !== 270010330782892032n) {
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
// An unhandled error occurred on the bot in production
|
||||
console.error(error ?? `An unhandled exception occurred but error was null or undefined`);
|
||||
// An unhandled error occurred on the bot in production
|
||||
console.error(error ?? `An unhandled exception occurred but error was null or undefined`);
|
||||
|
||||
if (!error) process.exit(1);
|
||||
if (!error) process.exit(1);
|
||||
|
||||
/*
|
||||
/*
|
||||
const embeds = new Embeds()
|
||||
.setDescription(["```js", error.stack, "```"].join(`\n`))
|
||||
.setTimestamp()
|
||||
@@ -66,124 +66,120 @@ process
|
||||
await bot.helpers.sendWebhookMessage(bot.transformers.snowflake(id), token, { embeds }).catch(console.error);
|
||||
*/
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
if (process.env.DEVELOPMENT === "true") {
|
||||
bot.logger.info(`[DEV MODE] Updating slash commands for dev server.`);
|
||||
updateDevCommands(bot);
|
||||
if (process.env.DEVELOPMENT === 'true') {
|
||||
bot.logger.info(`[DEV MODE] Updating slash commands for dev server.`);
|
||||
updateDevCommands(bot);
|
||||
}
|
||||
|
||||
// Handle events from the gateway
|
||||
const handleEvent = async (message: DiscordGatewayPayload, shardId: number) => {
|
||||
// EMITS RAW EVENT
|
||||
bot.events.raw(bot, message, shardId);
|
||||
// EMITS RAW EVENT
|
||||
bot.events.raw(bot, message, shardId);
|
||||
|
||||
if (message.t && message.t !== "RESUMED") {
|
||||
// When a guild or something isnt in cache this will fetch it before doing anything else
|
||||
if (!["READY", "GUILD_LOADED_DD"].includes(message.t)) {
|
||||
await bot.events.dispatchRequirements(bot, message, shardId);
|
||||
}
|
||||
if (message.t && message.t !== 'RESUMED') {
|
||||
// When a guild or something isnt in cache this will fetch it before doing anything else
|
||||
if (!['READY', 'GUILD_LOADED_DD'].includes(message.t)) {
|
||||
await bot.events.dispatchRequirements(bot, message, shardId);
|
||||
}
|
||||
|
||||
bot.handlers[message.t]?.(bot, message, shardId);
|
||||
}
|
||||
bot.handlers[message.t]?.(bot, message, shardId);
|
||||
}
|
||||
};
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
express.urlencoded({
|
||||
extended: true,
|
||||
}),
|
||||
express.urlencoded({
|
||||
extended: true,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.all("/", async (req, res) => {
|
||||
try {
|
||||
if (!EVENT_HANDLER_AUTHORIZATION || EVENT_HANDLER_AUTHORIZATION !== req.headers.authorization) {
|
||||
return res.status(401).json({ error: "Invalid authorization key." });
|
||||
}
|
||||
app.all('/', async (req, res) => {
|
||||
try {
|
||||
if (!EVENT_HANDLER_AUTHORIZATION || EVENT_HANDLER_AUTHORIZATION !== req.headers.authorization) {
|
||||
return res.status(401).json({ error: 'Invalid authorization key.' });
|
||||
}
|
||||
|
||||
const json = req.body as {
|
||||
message: DiscordGatewayPayload;
|
||||
shardId: number;
|
||||
};
|
||||
const json = req.body as {
|
||||
message: DiscordGatewayPayload;
|
||||
shardId: number;
|
||||
};
|
||||
|
||||
await handleEvent(json.message, json.shardId);
|
||||
await handleEvent(json.message, json.shardId);
|
||||
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error: any) {
|
||||
bot.logger.error(error);
|
||||
res.status(error.code).json(error);
|
||||
}
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error: any) {
|
||||
bot.logger.error(error);
|
||||
res.status(error.code).json(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(EVENT_HANDLER_PORT, () => {
|
||||
console.log(`Bot is listening at ${EVENT_HANDLER_URL};`);
|
||||
console.log(`Bot is listening at ${EVENT_HANDLER_URL};`);
|
||||
});
|
||||
|
||||
const connectRabbitmq = async () => {
|
||||
let connection: amqplib.Connection | undefined;
|
||||
let connection: amqplib.Connection | undefined;
|
||||
|
||||
try {
|
||||
connection = await amqplib.connect(
|
||||
`amqp://${process.env.MESSAGEQUEUE_USERNAME}:${process.env.MESSAGEQUEUE_PASSWORD}@${process.env.MESSAGEQUEUE_URL}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
}
|
||||
try {
|
||||
connection = await amqplib.connect(
|
||||
`amqp://${process.env.MESSAGEQUEUE_USERNAME}:${process.env.MESSAGEQUEUE_PASSWORD}@${process.env.MESSAGEQUEUE_URL}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
}
|
||||
|
||||
if (!connection) return;
|
||||
connection.on("error", (err) => {
|
||||
console.error(err);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
if (!connection) return;
|
||||
connection.on('error', (err) => {
|
||||
console.error(err);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
|
||||
connection.on("close", () => {
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
connection.on('close', () => {
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
|
||||
try {
|
||||
const channel = await connection.createChannel();
|
||||
try {
|
||||
const channel = await connection.createChannel();
|
||||
|
||||
await channel.assertExchange(
|
||||
"gatewayMessage",
|
||||
"x-message-deduplication",
|
||||
{
|
||||
durable: true,
|
||||
arguments: {
|
||||
"x-cache-size": 1000,
|
||||
"x-cache-ttl": 500,
|
||||
},
|
||||
},
|
||||
);
|
||||
await channel.assertExchange('gatewayMessage', 'x-message-deduplication', {
|
||||
durable: true,
|
||||
arguments: {
|
||||
'x-cache-size': 1000,
|
||||
'x-cache-ttl': 500,
|
||||
},
|
||||
});
|
||||
|
||||
await channel.assertQueue("gatewayMessageQueue");
|
||||
await channel.bindQueue("gatewayMessageQueue", "gatewayMessage", "");
|
||||
await channel.consume(
|
||||
"gatewayMessageQueue",
|
||||
async (msg) => {
|
||||
if (!msg) return;
|
||||
const json = JSON.parse(msg.content.toString()) as {
|
||||
message: DiscordGatewayPayload;
|
||||
shardId: number;
|
||||
};
|
||||
await channel.assertQueue('gatewayMessageQueue');
|
||||
await channel.bindQueue('gatewayMessageQueue', 'gatewayMessage', '');
|
||||
await channel.consume(
|
||||
'gatewayMessageQueue',
|
||||
async (msg) => {
|
||||
if (!msg) return;
|
||||
const json = JSON.parse(msg.content.toString()) as {
|
||||
message: DiscordGatewayPayload;
|
||||
shardId: number;
|
||||
};
|
||||
|
||||
await handleEvent(json.message, json.shardId);
|
||||
await handleEvent(json.message, json.shardId);
|
||||
|
||||
await channel.ack(msg);
|
||||
},
|
||||
{
|
||||
noAck: false,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
await channel.ack(msg);
|
||||
},
|
||||
{
|
||||
noAck: false,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.MESSAGEQUEUE_ENABLE === "true") {
|
||||
connectRabbitmq();
|
||||
if (process.env.MESSAGEQUEUE_ENABLE === 'true') {
|
||||
connectRabbitmq();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const english = {
|
||||
// Permissions
|
||||
NEED_VIP: "❌ Only VIP users or servers can use this feature.",
|
||||
// Permissions
|
||||
NEED_VIP: '❌ Only VIP users or servers can use this feature.',
|
||||
|
||||
// Execute Command
|
||||
EXECUTE_COMMAND_NOT_FOUND: "❌ Something went wrong. I was not able to find this command.",
|
||||
EXECUTE_COMMAND_ERROR: "❌ Something went wrong. The command execution has thrown an error.",
|
||||
// Execute Command
|
||||
EXECUTE_COMMAND_NOT_FOUND: '❌ Something went wrong. I was not able to find this command.',
|
||||
EXECUTE_COMMAND_ERROR: '❌ Something went wrong. The command execution has thrown an error.',
|
||||
|
||||
// Language Command
|
||||
LANGUAGE_NAME: "language",
|
||||
LANGUAGE_DESCRIPTION: "⚙️ Change the bots language.",
|
||||
LANGUAGE_KEY_NAME: "name",
|
||||
LANGUAGE_KEY_DESCRIPTION: "What language would you like to set?",
|
||||
LANGUAGE_UPDATED: (language: string) => `The language has been updated to ${language}`,
|
||||
// Language Command
|
||||
LANGUAGE_NAME: 'language',
|
||||
LANGUAGE_DESCRIPTION: '⚙️ Change the bots language.',
|
||||
LANGUAGE_KEY_NAME: 'name',
|
||||
LANGUAGE_KEY_DESCRIPTION: 'What language would you like to set?',
|
||||
LANGUAGE_UPDATED: (language: string) => `The language has been updated to ${language}`,
|
||||
|
||||
// Ping Command
|
||||
PING_NAME: "ping",
|
||||
PING_DESCRIPTION: "🏓 Check whether the bot is online and responsive.",
|
||||
PING_RESPONSE: "🏓 Pong! I am online and responsive! :clock10:",
|
||||
PING_RESPONSE_WITH_TIME: (time: number) => `🏓 Pong! ${time / 1000} seconds! I am online and responsive! :clock10:`,
|
||||
// Ping Command
|
||||
PING_NAME: 'ping',
|
||||
PING_DESCRIPTION: '🏓 Check whether the bot is online and responsive.',
|
||||
PING_RESPONSE: '🏓 Pong! I am online and responsive! :clock10:',
|
||||
PING_RESPONSE_WITH_TIME: (time: number) => `🏓 Pong! ${time / 1000} seconds! I am online and responsive! :clock10:`,
|
||||
} as const;
|
||||
|
||||
export default english;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import english from "./english.js";
|
||||
import english from './english.js';
|
||||
// import french from './french'
|
||||
// import german from './german'
|
||||
// import portuguese from './portuguese'
|
||||
@@ -6,15 +6,15 @@ import english from "./english.js";
|
||||
// import spanish from './spanish'
|
||||
|
||||
const languages: Record<LanguageNames, Language> & Record<string, Language> = {
|
||||
english,
|
||||
// french,
|
||||
// german,
|
||||
// portuguese,
|
||||
// russian,
|
||||
// spanish,
|
||||
english,
|
||||
// french,
|
||||
// german,
|
||||
// portuguese,
|
||||
// russian,
|
||||
// spanish,
|
||||
};
|
||||
|
||||
export default languages;
|
||||
|
||||
export type Language = Record<string, string | string[] | ((...args: any[]) => string)>;
|
||||
export type LanguageNames = "english";
|
||||
export type LanguageNames = 'english';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Embeds from "discordeno/embeds";
|
||||
import { bot } from "../bot.js";
|
||||
import { webhookURLToIDAndToken } from "../utils/webhook.js";
|
||||
import type english from "./english.js";
|
||||
import languages from "./languages.js";
|
||||
import Embeds from 'discordeno/embeds';
|
||||
import { bot } from '../bot.js';
|
||||
import { webhookURLToIDAndToken } from '../utils/webhook.js';
|
||||
import type english from './english.js';
|
||||
import languages from './languages.js';
|
||||
|
||||
const MISSING_TRANSLATION_WEBHOOK = process.env.MISSING_TRANSLATION_WEBHOOK;
|
||||
|
||||
@@ -10,75 +10,75 @@ const MISSING_TRANSLATION_WEBHOOK = process.env.MISSING_TRANSLATION_WEBHOOK;
|
||||
export const serverLanguages = new Map<bigint, keyof typeof languages>();
|
||||
|
||||
export function translate<K extends translationKeys>(
|
||||
guildIdOrLanguage: bigint | keyof typeof languages,
|
||||
key: K,
|
||||
...params: getArgs<K>
|
||||
guildIdOrLanguage: bigint | keyof typeof languages,
|
||||
key: K,
|
||||
...params: getArgs<K>
|
||||
): string {
|
||||
const language = getLanguage(guildIdOrLanguage);
|
||||
let value: string | ((...any: any[]) => string) | string[] | undefined = languages[language]?.[key];
|
||||
const language = getLanguage(guildIdOrLanguage);
|
||||
let value: string | ((...any: any[]) => string) | string[] | undefined = languages[language]?.[key];
|
||||
|
||||
// Was not able to be translated
|
||||
if (!value) {
|
||||
// Check if this key is available in english
|
||||
if (language !== "english") {
|
||||
value = languages.english[key];
|
||||
}
|
||||
// Was not able to be translated
|
||||
if (!value) {
|
||||
// Check if this key is available in english
|
||||
if (language !== 'english') {
|
||||
value = languages.english[key];
|
||||
}
|
||||
|
||||
// Still not found in english so default to using the KEY_ITSELF
|
||||
if (!value) value = key;
|
||||
// Still not found in english so default to using the KEY_ITSELF
|
||||
if (!value) value = key;
|
||||
|
||||
// Send a log webhook so the devs know sth is missing
|
||||
missingTranslation(language, key);
|
||||
}
|
||||
// Send a log webhook so the devs know sth is missing
|
||||
missingTranslation(language, key);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) return value.join("\n");
|
||||
if (Array.isArray(value)) return value.join('\n');
|
||||
|
||||
if (typeof value === "function") return value(...(params || []));
|
||||
if (typeof value === 'function') return value(...(params || []));
|
||||
|
||||
return value ;
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Get the language this guild has set, will always return "english" if it is not in cache */
|
||||
export function getLanguage(guildIdOrLanguage: bigint | keyof typeof languages) {
|
||||
return typeof guildIdOrLanguage === "string"
|
||||
? guildIdOrLanguage
|
||||
: serverLanguages.get(guildIdOrLanguage) ?? "english";
|
||||
return typeof guildIdOrLanguage === 'string'
|
||||
? guildIdOrLanguage
|
||||
: serverLanguages.get(guildIdOrLanguage) ?? 'english';
|
||||
}
|
||||
|
||||
export async function loadLanguage(guildId: bigint) {
|
||||
// TODO: add this settings
|
||||
// const settings = await database.findOne('guilds', guildId)
|
||||
const settings = { language: "undefined" };
|
||||
// TODO: add this settings
|
||||
// const settings = await database.findOne('guilds', guildId)
|
||||
const settings = { language: 'undefined' };
|
||||
|
||||
if (settings?.language && languages[settings.language]) {
|
||||
serverLanguages.set(guildId, settings.language);
|
||||
} else serverLanguages.set(guildId, "english");
|
||||
if (settings?.language && languages[settings.language]) {
|
||||
serverLanguages.set(guildId, settings.language);
|
||||
} else serverLanguages.set(guildId, 'english');
|
||||
}
|
||||
|
||||
/** Send a webhook for a missing translation key */
|
||||
export async function missingTranslation(language: keyof typeof languages, key: string) {
|
||||
if (!MISSING_TRANSLATION_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(MISSING_TRANSLATION_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
if (!MISSING_TRANSLATION_WEBHOOK) return;
|
||||
const { id, token } = webhookURLToIDAndToken(MISSING_TRANSLATION_WEBHOOK);
|
||||
if (!id || !token) return;
|
||||
|
||||
const embeds = new Embeds()
|
||||
.setTitle("Missing Translation")
|
||||
.setColor("RANDOM")
|
||||
.addField("Language", language, true)
|
||||
.addField("Key", key, true);
|
||||
const embeds = new Embeds()
|
||||
.setTitle('Missing Translation')
|
||||
.setColor('RANDOM')
|
||||
.addField('Language', language, true)
|
||||
.addField('Key', key, true);
|
||||
|
||||
await bot.helpers
|
||||
.sendWebhookMessage(bot.transformers.snowflake(id), token, {
|
||||
// SETUP-DD-TEMP: If you wish to make it @ mention you, please edit the next line.
|
||||
// content: `<@${owner id here}>`,
|
||||
embeds,
|
||||
wait: false,
|
||||
})
|
||||
.catch(bot.logger.error);
|
||||
await bot.helpers
|
||||
.sendWebhookMessage(bot.transformers.snowflake(id), token, {
|
||||
// SETUP-DD-TEMP: If you wish to make it @ mention you, please edit the next line.
|
||||
// content: `<@${owner id here}>`,
|
||||
embeds,
|
||||
wait: false,
|
||||
})
|
||||
.catch(bot.logger.error);
|
||||
}
|
||||
|
||||
// type translationKeys = keyof typeof english | string
|
||||
export type translationKeys = keyof typeof english;
|
||||
type getArgs<K extends translationKeys> = typeof english[K] extends (...any: any[]) => unknown
|
||||
? Parameters<typeof english[K]>
|
||||
: [];
|
||||
? Parameters<typeof english[K]>
|
||||
: [];
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// This file allows you to tell typescript about any additions you have made to the internal discordeno objects.
|
||||
import type { Interaction, InteractionCallbackData, InteractionResponse, Message } from "discordeno";
|
||||
import type { Interaction, InteractionCallbackData, InteractionResponse, Message } from 'discordeno';
|
||||
|
||||
export interface InteractionWithCustomProps extends Interaction {
|
||||
// Normally, to send a response you would have to do something like bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: "text here" } })
|
||||
// But with this reply method we added, it is as simple as interaction.reply("text here").
|
||||
// Feel free to delete these comments once you have understood the concept.
|
||||
/** Send a reply to an interaction. */
|
||||
reply: (response: InteractionResponse | string) => Promise<Message | undefined>;
|
||||
/** Edit a deferred reply of an interaction. */
|
||||
editReply: (response: InteractionCallbackData | string) => Promise<Message | undefined>;
|
||||
// Normally, to send a response you would have to do something like bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: "text here" } })
|
||||
// But with this reply method we added, it is as simple as interaction.reply("text here").
|
||||
// Feel free to delete these comments once you have understood the concept.
|
||||
/** Send a reply to an interaction. */
|
||||
reply: (response: InteractionResponse | string) => Promise<Message | undefined>;
|
||||
/** Edit a deferred reply of an interaction. */
|
||||
editReply: (response: InteractionCallbackData | string) => Promise<Message | undefined>;
|
||||
}
|
||||
|
||||
@@ -1,151 +1,151 @@
|
||||
import type { Interaction, Member, Message } from "discordeno";
|
||||
import { bot } from "../bot.js";
|
||||
import type { Interaction, Member, Message } from 'discordeno';
|
||||
import { bot } from '../bot.js';
|
||||
|
||||
export async function needMessage(
|
||||
memberId: bigint,
|
||||
channelId: bigint,
|
||||
options: MessageCollectorOptions & { amount?: 1 },
|
||||
memberId: bigint,
|
||||
channelId: bigint,
|
||||
options: MessageCollectorOptions & { amount?: 1 },
|
||||
): Promise<Message>;
|
||||
export async function needMessage(
|
||||
memberId: bigint,
|
||||
channelId: bigint,
|
||||
options: MessageCollectorOptions & { amount?: number },
|
||||
memberId: bigint,
|
||||
channelId: bigint,
|
||||
options: MessageCollectorOptions & { amount?: number },
|
||||
): Promise<Message[]>;
|
||||
export async function needMessage(memberId: bigint, channelId: bigint): Promise<Message>;
|
||||
export async function needMessage(memberId: bigint, channelId: bigint, options?: MessageCollectorOptions) {
|
||||
const messages = await collectMessages({
|
||||
key: memberId,
|
||||
channelId,
|
||||
createdAt: Date.now(),
|
||||
filter: options?.filter || ((msg) => memberId === msg.authorId),
|
||||
amount: options?.amount || 1,
|
||||
duration: options?.duration || (1000 * 60 * 5),
|
||||
});
|
||||
const messages = await collectMessages({
|
||||
key: memberId,
|
||||
channelId,
|
||||
createdAt: Date.now(),
|
||||
filter: options?.filter || ((msg) => memberId === msg.authorId),
|
||||
amount: options?.amount || 1,
|
||||
duration: options?.duration || 1000 * 60 * 5,
|
||||
});
|
||||
|
||||
return (options?.amount || 1) > 1 ? messages : messages[0];
|
||||
return (options?.amount || 1) > 1 ? messages : messages[0];
|
||||
}
|
||||
|
||||
export async function collectMessages(options: CollectMessagesOptions): Promise<Message[]> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
bot.collectors.messages.get(options.key)?.reject(
|
||||
"A new collector began before the user responded to the previous one.",
|
||||
);
|
||||
return await new Promise((resolve, reject) => {
|
||||
bot.collectors.messages
|
||||
.get(options.key)
|
||||
?.reject('A new collector began before the user responded to the previous one.');
|
||||
|
||||
bot.collectors.messages.set(options.key, {
|
||||
...options,
|
||||
messages: [],
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
bot.collectors.messages.set(options.key, {
|
||||
...options,
|
||||
messages: [],
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function processMessageCollectors(message: Message) {
|
||||
// IGNORE DMS
|
||||
if (!message.guildId) return;
|
||||
// IGNORE DMS
|
||||
if (!message.guildId) return;
|
||||
|
||||
const collector = bot.collectors.messages.get(message.authorId);
|
||||
// This user has no collectors pending or the message is in a different channel
|
||||
if (!collector || message.channelId !== collector.channelId) return;
|
||||
// This message is a response to a collector. Now running the filter function.
|
||||
if (!collector.filter(message)) return;
|
||||
const collector = bot.collectors.messages.get(message.authorId);
|
||||
// This user has no collectors pending or the message is in a different channel
|
||||
if (!collector || message.channelId !== collector.channelId) return;
|
||||
// This message is a response to a collector. Now running the filter function.
|
||||
if (!collector.filter(message)) return;
|
||||
|
||||
// If the necessary amount has been collected
|
||||
if (collector.amount === 1 || collector.amount === collector.messages.length + 1) {
|
||||
// Remove the collector
|
||||
bot.collectors.messages.delete(message.authorId);
|
||||
// Resolve the collector
|
||||
return collector.resolve([...collector.messages, message]);
|
||||
}
|
||||
// If the necessary amount has been collected
|
||||
if (collector.amount === 1 || collector.amount === collector.messages.length + 1) {
|
||||
// Remove the collector
|
||||
bot.collectors.messages.delete(message.authorId);
|
||||
// Resolve the collector
|
||||
return collector.resolve([...collector.messages, message]);
|
||||
}
|
||||
|
||||
// More messages still need to be collected
|
||||
collector.messages.push(message);
|
||||
// More messages still need to be collected
|
||||
collector.messages.push(message);
|
||||
}
|
||||
|
||||
export interface BaseCollectorOptions {
|
||||
/** The amount of messages to collect before resolving. Defaults to 1 */
|
||||
amount?: number;
|
||||
/** The amount of milliseconds this should collect for before expiring. Defaults to 5 minutes. */
|
||||
duration?: number;
|
||||
/** The amount of messages to collect before resolving. Defaults to 1 */
|
||||
amount?: number;
|
||||
/** The amount of milliseconds this should collect for before expiring. Defaults to 5 minutes. */
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export interface MessageCollectorOptions extends BaseCollectorOptions {
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (message: Message) => boolean;
|
||||
/** The amount of messages to collect before resolving. Defaults to 1 */
|
||||
amount?: number;
|
||||
/** The amount of milliseconds this should collect for before expiring. Defaults to 5 minutes. */
|
||||
duration?: number;
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (message: Message) => boolean;
|
||||
/** The amount of messages to collect before resolving. Defaults to 1 */
|
||||
amount?: number;
|
||||
/** The amount of milliseconds this should collect for before expiring. Defaults to 5 minutes. */
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export interface ReactionCollectorOptions extends BaseCollectorOptions {
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (userId: bigint, reaction: string, message: Message | { id: string }) => boolean;
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (userId: bigint, reaction: string, message: Message | { id: string }) => boolean;
|
||||
}
|
||||
|
||||
export interface BaseCollectorCreateOptions {
|
||||
/** The unique key that will be used to get responses for this. Ideally, meant to be for member id. */
|
||||
key: bigint;
|
||||
/** The amount of messages to collect before resolving. */
|
||||
amount: number;
|
||||
/** The timestamp when this collector was created */
|
||||
createdAt: number;
|
||||
/** The duration in milliseconds how long this collector should last. */
|
||||
duration: number;
|
||||
/** The unique key that will be used to get responses for this. Ideally, meant to be for member id. */
|
||||
key: bigint;
|
||||
/** The amount of messages to collect before resolving. */
|
||||
amount: number;
|
||||
/** The timestamp when this collector was created */
|
||||
createdAt: number;
|
||||
/** The duration in milliseconds how long this collector should last. */
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export interface CollectMessagesOptions extends BaseCollectorCreateOptions {
|
||||
/** The channel Id where this is listening to */
|
||||
channelId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (message: Message) => boolean;
|
||||
/** The channel Id where this is listening to */
|
||||
channelId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (message: Message) => boolean;
|
||||
}
|
||||
|
||||
export interface CollectReactionsOptions extends BaseCollectorCreateOptions {
|
||||
/** The message Id where this is listening to */
|
||||
messageId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (userId: bigint, reaction: string, message: Message | { id: string }) => boolean;
|
||||
/** The message Id where this is listening to */
|
||||
messageId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (userId: bigint, reaction: string, message: Message | { id: string }) => boolean;
|
||||
}
|
||||
|
||||
export interface MessageCollector extends CollectMessagesOptions {
|
||||
resolve: (value: Message[] | PromiseLike<Message[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the messages are stored if the amount to collect is more than 1. */
|
||||
messages: Message[];
|
||||
resolve: (value: Message[] | PromiseLike<Message[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the messages are stored if the amount to collect is more than 1. */
|
||||
messages: Message[];
|
||||
}
|
||||
|
||||
export interface ReactionCollector extends CollectReactionsOptions {
|
||||
resolve: (value: string[] | PromiseLike<string[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the reactions are stored if the amount to collect is more than 1. */
|
||||
reactions: string[];
|
||||
resolve: (value: string[] | PromiseLike<string[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the reactions are stored if the amount to collect is more than 1. */
|
||||
reactions: string[];
|
||||
}
|
||||
|
||||
export interface CollectButtonOptions extends BaseCollectorCreateOptions {
|
||||
/** The message Id where this is listening to */
|
||||
messageId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (message: Message, member?: Member) => boolean;
|
||||
/** The message Id where this is listening to */
|
||||
messageId: bigint;
|
||||
/** Function that will filter messages to determine whether to collect this message */
|
||||
filter: (message: Message, member?: Member) => boolean;
|
||||
}
|
||||
|
||||
export interface ButtonCollector extends CollectButtonOptions {
|
||||
resolve: (value: ButtonCollectorReturn[] | PromiseLike<ButtonCollectorReturn[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the buttons are stored if the amount to collect is more than 1. */
|
||||
buttons: ButtonCollectorReturn[];
|
||||
resolve: (value: ButtonCollectorReturn[] | PromiseLike<ButtonCollectorReturn[]>) => void;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
reject: (reason?: any) => void;
|
||||
/** Where the buttons are stored if the amount to collect is more than 1. */
|
||||
buttons: ButtonCollectorReturn[];
|
||||
}
|
||||
|
||||
export interface ButtonCollectorOptions extends BaseCollectorOptions {
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (message: Message, member?: Member) => boolean;
|
||||
/** Function that will filter messages to determine whether to collect this message. Defaults to making sure the message is sent by the same member. */
|
||||
filter?: (message: Message, member?: Member) => boolean;
|
||||
}
|
||||
|
||||
export interface ButtonCollectorReturn {
|
||||
customId: string;
|
||||
interaction: Omit<Interaction, "member">;
|
||||
member?: Member;
|
||||
customId: string;
|
||||
interaction: Omit<Interaction, 'member'>;
|
||||
member?: Member;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BotWithCustomProps } from "../../bot.js";
|
||||
import { customizeTransformers } from "./transformers/mod.js";
|
||||
import type { BotWithCustomProps } from '../../bot.js';
|
||||
import { customizeTransformers } from './transformers/mod.js';
|
||||
|
||||
export function customizeInternals(bot: BotWithCustomProps) {
|
||||
customizeTransformers(bot);
|
||||
customizeTransformers(bot);
|
||||
}
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
// Only keep the properties your bot uses. If your bot does not use emojis in cache, you can save all that memory.
|
||||
// This file is currently disabled, but you can enable it should you choose when you go the customizer file.
|
||||
// Feel free to delete this comment or file as you wish.
|
||||
import type { Guild } from "discordeno";
|
||||
import { Collection } from "discordeno";
|
||||
import type { BotWithCustomProps } from "../../../bot.js";
|
||||
import type { Guild } from 'discordeno';
|
||||
import { Collection } from 'discordeno';
|
||||
import type { BotWithCustomProps } from '../../../bot.js';
|
||||
|
||||
export function customizeGuildTransformer(bot: BotWithCustomProps) {
|
||||
bot.transformers.guild = function (bot, payload) {
|
||||
const guildId = bot.transformers.snowflake(payload.guild.id);
|
||||
bot.transformers.guild = function (bot, payload) {
|
||||
const guildId = bot.transformers.snowflake(payload.guild.id);
|
||||
|
||||
return {
|
||||
name: payload.guild.name,
|
||||
joinedAt: payload.guild.joined_at ? Date.parse(payload.guild.joined_at) : undefined,
|
||||
memberCount: payload.guild.member_count ?? 0,
|
||||
shardId: payload.shardId,
|
||||
icon: payload.guild.icon ? bot.utils.iconHashToBigInt(payload.guild.icon) : undefined,
|
||||
roles: new Collection(
|
||||
payload.guild.roles?.map((role) => {
|
||||
const result = bot.transformers.role(bot, { role, guildId });
|
||||
return [result.id, result];
|
||||
}),
|
||||
),
|
||||
id: guildId,
|
||||
ownerId: bot.transformers.snowflake(payload.guild.owner_id),
|
||||
} as unknown as Guild;
|
||||
};
|
||||
return {
|
||||
name: payload.guild.name,
|
||||
joinedAt: payload.guild.joined_at ? Date.parse(payload.guild.joined_at) : undefined,
|
||||
memberCount: payload.guild.member_count ?? 0,
|
||||
shardId: payload.shardId,
|
||||
icon: payload.guild.icon ? bot.utils.iconHashToBigInt(payload.guild.icon) : undefined,
|
||||
roles: new Collection(
|
||||
payload.guild.roles?.map((role) => {
|
||||
const result = bot.transformers.role(bot, { role, guildId });
|
||||
return [result.id, result];
|
||||
}),
|
||||
),
|
||||
id: guildId,
|
||||
ownerId: bot.transformers.snowflake(payload.guild.owner_id),
|
||||
} as unknown as Guild;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
// SETUP-DD-TEMP: This file serves as an example, of how to customize internal discordeno objects. Feel free to use, add more or remove as desired.
|
||||
import type { InteractionCallbackData, InteractionResponse} from "discordeno";
|
||||
import { InteractionResponseTypes } from "discordeno";
|
||||
import type { BotWithCustomProps } from "../../../bot.js";
|
||||
import type { InteractionCallbackData, InteractionResponse } from 'discordeno';
|
||||
import { InteractionResponseTypes } from 'discordeno';
|
||||
import type { BotWithCustomProps } from '../../../bot.js';
|
||||
|
||||
export function customizeInteractionTransformer(bot: BotWithCustomProps) {
|
||||
// Store the internal transformer function
|
||||
const oldInteraction = bot.transformers.interaction;
|
||||
// Store the internal transformer function
|
||||
const oldInteraction = bot.transformers.interaction;
|
||||
|
||||
// Overwrite the internal function.
|
||||
bot.transformers.interaction = function (_, payload) {
|
||||
// Run the old function to get the internal value.
|
||||
const interaction = oldInteraction(bot, payload);
|
||||
// Overwrite the internal function.
|
||||
bot.transformers.interaction = function (_, payload) {
|
||||
// Run the old function to get the internal value.
|
||||
const interaction = oldInteraction(bot, payload);
|
||||
|
||||
// Add anything to this object. In this case we add a Interaction.reply() method.
|
||||
Object.defineProperty(interaction, "reply", {
|
||||
value: function (response: InteractionResponse | string) {
|
||||
if (typeof response === "string") {
|
||||
response = { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: response } };
|
||||
}
|
||||
// Add anything to this object. In this case we add a Interaction.reply() method.
|
||||
Object.defineProperty(interaction, 'reply', {
|
||||
value: function (response: InteractionResponse | string) {
|
||||
if (typeof response === 'string') {
|
||||
response = { type: InteractionResponseTypes.ChannelMessageWithSource, data: { content: response } };
|
||||
}
|
||||
|
||||
return bot.helpers.sendInteractionResponse(interaction.id, interaction.token, response);
|
||||
},
|
||||
});
|
||||
Object.defineProperty(interaction, "editReply", {
|
||||
value: function (response: InteractionCallbackData | string) {
|
||||
if (typeof response === "string") {
|
||||
response = { content: response };
|
||||
}
|
||||
return bot.helpers.sendInteractionResponse(interaction.id, interaction.token, response);
|
||||
},
|
||||
});
|
||||
Object.defineProperty(interaction, 'editReply', {
|
||||
value: function (response: InteractionCallbackData | string) {
|
||||
if (typeof response === 'string') {
|
||||
response = { content: response };
|
||||
}
|
||||
|
||||
return bot.helpers.editOriginalInteractionResponse(interaction.token, response);
|
||||
},
|
||||
});
|
||||
// Add as many properties or methods you would like here.
|
||||
// NOTE: Whenever you add anything here, in order to get nice autocomplete you should also add it to the src/types/discordeno.ts file.
|
||||
return bot.helpers.editOriginalInteractionResponse(interaction.token, response);
|
||||
},
|
||||
});
|
||||
// Add as many properties or methods you would like here.
|
||||
// NOTE: Whenever you add anything here, in order to get nice autocomplete you should also add it to the src/types/discordeno.ts file.
|
||||
|
||||
// Return the new customized object.
|
||||
return interaction;
|
||||
};
|
||||
// Return the new customized object.
|
||||
return interaction;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { BotWithCustomProps } from "../../../bot.js";
|
||||
import type { BotWithCustomProps } from '../../../bot.js';
|
||||
// SETUP-DD-TEMP: Enable this comment if you want to enable this customizer.
|
||||
// import { customizeGuildTransformer } from "./guild.js";
|
||||
import { customizeInteractionTransformer } from "./interaction.js";
|
||||
import { customizeInteractionTransformer } from './interaction.js';
|
||||
|
||||
export function customizeTransformers(bot: BotWithCustomProps) {
|
||||
customizeInteractionTransformer(bot);
|
||||
// SETUP-DD-TEMP: Enable this comment if you want to enable this customizer.
|
||||
// customizeGuildTransformer(bot);
|
||||
customizeInteractionTransformer(bot);
|
||||
// SETUP-DD-TEMP: Enable this comment if you want to enable this customizer.
|
||||
// customizeGuildTransformer(bot);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import type {
|
||||
ApplicationCommandOptionTypes,
|
||||
ApplicationCommandTypes,
|
||||
Bot,
|
||||
Channel,
|
||||
Interaction,
|
||||
Member,
|
||||
PermissionStrings,
|
||||
Role,
|
||||
User,
|
||||
} from "discordeno";
|
||||
import type english from "../../languages/english.js";
|
||||
import type { translationKeys } from "../../languages/translate.js";
|
||||
import type { InteractionWithCustomProps } from "../../typings/discordeno.js";
|
||||
import type { PermissionLevelHandlers } from "./permLevels.js";
|
||||
ApplicationCommandOptionTypes,
|
||||
ApplicationCommandTypes,
|
||||
Bot,
|
||||
Channel,
|
||||
Interaction,
|
||||
Member,
|
||||
PermissionStrings,
|
||||
Role,
|
||||
User,
|
||||
} from 'discordeno';
|
||||
import type english from '../../languages/english.js';
|
||||
import type { translationKeys } from '../../languages/translate.js';
|
||||
import type { InteractionWithCustomProps } from '../../typings/discordeno.js';
|
||||
import type { PermissionLevelHandlers } from './permLevels.js';
|
||||
|
||||
export function createCommand<T extends readonly ArgumentDefinition[]>(command: Command<T>) {
|
||||
return command;
|
||||
return command;
|
||||
}
|
||||
|
||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
||||
@@ -25,268 +25,283 @@ type Identity<T> = { [P in keyof T]: T[P] };
|
||||
// TODO: make required by default true
|
||||
// Define each of the types here
|
||||
interface BaseDefinition {
|
||||
description: translationKeys;
|
||||
description: translationKeys;
|
||||
}
|
||||
|
||||
// Subcommand
|
||||
type SubcommandArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.SubCommand;
|
||||
// options: Omit<ArgumentDefinition, 'SubcommandArgumentDefinition' | 'SubcommandGroupArgumentDefinition'>[]
|
||||
options?: readonly ArgumentDefinition[];
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.SubCommand;
|
||||
// options: Omit<ArgumentDefinition, 'SubcommandArgumentDefinition' | 'SubcommandGroupArgumentDefinition'>[]
|
||||
options?: readonly ArgumentDefinition[];
|
||||
};
|
||||
|
||||
// SubcommandGroup
|
||||
type SubcommandGroupArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.SubCommandGroup;
|
||||
options: readonly SubcommandArgumentDefinition[];
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.SubCommandGroup;
|
||||
options: readonly SubcommandArgumentDefinition[];
|
||||
};
|
||||
|
||||
// String
|
||||
type StringArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.String;
|
||||
choices?: ReadonlyArray<{ name: string; value: string }>;
|
||||
required?: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.String;
|
||||
choices?: ReadonlyArray<{ name: string; value: string }>;
|
||||
required?: true;
|
||||
};
|
||||
type StringOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.String;
|
||||
choices?: ReadonlyArray<{ name: string; value: string }>;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.String;
|
||||
choices?: ReadonlyArray<{ name: string; value: string }>;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// Integer
|
||||
type IntegerArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Integer;
|
||||
choices?: ReadonlyArray<{ name: string; value: number }>;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Integer;
|
||||
choices?: ReadonlyArray<{ name: string; value: number }>;
|
||||
required: true;
|
||||
};
|
||||
type IntegerOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Integer;
|
||||
choices?: ReadonlyArray<{ name: string; value: number }>;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Integer;
|
||||
choices?: ReadonlyArray<{ name: string; value: number }>;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// BOOLEAN
|
||||
type BooleanArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Boolean;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Boolean;
|
||||
required: true;
|
||||
};
|
||||
type BooleanOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Boolean;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Boolean;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// USER
|
||||
type UserArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.User;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.User;
|
||||
required: true;
|
||||
};
|
||||
type UserOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.User;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.User;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// CHANNEL
|
||||
type ChannelArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Channel;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Channel;
|
||||
required: true;
|
||||
};
|
||||
type ChannelOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Channel;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Channel;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// ROLE
|
||||
type RoleArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Role;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Role;
|
||||
required: true;
|
||||
};
|
||||
type RoleOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Role;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Role;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// MENTIONABLE
|
||||
type MentionableArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Mentionable;
|
||||
required: true;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Mentionable;
|
||||
required: true;
|
||||
};
|
||||
type MentionableOptionalArgumentDefinition<N extends translationKeys = translationKeys> = BaseDefinition & {
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Mentionable;
|
||||
required?: false;
|
||||
name: N;
|
||||
type: ApplicationCommandOptionTypes.Mentionable;
|
||||
required?: false;
|
||||
};
|
||||
|
||||
// Add each of known ArgumentDefinitions to this union.
|
||||
export type ArgumentDefinition =
|
||||
| StringArgumentDefinition
|
||||
| StringOptionalArgumentDefinition
|
||||
| IntegerArgumentDefinition
|
||||
| IntegerOptionalArgumentDefinition
|
||||
| BooleanArgumentDefinition
|
||||
| BooleanOptionalArgumentDefinition
|
||||
| UserArgumentDefinition
|
||||
| UserOptionalArgumentDefinition
|
||||
| ChannelArgumentDefinition
|
||||
| ChannelOptionalArgumentDefinition
|
||||
| RoleArgumentDefinition
|
||||
| RoleOptionalArgumentDefinition
|
||||
| MentionableArgumentDefinition
|
||||
| MentionableOptionalArgumentDefinition
|
||||
| SubcommandArgumentDefinition
|
||||
| SubcommandGroupArgumentDefinition;
|
||||
| StringArgumentDefinition
|
||||
| StringOptionalArgumentDefinition
|
||||
| IntegerArgumentDefinition
|
||||
| IntegerOptionalArgumentDefinition
|
||||
| BooleanArgumentDefinition
|
||||
| BooleanOptionalArgumentDefinition
|
||||
| UserArgumentDefinition
|
||||
| UserOptionalArgumentDefinition
|
||||
| ChannelArgumentDefinition
|
||||
| ChannelOptionalArgumentDefinition
|
||||
| RoleArgumentDefinition
|
||||
| RoleOptionalArgumentDefinition
|
||||
| MentionableArgumentDefinition
|
||||
| MentionableOptionalArgumentDefinition
|
||||
| SubcommandArgumentDefinition
|
||||
| SubcommandGroupArgumentDefinition;
|
||||
|
||||
type getName<K extends translationKeys> = typeof english[K] extends string ? typeof english[K] : never;
|
||||
|
||||
// OPTIONALS MUST BE FIRST!!!
|
||||
export type ConvertArgumentDefinitionsToArgs<T extends readonly ArgumentDefinition[]> = Identity<
|
||||
UnionToIntersection<
|
||||
{
|
||||
[P in keyof T]: T[P] extends StringOptionalArgumentDefinition<infer N> // STRING
|
||||
? {
|
||||
// @ts-expect-error TODO: fix this some day
|
||||
[_ in getName<N>]?: T[P]["choices"] extends ReadonlyArray<{ name: string; value: string }> // @ts-expect-error
|
||||
? T[P]["choices"][number]["value"]
|
||||
: string;
|
||||
}
|
||||
: T[P] extends StringArgumentDefinition<infer N> ? {
|
||||
// @ts-expect-error TODO: fix this some day
|
||||
[_ in getName<N>]: T[P]["choices"] extends ReadonlyArray<{ name: string; value: string }> // @ts-expect-error
|
||||
? T[P]["choices"][number]["value"]
|
||||
: string;
|
||||
}
|
||||
// INTEGER
|
||||
: T[P] extends IntegerOptionalArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]?: T[P]["choices"] extends ReadonlyArray<{ name: string; value: number }> // @ts-expect-error
|
||||
? T[P]["choices"][number]["value"]
|
||||
: number;
|
||||
}
|
||||
: T[P] extends IntegerArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]: T[P]["choices"] extends ReadonlyArray<{ name: string; value: number }> // @ts-expect-error
|
||||
? T[P]["choices"][number]["value"]
|
||||
: number;
|
||||
}
|
||||
// BOOLEAN
|
||||
: T[P] extends BooleanOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: boolean }
|
||||
: T[P] extends BooleanArgumentDefinition<infer N> ? { [_ in getName<N>]: boolean }
|
||||
// USER
|
||||
: T[P] extends UserOptionalArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]?: {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: T[P] extends UserArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]: {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
// CHANNEL
|
||||
: T[P] extends ChannelOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Channel }
|
||||
: T[P] extends ChannelArgumentDefinition<infer N> ? { [_ in getName<N>]: Channel }
|
||||
// ROLE
|
||||
: T[P] extends RoleOptionalArgumentDefinition<infer N> ? { [_ in getName<N>]?: Role }
|
||||
: T[P] extends RoleArgumentDefinition<infer N> ? { [_ in getName<N>]: Role }
|
||||
// MENTIONABLE
|
||||
: T[P] extends MentionableOptionalArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]?:
|
||||
| Role
|
||||
| {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: T[P] extends MentionableArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]:
|
||||
| Role
|
||||
| {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
// SUBCOMMAND
|
||||
: T[P] extends SubcommandArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]?: T[P]["options"] extends readonly ArgumentDefinition[] // @ts-expect-error somehow this check does not work
|
||||
? ConvertArgumentDefinitionsToArgs<T[P]["options"]>
|
||||
: {};
|
||||
}
|
||||
// SUBCOMMANDGROUP
|
||||
: T[P] extends SubcommandGroupArgumentDefinition<infer N> ? {
|
||||
[_ in getName<N>]?: ConvertArgumentDefinitionsToArgs<T[P]["options"]>;
|
||||
}
|
||||
: never;
|
||||
}[number]
|
||||
>
|
||||
UnionToIntersection<
|
||||
{
|
||||
[P in keyof T]: T[P] extends StringOptionalArgumentDefinition<infer N> // STRING
|
||||
? {
|
||||
// @ts-expect-error TODO: fix this some day
|
||||
[_ in getName<N>]?: T[P]['choices'] extends ReadonlyArray<{ name: string; value: string }> // @ts-expect-error
|
||||
? T[P]['choices'][number]['value']
|
||||
: string;
|
||||
}
|
||||
: T[P] extends StringArgumentDefinition<infer N>
|
||||
? {
|
||||
// @ts-expect-error TODO: fix this some day
|
||||
[_ in getName<N>]: T[P]['choices'] extends ReadonlyArray<{ name: string; value: string }> // @ts-expect-error
|
||||
? T[P]['choices'][number]['value']
|
||||
: string;
|
||||
}
|
||||
: // INTEGER
|
||||
T[P] extends IntegerOptionalArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]?: T[P]['choices'] extends ReadonlyArray<{ name: string; value: number }> // @ts-expect-error
|
||||
? T[P]['choices'][number]['value']
|
||||
: number;
|
||||
}
|
||||
: T[P] extends IntegerArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]: T[P]['choices'] extends ReadonlyArray<{ name: string; value: number }> // @ts-expect-error
|
||||
? T[P]['choices'][number]['value']
|
||||
: number;
|
||||
}
|
||||
: // BOOLEAN
|
||||
T[P] extends BooleanOptionalArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]?: boolean }
|
||||
: T[P] extends BooleanArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]: boolean }
|
||||
: // USER
|
||||
T[P] extends UserOptionalArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]?: {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: T[P] extends UserArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]: {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: // CHANNEL
|
||||
T[P] extends ChannelOptionalArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]?: Channel }
|
||||
: T[P] extends ChannelArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]: Channel }
|
||||
: // ROLE
|
||||
T[P] extends RoleOptionalArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]?: Role }
|
||||
: T[P] extends RoleArgumentDefinition<infer N>
|
||||
? { [_ in getName<N>]: Role }
|
||||
: // MENTIONABLE
|
||||
T[P] extends MentionableOptionalArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]?:
|
||||
| Role
|
||||
| {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: T[P] extends MentionableArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]:
|
||||
| Role
|
||||
| {
|
||||
user: User;
|
||||
member: Member;
|
||||
};
|
||||
}
|
||||
: // SUBCOMMAND
|
||||
T[P] extends SubcommandArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]?: T[P]['options'] extends readonly ArgumentDefinition[] // @ts-expect-error somehow this check does not work
|
||||
? ConvertArgumentDefinitionsToArgs<T[P]['options']>
|
||||
: {};
|
||||
}
|
||||
: // SUBCOMMANDGROUP
|
||||
T[P] extends SubcommandGroupArgumentDefinition<infer N>
|
||||
? {
|
||||
[_ in getName<N>]?: ConvertArgumentDefinitionsToArgs<T[P]['options']>;
|
||||
}
|
||||
: never;
|
||||
}[number]
|
||||
>
|
||||
>;
|
||||
|
||||
export interface Command<T extends readonly ArgumentDefinition[]> {
|
||||
/** The name of the command, used for both slash and message commands. */
|
||||
name: translationKeys;
|
||||
/** The type of command. */
|
||||
type?: ApplicationCommandTypes;
|
||||
/** The description of the command */
|
||||
description: translationKeys;
|
||||
// TODO: consider type being a string like "number" | "user" for better ux
|
||||
/** The options for the command, used for both slash and message commands. */
|
||||
// options?: ApplicationCommandOption[];
|
||||
options?: T;
|
||||
execute: (bot: Bot, data: InteractionWithCustomProps, args: ConvertArgumentDefinitionsToArgs<T>) => unknown;
|
||||
subcommands?: Record<string, Omit<Command<any>, "subcommands"> & { group?: string }>;
|
||||
/** Whether the command should have a cooldown */
|
||||
cooldown?: {
|
||||
/** How long the user needs to wait after the first execution until he can use the command again */
|
||||
seconds: number;
|
||||
/** How often the user is allowed to use the command until he is in cooldown */
|
||||
allowedUses?: number;
|
||||
};
|
||||
nsfw?: boolean;
|
||||
/** By default false */
|
||||
global?: boolean;
|
||||
/** Dm only by default false */
|
||||
dmOnly?: boolean;
|
||||
/** The name of the command, used for both slash and message commands. */
|
||||
name: translationKeys;
|
||||
/** The type of command. */
|
||||
type?: ApplicationCommandTypes;
|
||||
/** The description of the command */
|
||||
description: translationKeys;
|
||||
// TODO: consider type being a string like "number" | "user" for better ux
|
||||
/** The options for the command, used for both slash and message commands. */
|
||||
// options?: ApplicationCommandOption[];
|
||||
options?: T;
|
||||
execute: (bot: Bot, data: InteractionWithCustomProps, args: ConvertArgumentDefinitionsToArgs<T>) => unknown;
|
||||
subcommands?: Record<string, Omit<Command<any>, 'subcommands'> & { group?: string }>;
|
||||
/** Whether the command should have a cooldown */
|
||||
cooldown?: {
|
||||
/** How long the user needs to wait after the first execution until he can use the command again */
|
||||
seconds: number;
|
||||
/** How often the user is allowed to use the command until he is in cooldown */
|
||||
allowedUses?: number;
|
||||
};
|
||||
nsfw?: boolean;
|
||||
/** By default false */
|
||||
global?: boolean;
|
||||
/** Dm only by default false */
|
||||
dmOnly?: boolean;
|
||||
|
||||
/** VIP only by default false */
|
||||
vipOnly?: boolean;
|
||||
/** VIP only by default false */
|
||||
vipOnly?: boolean;
|
||||
|
||||
advanced?: boolean;
|
||||
advanced?: boolean;
|
||||
|
||||
/** Whether or not this slash command should be enabled right now. Defaults to true. */
|
||||
enabled?: boolean;
|
||||
/** Whether or not this command is still in development and should be setup in the dev server for testing. */
|
||||
dev?: boolean;
|
||||
/** Whether or not this command will take longer than 3s and need to acknowledge to discord. */
|
||||
acknowledge?: boolean;
|
||||
/** Whether or not this slash command should be enabled right now. Defaults to true. */
|
||||
enabled?: boolean;
|
||||
/** Whether or not this command is still in development and should be setup in the dev server for testing. */
|
||||
dev?: boolean;
|
||||
/** Whether or not this command will take longer than 3s and need to acknowledge to discord. */
|
||||
acknowledge?: boolean;
|
||||
|
||||
permissionLevels?:
|
||||
| Array<keyof typeof PermissionLevelHandlers>
|
||||
| ((data: Interaction, command: Command<T>) => boolean | Promise<boolean>);
|
||||
botServerPermissions?: PermissionStrings[];
|
||||
botChannelPermissions?: PermissionStrings[];
|
||||
userServerPermissions?: PermissionStrings[];
|
||||
userChannelPermissions?: PermissionStrings[];
|
||||
permissionLevels?:
|
||||
| Array<keyof typeof PermissionLevelHandlers>
|
||||
| ((data: Interaction, command: Command<T>) => boolean | Promise<boolean>);
|
||||
botServerPermissions?: PermissionStrings[];
|
||||
botChannelPermissions?: PermissionStrings[];
|
||||
userServerPermissions?: PermissionStrings[];
|
||||
userChannelPermissions?: PermissionStrings[];
|
||||
}
|
||||
|
||||
export enum PermissionLevels {
|
||||
Member,
|
||||
Moderator,
|
||||
Admin,
|
||||
ServerOwner,
|
||||
BotSupporter,
|
||||
BotDev,
|
||||
BotOwner,
|
||||
Member,
|
||||
Moderator,
|
||||
Admin,
|
||||
ServerOwner,
|
||||
BotSupporter,
|
||||
BotDev,
|
||||
BotOwner,
|
||||
}
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import COMMANDS from "../../commands/mod.js";
|
||||
import COMMANDS from '../../commands/mod.js';
|
||||
|
||||
export async function validateSlashLimits() {
|
||||
const MAX_ALLOWED_CHARACTERS = 4000;
|
||||
const MAX_ALLOWED_CHARACTERS = 4000;
|
||||
|
||||
const commands = await fetch("https://cmd-counter-play.deno.dev/", {
|
||||
body: JSON.stringify(COMMANDS),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
}).then(async (res) => await res.json()).catch(() => undefined);
|
||||
const commands = await fetch('https://cmd-counter-play.deno.dev/', {
|
||||
body: JSON.stringify(COMMANDS),
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(async (res) => await res.json())
|
||||
.catch(() => undefined);
|
||||
|
||||
if (!commands) return;
|
||||
if (!commands) return;
|
||||
|
||||
const invalidCommandNames: string[] = [];
|
||||
const invalidCommandNames: string[] = [];
|
||||
|
||||
if (commands[0]?.characters > MAX_ALLOWED_CHARACTERS) {
|
||||
for (const command of commands) {
|
||||
if (command.characters <= MAX_ALLOWED_CHARACTERS) continue;
|
||||
if (commands[0]?.characters > MAX_ALLOWED_CHARACTERS) {
|
||||
for (const command of commands) {
|
||||
if (command.characters <= MAX_ALLOWED_CHARACTERS) continue;
|
||||
|
||||
invalidCommandNames.push(command.name);
|
||||
console.log(
|
||||
`[Invalid Command] The ${command.name} is not a valid command. It's total characters are (${command.characters}) which is more than the max allowed ${MAX_ALLOWED_CHARACTERS}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
invalidCommandNames.push(command.name);
|
||||
console.log(
|
||||
`[Invalid Command] The ${command.name} is not a valid command. It's total characters are (${command.characters}) which is more than the max allowed ${MAX_ALLOWED_CHARACTERS}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidCommandNames.length) throw new Error(`[Startup] Invalid commands: ${invalidCommandNames.join(", ")}`);
|
||||
if (invalidCommandNames.length) throw new Error(`[Startup] Invalid commands: ${invalidCommandNames.join(', ')}`);
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import type { Interaction } from "discordeno";
|
||||
import { validatePermissions } from "discordeno/permissions-plugin";
|
||||
import type { Command } from "./createCommand.js";
|
||||
import type { Interaction } from 'discordeno';
|
||||
import { validatePermissions } from 'discordeno/permissions-plugin';
|
||||
import type { Command } from './createCommand.js';
|
||||
|
||||
export default async function hasPermissionLevel(command: Command<any>, payload: Interaction) {
|
||||
// This command doesnt require a perm level so allow the command.
|
||||
if (!command.permissionLevels) return true;
|
||||
// This command doesnt require a perm level so allow the command.
|
||||
if (!command.permissionLevels) return true;
|
||||
|
||||
// If a custom function was provided
|
||||
if (typeof command.permissionLevels === "function") {
|
||||
return await command.permissionLevels(payload, command);
|
||||
}
|
||||
// If a custom function was provided
|
||||
if (typeof command.permissionLevels === 'function') {
|
||||
return await command.permissionLevels(payload, command);
|
||||
}
|
||||
|
||||
// If an array of perm levels was provided
|
||||
for (const permlevel of command.permissionLevels) {
|
||||
// If this user has one of the allowed perm level, the loop is canceled and command is allowed.
|
||||
if (await PermissionLevelHandlers[permlevel](payload, command)) return true;
|
||||
}
|
||||
// If an array of perm levels was provided
|
||||
for (const permlevel of command.permissionLevels) {
|
||||
// If this user has one of the allowed perm level, the loop is canceled and command is allowed.
|
||||
if (await PermissionLevelHandlers[permlevel](payload, command)) return true;
|
||||
}
|
||||
|
||||
// None of the perm levels were met. So cancel the command
|
||||
return false;
|
||||
// None of the perm levels were met. So cancel the command
|
||||
return false;
|
||||
}
|
||||
|
||||
export const PermissionLevelHandlers: Record<
|
||||
keyof typeof PermissionLevels,
|
||||
(payload: Interaction, command: Command<any>) => boolean | Promise<boolean>
|
||||
keyof typeof PermissionLevels,
|
||||
(payload: Interaction, command: Command<any>) => boolean | Promise<boolean>
|
||||
> = {
|
||||
MEMBER: () => true,
|
||||
MODERATOR: (payload) =>
|
||||
Boolean(payload.member?.permissions) && validatePermissions(payload.member!.permissions!, ["MANAGE_GUILD"]),
|
||||
ADMIN: (payload) =>
|
||||
Boolean(payload.member?.permissions) && validatePermissions(payload.member!.permissions!, ["ADMINISTRATOR"]),
|
||||
// TODO(cache): fix this
|
||||
SERVER_OWNER: () => false,
|
||||
BOT_SUPPORT: () => false,
|
||||
BOT_DEVS: () => false,
|
||||
BOT_OWNERS: (payload) => [130136895395987456n, 615542460151496705n].includes(payload.user.id),
|
||||
MEMBER: () => true,
|
||||
MODERATOR: (payload) =>
|
||||
Boolean(payload.member?.permissions) && validatePermissions(payload.member!.permissions!, ['MANAGE_GUILD']),
|
||||
ADMIN: (payload) =>
|
||||
Boolean(payload.member?.permissions) && validatePermissions(payload.member!.permissions!, ['ADMINISTRATOR']),
|
||||
// TODO(cache): fix this
|
||||
SERVER_OWNER: () => false,
|
||||
BOT_SUPPORT: () => false,
|
||||
BOT_DEVS: () => false,
|
||||
BOT_OWNERS: (payload) => [130136895395987456n, 615542460151496705n].includes(payload.user.id),
|
||||
};
|
||||
|
||||
export enum PermissionLevels {
|
||||
MEMBER,
|
||||
MODERATOR,
|
||||
ADMIN,
|
||||
SERVER_OWNER,
|
||||
BOT_SUPPORT,
|
||||
BOT_DEVS,
|
||||
BOT_OWNERS,
|
||||
MEMBER,
|
||||
MODERATOR,
|
||||
ADMIN,
|
||||
SERVER_OWNER,
|
||||
BOT_SUPPORT,
|
||||
BOT_DEVS,
|
||||
BOT_OWNERS,
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
import type { ApplicationCommandOption, Bot } from "discordeno";
|
||||
import { ApplicationCommandTypes } from "discordeno";
|
||||
import { prisma } from "../../../prisma.js";
|
||||
import { bot } from "../../bot.js";
|
||||
import COMMANDS from "../../commands/mod.js";
|
||||
import { serverLanguages, translate } from "../../languages/translate.js";
|
||||
import type { ArgumentDefinition } from "./createCommand.js";
|
||||
import type { ApplicationCommandOption, Bot } from 'discordeno';
|
||||
import { ApplicationCommandTypes } from 'discordeno';
|
||||
import { prisma } from '../../../prisma.js';
|
||||
import { bot } from '../../bot.js';
|
||||
import COMMANDS from '../../commands/mod.js';
|
||||
import { serverLanguages, translate } from '../../languages/translate.js';
|
||||
import type { ArgumentDefinition } from './createCommand.js';
|
||||
|
||||
const DEV_SERVER_ID = process.env.DEV_SERVER_ID as string;
|
||||
|
||||
export async function updateDevCommands(bot: Bot) {
|
||||
const cmds = Object.entries(COMMANDS)
|
||||
// ONLY DEV COMMANDS
|
||||
.filter(([_name, command]) => command?.dev);
|
||||
const cmds = Object.entries(COMMANDS)
|
||||
// ONLY DEV COMMANDS
|
||||
.filter(([_name, command]) => command?.dev);
|
||||
|
||||
if (!cmds.length) return;
|
||||
if (!cmds.length) return;
|
||||
|
||||
// DEV RELATED COMMANDS, USE upsertGlobalApplicationCommands TO UPDATE GLOBALLY
|
||||
await bot.helpers.upsertGuildApplicationCommands(
|
||||
bot.transformers.snowflake(DEV_SERVER_ID),
|
||||
cmds.map(([name, command]) => {
|
||||
const translatedName = translate(DEV_SERVER_ID, command.name);
|
||||
const translatedDescription = command.description ? translate(DEV_SERVER_ID, command.description) : "";
|
||||
// DEV RELATED COMMANDS, USE upsertGlobalApplicationCommands TO UPDATE GLOBALLY
|
||||
await bot.helpers.upsertGuildApplicationCommands(
|
||||
bot.transformers.snowflake(DEV_SERVER_ID),
|
||||
cmds.map(([name, command]) => {
|
||||
const translatedName = translate(DEV_SERVER_ID, command.name);
|
||||
const translatedDescription = command.description ? translate(DEV_SERVER_ID, command.description) : '';
|
||||
|
||||
if (command.type && command.type !== ApplicationCommandTypes.ChatInput) {
|
||||
return {
|
||||
name: (translatedName || name).toLowerCase(),
|
||||
type: command.type,
|
||||
};
|
||||
}
|
||||
if (command.type && command.type !== ApplicationCommandTypes.ChatInput) {
|
||||
return {
|
||||
name: (translatedName || name).toLowerCase(),
|
||||
type: command.type,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: (translatedName || name).toLowerCase(),
|
||||
description: translatedDescription || command.description,
|
||||
options: command.options
|
||||
? createOptions(bot.transformers.snowflake(DEV_SERVER_ID), command.options, command.name)
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
return {
|
||||
name: (translatedName || name).toLowerCase(),
|
||||
description: translatedDescription || command.description,
|
||||
options: command.options
|
||||
? createOptions(bot.transformers.snowflake(DEV_SERVER_ID), command.options, command.name)
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// SETUP-DD-TEMP: You can make this able to be updated dynicamally by moving this value to something in the database and having a command to update it on the fly or as part of CI.
|
||||
@@ -45,63 +45,63 @@ export const CURRENT_SLASH_COMMAND_VERSION = 1;
|
||||
|
||||
/** Whether the guild has the latest slash command version */
|
||||
export async function usesLatestCommandVersion(guildId: bigint): Promise<boolean> {
|
||||
return (await getCurrentCommandVersion(guildId)) === CURRENT_SLASH_COMMAND_VERSION;
|
||||
return (await getCurrentCommandVersion(guildId)) === CURRENT_SLASH_COMMAND_VERSION;
|
||||
}
|
||||
|
||||
/** Get the current slash command version for this guild */
|
||||
export async function getCurrentCommandVersion(guildId: bigint): Promise<number> {
|
||||
if (bot.commandVersions.has(guildId)) return bot.commandVersions.get(guildId)!;
|
||||
if (bot.commandVersions.has(guildId)) return bot.commandVersions.get(guildId)!;
|
||||
|
||||
const commandVersion = await prisma.commands.findUnique({ where: { id: guildId } });
|
||||
if (commandVersion) bot.commandVersions.set(guildId, commandVersion.version);
|
||||
const commandVersion = await prisma.commands.findUnique({ where: { id: guildId } });
|
||||
if (commandVersion) bot.commandVersions.set(guildId, commandVersion.version);
|
||||
|
||||
return commandVersion?.version ?? 0;
|
||||
return commandVersion?.version ?? 0;
|
||||
}
|
||||
|
||||
export async function updateCommandVersion(guildId: bigint): Promise<number> {
|
||||
// UPDATE THE VERSION SAVED IN THE DB
|
||||
await prisma.commands.upsert({
|
||||
where: { id: guildId },
|
||||
create: { id: guildId, version: CURRENT_SLASH_COMMAND_VERSION },
|
||||
update: { version: CURRENT_SLASH_COMMAND_VERSION },
|
||||
});
|
||||
// UPDATE THE VERSION SAVED IN THE DB
|
||||
await prisma.commands.upsert({
|
||||
where: { id: guildId },
|
||||
create: { id: guildId, version: CURRENT_SLASH_COMMAND_VERSION },
|
||||
update: { version: CURRENT_SLASH_COMMAND_VERSION },
|
||||
});
|
||||
|
||||
bot.commandVersions.set(guildId, CURRENT_SLASH_COMMAND_VERSION);
|
||||
return CURRENT_SLASH_COMMAND_VERSION;
|
||||
bot.commandVersions.set(guildId, CURRENT_SLASH_COMMAND_VERSION);
|
||||
return CURRENT_SLASH_COMMAND_VERSION;
|
||||
}
|
||||
|
||||
export async function updateGuildCommands(bot: Bot, guildId: bigint) {
|
||||
if (guildId === 547046977578336286n) return await updateDevCommands(bot);
|
||||
if (guildId === 547046977578336286n) return await updateDevCommands(bot);
|
||||
|
||||
await updateCommandVersion(guildId);
|
||||
await updateCommandVersion(guildId);
|
||||
|
||||
// GUILD RELATED COMMANDS
|
||||
await bot.helpers.upsertGuildApplicationCommands(
|
||||
guildId,
|
||||
Object.entries(COMMANDS)
|
||||
// ONLY GUILD COMMANDS
|
||||
.filter(([_name, command]) => !command.global && !command.dev)
|
||||
.map(([name, command]) => {
|
||||
// USER OPTED TO USE BASIC VERSION ONLY
|
||||
if (command.advanced === false) {
|
||||
return {
|
||||
name,
|
||||
description: translate("english", command.description),
|
||||
options: command.options ? createOptions("english", command.options, command.name) : undefined,
|
||||
};
|
||||
}
|
||||
// GUILD RELATED COMMANDS
|
||||
await bot.helpers.upsertGuildApplicationCommands(
|
||||
guildId,
|
||||
Object.entries(COMMANDS)
|
||||
// ONLY GUILD COMMANDS
|
||||
.filter(([_name, command]) => !command.global && !command.dev)
|
||||
.map(([name, command]) => {
|
||||
// USER OPTED TO USE BASIC VERSION ONLY
|
||||
if (command.advanced === false) {
|
||||
return {
|
||||
name,
|
||||
description: translate('english', command.description),
|
||||
options: command.options ? createOptions('english', command.options, command.name) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// ADVANCED VERSION WILL ALLOW TRANSLATION
|
||||
const translatedName = translate(guildId, command.name);
|
||||
const translatedDescription = translate(guildId, command.description);
|
||||
// ADVANCED VERSION WILL ALLOW TRANSLATION
|
||||
const translatedName = translate(guildId, command.name);
|
||||
const translatedDescription = translate(guildId, command.description);
|
||||
|
||||
return {
|
||||
name: translatedName.toLowerCase(),
|
||||
description: translatedDescription,
|
||||
options: command.options ? createOptions(guildId, command.options, command.name) : undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
return {
|
||||
name: translatedName.toLowerCase(),
|
||||
description: translatedDescription,
|
||||
options: command.options ? createOptions(guildId, command.options, command.name) : undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// USED TO CACHE CONVERTED COMMANDS AFTER START TO PREVENT UNNECESSARY LOOPS
|
||||
@@ -109,41 +109,41 @@ const convertedCache = new Map<string, ApplicationCommandOption[]>();
|
||||
|
||||
/** Creates the commands options including subcommands. Also translates them. */
|
||||
function createOptions(
|
||||
guildId: bigint | "english",
|
||||
options: readonly ArgumentDefinition[],
|
||||
commandName?: string,
|
||||
guildId: bigint | 'english',
|
||||
options: readonly ArgumentDefinition[],
|
||||
commandName?: string,
|
||||
): ApplicationCommandOption[] | undefined {
|
||||
const language = guildId === "english" ? "english" : serverLanguages.get(guildId) ?? "english";
|
||||
if (commandName && convertedCache.has(`${language}-${commandName}`)) {
|
||||
return convertedCache.get(`${language}-${commandName}`)!;
|
||||
}
|
||||
const language = guildId === 'english' ? 'english' : serverLanguages.get(guildId) ?? 'english';
|
||||
if (commandName && convertedCache.has(`${language}-${commandName}`)) {
|
||||
return convertedCache.get(`${language}-${commandName}`)!;
|
||||
}
|
||||
|
||||
const newOptions: ApplicationCommandOption[] = [];
|
||||
const newOptions: ApplicationCommandOption[] = [];
|
||||
|
||||
for (const option of options || []) {
|
||||
const optionName = translate(guildId, option.name);
|
||||
const optionDescription = translate(guildId, option.description);
|
||||
for (const option of options || []) {
|
||||
const optionName = translate(guildId, option.name);
|
||||
const optionDescription = translate(guildId, option.description);
|
||||
|
||||
// TODO: remove this ts ignore
|
||||
// @ts-expect-error
|
||||
const choices = option.choices?.map((choice) => ({
|
||||
...choice,
|
||||
name: translate(guildId, choice.name),
|
||||
}));
|
||||
// TODO: remove this ts ignore
|
||||
// @ts-expect-error
|
||||
const choices = option.choices?.map((choice) => ({
|
||||
...choice,
|
||||
name: translate(guildId, choice.name),
|
||||
}));
|
||||
|
||||
newOptions.push({
|
||||
...option,
|
||||
name: optionName.toLowerCase(),
|
||||
description: optionDescription || "No description available.",
|
||||
choices,
|
||||
// @ts-expect-error fix this
|
||||
options: option.options
|
||||
// @ts-expect-error fix this
|
||||
? createOptions(bot, guildId, option.options)
|
||||
: undefined,
|
||||
} as ApplicationCommandOption);
|
||||
}
|
||||
if (commandName) convertedCache.set(`${language}-${commandName}`, newOptions);
|
||||
newOptions.push({
|
||||
...option,
|
||||
name: optionName.toLowerCase(),
|
||||
description: optionDescription || 'No description available.',
|
||||
choices,
|
||||
// @ts-expect-error fix this
|
||||
options: option.options
|
||||
? // @ts-expect-error fix this
|
||||
createOptions(bot, guildId, option.options)
|
||||
: undefined,
|
||||
} as ApplicationCommandOption);
|
||||
}
|
||||
if (commandName) convertedCache.set(`${language}-${commandName}`, newOptions);
|
||||
|
||||
return newOptions;
|
||||
return newOptions;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/** Get the webhook id and token from a webhook url. */
|
||||
export function webhookURLToIDAndToken(url: string) {
|
||||
const [id, token] = url.substring(url.indexOf("webhooks/") + 9).split(
|
||||
"/",
|
||||
);
|
||||
const [id, token] = url.substring(url.indexOf('webhooks/') + 9).split('/');
|
||||
|
||||
return {
|
||||
id,
|
||||
token,
|
||||
};
|
||||
return {
|
||||
id,
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getBotIdFromToken, Intents } from "discordeno";
|
||||
import dotenv from "dotenv";
|
||||
import { getBotIdFromToken, Intents } from 'discordeno';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
/** The bot id, derived from the bot token. */
|
||||
@@ -11,19 +11,19 @@ export const GATEWAY_URL = `http://${process.env.GATEWAY_HOST}:${process.env.GAT
|
||||
// Gateway Proxy Configurations
|
||||
/** The gateway intents you would like to use. */
|
||||
export const INTENTS: Intents =
|
||||
// SETUP-DD-TEMP: Add the intents you want enabled here. Or Delete the intents you don't want in your bot.
|
||||
Intents.DirectMessageReactions |
|
||||
Intents.DirectMessageTyping |
|
||||
Intents.DirectMessages |
|
||||
Intents.GuildBans |
|
||||
Intents.GuildEmojis |
|
||||
Intents.GuildIntegrations |
|
||||
Intents.GuildInvites |
|
||||
Intents.GuildMembers |
|
||||
Intents.GuildMessageReactions |
|
||||
Intents.GuildMessageTyping |
|
||||
Intents.GuildMessages |
|
||||
Intents.GuildPresences |
|
||||
Intents.GuildVoiceStates |
|
||||
Intents.GuildWebhooks |
|
||||
Intents.Guilds;
|
||||
// SETUP-DD-TEMP: Add the intents you want enabled here. Or Delete the intents you don't want in your bot.
|
||||
Intents.DirectMessageReactions |
|
||||
Intents.DirectMessageTyping |
|
||||
Intents.DirectMessages |
|
||||
Intents.GuildBans |
|
||||
Intents.GuildEmojis |
|
||||
Intents.GuildIntegrations |
|
||||
Intents.GuildInvites |
|
||||
Intents.GuildMembers |
|
||||
Intents.GuildMessageReactions |
|
||||
Intents.GuildMessageTyping |
|
||||
Intents.GuildMessages |
|
||||
Intents.GuildPresences |
|
||||
Intents.GuildVoiceStates |
|
||||
Intents.GuildWebhooks |
|
||||
Intents.Guilds;
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import dotenv from "dotenv";
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
import { Collection, createBot, createGatewayManager, createRestManager } from "discordeno";
|
||||
import { createLogger } from "discordeno/logger";
|
||||
import fastify from "fastify";
|
||||
import { nanoid } from "nanoid";
|
||||
import { Worker } from "worker_threads";
|
||||
import { EVENT_HANDLER_URL, INTENTS, REST_URL } from "../configs.js";
|
||||
import type { WorkerCreateData, WorkerGetShardInfo, WorkerMessage, WorkerShardInfo, WorkerShardPayload } from "./worker.js";
|
||||
import { Collection, createBot, createGatewayManager, createRestManager } from 'discordeno';
|
||||
import { createLogger } from 'discordeno/logger';
|
||||
import fastify from 'fastify';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Worker } from 'worker_threads';
|
||||
import { EVENT_HANDLER_URL, INTENTS, REST_URL } from '../configs.js';
|
||||
import type {
|
||||
WorkerCreateData,
|
||||
WorkerGetShardInfo,
|
||||
WorkerMessage,
|
||||
WorkerShardInfo,
|
||||
WorkerShardPayload,
|
||||
} from './worker.js';
|
||||
dotenv.config();
|
||||
|
||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string;
|
||||
@@ -18,181 +24,181 @@ const SHARDS_PER_WORKER = Number(process.env.SHARDS_PER_WORKER as string);
|
||||
const TOTAL_SHARDS = process.env.TOTAL_SHARDS ? Number(process.env.TOTAL_SHARDS) : undefined;
|
||||
const TOTAL_WORKERS = Number(process.env.TOTAL_WORKERS as string);
|
||||
|
||||
const log = createLogger({ name: "[MANAGER]" });
|
||||
const log = createLogger({ name: '[MANAGER]' });
|
||||
|
||||
const bot = createBot({
|
||||
token: DISCORD_TOKEN,
|
||||
token: DISCORD_TOKEN,
|
||||
});
|
||||
|
||||
bot.rest = createRestManager({
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
});
|
||||
|
||||
const gatewayBot = await bot.helpers.getGatewayBot();
|
||||
|
||||
const gateway = createGatewayManager({
|
||||
gatewayBot,
|
||||
gatewayConfig: {
|
||||
token: DISCORD_TOKEN,
|
||||
intents: INTENTS,
|
||||
},
|
||||
// force the total amount of shards
|
||||
totalShards: TOTAL_SHARDS,
|
||||
shardsPerWorker: SHARDS_PER_WORKER,
|
||||
totalWorkers: TOTAL_WORKERS,
|
||||
gatewayBot,
|
||||
gatewayConfig: {
|
||||
token: DISCORD_TOKEN,
|
||||
intents: INTENTS,
|
||||
},
|
||||
// force the total amount of shards
|
||||
totalShards: TOTAL_SHARDS,
|
||||
shardsPerWorker: SHARDS_PER_WORKER,
|
||||
totalWorkers: TOTAL_WORKERS,
|
||||
|
||||
handleDiscordPayload: () => {},
|
||||
handleDiscordPayload: () => {},
|
||||
|
||||
tellWorkerToIdentify: async (_gateway, workerId, shardId, _bucketId) => {
|
||||
log.info("TELL TO IDENTIFY", { workerId, shardId, _bucketId });
|
||||
tellWorkerToIdentify: async (_gateway, workerId, shardId, _bucketId) => {
|
||||
log.info('TELL TO IDENTIFY', { workerId, shardId, _bucketId });
|
||||
|
||||
let worker = workers.get(workerId);
|
||||
if (!worker) {
|
||||
worker = createWorker(workerId);
|
||||
workers.set(workerId, worker);
|
||||
}
|
||||
let worker = workers.get(workerId);
|
||||
if (!worker) {
|
||||
worker = createWorker(workerId);
|
||||
workers.set(workerId, worker);
|
||||
}
|
||||
|
||||
const identify: WorkerMessage = {
|
||||
type: "IDENTIFY_SHARD",
|
||||
shardId,
|
||||
};
|
||||
const identify: WorkerMessage = {
|
||||
type: 'IDENTIFY_SHARD',
|
||||
shardId,
|
||||
};
|
||||
|
||||
worker.postMessage(identify);
|
||||
},
|
||||
worker.postMessage(identify);
|
||||
},
|
||||
});
|
||||
|
||||
const workers = new Collection<number, Worker>();
|
||||
const nonces = new Collection<string, (data: any) => void>();
|
||||
|
||||
function createWorker(workerId: number) {
|
||||
console.log(TOTAL_SHARDS, gateway.manager.totalShards, "SHARDS");
|
||||
console.log(TOTAL_SHARDS, gateway.manager.totalShards, 'SHARDS');
|
||||
|
||||
const workerData: WorkerCreateData = {
|
||||
intents: gateway.manager.gatewayConfig.intents ?? 0,
|
||||
token: DISCORD_TOKEN,
|
||||
handlerUrls: [EVENT_HANDLER_URL],
|
||||
handlerAuthorization: EVENT_HANDLER_AUTHORIZATION,
|
||||
path: "./worker.ts",
|
||||
totalShards: gateway.manager.totalShards,
|
||||
workerId,
|
||||
};
|
||||
const workerData: WorkerCreateData = {
|
||||
intents: gateway.manager.gatewayConfig.intents ?? 0,
|
||||
token: DISCORD_TOKEN,
|
||||
handlerUrls: [EVENT_HANDLER_URL],
|
||||
handlerAuthorization: EVENT_HANDLER_AUTHORIZATION,
|
||||
path: './worker.ts',
|
||||
totalShards: gateway.manager.totalShards,
|
||||
workerId,
|
||||
};
|
||||
|
||||
const worker = new Worker("./dist/gateway/worker.js", {
|
||||
workerData,
|
||||
});
|
||||
const worker = new Worker('./dist/gateway/worker.js', {
|
||||
workerData,
|
||||
});
|
||||
|
||||
worker.on("message", async (data: ManagerMessage) => {
|
||||
log.info({ data });
|
||||
switch (data.type) {
|
||||
case "REQUEST_IDENTIFY": {
|
||||
log.info("REQUESTING IDENTIFY #", data.shardId);
|
||||
await gateway.manager.requestIdentify(data.shardId);
|
||||
worker.on('message', async (data: ManagerMessage) => {
|
||||
log.info({ data });
|
||||
switch (data.type) {
|
||||
case 'REQUEST_IDENTIFY': {
|
||||
log.info('REQUESTING IDENTIFY #', data.shardId);
|
||||
await gateway.manager.requestIdentify(data.shardId);
|
||||
|
||||
const allowIdentify: WorkerMessage = {
|
||||
type: "ALLOW_IDENTIFY",
|
||||
shardId: data.shardId,
|
||||
};
|
||||
const allowIdentify: WorkerMessage = {
|
||||
type: 'ALLOW_IDENTIFY',
|
||||
shardId: data.shardId,
|
||||
};
|
||||
|
||||
worker.postMessage(allowIdentify);
|
||||
worker.postMessage(allowIdentify);
|
||||
|
||||
break;
|
||||
}
|
||||
case "NONCE_REPLY": {
|
||||
nonces.get(data.nonce)?.(data.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'NONCE_REPLY': {
|
||||
nonces.get(data.nonce)?.(data.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return worker;
|
||||
return worker;
|
||||
}
|
||||
|
||||
gateway.spawnShards();
|
||||
|
||||
const server = fastify();
|
||||
|
||||
server.post("/", async (request, reply) => {
|
||||
if (request.headers.authorization !== GATEWAY_AUTHORIZATION) {
|
||||
reply.code(StatusCodes.Unauthorized);
|
||||
server.post('/', async (request, reply) => {
|
||||
if (request.headers.authorization !== GATEWAY_AUTHORIZATION) {
|
||||
reply.code(StatusCodes.Unauthorized);
|
||||
|
||||
return reply.send({ processing: false, error: false, message: "Invalid authorization header." });
|
||||
}
|
||||
return reply.send({ processing: false, error: false, message: 'Invalid authorization header.' });
|
||||
}
|
||||
|
||||
if (!request.body) {
|
||||
reply.code(StatusCodes.BadRequest);
|
||||
if (!request.body) {
|
||||
reply.code(StatusCodes.BadRequest);
|
||||
|
||||
return reply.send({ processing: false, error: false, message: "Empty body." });
|
||||
}
|
||||
return reply.send({ processing: false, error: false, message: 'Empty body.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const data = request.body as WorkerShardPayload | Omit<WorkerGetShardInfo, "nonce">;
|
||||
switch (data.type) {
|
||||
case "SHARD_PAYLOAD": {
|
||||
const workerId = gateway.calculateWorkerId(data.shardId);
|
||||
const worker = workers.get(workerId);
|
||||
try {
|
||||
const data = request.body as WorkerShardPayload | Omit<WorkerGetShardInfo, 'nonce'>;
|
||||
switch (data.type) {
|
||||
case 'SHARD_PAYLOAD': {
|
||||
const workerId = gateway.calculateWorkerId(data.shardId);
|
||||
const worker = workers.get(workerId);
|
||||
|
||||
worker?.postMessage(data);
|
||||
worker?.postMessage(data);
|
||||
|
||||
break;
|
||||
}
|
||||
case "GET_SHARD_INFO": {
|
||||
const infos = await Promise.all(
|
||||
workers.map(async (worker) => {
|
||||
const nonce = nanoid();
|
||||
break;
|
||||
}
|
||||
case 'GET_SHARD_INFO': {
|
||||
const infos = await Promise.all(
|
||||
workers.map(async (worker) => {
|
||||
const nonce = nanoid();
|
||||
|
||||
return await new Promise<WorkerShardInfo[]>((resolve) => {
|
||||
worker.postMessage({ type: "GET_SHARD_INFO", nonce });
|
||||
return await new Promise<WorkerShardInfo[]>((resolve) => {
|
||||
worker.postMessage({ type: 'GET_SHARD_INFO', nonce });
|
||||
|
||||
nonces.set(nonce, resolve);
|
||||
});
|
||||
}),
|
||||
).then((res) =>
|
||||
res.reduce((acc, cur) => {
|
||||
acc.push(...cur);
|
||||
return acc;
|
||||
}, [] as WorkerShardInfo[])
|
||||
);
|
||||
nonces.set(nonce, resolve);
|
||||
});
|
||||
}),
|
||||
).then((res) =>
|
||||
res.reduce((acc, cur) => {
|
||||
acc.push(...cur);
|
||||
return acc;
|
||||
}, [] as WorkerShardInfo[]),
|
||||
);
|
||||
|
||||
reply.code(StatusCodes.Ok);
|
||||
reply.code(StatusCodes.Ok);
|
||||
|
||||
return reply.send(infos);
|
||||
}
|
||||
}
|
||||
return reply.send(infos);
|
||||
}
|
||||
}
|
||||
|
||||
reply.code(StatusCodes.Ok);
|
||||
reply.code(StatusCodes.Ok);
|
||||
|
||||
return reply.send({ processing: true });
|
||||
} catch {
|
||||
reply.code(StatusCodes.BadRequest);
|
||||
return reply.send({ processing: true });
|
||||
} catch {
|
||||
reply.code(StatusCodes.BadRequest);
|
||||
|
||||
return reply.send({ processing: false, error: true, message: "Failed to parse body." });
|
||||
}
|
||||
return reply.send({ processing: false, error: true, message: 'Failed to parse body.' });
|
||||
}
|
||||
});
|
||||
|
||||
server.listen({ port: GATEWAY_PORT }).catch((error) => {
|
||||
log.error(["[FASTIFY ERROR", error].join("\n"));
|
||||
process.exit(1);
|
||||
log.error(['[FASTIFY ERROR', error].join('\n'));
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
export type ManagerMessage = ManagerRequestIdentify | ManagerNonceReply<WorkerShardInfo[]>;
|
||||
|
||||
export interface ManagerRequestIdentify {
|
||||
type: "REQUEST_IDENTIFY";
|
||||
shardId: number;
|
||||
type: 'REQUEST_IDENTIFY';
|
||||
shardId: number;
|
||||
}
|
||||
|
||||
export interface ManagerNonceReply<T> {
|
||||
type: "NONCE_REPLY";
|
||||
nonce: string;
|
||||
data: T;
|
||||
type: 'NONCE_REPLY';
|
||||
nonce: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
enum StatusCodes {
|
||||
Ok = 200,
|
||||
Ok = 200,
|
||||
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
|
||||
InternalServerError = 500,
|
||||
InternalServerError = 500,
|
||||
}
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import dotenv from "dotenv";
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
import amqplib from "amqplib";
|
||||
import amqplib from 'amqplib';
|
||||
import type {
|
||||
DiscordGuild,
|
||||
DiscordReady,
|
||||
DiscordUnavailableGuild,
|
||||
Shard,
|
||||
ShardSocketRequest,
|
||||
ShardState} from "discordeno";
|
||||
import {
|
||||
createShardManager,
|
||||
GatewayEventNames
|
||||
} from "discordeno";
|
||||
import { createLogger } from "discordeno/logger";
|
||||
import fetch from "node-fetch";
|
||||
import crypto from "node:crypto";
|
||||
import { parentPort, workerData } from "worker_threads";
|
||||
DiscordGuild,
|
||||
DiscordReady,
|
||||
DiscordUnavailableGuild,
|
||||
Shard,
|
||||
ShardSocketRequest,
|
||||
ShardState,
|
||||
} from 'discordeno';
|
||||
import { createShardManager, GatewayEventNames } from 'discordeno';
|
||||
import { createLogger } from 'discordeno/logger';
|
||||
import fetch from 'node-fetch';
|
||||
import crypto from 'node:crypto';
|
||||
import { parentPort, workerData } from 'worker_threads';
|
||||
import type { ManagerMessage } from './index.js';
|
||||
dotenv.config();
|
||||
|
||||
if (!parentPort) {
|
||||
throw new Error("Parent port is null");
|
||||
throw new Error('Parent port is null');
|
||||
}
|
||||
|
||||
const script: WorkerCreateData = workerData;
|
||||
@@ -31,211 +29,202 @@ const identifyPromises = new Map<number, () => void>();
|
||||
|
||||
let channel: amqplib.Channel | undefined;
|
||||
|
||||
const useMessageQueue = process.env.MESSAGEQUEUE_ENABLE === "true";
|
||||
const useMessageQueue = process.env.MESSAGEQUEUE_ENABLE === 'true';
|
||||
|
||||
// Store guild ids, loading guild ids to change GUILD_CREATE event to GUILD_LOADED_DD if needed.
|
||||
const guildIds: Set<bigint> = new Set();
|
||||
const loadingGuildIds: Set<bigint> = new Set();
|
||||
|
||||
const manager = createShardManager({
|
||||
gatewayConfig: {
|
||||
intents: script.intents,
|
||||
token: script.token,
|
||||
},
|
||||
shardIds: [],
|
||||
totalShards: script.totalShards,
|
||||
handleMessage: async (shard, message) => {
|
||||
const url = script.handlerUrls[shard.id % script.handlerUrls.length];
|
||||
if (!url) return console.log("ERROR: NO URL FOUND TO SEND MESSAGE");
|
||||
gatewayConfig: {
|
||||
intents: script.intents,
|
||||
token: script.token,
|
||||
},
|
||||
shardIds: [],
|
||||
totalShards: script.totalShards,
|
||||
handleMessage: async (shard, message) => {
|
||||
const url = script.handlerUrls[shard.id % script.handlerUrls.length];
|
||||
if (!url) return console.log('ERROR: NO URL FOUND TO SEND MESSAGE');
|
||||
|
||||
if (message.t === "READY") {
|
||||
// Marks which guilds the bot in when initial loading in cache.
|
||||
(message.d as DiscordReady).guilds.forEach((g) => loadingGuildIds.add(BigInt(g.id)));
|
||||
}
|
||||
if (message.t === 'READY') {
|
||||
// Marks which guilds the bot in when initial loading in cache.
|
||||
(message.d as DiscordReady).guilds.forEach((g) => loadingGuildIds.add(BigInt(g.id)));
|
||||
}
|
||||
|
||||
// If GUILD_CREATE event came from a shard loaded event, change event to GUILD_LOADED_DD.
|
||||
if (message.t === "GUILD_CREATE") {
|
||||
const guild = message.d as DiscordGuild;
|
||||
const id = BigInt(guild.id);
|
||||
// If GUILD_CREATE event came from a shard loaded event, change event to GUILD_LOADED_DD.
|
||||
if (message.t === 'GUILD_CREATE') {
|
||||
const guild = message.d as DiscordGuild;
|
||||
const id = BigInt(guild.id);
|
||||
|
||||
const existing = guildIds.has(id);
|
||||
if (existing) return;
|
||||
const existing = guildIds.has(id);
|
||||
if (existing) return;
|
||||
|
||||
if (loadingGuildIds.has(id)) {
|
||||
(message.t ) = "GUILD_LOADED_DD";
|
||||
if (loadingGuildIds.has(id)) {
|
||||
message.t = 'GUILD_LOADED_DD';
|
||||
|
||||
loadingGuildIds.delete(id);
|
||||
}
|
||||
loadingGuildIds.delete(id);
|
||||
}
|
||||
|
||||
guildIds.add(id);
|
||||
}
|
||||
guildIds.add(id);
|
||||
}
|
||||
|
||||
// Delete guild id from cache so GUILD_CREATE from the same guild later works properly.
|
||||
if (message.t === "GUILD_DELETE") {
|
||||
const guild = message.d as DiscordUnavailableGuild;
|
||||
// Delete guild id from cache so GUILD_CREATE from the same guild later works properly.
|
||||
if (message.t === 'GUILD_DELETE') {
|
||||
const guild = message.d as DiscordUnavailableGuild;
|
||||
|
||||
if (guild.unavailable) return;
|
||||
if (guild.unavailable) return;
|
||||
|
||||
guildIds.delete(BigInt(guild.id));
|
||||
}
|
||||
guildIds.delete(BigInt(guild.id));
|
||||
}
|
||||
|
||||
if (useMessageQueue) {
|
||||
if (!channel) return;
|
||||
await channel.publish(
|
||||
"gatewayMessage",
|
||||
"",
|
||||
Buffer.from(JSON.stringify({ shard, message })),
|
||||
{
|
||||
contentType: "application/json",
|
||||
headers: {
|
||||
"x-deduplication-header": crypto.createHash("md5").update(JSON.stringify(message.d)).digest("hex"),
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ message, shardId: shard.id }),
|
||||
headers: { "Content-Type": "application/json", Authorization: script.handlerAuthorization },
|
||||
}).catch((error) => log.error(error));
|
||||
}
|
||||
if (useMessageQueue) {
|
||||
if (!channel) return;
|
||||
await channel.publish('gatewayMessage', '', Buffer.from(JSON.stringify({ shard, message })), {
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'x-deduplication-header': crypto.createHash('md5').update(JSON.stringify(message.d)).digest('hex'),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ message, shardId: shard.id }),
|
||||
headers: { 'Content-Type': 'application/json', Authorization: script.handlerAuthorization },
|
||||
}).catch((error) => log.error(error));
|
||||
}
|
||||
|
||||
log.debug({ shardId: shard.id, message });
|
||||
},
|
||||
requestIdentify: async function (shardId: number): Promise<void> {
|
||||
return await new Promise((resolve) => {
|
||||
identifyPromises.set(shardId, resolve);
|
||||
log.debug({ shardId: shard.id, message });
|
||||
},
|
||||
requestIdentify: async function (shardId: number): Promise<void> {
|
||||
return await new Promise((resolve) => {
|
||||
identifyPromises.set(shardId, resolve);
|
||||
|
||||
const identifyRequest: ManagerMessage = {
|
||||
type: "REQUEST_IDENTIFY",
|
||||
shardId,
|
||||
};
|
||||
const identifyRequest: ManagerMessage = {
|
||||
type: 'REQUEST_IDENTIFY',
|
||||
shardId,
|
||||
};
|
||||
|
||||
parentPort?.postMessage(identifyRequest);
|
||||
});
|
||||
},
|
||||
parentPort?.postMessage(identifyRequest);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function buildShardInfo(shard: Shard): WorkerShardInfo {
|
||||
return {
|
||||
workerId: script.workerId,
|
||||
shardId: shard.id,
|
||||
rtt: shard.heart.rtt || -1,
|
||||
state: shard.state,
|
||||
};
|
||||
return {
|
||||
workerId: script.workerId,
|
||||
shardId: shard.id,
|
||||
rtt: shard.heart.rtt || -1,
|
||||
state: shard.state,
|
||||
};
|
||||
}
|
||||
|
||||
parentPort.on("message", async (data: WorkerMessage) => {
|
||||
switch (data.type) {
|
||||
case "IDENTIFY_SHARD": {
|
||||
log.info(`starting to identify shard #${data.shardId}`);
|
||||
await manager.identify(data.shardId);
|
||||
parentPort.on('message', async (data: WorkerMessage) => {
|
||||
switch (data.type) {
|
||||
case 'IDENTIFY_SHARD': {
|
||||
log.info(`starting to identify shard #${data.shardId}`);
|
||||
await manager.identify(data.shardId);
|
||||
|
||||
break;
|
||||
}
|
||||
case "ALLOW_IDENTIFY": {
|
||||
identifyPromises.get(data.shardId)?.();
|
||||
identifyPromises.delete(data.shardId);
|
||||
break;
|
||||
}
|
||||
case 'ALLOW_IDENTIFY': {
|
||||
identifyPromises.get(data.shardId)?.();
|
||||
identifyPromises.delete(data.shardId);
|
||||
|
||||
break;
|
||||
}
|
||||
case "SHARD_PAYLOAD": {
|
||||
manager.shards.get(data.shardId)?.send(data.data);
|
||||
break;
|
||||
}
|
||||
case 'SHARD_PAYLOAD': {
|
||||
manager.shards.get(data.shardId)?.send(data.data);
|
||||
|
||||
break;
|
||||
}
|
||||
case "GET_SHARD_INFO": {
|
||||
const infos = manager.shards.map(buildShardInfo);
|
||||
break;
|
||||
}
|
||||
case 'GET_SHARD_INFO': {
|
||||
const infos = manager.shards.map(buildShardInfo);
|
||||
|
||||
parentPort?.postMessage({ type: "NONCE_REPLY", nonce: data.nonce, data: infos });
|
||||
}
|
||||
}
|
||||
parentPort?.postMessage({ type: 'NONCE_REPLY', nonce: data.nonce, data: infos });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export type WorkerMessage = WorkerIdentifyShard | WorkerAllowIdentify | WorkerShardPayload | WorkerGetShardInfo;
|
||||
|
||||
export interface WorkerIdentifyShard {
|
||||
type: "IDENTIFY_SHARD";
|
||||
shardId: number;
|
||||
type: 'IDENTIFY_SHARD';
|
||||
shardId: number;
|
||||
}
|
||||
|
||||
export interface WorkerAllowIdentify {
|
||||
type: "ALLOW_IDENTIFY";
|
||||
shardId: number;
|
||||
type: 'ALLOW_IDENTIFY';
|
||||
shardId: number;
|
||||
}
|
||||
|
||||
export interface WorkerShardPayload {
|
||||
type: "SHARD_PAYLOAD";
|
||||
shardId: number;
|
||||
data: ShardSocketRequest;
|
||||
type: 'SHARD_PAYLOAD';
|
||||
shardId: number;
|
||||
data: ShardSocketRequest;
|
||||
}
|
||||
|
||||
export interface WorkerGetShardInfo {
|
||||
type: "GET_SHARD_INFO";
|
||||
nonce: string;
|
||||
type: 'GET_SHARD_INFO';
|
||||
nonce: string;
|
||||
}
|
||||
|
||||
export interface WorkerCreateData {
|
||||
intents: number;
|
||||
token: string;
|
||||
handlerUrls: string[];
|
||||
handlerAuthorization: string;
|
||||
path: string;
|
||||
totalShards: number;
|
||||
workerId: number;
|
||||
intents: number;
|
||||
token: string;
|
||||
handlerUrls: string[];
|
||||
handlerAuthorization: string;
|
||||
path: string;
|
||||
totalShards: number;
|
||||
workerId: number;
|
||||
}
|
||||
|
||||
export interface WorkerShardInfo {
|
||||
workerId: number;
|
||||
shardId: number;
|
||||
rtt: number;
|
||||
state: ShardState;
|
||||
workerId: number;
|
||||
shardId: number;
|
||||
rtt: number;
|
||||
state: ShardState;
|
||||
}
|
||||
|
||||
const connectRabbitmq = async () => {
|
||||
let connection: amqplib.Connection | undefined;
|
||||
let connection: amqplib.Connection | undefined;
|
||||
|
||||
try {
|
||||
connection = await amqplib.connect(
|
||||
`amqp://${process.env.MESSAGEQUEUE_USERNAME}:${process.env.MESSAGEQUEUE_PASSWORD}@${process.env.MESSAGEQUEUE_URL}`,
|
||||
);
|
||||
} catch (error) {
|
||||
channel = undefined;
|
||||
log.error(error);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
}
|
||||
try {
|
||||
connection = await amqplib.connect(
|
||||
`amqp://${process.env.MESSAGEQUEUE_USERNAME}:${process.env.MESSAGEQUEUE_PASSWORD}@${process.env.MESSAGEQUEUE_URL}`,
|
||||
);
|
||||
} catch (error) {
|
||||
channel = undefined;
|
||||
log.error(error);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
}
|
||||
|
||||
if (!connection) return;
|
||||
connection.on("error", (err) => {
|
||||
channel = undefined;
|
||||
log.error(err);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
if (!connection) return;
|
||||
connection.on('error', (err) => {
|
||||
channel = undefined;
|
||||
log.error(err);
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
|
||||
connection.on("close", () => {
|
||||
channel = undefined;
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
connection.on('close', () => {
|
||||
channel = undefined;
|
||||
setTimeout(connectRabbitmq, 1000);
|
||||
});
|
||||
|
||||
try {
|
||||
channel = await connection.createChannel();
|
||||
await channel.assertExchange(
|
||||
"gatewayMessage",
|
||||
"x-message-deduplication",
|
||||
{
|
||||
durable: true,
|
||||
arguments: {
|
||||
"x-cache-size": 1000,
|
||||
"x-cache-ttl": 500,
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
channel = undefined;
|
||||
}
|
||||
try {
|
||||
channel = await connection.createChannel();
|
||||
await channel.assertExchange('gatewayMessage', 'x-message-deduplication', {
|
||||
durable: true,
|
||||
arguments: {
|
||||
'x-cache-size': 1000,
|
||||
'x-cache-ttl': 500,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
channel = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
if (useMessageQueue) {
|
||||
connectRabbitmq();
|
||||
connectRabbitmq();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
export const prisma = new PrismaClient();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import dotenv from "dotenv";
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
import { BASE_URL, createRestManager } from "discordeno";
|
||||
import express from "express";
|
||||
import { setupAnalyticsHooks } from "../analytics.js";
|
||||
import { REST_URL } from "../configs.js";
|
||||
import { BASE_URL, createRestManager } from 'discordeno';
|
||||
import express from 'express';
|
||||
import { setupAnalyticsHooks } from '../analytics.js';
|
||||
import { REST_URL } from '../configs.js';
|
||||
dotenv.config();
|
||||
|
||||
const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string;
|
||||
@@ -11,10 +11,10 @@ const REST_AUTHORIZATION = process.env.REST_AUTHORIZATION as string;
|
||||
const REST_PORT = process.env.REST_PORT as string;
|
||||
|
||||
const rest = createRestManager({
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
debug: console.log,
|
||||
token: DISCORD_TOKEN,
|
||||
secretKey: REST_AUTHORIZATION,
|
||||
customUrl: REST_URL,
|
||||
debug: console.log,
|
||||
});
|
||||
|
||||
// Add send fetching analytics hook to rest
|
||||
@@ -22,39 +22,39 @@ setupAnalyticsHooks(rest);
|
||||
|
||||
// @ts-expect-error
|
||||
rest.convertRestError = (errorStack, data) => {
|
||||
if (!data) return { message: errorStack.message };
|
||||
return { ...data, message: errorStack.message };
|
||||
if (!data) return { message: errorStack.message };
|
||||
return { ...data, message: errorStack.message };
|
||||
};
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
express.urlencoded({
|
||||
extended: true,
|
||||
}),
|
||||
express.urlencoded({
|
||||
extended: true,
|
||||
}),
|
||||
);
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.all("/*", async (req, res) => {
|
||||
if (!REST_AUTHORIZATION || REST_AUTHORIZATION !== req.headers.authorization) {
|
||||
return res.status(401).json({ error: "Invalid authorization key." });
|
||||
}
|
||||
app.all('/*', async (req, res) => {
|
||||
if (!REST_AUTHORIZATION || REST_AUTHORIZATION !== req.headers.authorization) {
|
||||
return res.status(401).json({ error: 'Invalid authorization key.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await rest.runMethod(rest, req.method , `${BASE_URL}${req.url}`, req.body);
|
||||
try {
|
||||
const result = await rest.runMethod(rest, req.method, `${BASE_URL}${req.url}`, req.body);
|
||||
|
||||
if (result) {
|
||||
res.status(200).json(result);
|
||||
} else {
|
||||
res.status(204).json();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
if (result) {
|
||||
res.status(200).json(result);
|
||||
} else {
|
||||
res.status(204).json();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(REST_PORT, () => {
|
||||
console.log(`REST listening at ${REST_URL}`);
|
||||
console.log(`REST listening at ${REST_URL}`);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { dotEnvConfig } from './deps.ts.js';
|
||||
import { dotEnvConfig } from './deps.ts.js'
|
||||
|
||||
dotEnvConfig({ export: true });
|
||||
export const BOT_TOKEN = process.env.BOT_TOKEN || "";
|
||||
export const BOT_ID = BigInt(atob(BOT_TOKEN.split(".")[0]));
|
||||
dotEnvConfig({ export: true })
|
||||
export const BOT_TOKEN = process.env.BOT_TOKEN || ''
|
||||
export const BOT_ID = BigInt(atob(BOT_TOKEN.split('.')[0]))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/mod.ts";
|
||||
export * from "https://deno.land/x/discordeno@17.0.0/plugins/mod.ts";
|
||||
export { config as dotEnvConfig } from "https://deno.land/x/dotenv@v3.1.0/mod.ts";
|
||||
export * from "https://deno.land/std@0.117.0/fmt/colors.ts";
|
||||
export * from 'https://deno.land/x/discordeno@17.0.0/mod.ts'
|
||||
export * from 'https://deno.land/x/discordeno@17.0.0/plugins/mod.ts'
|
||||
export { config as dotEnvConfig } from 'https://deno.land/x/dotenv@v3.1.0/mod.ts'
|
||||
export * from 'https://deno.land/std@0.117.0/fmt/colors.ts'
|
||||
|
||||
+20
-28
@@ -1,27 +1,19 @@
|
||||
import {
|
||||
ActivityTypes,
|
||||
createBot,
|
||||
enableCachePlugin,
|
||||
enableCacheSweepers,
|
||||
fastFileLoader,
|
||||
GatewayIntents,
|
||||
startBot,
|
||||
} from './deps.ts.js';
|
||||
import { BOT_ID, BOT_TOKEN } from './configs.ts.js';
|
||||
import { logger } from './src/utils/logger.ts.js';
|
||||
import { events } from './src/events/mod.ts.js';
|
||||
import { updateCommands } from './src/utils/helpers.ts.js';
|
||||
import { ActivityTypes, createBot, enableCachePlugin, enableCacheSweepers, fastFileLoader, GatewayIntents, startBot } from './deps.ts.js'
|
||||
import { BOT_ID, BOT_TOKEN } from './configs.ts.js'
|
||||
import { logger } from './src/utils/logger.ts.js'
|
||||
import { events } from './src/events/mod.ts.js'
|
||||
import { updateCommands } from './src/utils/helpers.ts.js'
|
||||
|
||||
const log = logger({ name: "Main" });
|
||||
const log = logger({ name: 'Main' })
|
||||
|
||||
log.info("Starting Bot, this might take a while...");
|
||||
log.info('Starting Bot, this might take a while...')
|
||||
|
||||
const paths = ["./src/events", "./src/commands"];
|
||||
const paths = ['./src/events', './src/commands']
|
||||
await fastFileLoader(paths).catch((err) => {
|
||||
log.fatal(`Unable to Import ${paths}`);
|
||||
log.fatal(err);
|
||||
Deno.exit(1);
|
||||
});
|
||||
log.fatal(`Unable to Import ${paths}`)
|
||||
log.fatal(err)
|
||||
Deno.exit(1)
|
||||
})
|
||||
|
||||
export const bot = enableCachePlugin(
|
||||
createBot({
|
||||
@@ -30,25 +22,25 @@ export const bot = enableCachePlugin(
|
||||
intents: GatewayIntents.Guilds,
|
||||
events,
|
||||
}),
|
||||
);
|
||||
)
|
||||
|
||||
// @ts-nocheck: no-updated-depencdencies
|
||||
enableCacheSweepers(bot);
|
||||
enableCacheSweepers(bot)
|
||||
|
||||
bot.gateway.manager.createShardOptions.makePresence = (shardId: number) => {
|
||||
return {
|
||||
shardId,
|
||||
status: "online",
|
||||
status: 'online',
|
||||
activities: [
|
||||
{
|
||||
name: "Discordeno is the Best Lib",
|
||||
name: 'Discordeno is the Best Lib',
|
||||
type: ActivityTypes.Game,
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await startBot(bot);
|
||||
await startBot(bot)
|
||||
|
||||
await updateCommands(bot);
|
||||
await updateCommands(bot)
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import type { ApplicationCommandOption, ApplicationCommandTypes, Bot, Interaction } from '../../deps.ts.js';
|
||||
import { Collection } from '../../deps.ts.js';
|
||||
import type { ApplicationCommandOption, ApplicationCommandTypes, Bot, Interaction } from '../../deps.ts.js'
|
||||
import { Collection } from '../../deps.ts.js'
|
||||
|
||||
export type subCommand = Omit<Command, "subcommands">;
|
||||
export type subCommand = Omit<Command, 'subcommands'>
|
||||
export interface subCommandGroup {
|
||||
name: string;
|
||||
subCommands: subCommand[];
|
||||
name: string
|
||||
subCommands: subCommand[]
|
||||
}
|
||||
export interface Command {
|
||||
name: string;
|
||||
description: string;
|
||||
usage?: string[];
|
||||
options?: ApplicationCommandOption[];
|
||||
type: ApplicationCommandTypes;
|
||||
name: string
|
||||
description: string
|
||||
usage?: string[]
|
||||
options?: ApplicationCommandOption[]
|
||||
type: ApplicationCommandTypes
|
||||
/** Defaults to `Guild` */
|
||||
scope?: "Global" | "Guild";
|
||||
execute: (bot: Bot, interaction: Interaction) => unknown;
|
||||
subcommands?: Array<subCommandGroup | subCommand>;
|
||||
scope?: 'Global' | 'Guild'
|
||||
execute: (bot: Bot, interaction: Interaction) => unknown
|
||||
subcommands?: Array<subCommandGroup | subCommand>
|
||||
}
|
||||
|
||||
export const commands = new Collection<string, Command>();
|
||||
export const commands = new Collection<string, Command>()
|
||||
|
||||
export function createCommand(command: Command) {
|
||||
commands.set(command.name, command);
|
||||
commands.set(command.name, command)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js';
|
||||
import { humanizeMilliseconds, snowflakeToTimestamp } from '../utils/helpers.ts.js';
|
||||
import { createCommand } from './mod.ts.js';
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '../../deps.ts.js'
|
||||
import { humanizeMilliseconds, snowflakeToTimestamp } from '../utils/helpers.ts.js'
|
||||
import { createCommand } from './mod.ts.js'
|
||||
|
||||
createCommand({
|
||||
name: "ping",
|
||||
description: "Ping the Bot!",
|
||||
name: 'ping',
|
||||
description: 'Ping the Bot!',
|
||||
type: ApplicationCommandTypes.ChatInput,
|
||||
scope: "Global",
|
||||
scope: 'Global',
|
||||
execute: async (bot, interaction) => {
|
||||
const ping = Date.now() - snowflakeToTimestamp(interaction.id);
|
||||
await bot.helpers.sendInteractionResponse(
|
||||
interaction.id,
|
||||
interaction.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `🏓 Pong! Ping ${ping}ms (${humanizeMilliseconds(ping)})`,
|
||||
},
|
||||
const ping = Date.now() - snowflakeToTimestamp(interaction.id)
|
||||
await bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: {
|
||||
content: `🏓 Pong! Ping ${ping}ms (${humanizeMilliseconds(ping)})`,
|
||||
},
|
||||
);
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { events } from './mod.ts.js';
|
||||
import { updateGuildCommands } from '../utils/helpers.ts.js';
|
||||
import { events } from './mod.ts.js'
|
||||
import { updateGuildCommands } from '../utils/helpers.ts.js'
|
||||
|
||||
events.guildCreate = async (bot, guild) => await updateGuildCommands(bot, guild);
|
||||
events.guildCreate = async (bot, guild) => await updateGuildCommands(bot, guild)
|
||||
|
||||
@@ -1,135 +1,111 @@
|
||||
import type {
|
||||
BotWithCache,
|
||||
Guild} from '../../deps.ts.js';
|
||||
import {
|
||||
ApplicationCommandOptionTypes,
|
||||
bgBlack,
|
||||
bgYellow,
|
||||
black,
|
||||
green,
|
||||
red,
|
||||
white,
|
||||
yellow,
|
||||
} from '../../deps.ts.js';
|
||||
import { events } from './mod.ts.js';
|
||||
import { logger } from '../utils/logger.ts.js';
|
||||
import { getGuildFromId, isSubCommand, isSubCommandGroup } from '../utils/helpers.ts.js';
|
||||
import type { Command} from '../commands/mod.ts.js';
|
||||
import { commands } from '../commands/mod.ts.js';
|
||||
import type { BotWithCache, Guild } from '../../deps.ts.js'
|
||||
import { ApplicationCommandOptionTypes, bgBlack, bgYellow, black, green, red, white, yellow } from '../../deps.ts.js'
|
||||
import { events } from './mod.ts.js'
|
||||
import { logger } from '../utils/logger.ts.js'
|
||||
import { getGuildFromId, isSubCommand, isSubCommandGroup } from '../utils/helpers.ts.js'
|
||||
import type { Command } from '../commands/mod.ts.js'
|
||||
import { commands } from '../commands/mod.ts.js'
|
||||
|
||||
const log = logger({ name: "Event: InteractionCreate" });
|
||||
const log = logger({ name: 'Event: InteractionCreate' })
|
||||
|
||||
events.interactionCreate = async (rawBot, interaction) => {
|
||||
const bot = rawBot as BotWithCache;
|
||||
const bot = rawBot as BotWithCache
|
||||
|
||||
if (interaction.data && interaction.id) {
|
||||
let guildName = "Direct Message";
|
||||
let guild = {} as Guild;
|
||||
let guildName = 'Direct Message'
|
||||
let guild = {} as Guild
|
||||
|
||||
// Set guild, if there was an error getting the guild, then just say it was a DM. (What else are we going to do?)
|
||||
if (interaction.guildId) {
|
||||
const guildOrVoid = await getGuildFromId(bot, interaction.guildId).catch(
|
||||
(err) => {
|
||||
log.error(err);
|
||||
},
|
||||
);
|
||||
const guildOrVoid = await getGuildFromId(bot, interaction.guildId).catch((err) => {
|
||||
log.error(err)
|
||||
})
|
||||
if (guildOrVoid) {
|
||||
guild = guildOrVoid;
|
||||
guildName = guild.name;
|
||||
guild = guildOrVoid
|
||||
guildName = guild.name
|
||||
}
|
||||
}
|
||||
|
||||
log.info(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(white(`Trigger`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${bgBlack(white(`Trigger`))}] by ${interaction.user.username}#${
|
||||
interaction.user.discriminator
|
||||
} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
|
||||
)
|
||||
|
||||
let command: undefined | Command = interaction.data.name ? commands.get(interaction.data.name) : undefined;
|
||||
let commandName = command?.name;
|
||||
let command: undefined | Command = interaction.data.name ? commands.get(interaction.data.name) : undefined
|
||||
let commandName = command?.name
|
||||
|
||||
if (command !== undefined) {
|
||||
if (interaction.data.name) {
|
||||
if (interaction.data.options?.[0]) {
|
||||
const optionType = interaction.data.options[0].type;
|
||||
const optionType = interaction.data.options[0].type
|
||||
|
||||
if (optionType === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!command.subcommands) return;
|
||||
if (!command.subcommands) return
|
||||
|
||||
// Try to find the subcommand group
|
||||
const subCommandGroup = command.subcommands?.find(
|
||||
(command) => command.name == interaction.data?.options?.[0].name,
|
||||
);
|
||||
if (!subCommandGroup) return;
|
||||
const subCommandGroup = command.subcommands?.find((command) => command.name == interaction.data?.options?.[0].name)
|
||||
if (!subCommandGroup) return
|
||||
|
||||
if (isSubCommand(subCommandGroup)) return;
|
||||
if (isSubCommand(subCommandGroup)) return
|
||||
|
||||
// Get name of the command which we are looking for
|
||||
const targetCmdName = interaction.data.options?.[0].options?.[0].name ||
|
||||
interaction.data.options?.[0].options?.[0].name;
|
||||
if (!targetCmdName) return;
|
||||
const targetCmdName = interaction.data.options?.[0].options?.[0].name || interaction.data.options?.[0].options?.[0].name
|
||||
if (!targetCmdName) return
|
||||
|
||||
// Try to find the command
|
||||
command = subCommandGroup.subCommands.find((c) => c.name === targetCmdName);
|
||||
command = subCommandGroup.subCommands.find((c) => c.name === targetCmdName)
|
||||
|
||||
commandName += ` ${subCommandGroup.name} ${command?.name}`;
|
||||
commandName += ` ${subCommandGroup.name} ${command?.name}`
|
||||
|
||||
// Normal
|
||||
}
|
||||
|
||||
if (optionType === ApplicationCommandOptionTypes.SubCommandGroup) {
|
||||
// Check if command has subcommand and handle types
|
||||
if (!command?.subcommands) return;
|
||||
if (!command?.subcommands) return
|
||||
|
||||
// Try to find the command
|
||||
const found = command.subcommands.find((command) => command.name == interaction.data?.options?.[0].name);
|
||||
if (!found) return;
|
||||
const found = command.subcommands.find((command) => command.name == interaction.data?.options?.[0].name)
|
||||
if (!found) return
|
||||
|
||||
if (isSubCommandGroup(found)) return;
|
||||
if (isSubCommandGroup(found)) return
|
||||
|
||||
command = found;
|
||||
commandName += ` ${command?.name}`;
|
||||
command = found
|
||||
commandName += ` ${command?.name}`
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (command) {
|
||||
command.execute(rawBot, interaction);
|
||||
command.execute(rawBot, interaction)
|
||||
log.info(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(green(`Success`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${bgBlack(green(`Success`))}] by ${interaction.user.username}#${
|
||||
interaction.user.discriminator
|
||||
} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
|
||||
)
|
||||
} else {
|
||||
throw "";
|
||||
throw ''
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(red(`Error`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
err.length ? log.error(err) : undefined;
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${bgBlack(red(`Error`))}] by ${interaction.user.username}#${
|
||||
interaction.user.discriminator
|
||||
} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
|
||||
)
|
||||
err.length ? log.error(err) : undefined
|
||||
}
|
||||
} else {
|
||||
log.warn(
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${
|
||||
bgBlack(yellow(`Not Found`))
|
||||
}] by ${interaction.user.username}#${interaction.user.discriminator} in ${guildName}${
|
||||
guildName !== "Direct Message" ? ` (${guild.id})` : ``
|
||||
}`,
|
||||
);
|
||||
`[Command: ${bgYellow(black(String(interaction.data.name)))} - ${bgBlack(yellow(`Not Found`))}] by ${interaction.user.username}#${
|
||||
interaction.user.discriminator
|
||||
} in ${guildName}${guildName !== 'Direct Message' ? ` (${guild.id})` : ``}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
// Handle subcommands
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import type { EventHandlers } from '../../deps.ts.js';
|
||||
import type { EventHandlers } from '../../deps.ts.js'
|
||||
|
||||
export const events: Partial<EventHandlers> = {};
|
||||
export const events: Partial<EventHandlers> = {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { events } from './mod.ts.js';
|
||||
import { logger } from '../utils/logger.ts.js';
|
||||
import { events } from './mod.ts.js'
|
||||
import { logger } from '../utils/logger.ts.js'
|
||||
|
||||
const log = logger({ name: "Event: Ready" });
|
||||
const log = logger({ name: 'Event: Ready' })
|
||||
|
||||
events.ready = () => {
|
||||
log.info("Bot Ready");
|
||||
};
|
||||
log.info('Bot Ready')
|
||||
}
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
import type {
|
||||
Bot,
|
||||
BotWithCache,
|
||||
CreateApplicationCommand,
|
||||
Guild,
|
||||
MakeRequired} from '../../deps.ts.js';
|
||||
import {
|
||||
getGuild,
|
||||
hasProperty,
|
||||
upsertGuildApplicationCommands,
|
||||
} from '../../deps.ts.js';
|
||||
import { logger } from './logger.ts.js';
|
||||
import type { subCommand, subCommandGroup } from '../commands/mod.ts.js';
|
||||
import { commands } from '../commands/mod.ts.js';
|
||||
import type { Bot, BotWithCache, CreateApplicationCommand, Guild, MakeRequired } from '../../deps.ts.js'
|
||||
import { getGuild, hasProperty, upsertGuildApplicationCommands } from '../../deps.ts.js'
|
||||
import { logger } from './logger.ts.js'
|
||||
import type { subCommand, subCommandGroup } from '../commands/mod.ts.js'
|
||||
import { commands } from '../commands/mod.ts.js'
|
||||
|
||||
const log = logger({ name: "Helpers" });
|
||||
const log = logger({ name: 'Helpers' })
|
||||
|
||||
/** This function will update all commands, or the defined scope */
|
||||
export async function updateCommands(bot: BotWithCache, scope?: "Guild" | "Global") {
|
||||
const globalCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
export async function updateCommands(bot: BotWithCache, scope?: 'Guild' | 'Global') {
|
||||
const globalCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
|
||||
|
||||
for (const command of commands.values()) {
|
||||
if (command.scope) {
|
||||
if (command.scope === "Guild") {
|
||||
if (command.scope === 'Guild') {
|
||||
perGuildCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
} else if (command.scope === "Global") {
|
||||
})
|
||||
} else if (command.scope === 'Global') {
|
||||
globalCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
})
|
||||
}
|
||||
} else {
|
||||
perGuildCommands.push({
|
||||
@@ -43,87 +34,87 @@ export async function updateCommands(bot: BotWithCache, scope?: "Guild" | "Globa
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (globalCommands.length && (scope === "Global" || scope === undefined)) {
|
||||
log.info("Updating Global Commands, changes should apply in short...");
|
||||
await bot.helpers.upsertGlobalApplicationCommands(globalCommands).catch(log.error);
|
||||
if (globalCommands.length && (scope === 'Global' || scope === undefined)) {
|
||||
log.info('Updating Global Commands, changes should apply in short...')
|
||||
await bot.helpers.upsertGlobalApplicationCommands(globalCommands).catch(log.error)
|
||||
}
|
||||
|
||||
if (perGuildCommands.length && (scope === "Guild" || scope === undefined)) {
|
||||
if (perGuildCommands.length && (scope === 'Guild' || scope === undefined)) {
|
||||
await bot.guilds.forEach(async (guild: Guild) => {
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands);
|
||||
});
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Update commands for a guild */
|
||||
export async function updateGuildCommands(bot: Bot, guild: Guild) {
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, "name">> = [];
|
||||
const perGuildCommands: Array<MakeRequired<CreateApplicationCommand, 'name'>> = []
|
||||
|
||||
for (const command of commands.values()) {
|
||||
if (command.scope) {
|
||||
if (command.scope === "Guild") {
|
||||
if (command.scope === 'Guild') {
|
||||
perGuildCommands.push({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type,
|
||||
options: command.options ? command.options : undefined,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (perGuildCommands.length) {
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands);
|
||||
await upsertGuildApplicationCommands(bot, guild.id, perGuildCommands)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGuildFromId(bot: BotWithCache, guildId: bigint): Promise<Guild> {
|
||||
let returnValue: Guild = {} as Guild;
|
||||
let returnValue: Guild = {} as Guild
|
||||
|
||||
if (guildId !== 0n) {
|
||||
if (bot.guilds.get(guildId)) {
|
||||
returnValue = bot.guilds.get(guildId) as Guild;
|
||||
returnValue = bot.guilds.get(guildId) as Guild
|
||||
}
|
||||
|
||||
await getGuild(bot, guildId).then((guild) => {
|
||||
if (guild) bot.guilds.set(guildId, guild);
|
||||
if (guild) returnValue = guild;
|
||||
});
|
||||
if (guild) bot.guilds.set(guildId, guild)
|
||||
if (guild) returnValue = guild
|
||||
})
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
return returnValue
|
||||
}
|
||||
|
||||
export function snowflakeToTimestamp(id: bigint) {
|
||||
return Number(id / 4194304n + 1420070400000n);
|
||||
return Number(id / 4194304n + 1420070400000n)
|
||||
}
|
||||
|
||||
export function humanizeMilliseconds(milliseconds: number) {
|
||||
// Gets ms into seconds
|
||||
const time = milliseconds / 1000;
|
||||
if (time < 1) return "1s";
|
||||
const time = milliseconds / 1000
|
||||
if (time < 1) return '1s'
|
||||
|
||||
const days = Math.floor(time / 86400);
|
||||
const hours = Math.floor((time % 86400) / 3600);
|
||||
const minutes = Math.floor(((time % 86400) % 3600) / 60);
|
||||
const seconds = Math.floor(((time % 86400) % 3600) % 60);
|
||||
const days = Math.floor(time / 86400)
|
||||
const hours = Math.floor((time % 86400) / 3600)
|
||||
const minutes = Math.floor(((time % 86400) % 3600) / 60)
|
||||
const seconds = Math.floor(((time % 86400) % 3600) % 60)
|
||||
|
||||
const dayString = days ? `${days}d ` : "";
|
||||
const hourString = hours ? `${hours}h ` : "";
|
||||
const minuteString = minutes ? `${minutes}m ` : "";
|
||||
const secondString = seconds ? `${seconds}s ` : "";
|
||||
const dayString = days ? `${days}d ` : ''
|
||||
const hourString = hours ? `${hours}h ` : ''
|
||||
const minuteString = minutes ? `${minutes}m ` : ''
|
||||
const secondString = seconds ? `${seconds}s ` : ''
|
||||
|
||||
return `${dayString}${hourString}${minuteString}${secondString}`;
|
||||
return `${dayString}${hourString}${minuteString}${secondString}`
|
||||
}
|
||||
|
||||
export function isSubCommand(data: subCommand | subCommandGroup): data is subCommand {
|
||||
return !hasProperty(data, "subCommands");
|
||||
return !hasProperty(data, 'subCommands')
|
||||
}
|
||||
|
||||
export function isSubCommandGroup(data: subCommand | subCommandGroup): data is subCommandGroup {
|
||||
return hasProperty(data, "subCommands");
|
||||
return hasProperty(data, 'subCommands')
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js';
|
||||
import { bold, cyan, gray, italic, red, yellow } from '../../deps.ts.js'
|
||||
|
||||
export enum LogLevels {
|
||||
Debug,
|
||||
@@ -10,81 +10,81 @@ export enum LogLevels {
|
||||
}
|
||||
|
||||
const prefixes = new Map<LogLevels, string>([
|
||||
[LogLevels.Debug, "DEBUG"],
|
||||
[LogLevels.Info, "INFO"],
|
||||
[LogLevels.Warn, "WARN"],
|
||||
[LogLevels.Error, "ERROR"],
|
||||
[LogLevels.Fatal, "FATAL"],
|
||||
]);
|
||||
[LogLevels.Debug, 'DEBUG'],
|
||||
[LogLevels.Info, 'INFO'],
|
||||
[LogLevels.Warn, 'WARN'],
|
||||
[LogLevels.Error, 'ERROR'],
|
||||
[LogLevels.Fatal, 'FATAL'],
|
||||
])
|
||||
|
||||
const noColor: (str: string) => string = (msg) => msg;
|
||||
const noColor: (str: string) => string = (msg) => msg
|
||||
const colorFunctions = new Map<LogLevels, (str: string) => string>([
|
||||
[LogLevels.Debug, gray],
|
||||
[LogLevels.Info, cyan],
|
||||
[LogLevels.Warn, yellow],
|
||||
[LogLevels.Error, (str: string) => red(str)],
|
||||
[LogLevels.Fatal, (str: string) => red(bold(italic(str)))],
|
||||
]);
|
||||
])
|
||||
|
||||
export function logger({
|
||||
logLevel = LogLevels.Info,
|
||||
name,
|
||||
}: {
|
||||
logLevel?: LogLevels;
|
||||
name?: string;
|
||||
logLevel?: LogLevels
|
||||
name?: string
|
||||
} = {}) {
|
||||
function log(level: LogLevels, ...args: any[]) {
|
||||
if (level < logLevel) return;
|
||||
if (level < logLevel) return
|
||||
|
||||
let color = colorFunctions.get(level);
|
||||
if (!color) color = noColor;
|
||||
let color = colorFunctions.get(level)
|
||||
if (!color) color = noColor
|
||||
|
||||
const date = new Date();
|
||||
const date = new Date()
|
||||
const log = [
|
||||
`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`,
|
||||
color(prefixes.get(level) || "DEBUG"),
|
||||
name ? `${name} >` : ">",
|
||||
color(prefixes.get(level) || 'DEBUG'),
|
||||
name ? `${name} >` : '>',
|
||||
...args,
|
||||
];
|
||||
]
|
||||
|
||||
switch (level) {
|
||||
case LogLevels.Debug:
|
||||
return console.debug(...log);
|
||||
return console.debug(...log)
|
||||
case LogLevels.Info:
|
||||
return console.info(...log);
|
||||
return console.info(...log)
|
||||
case LogLevels.Warn:
|
||||
return console.warn(...log);
|
||||
return console.warn(...log)
|
||||
case LogLevels.Error:
|
||||
return console.error(...log);
|
||||
return console.error(...log)
|
||||
case LogLevels.Fatal:
|
||||
return console.error(...log);
|
||||
return console.error(...log)
|
||||
default:
|
||||
return console.log(...log);
|
||||
return console.log(...log)
|
||||
}
|
||||
}
|
||||
|
||||
function setLevel(level: LogLevels) {
|
||||
logLevel = level;
|
||||
logLevel = level
|
||||
}
|
||||
|
||||
function debug(...args: any[]) {
|
||||
log(LogLevels.Debug, ...args);
|
||||
log(LogLevels.Debug, ...args)
|
||||
}
|
||||
|
||||
function info(...args: any[]) {
|
||||
log(LogLevels.Info, ...args);
|
||||
log(LogLevels.Info, ...args)
|
||||
}
|
||||
|
||||
function warn(...args: any[]) {
|
||||
log(LogLevels.Warn, ...args);
|
||||
log(LogLevels.Warn, ...args)
|
||||
}
|
||||
|
||||
function error(...args: any[]) {
|
||||
log(LogLevels.Error, ...args);
|
||||
log(LogLevels.Error, ...args)
|
||||
}
|
||||
|
||||
function fatal(...args: any[]) {
|
||||
log(LogLevels.Fatal, ...args);
|
||||
log(LogLevels.Fatal, ...args)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -95,7 +95,7 @@ export function logger({
|
||||
warn,
|
||||
error,
|
||||
fatal,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const log = logger();
|
||||
export const log = logger()
|
||||
|
||||
+75
-75
@@ -1,4 +1,4 @@
|
||||
import type { CreateGatewayManagerOptions, GatewayManager, Shard } from '@discordeno/gateway'
|
||||
import type { CreateGatewayManagerOptions, GatewayManager, DiscordenoShard } from '@discordeno/gateway'
|
||||
import { createGatewayManager, ShardSocketCloseCodes } from '@discordeno/gateway'
|
||||
import type { CreateRestManagerOptions, RestManager } from '@discordeno/rest'
|
||||
import { createRestManager } from '@discordeno/rest'
|
||||
@@ -61,22 +61,22 @@ import { createLogger } from '@discordeno/utils'
|
||||
*/
|
||||
export function createBot(options: CreateBotOptions): Bot {
|
||||
if (!options.rest) options.rest = { token: options.token }
|
||||
if (!options.gateway) options.gateway = { token: options.token, events: {} };
|
||||
if (!options.gateway) options.gateway = { token: options.token, events: {} }
|
||||
if (!options.gateway.events.message) {
|
||||
options.gateway.events.message = async (shard, data) => {
|
||||
// TRIGGER RAW EVENT
|
||||
bot.events.raw?.(data, shard)
|
||||
// TRIGGER RAW EVENT
|
||||
bot.events.raw?.(data, shard)
|
||||
|
||||
if (!data.t) return
|
||||
if (!data.t) return
|
||||
|
||||
// RUN DISPATCH CHECK
|
||||
await bot.events.dispatchRequirements?.(data, shard)
|
||||
bot.events[
|
||||
data.t.toLowerCase().replace(/_([a-z])/g, function (g) {
|
||||
return g[1].toUpperCase()
|
||||
}) as keyof EventHandlers
|
||||
// @ts-expect-error as any gets removed by linter
|
||||
]?.(data.d, shard)
|
||||
// RUN DISPATCH CHECK
|
||||
await bot.events.dispatchRequirements?.(data, shard)
|
||||
bot.events[
|
||||
data.t.toLowerCase().replace(/_([a-z])/g, function (g) {
|
||||
return g[1].toUpperCase()
|
||||
}) as keyof EventHandlers
|
||||
// @ts-expect-error as any gets removed by linter
|
||||
]?.(data.d, shard)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,68 +134,68 @@ export interface Bot {
|
||||
|
||||
export interface EventHandlers {
|
||||
// Custom events here
|
||||
dispatchRequirements: (payload: Camelize<DiscordGatewayPayload>, shard: Shard) => unknown
|
||||
raw: (payload: Camelize<DiscordGatewayPayload>, shard: Shard) => unknown
|
||||
dispatchRequirements: (payload: Camelize<DiscordGatewayPayload>, shard: DiscordenoShard) => unknown
|
||||
raw: (payload: Camelize<DiscordGatewayPayload>, shard: DiscordenoShard) => unknown
|
||||
|
||||
// Gateway events below this
|
||||
applicationCommandPermissionsUpdate: (payload: Camelize<DiscordGuildApplicationCommandPermissions>, shard: Shard) => unknown
|
||||
auditLogEntryCreate: (payload: Camelize<DiscordAuditLogEntry>, shard: Shard) => unknown
|
||||
autoModerationRuleCreate: (payload: Camelize<DiscordAutoModerationRule>, shard: Shard) => unknown
|
||||
autoModerationRuleUpdate: (payload: Camelize<DiscordAutoModerationRule>, shard: Shard) => unknown
|
||||
autoModerationRuleDelete: (payload: Camelize<DiscordAutoModerationRule>, shard: Shard) => unknown
|
||||
autoModerationActionExecution: (payload: Camelize<DiscordAutoModerationActionExecution>, shard: Shard) => unknown
|
||||
channelCreate: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
channelUpdate: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
channelDelete: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
channelPinsUpdate: (payload: Camelize<DiscordChannelPinsUpdate>, shard: Shard) => unknown
|
||||
threadCreate: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
threadUpdate: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
threadDelete: (payload: Camelize<DiscordChannel>, shard: Shard) => unknown
|
||||
threadListSync: (payload: Camelize<DiscordThreadListSync>, shard: Shard) => unknown
|
||||
threadMemberUpdate: (payload: Camelize<DiscordThreadMemberUpdate>, shard: Shard) => unknown
|
||||
threadMembersUpdate: (payload: Camelize<DiscordThreadMembersUpdate>, shard: Shard) => unknown
|
||||
guildCreate: (payload: Camelize<DiscordGuild>, shard: Shard) => unknown
|
||||
guildUpdate: (payload: Camelize<DiscordGuild>, shard: Shard) => unknown
|
||||
guildDelete: (payload: Camelize<DiscordUnavailableGuild>, shard: Shard) => unknown
|
||||
guildBanAdd: (payload: Camelize<DiscordGuildBanAddRemove>, shard: Shard) => unknown
|
||||
guildBanRemove: (payload: Camelize<DiscordGuildBanAddRemove>, shard: Shard) => unknown
|
||||
guildEmojisUpdate: (payload: Camelize<DiscordGuildEmojisUpdate>, shard: Shard) => unknown
|
||||
guildStickersUpdate: (payload: Camelize<DiscordGuildStickersUpdate>, shard: Shard) => unknown
|
||||
guildIntegrationsUpdate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: Shard) => unknown
|
||||
guildMemberAdd: (payload: Camelize<DiscordGuildMemberAdd>, shard: Shard) => unknown
|
||||
guildMemberRemove: (payload: Camelize<DiscordGuildMemberRemove>, shard: Shard) => unknown
|
||||
guildMemberUpdate: (payload: Camelize<DiscordGuildMemberUpdate>, shard: Shard) => unknown
|
||||
guildMembersChunk: (payload: Camelize<DiscordGuildMembersChunk>, shard: Shard) => unknown
|
||||
guildRoleCreate: (payload: Camelize<DiscordGuildRoleCreate>, shard: Shard) => unknown
|
||||
guildRoleUpdate: (payload: Camelize<DiscordGuildRoleUpdate>, shard: Shard) => unknown
|
||||
guildRoleDelete: (payload: Camelize<DiscordGuildRoleDelete>, shard: Shard) => unknown
|
||||
guildScheduledEventCreate: (payload: Camelize<DiscordScheduledEvent>, shard: Shard) => unknown
|
||||
guildScheduledEventUpdate: (payload: Camelize<DiscordScheduledEvent>, shard: Shard) => unknown
|
||||
guildScheduledEventDelete: (payload: Camelize<DiscordScheduledEvent>, shard: Shard) => unknown
|
||||
guildScheduledEventUserAdd: (payload: Camelize<DiscordScheduledEventUserAdd>, shard: Shard) => unknown
|
||||
guildScheduledEventUserRemove: (payload: Camelize<DiscordScheduledEventUserRemove>, shard: Shard) => unknown
|
||||
integrationCreate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: Shard) => unknown
|
||||
integrationUpdate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: Shard) => unknown
|
||||
integrationDelete: (payload: Camelize<DiscordIntegrationDelete>, shard: Shard) => unknown
|
||||
interactionCreate: (payload: Camelize<DiscordInteraction>, shard: Shard) => unknown
|
||||
inviteCreate: (payload: Camelize<DiscordInviteCreate>, shard: Shard) => unknown
|
||||
inviteDelete: (payload: Camelize<DiscordInviteDelete>, shard: Shard) => unknown
|
||||
messageCreate: (payload: Camelize<DiscordMessage>, shard: Shard) => unknown
|
||||
messageUpdate: (payload: Camelize<DiscordMessage>, shard: Shard) => unknown
|
||||
messageDelete: (payload: Camelize<DiscordMessageDelete>, shard: Shard) => unknown
|
||||
messageDeleteBulk: (payload: Camelize<DiscordMessageDeleteBulk>, shard: Shard) => unknown
|
||||
messageReactionAdd: (payload: Camelize<DiscordMessageReactionAdd>, shard: Shard) => unknown
|
||||
messageReactionRemove: (payload: Camelize<DiscordMessageReactionRemove>, shard: Shard) => unknown
|
||||
messageReactionRemoveAll: (payload: Camelize<DiscordMessageReactionRemoveAll>, shard: Shard) => unknown
|
||||
messageReactionRemoveEmoji: (payload: Camelize<DiscordMessageReactionRemoveEmoji>, shard: Shard) => unknown
|
||||
presenceUpdate: (payload: Camelize<DiscordPresenceUpdate>, shard: Shard) => unknown
|
||||
ready: (payload: Camelize<DiscordReady>, shard: Shard) => unknown
|
||||
stageInstanceCreate: (payload: Camelize<DiscordStageInstance>, shard: Shard) => unknown
|
||||
stageInstanceUpdate: (payload: Camelize<DiscordStageInstance>, shard: Shard) => unknown
|
||||
stageInstanceDelete: (payload: Camelize<DiscordStageInstance>, shard: Shard) => unknown
|
||||
typingStart: (payload: Camelize<DiscordTypingStart>, shard: Shard) => unknown
|
||||
userUpdate: (payload: Camelize<DiscordUser>, shard: Shard) => unknown
|
||||
voiceStateUpdate: (payload: Camelize<DiscordVoiceState>, shard: Shard) => unknown
|
||||
voiceServerUpdate: (payload: Camelize<DiscordVoiceServerUpdate>, shard: Shard) => unknown
|
||||
webhooksUpdate: (payload: Camelize<DiscordWebhookUpdate>, shard: Shard) => unknown
|
||||
applicationCommandPermissionsUpdate: (payload: Camelize<DiscordGuildApplicationCommandPermissions>, shard: DiscordenoShard) => unknown
|
||||
auditLogEntryCreate: (payload: Camelize<DiscordAuditLogEntry>, shard: DiscordenoShard) => unknown
|
||||
autoModerationRuleCreate: (payload: Camelize<DiscordAutoModerationRule>, shard: DiscordenoShard) => unknown
|
||||
autoModerationRuleUpdate: (payload: Camelize<DiscordAutoModerationRule>, shard: DiscordenoShard) => unknown
|
||||
autoModerationRuleDelete: (payload: Camelize<DiscordAutoModerationRule>, shard: DiscordenoShard) => unknown
|
||||
autoModerationActionExecution: (payload: Camelize<DiscordAutoModerationActionExecution>, shard: DiscordenoShard) => unknown
|
||||
channelCreate: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
channelUpdate: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
channelDelete: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
channelPinsUpdate: (payload: Camelize<DiscordChannelPinsUpdate>, shard: DiscordenoShard) => unknown
|
||||
threadCreate: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
threadUpdate: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
threadDelete: (payload: Camelize<DiscordChannel>, shard: DiscordenoShard) => unknown
|
||||
threadListSync: (payload: Camelize<DiscordThreadListSync>, shard: DiscordenoShard) => unknown
|
||||
threadMemberUpdate: (payload: Camelize<DiscordThreadMemberUpdate>, shard: DiscordenoShard) => unknown
|
||||
threadMembersUpdate: (payload: Camelize<DiscordThreadMembersUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildCreate: (payload: Camelize<DiscordGuild>, shard: DiscordenoShard) => unknown
|
||||
guildUpdate: (payload: Camelize<DiscordGuild>, shard: DiscordenoShard) => unknown
|
||||
guildDelete: (payload: Camelize<DiscordUnavailableGuild>, shard: DiscordenoShard) => unknown
|
||||
guildBanAdd: (payload: Camelize<DiscordGuildBanAddRemove>, shard: DiscordenoShard) => unknown
|
||||
guildBanRemove: (payload: Camelize<DiscordGuildBanAddRemove>, shard: DiscordenoShard) => unknown
|
||||
guildEmojisUpdate: (payload: Camelize<DiscordGuildEmojisUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildStickersUpdate: (payload: Camelize<DiscordGuildStickersUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildIntegrationsUpdate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildMemberAdd: (payload: Camelize<DiscordGuildMemberAdd>, shard: DiscordenoShard) => unknown
|
||||
guildMemberRemove: (payload: Camelize<DiscordGuildMemberRemove>, shard: DiscordenoShard) => unknown
|
||||
guildMemberUpdate: (payload: Camelize<DiscordGuildMemberUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildMembersChunk: (payload: Camelize<DiscordGuildMembersChunk>, shard: DiscordenoShard) => unknown
|
||||
guildRoleCreate: (payload: Camelize<DiscordGuildRoleCreate>, shard: DiscordenoShard) => unknown
|
||||
guildRoleUpdate: (payload: Camelize<DiscordGuildRoleUpdate>, shard: DiscordenoShard) => unknown
|
||||
guildRoleDelete: (payload: Camelize<DiscordGuildRoleDelete>, shard: DiscordenoShard) => unknown
|
||||
guildScheduledEventCreate: (payload: Camelize<DiscordScheduledEvent>, shard: DiscordenoShard) => unknown
|
||||
guildScheduledEventUpdate: (payload: Camelize<DiscordScheduledEvent>, shard: DiscordenoShard) => unknown
|
||||
guildScheduledEventDelete: (payload: Camelize<DiscordScheduledEvent>, shard: DiscordenoShard) => unknown
|
||||
guildScheduledEventUserAdd: (payload: Camelize<DiscordScheduledEventUserAdd>, shard: DiscordenoShard) => unknown
|
||||
guildScheduledEventUserRemove: (payload: Camelize<DiscordScheduledEventUserRemove>, shard: DiscordenoShard) => unknown
|
||||
integrationCreate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: DiscordenoShard) => unknown
|
||||
integrationUpdate: (payload: Camelize<DiscordIntegrationCreateUpdate>, shard: DiscordenoShard) => unknown
|
||||
integrationDelete: (payload: Camelize<DiscordIntegrationDelete>, shard: DiscordenoShard) => unknown
|
||||
interactionCreate: (payload: Camelize<DiscordInteraction>, shard: DiscordenoShard) => unknown
|
||||
inviteCreate: (payload: Camelize<DiscordInviteCreate>, shard: DiscordenoShard) => unknown
|
||||
inviteDelete: (payload: Camelize<DiscordInviteDelete>, shard: DiscordenoShard) => unknown
|
||||
messageCreate: (payload: Camelize<DiscordMessage>, shard: DiscordenoShard) => unknown
|
||||
messageUpdate: (payload: Camelize<DiscordMessage>, shard: DiscordenoShard) => unknown
|
||||
messageDelete: (payload: Camelize<DiscordMessageDelete>, shard: DiscordenoShard) => unknown
|
||||
messageDeleteBulk: (payload: Camelize<DiscordMessageDeleteBulk>, shard: DiscordenoShard) => unknown
|
||||
messageReactionAdd: (payload: Camelize<DiscordMessageReactionAdd>, shard: DiscordenoShard) => unknown
|
||||
messageReactionRemove: (payload: Camelize<DiscordMessageReactionRemove>, shard: DiscordenoShard) => unknown
|
||||
messageReactionRemoveAll: (payload: Camelize<DiscordMessageReactionRemoveAll>, shard: DiscordenoShard) => unknown
|
||||
messageReactionRemoveEmoji: (payload: Camelize<DiscordMessageReactionRemoveEmoji>, shard: DiscordenoShard) => unknown
|
||||
presenceUpdate: (payload: Camelize<DiscordPresenceUpdate>, shard: DiscordenoShard) => unknown
|
||||
ready: (payload: Camelize<DiscordReady>, shard: DiscordenoShard) => unknown
|
||||
stageInstanceCreate: (payload: Camelize<DiscordStageInstance>, shard: DiscordenoShard) => unknown
|
||||
stageInstanceUpdate: (payload: Camelize<DiscordStageInstance>, shard: DiscordenoShard) => unknown
|
||||
stageInstanceDelete: (payload: Camelize<DiscordStageInstance>, shard: DiscordenoShard) => unknown
|
||||
typingStart: (payload: Camelize<DiscordTypingStart>, shard: DiscordenoShard) => unknown
|
||||
userUpdate: (payload: Camelize<DiscordUser>, shard: DiscordenoShard) => unknown
|
||||
voiceStateUpdate: (payload: Camelize<DiscordVoiceState>, shard: DiscordenoShard) => unknown
|
||||
voiceServerUpdate: (payload: Camelize<DiscordVoiceServerUpdate>, shard: DiscordenoShard) => unknown
|
||||
webhooksUpdate: (payload: Camelize<DiscordWebhookUpdate>, shard: DiscordenoShard) => unknown
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { delay, logger } from '@discordeno/utils'
|
||||
import chai from 'chai'
|
||||
import chaiAsPromised from 'chai-as-promised'
|
||||
import { describe, it } from 'mocha'
|
||||
import type { EventHandlers } from '../../src/bot.js';
|
||||
import type { EventHandlers } from '../../src/bot.js'
|
||||
import { createBot } from '../../src/bot.js'
|
||||
import { token } from './constants.js'
|
||||
chai.use(chaiAsPromised)
|
||||
@@ -35,12 +35,12 @@ describe('[Bot] Delete any guild owned guilds', () => {
|
||||
},
|
||||
events: {
|
||||
async guildCreate(payload, shard) {
|
||||
if (payload.joinedAt && (Date.now() - Date.parse(payload.joinedAt)) < 360000) {
|
||||
return;
|
||||
if (payload.joinedAt && Date.now() - Date.parse(payload.joinedAt) < 360000) {
|
||||
return
|
||||
}
|
||||
|
||||
if (bot.rest.applicationId.toString() === payload.ownerId) {
|
||||
logger.debug(`Deleting one of the bot created guilds.`, payload.id);
|
||||
logger.debug(`Deleting one of the bot created guilds.`, payload.id)
|
||||
await bot.rest.deleteGuild(payload.id)
|
||||
}
|
||||
},
|
||||
|
||||
+127
-127
@@ -1,140 +1,140 @@
|
||||
export class Collection<K, V> extends Map<K, V> {
|
||||
limit: number | undefined;
|
||||
limit: number | undefined
|
||||
|
||||
set(key: K, value: V): this {
|
||||
// When this collection is limitd make sure we can add first
|
||||
if ((this.limit ?? this.limit === 0) && this.size >= this.limit) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return super.set(key, value);
|
||||
set(key: K, value: V): this {
|
||||
// When this collection is limitd make sure we can add first
|
||||
if ((this.limit ?? this.limit === 0) && this.size >= this.limit) {
|
||||
return this
|
||||
}
|
||||
|
||||
forceSet(key: K, value: V): this {
|
||||
return super.set(key, value);
|
||||
}
|
||||
return super.set(key, value)
|
||||
}
|
||||
|
||||
array(): V[] {
|
||||
return [...this.values()];
|
||||
}
|
||||
forceSet(key: K, value: V): this {
|
||||
return super.set(key, value)
|
||||
}
|
||||
|
||||
/** Retrieve the value of the first element in this collection */
|
||||
first(): V | undefined {
|
||||
return this.values().next().value;
|
||||
}
|
||||
array(): V[] {
|
||||
return [...this.values()]
|
||||
}
|
||||
|
||||
last(): V | undefined {
|
||||
return [...this.values()][this.size - 1];
|
||||
}
|
||||
/** Retrieve the value of the first element in this collection */
|
||||
first(): V | undefined {
|
||||
return this.values().next().value
|
||||
}
|
||||
|
||||
random(): V | undefined {
|
||||
const array = [...this.values()];
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
last(): V | undefined {
|
||||
return [...this.values()][this.size - 1]
|
||||
}
|
||||
|
||||
find(callback: (value: V, key: K) => boolean): V | undefined {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return value;
|
||||
}
|
||||
}
|
||||
random(): V | undefined {
|
||||
const array = [...this.values()]
|
||||
return array[Math.floor(Math.random() * array.length)]
|
||||
}
|
||||
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray?: true): V[];
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray: false): Collection<K, V>;
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray = true): Collection<K, V> | V[] {
|
||||
const relevant = new Collection<K, V>();
|
||||
this.forEach((value, key) => {
|
||||
if (callback(value, key)) relevant.set(key, value);
|
||||
});
|
||||
|
||||
return returnArray ? relevant.array() : relevant;
|
||||
}
|
||||
|
||||
map<T>(callback: (value: V, key: K) => T): T[] {
|
||||
const results = [];
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
results.push(callback(value, key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
some(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
every(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (!callback(value, key)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
reduce<T>(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T {
|
||||
let accumulator: T = initialValue!;
|
||||
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
accumulator = callback(accumulator, value, key);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a object to the collection.
|
||||
* @deprecated Recommend using Collection.set(). Keeping for the sake of Eris API.
|
||||
* @deprecated extra parameter. No longer used, keeping for sake of Eris API.
|
||||
*/
|
||||
add(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
if (this.limit === 0) return obj;
|
||||
|
||||
const existing = this.get(obj.id);
|
||||
if (existing && !replace) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
this.set(obj.id, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
remove(obj: { id: K }): V | undefined {
|
||||
const item = this.get(obj.id);
|
||||
if (!item) return;
|
||||
|
||||
this.delete(obj.id);
|
||||
return item;
|
||||
}
|
||||
|
||||
update(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
const item = this.get(obj.id);
|
||||
if (!item) {
|
||||
this.set(obj.id, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-expect-error some eris magic at play here
|
||||
item.update?.(obj, extra);
|
||||
return item;
|
||||
}
|
||||
|
||||
toRecord(): Record<string, V> {
|
||||
const record: Record<string, V> = {};
|
||||
for (const [key, value] of this.entries()) {
|
||||
// @ts-expect-error should work fine
|
||||
const finalKey = typeof key === 'string' ? key : key.toString();
|
||||
record[finalKey] = value;
|
||||
}
|
||||
|
||||
return record;
|
||||
find(callback: (value: V, key: K) => boolean): V | undefined {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (callback(value, key)) return value
|
||||
}
|
||||
}
|
||||
|
||||
export default Collection;
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray?: true): V[]
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray: false): Collection<K, V>
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray = true): Collection<K, V> | V[] {
|
||||
const relevant = new Collection<K, V>()
|
||||
this.forEach((value, key) => {
|
||||
if (callback(value, key)) relevant.set(key, value)
|
||||
})
|
||||
|
||||
return returnArray ? relevant.array() : relevant
|
||||
}
|
||||
|
||||
map<T>(callback: (value: V, key: K) => T): T[] {
|
||||
const results = []
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
results.push(callback(value, key))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
some(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (callback(value, key)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
every(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (!callback(value, key)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
reduce<T>(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T {
|
||||
let accumulator: T = initialValue!
|
||||
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
accumulator = callback(accumulator, value, key)
|
||||
}
|
||||
|
||||
return accumulator
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a object to the collection.
|
||||
* @deprecated Recommend using Collection.set(). Keeping for the sake of Eris API.
|
||||
* @deprecated extra parameter. No longer used, keeping for sake of Eris API.
|
||||
*/
|
||||
add(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
if (this.limit === 0) return obj
|
||||
|
||||
const existing = this.get(obj.id)
|
||||
if (existing && !replace) {
|
||||
return existing
|
||||
}
|
||||
|
||||
this.set(obj.id, obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
remove(obj: { id: K }): V | undefined {
|
||||
const item = this.get(obj.id)
|
||||
if (!item) return
|
||||
|
||||
this.delete(obj.id)
|
||||
return item
|
||||
}
|
||||
|
||||
update(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
const item = this.get(obj.id)
|
||||
if (!item) {
|
||||
this.set(obj.id, obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
// @ts-expect-error some eris magic at play here
|
||||
item.update?.(obj, extra)
|
||||
return item
|
||||
}
|
||||
|
||||
toRecord(): Record<string, V> {
|
||||
const record: Record<string, V> = {}
|
||||
for (const [key, value] of this.entries()) {
|
||||
// @ts-expect-error should work fine
|
||||
const finalKey = typeof key === 'string' ? key : key.toString()
|
||||
record[finalKey] = value
|
||||
}
|
||||
|
||||
return record
|
||||
}
|
||||
}
|
||||
|
||||
export default Collection
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import type { DiscordChannel } from "@discordeno/types"
|
||||
import type { StageInstanceOptions } from "../../typings.js"
|
||||
import type StageInstance from "../guilds/StageInstance.js"
|
||||
import VoiceChannel from "./Voice.js"
|
||||
|
||||
import type { DiscordChannel } from '@discordeno/types'
|
||||
import type { StageInstanceOptions } from '../../typings.js'
|
||||
import type StageInstance from '../guilds/StageInstance.js'
|
||||
import VoiceChannel from './Voice.js'
|
||||
|
||||
export class StageChannel extends VoiceChannel {
|
||||
/** The topic of the channel */
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { BigString, DiscordThreadMember } from "@discordeno/types"
|
||||
import Base from "../../../Base.js"
|
||||
import type Client from "../../../Client.js"
|
||||
import type Member from "../../guilds/Member.js"
|
||||
|
||||
import type { BigString, DiscordThreadMember } from '@discordeno/types'
|
||||
import Base from '../../../Base.js'
|
||||
import type Client from '../../../Client.js'
|
||||
import type Member from '../../guilds/Member.js'
|
||||
|
||||
export class ThreadMember extends Base {
|
||||
client: Client
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import type { DiscordChannel } from "@discordeno/types";
|
||||
import type Client from "../../../Client.js";
|
||||
import ThreadChannel from "./Thread.js";
|
||||
|
||||
import type { DiscordChannel } from '@discordeno/types'
|
||||
import type Client from '../../../Client.js'
|
||||
import ThreadChannel from './Thread.js'
|
||||
|
||||
export class PrivateThreadChannel extends ThreadChannel {
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client, messageLimit);
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client, messageLimit)
|
||||
|
||||
this.update(data);
|
||||
}
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
if(data.thread_metadata !== undefined) {
|
||||
this.threadMetadata = {
|
||||
archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp),
|
||||
archived: data.thread_metadata.archived,
|
||||
autoArchiveDuration: data.thread_metadata.auto_archive_duration,
|
||||
invitable: data.thread_metadata.invitable,
|
||||
locked: data.thread_metadata.locked
|
||||
};
|
||||
}
|
||||
update(data: DiscordChannel): void {
|
||||
if (data.thread_metadata !== undefined) {
|
||||
this.threadMetadata = {
|
||||
archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp),
|
||||
archived: data.thread_metadata.archived,
|
||||
autoArchiveDuration: data.thread_metadata.auto_archive_duration,
|
||||
invitable: data.thread_metadata.invitable,
|
||||
locked: data.thread_metadata.locked,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PrivateThreadChannel;
|
||||
export default PrivateThreadChannel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
import Base from '../../Base.js';
|
||||
import Base from '../../Base.js'
|
||||
|
||||
import type { DiscordStageInstance } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { DiscordUnavailableGuild } from "@discordeno/types"
|
||||
import Base from "../../Base.js"
|
||||
import type Client from "../../Client.js"
|
||||
|
||||
import type { DiscordUnavailableGuild } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
|
||||
export class UnavailableGuild extends Base {
|
||||
/** Whether or not the guild is unavailable. */
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '@discordeno/types';
|
||||
import { ApplicationCommandTypes, InteractionResponseTypes } from '@discordeno/types'
|
||||
|
||||
import Collection from '../../Collection.js';
|
||||
import Channel from '../channels/Channel.js';
|
||||
import Member from '../guilds/Member.js';
|
||||
import Role from '../guilds/Role.js';
|
||||
import Message from '../Message.js';
|
||||
import User from '../users/User.js';
|
||||
import Interaction from './Interaction.js';
|
||||
import Collection from '../../Collection.js'
|
||||
import Channel from '../channels/Channel.js'
|
||||
import Member from '../guilds/Member.js'
|
||||
import Role from '../guilds/Role.js'
|
||||
import Message from '../Message.js'
|
||||
import User from '../users/User.js'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
import type {
|
||||
BigString,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { DiscordVoiceState } from "@discordeno/types"
|
||||
import { ToggleBitfield } from "./Toggle.js"
|
||||
|
||||
import type { DiscordVoiceState } from '@discordeno/types'
|
||||
import { ToggleBitfield } from './Toggle.js'
|
||||
|
||||
export const VoiceStateToggle = {
|
||||
/** Whether this user is deafened by the server */
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { PremiumTypes, DiscordUser } from "@discordeno/types"
|
||||
import type Client from "../../Client.js"
|
||||
import User from "./User.js"
|
||||
|
||||
import type { PremiumTypes, DiscordUser } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import User from './User.js'
|
||||
|
||||
export class ExtendedUser extends User {
|
||||
email?: string | null
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-dynamic-delete */
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { Shard as DiscordenoShard, ShardState } from '@discordeno/gateway'
|
||||
import { DiscordenoShard, ShardState } from '@discordeno/gateway'
|
||||
import type { DiscordGuildStickersUpdate, DiscordThreadMemberUpdate } from '@discordeno/types'
|
||||
import {
|
||||
ActivityTypes,
|
||||
|
||||
+1
-2966
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { EventEmitter } from "node:events"
|
||||
import { EventEmitter } from 'node:events'
|
||||
|
||||
class BrowserWebSocketError extends Error {
|
||||
static CONNECTING: 0 = 0
|
||||
@@ -49,7 +49,7 @@ class BrowserWebSocket extends EventEmitter {
|
||||
}
|
||||
|
||||
static set CONNECTING(state: number) {
|
||||
BrowserWebSocket.CONNECTING = state;
|
||||
BrowserWebSocket.CONNECTING = state
|
||||
}
|
||||
|
||||
static get OPEN() {
|
||||
@@ -57,7 +57,7 @@ class BrowserWebSocket extends EventEmitter {
|
||||
}
|
||||
|
||||
static set OPEN(state: number) {
|
||||
BrowserWebSocket.OPEN = state;
|
||||
BrowserWebSocket.OPEN = state
|
||||
}
|
||||
|
||||
static get CLOSING() {
|
||||
@@ -65,7 +65,7 @@ class BrowserWebSocket extends EventEmitter {
|
||||
}
|
||||
|
||||
static set CLOSING(state: number) {
|
||||
BrowserWebSocket.CLOSING = state;
|
||||
BrowserWebSocket.CLOSING = state
|
||||
}
|
||||
|
||||
static get CLOSED() {
|
||||
@@ -73,7 +73,7 @@ class BrowserWebSocket extends EventEmitter {
|
||||
}
|
||||
|
||||
static set CLOSED(state: number) {
|
||||
BrowserWebSocket.CLOSED = state;
|
||||
BrowserWebSocket.CLOSED = state
|
||||
}
|
||||
|
||||
close(code?: number, reason?: string) {
|
||||
|
||||
@@ -2,6 +2,6 @@ import { describe, it } from 'mocha'
|
||||
|
||||
describe('index.ts', () => {
|
||||
it('will import without error', async () => {
|
||||
await import('../src/index.js')
|
||||
await import('../src/index.js')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ This WS service is meant for ADVANCED DEVELOPERS ONLY!
|
||||
```ts
|
||||
createGatewayManager({
|
||||
// TODO: (docs) Fill this out
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
## API/Docs
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { RequestMemberRequest } from './manager.js'
|
||||
import type { BotStatusUpdate, ShardEvents, ShardGatewayConfig, ShardHeart, ShardSocketRequest, StatusUpdate, UpdateVoiceState } from './types.js'
|
||||
import { ShardSocketCloseCodes, ShardState } from './types.js'
|
||||
|
||||
export class Shard {
|
||||
export class DiscordenoShard {
|
||||
/** The id of the shard */
|
||||
id: number
|
||||
/** The connection config details that this shard will used to connect to discord. */
|
||||
@@ -105,7 +105,7 @@ export class Shard {
|
||||
}
|
||||
|
||||
/** Connect the shard with the gateway and start heartbeating. This will not identify the shard to the gateway. */
|
||||
async connect(): Promise<Shard> {
|
||||
async connect(): Promise<DiscordenoShard> {
|
||||
// Only set the shard to `Connecting` state,
|
||||
// if the connection request does not come from an identify or resume action.
|
||||
if (![ShardState.Identifying, ShardState.Resuming].includes(this.state)) {
|
||||
@@ -750,4 +750,4 @@ export interface ShardCreateOptions {
|
||||
events: ShardEvents
|
||||
}
|
||||
|
||||
export default Shard
|
||||
export default DiscordenoShard
|
||||
|
||||
@@ -173,7 +173,7 @@ export interface StatusUpdate {
|
||||
// /** Unix time (in milliseconds) of when the client went idle, or null if the client is not idle */
|
||||
// since: number | null;
|
||||
/** The user's activities */
|
||||
activities?: Camelize<Array<Omit<DiscordActivity, "created_at">>>
|
||||
activities?: Camelize<Array<Omit<DiscordActivity, 'created_at'>>>
|
||||
/** The user's new status */
|
||||
status: keyof typeof PresenceStatus
|
||||
// /** Whether or not the client is afk */
|
||||
|
||||
@@ -7,9 +7,7 @@ import { delay, logger } from '@discordeno/utils'
|
||||
* @param options The options used to configure this bucket.
|
||||
* @returns RefillingBucket
|
||||
*/
|
||||
export function createInvalidRequestBucket (
|
||||
options: InvalidRequestBucketOptions
|
||||
): InvalidRequestBucket {
|
||||
export function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket {
|
||||
const bucket: InvalidRequestBucket = {
|
||||
current: options.current ?? 0,
|
||||
max: options.max ?? 10000,
|
||||
@@ -87,7 +85,7 @@ export function createInvalidRequestBucket (
|
||||
bucket.timeoutId = undefined
|
||||
}, bucket.interval)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return bucket
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
isGetMessagesLimit,
|
||||
logger,
|
||||
processReactionString,
|
||||
urlToBase64
|
||||
urlToBase64,
|
||||
} from '@discordeno/utils'
|
||||
|
||||
import { createInvalidRequestBucket } from './invalidBucket.js'
|
||||
@@ -22,9 +22,11 @@ import { Queue } from './queue.js'
|
||||
|
||||
import type {
|
||||
BigString,
|
||||
Camelize, DiscordApplication,
|
||||
Camelize,
|
||||
DiscordApplication,
|
||||
DiscordApplicationCommand,
|
||||
DiscordApplicationCommandPermissions, DiscordAuditLog,
|
||||
DiscordApplicationCommandPermissions,
|
||||
DiscordAuditLog,
|
||||
DiscordAutoModerationRule,
|
||||
DiscordBan,
|
||||
DiscordChannel,
|
||||
@@ -43,7 +45,8 @@ import type {
|
||||
DiscordListArchivedThreads,
|
||||
DiscordMember,
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessage, DiscordPrunedCount,
|
||||
DiscordMessage,
|
||||
DiscordPrunedCount,
|
||||
DiscordRole,
|
||||
DiscordScheduledEvent,
|
||||
DiscordStageInstance,
|
||||
@@ -55,8 +58,11 @@ import type {
|
||||
DiscordVanityUrl,
|
||||
DiscordVoiceRegion,
|
||||
DiscordWebhook,
|
||||
DiscordWelcomeScreen, GetMessagesOptions, GetScheduledEventUsers, MfaLevels,
|
||||
ModifyGuildTemplate
|
||||
DiscordWelcomeScreen,
|
||||
GetMessagesOptions,
|
||||
GetScheduledEventUsers,
|
||||
MfaLevels,
|
||||
ModifyGuildTemplate,
|
||||
} from '@discordeno/types'
|
||||
import type { CreateRestManagerOptions, RestManager, SendRequestOptions } from './types.js'
|
||||
|
||||
@@ -873,7 +879,7 @@ export function createRestManager(options: CreateRestManagerOptions): RestManage
|
||||
rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get('X-RateLimit-Scope') === 'shared')
|
||||
|
||||
const resetAfter = response.headers.get('x-ratelimit-reset-after')
|
||||
logger.warn(`Request to ${url} was rate limited. Reset after ${resetAfter} seconds.`,);
|
||||
logger.warn(`Request to ${url} was rate limited. Reset after ${resetAfter} seconds.`)
|
||||
if (resetAfter) await delay(Number(resetAfter) * 1000)
|
||||
// process the response to prevent mem leak
|
||||
await response.json()
|
||||
|
||||
@@ -1,4 +1,99 @@
|
||||
import type { ApplicationCommandPermissions, AtLeastOne, BeginGuildPrune, BigString, Camelize, CreateApplicationCommand, CreateAutoModerationRuleOptions, CreateChannelInvite, CreateForumPostWithMessage, CreateGuild, CreateGuildBan, CreateGuildChannel, CreateGuildEmoji, CreateGuildFromTemplate, CreateGuildRole, CreateGuildStickerOptions, CreateMessageOptions, CreateScheduledEvent, CreateStageInstance, CreateTemplate, DeleteWebhookMessageOptions, DiscordActiveThreads, DiscordApplication, DiscordApplicationCommand, DiscordApplicationCommandPermissions, DiscordArchivedThreads, DiscordAuditLog, DiscordAutoModerationRule, DiscordBan, DiscordChannel, DiscordEmoji, DiscordFollowedChannel, DiscordGetGatewayBot, DiscordGuild, DiscordGuildPreview, DiscordGuildWidget, DiscordGuildWidgetSettings, DiscordIntegration, DiscordInvite, DiscordInviteMetadata, DiscordMember, DiscordMemberWithUser, DiscordMessage, DiscordModifyGuildWelcomeScreen, DiscordPrunedCount, DiscordRole, DiscordScheduledEvent, DiscordStageInstance, DiscordSticker, DiscordStickerPack, DiscordTemplate, DiscordThreadMember, DiscordUser, DiscordVanityUrl, DiscordVoiceRegion, DiscordWebhook, DiscordWelcomeScreen, EditAutoModerationRuleOptions, EditBotMemberOptions, EditChannelPermissionOverridesOptions, EditGuildRole, EditGuildStickerOptions, EditMessage, EditOwnVoiceState, EditScheduledEvent, EditStageInstanceOptions, EditUserVoiceState, ExecuteWebhook, GetBans, GetGuildAuditLog, GetGuildPruneCountQuery, GetInvite, GetMessagesOptions, GetReactions, GetScheduledEvents, GetScheduledEventUsers, GetWebhookMessageOptions, InteractionCallbackData, InteractionResponse, ListArchivedThreads, ListGuildMembers, MfaLevels, ModifyChannel, ModifyGuild, ModifyGuildChannelPositions, ModifyGuildEmoji, ModifyGuildMember, ModifyGuildTemplate, ModifyRolePositions, ModifyWebhook, SearchMembers, StartThreadWithMessage, StartThreadWithoutMessage, WithReason } from "@discordeno/types"
|
||||
import type {
|
||||
ApplicationCommandPermissions,
|
||||
AtLeastOne,
|
||||
BeginGuildPrune,
|
||||
BigString,
|
||||
Camelize,
|
||||
CreateApplicationCommand,
|
||||
CreateAutoModerationRuleOptions,
|
||||
CreateChannelInvite,
|
||||
CreateForumPostWithMessage,
|
||||
CreateGuild,
|
||||
CreateGuildBan,
|
||||
CreateGuildChannel,
|
||||
CreateGuildEmoji,
|
||||
CreateGuildFromTemplate,
|
||||
CreateGuildRole,
|
||||
CreateGuildStickerOptions,
|
||||
CreateMessageOptions,
|
||||
CreateScheduledEvent,
|
||||
CreateStageInstance,
|
||||
CreateTemplate,
|
||||
DeleteWebhookMessageOptions,
|
||||
DiscordActiveThreads,
|
||||
DiscordApplication,
|
||||
DiscordApplicationCommand,
|
||||
DiscordApplicationCommandPermissions,
|
||||
DiscordArchivedThreads,
|
||||
DiscordAuditLog,
|
||||
DiscordAutoModerationRule,
|
||||
DiscordBan,
|
||||
DiscordChannel,
|
||||
DiscordEmoji,
|
||||
DiscordFollowedChannel,
|
||||
DiscordGetGatewayBot,
|
||||
DiscordGuild,
|
||||
DiscordGuildPreview,
|
||||
DiscordGuildWidget,
|
||||
DiscordGuildWidgetSettings,
|
||||
DiscordIntegration,
|
||||
DiscordInvite,
|
||||
DiscordInviteMetadata,
|
||||
DiscordMember,
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessage,
|
||||
DiscordModifyGuildWelcomeScreen,
|
||||
DiscordPrunedCount,
|
||||
DiscordRole,
|
||||
DiscordScheduledEvent,
|
||||
DiscordStageInstance,
|
||||
DiscordSticker,
|
||||
DiscordStickerPack,
|
||||
DiscordTemplate,
|
||||
DiscordThreadMember,
|
||||
DiscordUser,
|
||||
DiscordVanityUrl,
|
||||
DiscordVoiceRegion,
|
||||
DiscordWebhook,
|
||||
DiscordWelcomeScreen,
|
||||
EditAutoModerationRuleOptions,
|
||||
EditBotMemberOptions,
|
||||
EditChannelPermissionOverridesOptions,
|
||||
EditGuildRole,
|
||||
EditGuildStickerOptions,
|
||||
EditMessage,
|
||||
EditOwnVoiceState,
|
||||
EditScheduledEvent,
|
||||
EditStageInstanceOptions,
|
||||
EditUserVoiceState,
|
||||
ExecuteWebhook,
|
||||
GetBans,
|
||||
GetGuildAuditLog,
|
||||
GetGuildPruneCountQuery,
|
||||
GetInvite,
|
||||
GetMessagesOptions,
|
||||
GetReactions,
|
||||
GetScheduledEvents,
|
||||
GetScheduledEventUsers,
|
||||
GetWebhookMessageOptions,
|
||||
InteractionCallbackData,
|
||||
InteractionResponse,
|
||||
ListArchivedThreads,
|
||||
ListGuildMembers,
|
||||
MfaLevels,
|
||||
ModifyChannel,
|
||||
ModifyGuild,
|
||||
ModifyGuildChannelPositions,
|
||||
ModifyGuildEmoji,
|
||||
ModifyGuildMember,
|
||||
ModifyGuildTemplate,
|
||||
ModifyRolePositions,
|
||||
ModifyWebhook,
|
||||
SearchMembers,
|
||||
StartThreadWithMessage,
|
||||
StartThreadWithoutMessage,
|
||||
WithReason,
|
||||
} from '@discordeno/types'
|
||||
import type { InvalidRequestBucket } from './invalidBucket.js'
|
||||
import type { Queue } from './queue.js'
|
||||
import type { RestRoutes } from './typings/routes.js'
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import type { Camelize, DiscordChannel, DiscordGuild } from '@discordeno/types'
|
||||
import {
|
||||
AutoModerationActionType,
|
||||
AutoModerationEventTypes,
|
||||
AutoModerationTriggerTypes
|
||||
} from '@discordeno/types'
|
||||
import { AutoModerationActionType, AutoModerationEventTypes, AutoModerationTriggerTypes } from '@discordeno/types'
|
||||
import { expect } from 'chai'
|
||||
import { e2ecache, rest } from './utils.js'
|
||||
|
||||
@@ -18,7 +14,7 @@ before(async () => {
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id && !e2ecache.deletedGuild) {
|
||||
e2ecache.deletedGuild = true;
|
||||
e2ecache.deletedGuild = true
|
||||
await rest.deleteGuild(e2ecache.guild.id)
|
||||
}
|
||||
})
|
||||
@@ -30,13 +26,13 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.BlockMessage
|
||||
}
|
||||
]
|
||||
type: AutoModerationActionType.BlockMessage,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -45,20 +41,12 @@ describe('[automod] Run automod tests', async () => {
|
||||
|
||||
expect(fetchedRule.id).to.be.exist
|
||||
expect(fetchedRule.name).to.equal(rule.name)
|
||||
expect(fetchedRule.eventType).to.equal(
|
||||
AutoModerationEventTypes.MessageSend
|
||||
)
|
||||
expect(fetchedRule.triggerType).to.equal(
|
||||
AutoModerationTriggerTypes.Keyword
|
||||
)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal(
|
||||
'iblamewolf'
|
||||
)
|
||||
expect(fetchedRule.eventType).to.equal(AutoModerationEventTypes.MessageSend)
|
||||
expect(fetchedRule.triggerType).to.equal(AutoModerationTriggerTypes.Keyword)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal('iblamewolf')
|
||||
expect(fetchedRule.actions).to.be.exist
|
||||
expect(fetchedRule.actions[0]).to.be.exist
|
||||
expect(fetchedRule.actions[0].type).to.equal(
|
||||
AutoModerationActionType.BlockMessage
|
||||
)
|
||||
expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage)
|
||||
|
||||
await rest.deleteAutomodRule(e2ecache.guild.id, rule.id)
|
||||
})
|
||||
@@ -69,16 +57,16 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.Timeout,
|
||||
metadata: {
|
||||
durationSeconds: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
durationSeconds: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -87,20 +75,12 @@ describe('[automod] Run automod tests', async () => {
|
||||
|
||||
expect(fetchedRule.id).to.be.exist
|
||||
expect(fetchedRule.name).to.equal(rule.name)
|
||||
expect(fetchedRule.eventType).to.equal(
|
||||
AutoModerationEventTypes.MessageSend
|
||||
)
|
||||
expect(fetchedRule.triggerType).to.equal(
|
||||
AutoModerationTriggerTypes.Keyword
|
||||
)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal(
|
||||
'iblamewolf'
|
||||
)
|
||||
expect(fetchedRule.eventType).to.equal(AutoModerationEventTypes.MessageSend)
|
||||
expect(fetchedRule.triggerType).to.equal(AutoModerationTriggerTypes.Keyword)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal('iblamewolf')
|
||||
expect(fetchedRule.actions).to.be.exist
|
||||
expect(fetchedRule.actions[0]).to.be.exist
|
||||
expect(fetchedRule.actions[0].type).to.equal(
|
||||
AutoModerationActionType.Timeout
|
||||
)
|
||||
expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.Timeout)
|
||||
expect(fetchedRule.actions[0].metadata?.durationSeconds).to.equal(10)
|
||||
|
||||
await rest.deleteAutomodRule(e2ecache.guild.id, rule.id)
|
||||
@@ -112,19 +92,19 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.BlockMessage
|
||||
type: AutoModerationActionType.BlockMessage,
|
||||
},
|
||||
{
|
||||
type: AutoModerationActionType.Timeout,
|
||||
metadata: {
|
||||
durationSeconds: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
durationSeconds: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -137,7 +117,7 @@ describe('[automod] Run automod tests', async () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
channel = await rest.createChannel(e2ecache.guild.id, {
|
||||
name: 'test'
|
||||
name: 'test',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -151,16 +131,16 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.SendAlertMessage,
|
||||
metadata: {
|
||||
channelId: channel.id
|
||||
}
|
||||
}
|
||||
]
|
||||
channelId: channel.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -169,20 +149,12 @@ describe('[automod] Run automod tests', async () => {
|
||||
|
||||
expect(fetchedRule.id).to.be.exist
|
||||
expect(fetchedRule.name).to.equal(rule.name)
|
||||
expect(fetchedRule.eventType).to.equal(
|
||||
AutoModerationEventTypes.MessageSend
|
||||
)
|
||||
expect(fetchedRule.triggerType).to.equal(
|
||||
AutoModerationTriggerTypes.Keyword
|
||||
)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal(
|
||||
'iblamewolf'
|
||||
)
|
||||
expect(fetchedRule.eventType).to.equal(AutoModerationEventTypes.MessageSend)
|
||||
expect(fetchedRule.triggerType).to.equal(AutoModerationTriggerTypes.Keyword)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal('iblamewolf')
|
||||
expect(fetchedRule.actions).to.be.exist
|
||||
expect(fetchedRule.actions[0]).to.be.exist
|
||||
expect(fetchedRule.actions[0].type).to.equal(
|
||||
AutoModerationActionType.SendAlertMessage
|
||||
)
|
||||
expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.SendAlertMessage)
|
||||
expect(fetchedRule.actions[0].metadata?.channelId).to.equal(channel.id)
|
||||
|
||||
await rest.deleteAutomodRule(e2ecache.guild.id, rule.id)
|
||||
@@ -194,22 +166,22 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.SendAlertMessage,
|
||||
metadata: {
|
||||
channelId: channel.id
|
||||
}
|
||||
channelId: channel.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: AutoModerationActionType.Timeout,
|
||||
metadata: {
|
||||
durationSeconds: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
durationSeconds: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -223,25 +195,25 @@ describe('[automod] Run automod tests', async () => {
|
||||
eventType: AutoModerationEventTypes.MessageSend,
|
||||
triggerType: AutoModerationTriggerTypes.Keyword,
|
||||
triggerMetadata: {
|
||||
keywordFilter: ['iblamewolf']
|
||||
keywordFilter: ['iblamewolf'],
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
type: AutoModerationActionType.BlockMessage
|
||||
type: AutoModerationActionType.BlockMessage,
|
||||
},
|
||||
{
|
||||
type: AutoModerationActionType.SendAlertMessage,
|
||||
metadata: {
|
||||
channelId: channel.id
|
||||
}
|
||||
channelId: channel.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: AutoModerationActionType.Timeout,
|
||||
metadata: {
|
||||
durationSeconds: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
durationSeconds: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(rule.id).to.be.exist
|
||||
@@ -250,30 +222,18 @@ describe('[automod] Run automod tests', async () => {
|
||||
const fetchedRule = await rest.getAutomodRule(e2ecache.guild.id, rule.id)
|
||||
expect(fetchedRule.id).to.be.exist
|
||||
expect(fetchedRule.name).to.equal(rule.name)
|
||||
expect(fetchedRule.eventType).to.equal(
|
||||
AutoModerationEventTypes.MessageSend
|
||||
)
|
||||
expect(fetchedRule.triggerType).to.equal(
|
||||
AutoModerationTriggerTypes.Keyword
|
||||
)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal(
|
||||
'iblamewolf'
|
||||
)
|
||||
expect(fetchedRule.eventType).to.equal(AutoModerationEventTypes.MessageSend)
|
||||
expect(fetchedRule.triggerType).to.equal(AutoModerationTriggerTypes.Keyword)
|
||||
expect(fetchedRule.triggerMetadata?.keywordFilter?.[0]).to.equal('iblamewolf')
|
||||
expect(fetchedRule.actions).to.be.exist
|
||||
expect(fetchedRule.actions[0]).to.be.exist
|
||||
expect(fetchedRule.actions[1].metadata).to.be.exist
|
||||
expect(fetchedRule.actions[2].metadata).to.be.exist
|
||||
expect(fetchedRule.actions[1].metadata.channelId).to.equal(channel.id)
|
||||
expect(fetchedRule.actions[2].metadata.durationSeconds).to.equal(10)
|
||||
expect(fetchedRule.actions[0].type).to.equal(
|
||||
AutoModerationActionType.BlockMessage
|
||||
)
|
||||
expect(fetchedRule.actions[1].type).to.equal(
|
||||
AutoModerationActionType.SendAlertMessage
|
||||
)
|
||||
expect(fetchedRule.actions[2].type).to.equal(
|
||||
AutoModerationActionType.Timeout
|
||||
)
|
||||
expect(fetchedRule.actions[0].type).to.equal(AutoModerationActionType.BlockMessage)
|
||||
expect(fetchedRule.actions[1].type).to.equal(AutoModerationActionType.SendAlertMessage)
|
||||
expect(fetchedRule.actions[2].type).to.equal(AutoModerationActionType.Timeout)
|
||||
|
||||
await rest.deleteAutomodRule(e2ecache.guild.id, rule.id)
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ before(async () => {
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id && !e2ecache.deletedGuild) {
|
||||
e2ecache.deletedGuild = true;
|
||||
e2ecache.deletedGuild = true
|
||||
await rest.deleteGuild(e2ecache.guild.id)
|
||||
}
|
||||
})
|
||||
@@ -37,9 +37,9 @@ describe('[member] Member tests', async () => {
|
||||
|
||||
it('Gets a member list and checks if the bot is in the member list', async () => {
|
||||
const members = await rest.getMembers(e2ecache.communityGuildId, {
|
||||
limit: 10
|
||||
limit: 10,
|
||||
})
|
||||
expect(members.some(m => m.user.id === rest.applicationId.toString())).to.equal(true)
|
||||
expect(members.some((m) => m.user.id === rest.applicationId.toString())).to.equal(true)
|
||||
})
|
||||
|
||||
// fetch a single member by id
|
||||
@@ -64,7 +64,7 @@ describe('[member] Member tests', async () => {
|
||||
// ban member from guild with a reason
|
||||
it('ban member from guild with a reason', async () => {
|
||||
await rest.banMember(e2ecache.communityGuildId, ianID, {
|
||||
reason: 'Blame Wolf'
|
||||
reason: 'Blame Wolf',
|
||||
})
|
||||
expect(await rest.getBan(e2ecache.communityGuildId, ianID)).to.exist
|
||||
})
|
||||
@@ -72,7 +72,7 @@ describe('[member] Member tests', async () => {
|
||||
// ban member from guild and delete messages
|
||||
it('ban member from guild and delete messages', async () => {
|
||||
await rest.banMember(e2ecache.communityGuildId, ltsID, {
|
||||
deleteMessageSeconds: 604800
|
||||
deleteMessageSeconds: 604800,
|
||||
})
|
||||
expect(await rest.getBan(e2ecache.communityGuildId, ltsID)).to.exist
|
||||
})
|
||||
@@ -85,13 +85,9 @@ describe('[member] Member tests', async () => {
|
||||
|
||||
// unban member from guild
|
||||
it('unban member from guild', async () => {
|
||||
await Promise.all([
|
||||
rest.unbanMember(e2ecache.communityGuildId, wolfID),
|
||||
rest.unbanMember(e2ecache.communityGuildId, ianID)
|
||||
])
|
||||
await Promise.all([rest.unbanMember(e2ecache.communityGuildId, wolfID), rest.unbanMember(e2ecache.communityGuildId, ianID)])
|
||||
|
||||
await expect(rest.getBan(e2ecache.communityGuildId, wolfID)).to.eventually
|
||||
.rejected
|
||||
await expect(rest.getBan(e2ecache.communityGuildId, wolfID)).to.eventually.rejected
|
||||
})
|
||||
})
|
||||
|
||||
@@ -99,13 +95,13 @@ describe('[member] Member tests', async () => {
|
||||
it("Edit a bot's nickname", async () => {
|
||||
const nick = 'lts20050703'
|
||||
const member = await rest.editBotMember(e2ecache.communityGuildId, {
|
||||
nick
|
||||
nick,
|
||||
})
|
||||
expect(member.nick).to.equal(nick)
|
||||
|
||||
// Change nickname back
|
||||
const member2 = await rest.editBotMember(e2ecache.communityGuildId, {
|
||||
nick: null
|
||||
nick: null,
|
||||
})
|
||||
expect(member2.nick).to.null
|
||||
})
|
||||
@@ -119,7 +115,7 @@ describe('[member] Member tests', async () => {
|
||||
expect(channel?.id).to.exist
|
||||
|
||||
const message = await rest.sendMessage(channel.id, {
|
||||
content: 'https://i.imgur.com/doG55NR.png'
|
||||
content: 'https://i.imgur.com/doG55NR.png',
|
||||
})
|
||||
expect(message?.content).to.exist
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ before(async () => {
|
||||
after(async () => {
|
||||
if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId)
|
||||
if (e2ecache.guild.id && !e2ecache.deletedGuild) {
|
||||
e2ecache.deletedGuild = true;
|
||||
e2ecache.deletedGuild = true
|
||||
await rest.deleteGuild(e2ecache.guild.id)
|
||||
}
|
||||
})
|
||||
@@ -26,51 +26,73 @@ describe('[rest] Message related tests', () => {
|
||||
})
|
||||
|
||||
it('With an image', async () => {
|
||||
const image = await fetch("https://cdn.discordapp.com/avatars/270010330782892032/d031ea881688526d1ae235fd2843e53c.jpg?size=2048").then(async res => await res.blob()).catch(()=> undefined)
|
||||
const image = await fetch('https://cdn.discordapp.com/avatars/270010330782892032/d031ea881688526d1ae235fd2843e53c.jpg?size=2048')
|
||||
.then(async (res) => await res.blob())
|
||||
.catch(() => undefined)
|
||||
expect(image).to.not.be.undefined
|
||||
if (!image) throw new Error("Was not able to fetch the image.")
|
||||
if (!image) throw new Error('Was not able to fetch the image.')
|
||||
|
||||
const message = await rest.sendMessage('1041029705790402611', { file: { blob: image, name: "gamer" }})
|
||||
const message = await rest.sendMessage('1041029705790402611', { file: { blob: image, name: 'gamer' } })
|
||||
expect(message.attachments.length).to.be.greaterThan(0)
|
||||
const [attachment] = message.attachments
|
||||
|
||||
expect(attachment.filename).to.be.equal("gamer")
|
||||
expect(attachment.filename).to.be.equal('gamer')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('Rate limit manager testing', () => {
|
||||
it('Send 10 messages to 1 channel', async () => {
|
||||
await Promise.all([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(async (i) => {
|
||||
await rest.sendMessage('1041029705790402611', { content: `10 messages to 1 channel testing rate limit manager ${i}` })
|
||||
}))
|
||||
await Promise.all(
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(async (i) => {
|
||||
await rest.sendMessage('1041029705790402611', { content: `10 messages to 1 channel testing rate limit manager ${i}` })
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
// TODO: Make this dynamic when we can create channels
|
||||
const spamChannelIds = [
|
||||
'1041029705790402611', '1041029706838966393',
|
||||
'1041029707459731586', '1041029708004995199',
|
||||
'1041029708453789766', '1041029709049385010',
|
||||
'1041029709632377003', '1041029710227976313',
|
||||
'1041029710764834856', '1041029711414956202',
|
||||
'1041029712153149524', '1041029712933306459',
|
||||
'1041029713566646313', '1041029714254508042',
|
||||
'1041029714921406555', '1041029716334870629',
|
||||
'1041029717127614636', '1041029717689647114',
|
||||
'1041029718603997214', '1041029719925215302',
|
||||
'1041029721179308082', '1041029721988812860',
|
||||
'1041029722466943037', '1041029723217743964',
|
||||
'1041029723872034826', '1041029724492804156',
|
||||
'1041029725117743144', '1041029725818212474',
|
||||
'1041029726531227741', '1041029727231684638'
|
||||
'1041029705790402611',
|
||||
'1041029706838966393',
|
||||
'1041029707459731586',
|
||||
'1041029708004995199',
|
||||
'1041029708453789766',
|
||||
'1041029709049385010',
|
||||
'1041029709632377003',
|
||||
'1041029710227976313',
|
||||
'1041029710764834856',
|
||||
'1041029711414956202',
|
||||
'1041029712153149524',
|
||||
'1041029712933306459',
|
||||
'1041029713566646313',
|
||||
'1041029714254508042',
|
||||
'1041029714921406555',
|
||||
'1041029716334870629',
|
||||
'1041029717127614636',
|
||||
'1041029717689647114',
|
||||
'1041029718603997214',
|
||||
'1041029719925215302',
|
||||
'1041029721179308082',
|
||||
'1041029721988812860',
|
||||
'1041029722466943037',
|
||||
'1041029723217743964',
|
||||
'1041029723872034826',
|
||||
'1041029724492804156',
|
||||
'1041029725117743144',
|
||||
'1041029725818212474',
|
||||
'1041029726531227741',
|
||||
'1041029727231684638',
|
||||
]
|
||||
|
||||
it('Send 10 messages to 10 channels', async () => {
|
||||
await Promise.all(spamChannelIds.map(async (channelId) => {
|
||||
await Promise.all([...Array(10).keys()].map(async (_, index) => {
|
||||
await rest.sendMessage(channelId, { content: `testing rate limit manager ${index}` })
|
||||
}))
|
||||
}))
|
||||
await Promise.all(
|
||||
spamChannelIds.map(async (channelId) => {
|
||||
await Promise.all(
|
||||
[...Array(10).keys()].map(async (_, index) => {
|
||||
await rest.sendMessage(channelId, { content: `testing rate limit manager ${index}` })
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('[role] Role tests', async () => {
|
||||
it('Edit the roles hoist', async () => {
|
||||
expect(role.hoist).to.equal(false)
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
hoist: true
|
||||
hoist: true,
|
||||
})
|
||||
expect(edited.hoist).to.equal(true)
|
||||
})
|
||||
@@ -100,7 +100,7 @@ describe('[role] Role tests', async () => {
|
||||
await rest.editRole(e2ecache.guild.id, role.id, { hoist: true })
|
||||
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
hoist: false
|
||||
hoist: false,
|
||||
})
|
||||
expect(edited.hoist).to.equal(false)
|
||||
})
|
||||
@@ -108,7 +108,7 @@ describe('[role] Role tests', async () => {
|
||||
// Edit the roles mentionable
|
||||
it('Edit the roles mentionable', async () => {
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
mentionable: true
|
||||
mentionable: true,
|
||||
})
|
||||
expect(edited.mentionable).to.equal(true)
|
||||
})
|
||||
@@ -116,10 +116,10 @@ describe('[role] Role tests', async () => {
|
||||
// Make mentionable false
|
||||
it('Make mentionable false', async () => {
|
||||
await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
mentionable: true
|
||||
mentionable: true,
|
||||
})
|
||||
const edited = await rest.editRole(e2ecache.guild.id, role.id, {
|
||||
mentionable: false
|
||||
mentionable: false,
|
||||
})
|
||||
expect(edited.mentionable).to.equal(false)
|
||||
})
|
||||
|
||||
@@ -14,5 +14,5 @@ rest.deleteQueueDelay = 10000
|
||||
export const e2ecache = {
|
||||
guild: await rest.createGuild({ name: 'ddenotester' }),
|
||||
deletedGuild: false,
|
||||
communityGuildId: E2E_TEST_GUILD_ID
|
||||
communityGuildId: E2E_TEST_GUILD_ID,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai'
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha'
|
||||
import sinon from 'sinon'
|
||||
import type { RestManager } from '../../src/manager.js'
|
||||
import type { RestManager } from '../../src/types.js'
|
||||
import { createRestManager } from '../../src/manager.js'
|
||||
import { fakeToken as token } from '../constants.js'
|
||||
|
||||
|
||||
@@ -103,28 +103,24 @@ Have your cache setup in any way you like. Redis, PGSQL or any cache layer you w
|
||||
Here is a minimal example to get started with:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createBot,
|
||||
Intents,
|
||||
startBot,
|
||||
} from "https://deno.land/x/discordeno@13.0.0/mod.ts";
|
||||
import { createBot, Intents, startBot } from 'https://deno.land/x/discordeno@13.0.0/mod.ts'
|
||||
|
||||
const bot = createBot({
|
||||
token: process.env.DISCORD_TOKEN,
|
||||
intents: Intents.Guilds | Intents.GuildMessages,
|
||||
events: {
|
||||
ready() {
|
||||
console.log("Successfully connected to gateway");
|
||||
console.log('Successfully connected to gateway')
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
// Another way to do events
|
||||
bot.events.messageCreate = function (b, message) {
|
||||
// Process the message here with your command handler.
|
||||
};
|
||||
}
|
||||
|
||||
await startBot(bot);
|
||||
await startBot(bot)
|
||||
```
|
||||
|
||||
### Tools
|
||||
|
||||
@@ -77,50 +77,6 @@ export interface DiscordUser {
|
||||
banner?: string
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/user#connection-object */
|
||||
// export interface DiscordConnection {
|
||||
// /** id of the connection account */
|
||||
// id: string
|
||||
// /** The username of the connection account */
|
||||
// name: string
|
||||
// /** The service of the connection (twitch, youtube) */
|
||||
// type: DiscordConnectionServices
|
||||
// /** Whether the connection is revoked */
|
||||
// revoked?: boolean
|
||||
// /** Whether the connection is verified */
|
||||
// verified: boolean
|
||||
// /** Whether friend sync is enabled for this connection */
|
||||
// friend_sync: boolean
|
||||
// /** Whether activities related to this connection will be shown in presence updates */
|
||||
// show_activity: boolean
|
||||
// /** Visibility of this connection */
|
||||
// visibility: VisibilityTypes
|
||||
|
||||
// /** An array of partial server integrations */
|
||||
// integrations?: DiscordIntegration[]
|
||||
// /** Whether this connection has a corresponding third party OAuth2 token. */
|
||||
// two_way_link: boolean
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/user#connection-object-services */
|
||||
// export type DiscordConnectionServices =
|
||||
// | 'battlenet'
|
||||
// | 'ebay'
|
||||
// | 'epicgames'
|
||||
// | 'facebook'
|
||||
// | 'github'
|
||||
// | 'leagueoflegends'
|
||||
// | 'playstation'
|
||||
// | 'reddit'
|
||||
// | 'riotgames'
|
||||
// | 'spotify'
|
||||
// | 'skype'
|
||||
// | 'steam'
|
||||
// | 'twitch'
|
||||
// | 'twitter'
|
||||
// | 'xbox'
|
||||
// | 'youtube'
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#integration-object-integration-structure */
|
||||
export interface DiscordIntegration {
|
||||
/** Integration Id */
|
||||
@@ -613,7 +569,6 @@ export interface DiscordGuild {
|
||||
presences?: Array<Partial<DiscordPresenceUpdate>>
|
||||
/** Banner hash */
|
||||
banner: string | null
|
||||
// TODO: Can be optimized to a number but is it worth it?
|
||||
/** The preferred locale of a Community guild; used in server discovery and notices from Discord; defaults to "en-US" */
|
||||
preferred_locale: string
|
||||
/** The id of the channel where admins and moderators of Community guilds receive notices from Discord */
|
||||
@@ -851,11 +806,6 @@ export interface DiscordThreadMetadata {
|
||||
create_timestamp?: string | null
|
||||
}
|
||||
|
||||
// export interface DiscordThreadMemberBase {
|
||||
// /** Any user-thread settings, currently only used for notifications */
|
||||
// flags: number
|
||||
// }
|
||||
|
||||
export interface DiscordThreadMember {
|
||||
/** Any user-thread settings, currently only used for notifications */
|
||||
flags: number
|
||||
@@ -867,13 +817,6 @@ export interface DiscordThreadMember {
|
||||
join_timestamp: string
|
||||
}
|
||||
|
||||
// export interface DiscordThreadMemberGuildCreate {
|
||||
// /** Any user-thread settings, currently only used for notifications */
|
||||
// flags: number
|
||||
// /** The time the current user last joined the thread */
|
||||
// join_timestamp: string
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway-events#activity-object */
|
||||
export interface DiscordActivity {
|
||||
/** The activity's name */
|
||||
@@ -1376,27 +1319,6 @@ export interface DiscordInteractionDataOption {
|
||||
focused?: boolean
|
||||
}
|
||||
|
||||
// export interface DiscordInteractionDataResolved {
|
||||
// /** The Ids and Message objects */
|
||||
// messages?: Record<string, DiscordMessage>
|
||||
// /** The Ids and User objects */
|
||||
// users?: Record<string, DiscordUser>
|
||||
// /** The Ids and partial Member objects */
|
||||
// members?: Record<
|
||||
// string,
|
||||
// Omit<DiscordInteractionMember, 'user' | 'deaf' | 'mute'>
|
||||
// >
|
||||
// /** The Ids and Role objects */
|
||||
// roles?: Record<string, DiscordRole>
|
||||
// /** The Ids and partial Channel objects */
|
||||
// channels?: Record<
|
||||
// string,
|
||||
// Pick<DiscordChannel, 'id' | 'name' | 'type' | 'permissions'>
|
||||
// >
|
||||
// /** The Ids and attachments objects */
|
||||
// attachments?: Record<string, DiscordAttachment>
|
||||
// }
|
||||
|
||||
export interface DiscordListActiveThreads {
|
||||
/** The active threads */
|
||||
threads: DiscordChannel[]
|
||||
@@ -1483,7 +1405,6 @@ export enum AutoModerationTriggerTypes {
|
||||
}
|
||||
|
||||
export interface DiscordAutoModerationRuleTriggerMetadata {
|
||||
// TODO: discord is considering renaming this before release
|
||||
/** The keywords needed to match. Only present when TriggerType.Keyword */
|
||||
keyword_filter?: string[]
|
||||
/** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */
|
||||
@@ -2010,39 +1931,6 @@ export interface DiscordGuildPreview {
|
||||
stickers: DiscordSticker[]
|
||||
}
|
||||
|
||||
// export interface DiscordDiscoveryCategory {
|
||||
// /** Numeric id of the category */
|
||||
// id: number
|
||||
// /** The name of this category, in multiple languages */
|
||||
// name: DiscordDiscoveryName
|
||||
// /** Whether this category can be set as a guild's primary category */
|
||||
// is_primary: boolean
|
||||
// }
|
||||
|
||||
// export interface DiscordDiscoveryName {
|
||||
// /** The name in English */
|
||||
// default: string
|
||||
// /** The name in other languages */
|
||||
// localizations?: Record<string, string>
|
||||
// }
|
||||
|
||||
// export interface DiscordDiscoveryMetadata {
|
||||
// /** The guild Id */
|
||||
// guild_id: string
|
||||
// /** The id of the primary discovery category set for this guild */
|
||||
// primary_category_id: number
|
||||
// /** Up to 10 discovery search keywords set for this guild */
|
||||
// keywords: string[] | null
|
||||
// /** Whether guild info is shown when custom emojis from this guild are clicked */
|
||||
// emoji_discoverability_enabled: boolean
|
||||
// /** When the server's partner application was accepted or denied, for applications via Server Settings */
|
||||
// partner_actioned_timestamp: string | null
|
||||
// /** When the server applied for partnership, if it has a pending application */
|
||||
// partner_application_timestamp: string | null
|
||||
// /** Ids of up to 5 discovery subcategories set for this guild */
|
||||
// category_ids: number[]
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#followed-channel-object */
|
||||
export interface DiscordFollowedChannel {
|
||||
/** Source message id */
|
||||
@@ -2081,48 +1969,6 @@ export interface DiscordGuildMembersChunk {
|
||||
nonce?: string
|
||||
}
|
||||
|
||||
// export interface DiscordComponent {
|
||||
// /** component type */
|
||||
// type: MessageComponentTypes
|
||||
// /** a developer-defined identifier for the component, max 100 characters */
|
||||
// custom_id?: string
|
||||
// /** whether the component is disabled, default false */
|
||||
// disabled?: boolean
|
||||
// /** For different styles/colors of the buttons */
|
||||
// style?: ButtonStyles | TextStyles
|
||||
// /** text that appears on the button (max 80 characters) */
|
||||
// label?: string
|
||||
// /** the dev-define value of the option, max 100 characters for select or 4000 for input. */
|
||||
// value?: string
|
||||
// /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */
|
||||
// emoji?: {
|
||||
// /** Emoji id */
|
||||
// id?: string
|
||||
// /** Emoji name */
|
||||
// name?: string
|
||||
// /** Whether this emoji is animated */
|
||||
// animated?: boolean
|
||||
// }
|
||||
// /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */
|
||||
// url?: string
|
||||
// /** The choices! Maximum of 25 items. */
|
||||
// options?: DiscordSelectOption[]
|
||||
// /** A custom placeholder text if nothing is selected. Maximum 150 characters. */
|
||||
// placeholder?: string
|
||||
// /** The minimum number of items that must be selected. Default 1. Between 1-25. */
|
||||
// min_values?: number
|
||||
// /** The maximum number of items that can be selected. Default 1. Between 1-25. */
|
||||
// max_values?: number
|
||||
// /** The minimum input length for a text input. Between 0-4000. */
|
||||
// min_length?: number
|
||||
// /** The maximum input length for a text input. Between 1-4000. */
|
||||
// max_length?: number
|
||||
// /** a list of child components */
|
||||
// components?: DiscordComponent[]
|
||||
// /** whether this component is required to be filled, default true */
|
||||
// required?: boolean
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway#channel-pins-update */
|
||||
export interface DiscordChannelPinsUpdate {
|
||||
/** The id of the guild */
|
||||
@@ -2355,13 +2201,6 @@ export interface DiscordGuildStickersUpdate {
|
||||
stickers: DiscordSticker[]
|
||||
}
|
||||
|
||||
// export interface DiscordAddGuildDiscoverySubcategory {
|
||||
// /** The guild Id of the subcategory was added to */
|
||||
// guild_id: string
|
||||
// /** The Id of the subcategory added */
|
||||
// category_id: number
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway#guild-member-update */
|
||||
export interface DiscordGuildMemberUpdate {
|
||||
/** The id of the guild */
|
||||
@@ -2391,12 +2230,6 @@ export interface DiscordGuildMemberUpdate {
|
||||
/** https://discord.com/developers/docs/topics/gateway#message-reaction-remove-all */
|
||||
export interface DiscordMessageReactionRemoveAll extends Pick<DiscordMessageReactionAdd, 'channel_id' | 'message_id' | 'guild_id'> {}
|
||||
|
||||
// // TODO: add docs link
|
||||
// export interface DiscordValidateDiscoverySearchTerm {
|
||||
// /** Whether the provided term is valid */
|
||||
// valid: boolean
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway#guild-role-update */
|
||||
export interface DiscordGuildRoleUpdate {
|
||||
/** The id of the guild */
|
||||
@@ -2480,23 +2313,6 @@ export interface DiscordInstallParams {
|
||||
permissions: string
|
||||
}
|
||||
|
||||
// export interface DiscordInteractionResponse {
|
||||
// type: InteractionResponseTypes
|
||||
// data?: DiscordInteractionCallbackData
|
||||
// }
|
||||
|
||||
// export interface DiscordInteractionCallbackData {
|
||||
// tts?: boolean
|
||||
// title?: string
|
||||
// flags?: number
|
||||
// content?: string
|
||||
// choices?: DiscordApplicationCommandOptionChoice[]
|
||||
// custom_id?: string
|
||||
// embeds?: DiscordEmbed[]
|
||||
// allowed_mentions?: DiscordAllowedMentions
|
||||
// components?: DiscordComponent[]
|
||||
// }
|
||||
|
||||
export interface DiscordForumTag {
|
||||
/** The id of the tag */
|
||||
id: string
|
||||
@@ -2517,42 +2333,6 @@ export interface DiscordDefaultReactionEmoji {
|
||||
emoji_name: string | null
|
||||
}
|
||||
|
||||
// export interface DiscordCreateAutomoderationRule {
|
||||
// /** The name of the rule. */
|
||||
// name: string
|
||||
// /** The type of event to trigger the rule on. */
|
||||
// event_type: AutoModerationEventTypes
|
||||
// /** The type of trigger to use for the rule. */
|
||||
// trigger_type: AutoModerationTriggerTypes
|
||||
// /** The metadata to use for the trigger. */
|
||||
// trigger_metadata: DiscordAutoModerationRuleTriggerMetadata
|
||||
// /** The actions that will trigger for this rule */
|
||||
// actions: DiscordAutoModerationAction[]
|
||||
// /** Whether the rule should be enabled, true by default. */
|
||||
// enabled?: boolean
|
||||
// /** The role ids that should not be effected by the rule */
|
||||
// exempt_roles?: string[]
|
||||
// /** The channel ids that should not be effected by the rule. */
|
||||
// exempt_channels?: string[]
|
||||
// }
|
||||
|
||||
// export interface DiscordModifyAutomoderationRule {
|
||||
// /** The name of the rule. */
|
||||
// name: string
|
||||
// /** The type of event to trigger the rule on. */
|
||||
// event_type: AutoModerationEventTypes
|
||||
// /** The metadata to use for the trigger. */
|
||||
// trigger_metadata: DiscordAutoModerationRuleTriggerMetadata
|
||||
// /** The actions that will trigger for this rule */
|
||||
// actions: DiscordAutoModerationAction[]
|
||||
// /** Whether the rule should be enabled, true by default. */
|
||||
// enabled?: boolean
|
||||
// /** The role ids that should not be effected by the rule */
|
||||
// exempt_roles?: string[]
|
||||
// /** The channel ids that should not be effected by the rule. */
|
||||
// exempt_channels?: string[]
|
||||
// }
|
||||
|
||||
export interface DiscordModifyChannel {
|
||||
/** 1-100 character channel name */
|
||||
name?: string
|
||||
@@ -2679,26 +2459,6 @@ export interface DiscordCreateGuildChannel {
|
||||
default_sort_order?: SortOrderTypes | null
|
||||
}
|
||||
|
||||
// export interface DiscordBulkDeleteMessages {
|
||||
// messages: string[]
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/channel#edit-message-json-params */
|
||||
// export interface DiscordEditMessage {
|
||||
// /** The new message contents (up to 2000 characters) */
|
||||
// content?: string | null
|
||||
// /** Embedded `rich` content (up to 6000 characters) */
|
||||
// embeds?: DiscordEmbed[] | null
|
||||
// /** Edit the flags of the message (only `SUPPRESS_EMBEDS` can currently be set/unset) */
|
||||
// flags?: 4 | null
|
||||
// /** Allowed mentions for the message */
|
||||
// allowed_mentions?: DiscordAllowedMentions
|
||||
// /** When specified (adding new attachments), attachments which are not provided in this list will be removed. */
|
||||
// attachments?: DiscordAttachment[]
|
||||
// /** The components you would like to have sent in this message */
|
||||
// components?: DiscordMessageComponents
|
||||
// }
|
||||
|
||||
export interface DiscordCreateMessage {
|
||||
/** The message contents (up to 2000 characters) */
|
||||
content?: string
|
||||
@@ -2707,9 +2467,9 @@ export interface DiscordCreateMessage {
|
||||
/** true if this is a TTS message */
|
||||
tts?: boolean
|
||||
/** Embedded `rich` content (up to 6000 characters) */
|
||||
// embeds?: DiscordEmbed[]
|
||||
embeds?: DiscordEmbed[]
|
||||
/** Allowed mentions for the message */
|
||||
// allowed_mentions?: DiscordAllowedMentions
|
||||
allowed_mentions?: DiscordAllowedMentions
|
||||
/** Include to make your message a reply */
|
||||
message_reference?: {
|
||||
/** id of the originating message */
|
||||
@@ -2725,166 +2485,11 @@ export interface DiscordCreateMessage {
|
||||
fail_if_not_exists: boolean
|
||||
}
|
||||
/** The components you would like to have sent in this message */
|
||||
// components?: DiscordMessageComponents
|
||||
components?: DiscordMessageComponents
|
||||
/** IDs of up to 3 stickers in the server to send in the message */
|
||||
stickerIds?: [string] | [string, string] | [string, string, string]
|
||||
}
|
||||
|
||||
// export interface DiscordCreateScheduledEvent {
|
||||
// /** the channel id of the scheduled event. */
|
||||
// channel_id?: string
|
||||
// /** location of the event. Required for events with `entityType: ScheduledEventEntityType.External` */
|
||||
// location?: string
|
||||
// /** the name of the scheduled event */
|
||||
// name: string
|
||||
// /** the description of the scheduled event */
|
||||
// description: string
|
||||
// /** the time the scheduled event will start */
|
||||
// scheduled_start_time: string
|
||||
// /** the time the scheduled event will end if it does end. Required for events with `entityType: ScheduledEventEntityType.External` */
|
||||
// scheduled_end_time?: string
|
||||
// /** the privacy level of the scheduled event */
|
||||
// privacy_level?: ScheduledEventPrivacyLevel
|
||||
// /** the type of hosting entity associated with a scheduled event */
|
||||
// entity_type: ScheduledEventEntityType
|
||||
// }
|
||||
|
||||
// export interface DiscordEditScheduledEvent {
|
||||
// /** the channel id of the scheduled event. null if switching to external event. */
|
||||
// channel_id: string | null
|
||||
// /** location of the event */
|
||||
// location?: string
|
||||
// /** the name of the scheduled event */
|
||||
// name: string
|
||||
// /** the description of the scheduled event */
|
||||
// description?: string
|
||||
// /** the time the scheduled event will start */
|
||||
// scheduled_start_time: string
|
||||
// /** the time the scheduled event will end if it does end. */
|
||||
// scheduled_end_time?: string
|
||||
// /** the privacy level of the scheduled event */
|
||||
// privacy_level: ScheduledEventPrivacyLevel
|
||||
// /** the type of hosting entity associated with a scheduled event */
|
||||
// entity_type: ScheduledEventEntityType
|
||||
// /** the status of the scheduled event */
|
||||
// status: ScheduledEventStatus
|
||||
// }
|
||||
|
||||
// export interface DiscordCreateChannelInvite {
|
||||
// /** Duration of invite in seconds before expiry, or 0 for never. Between 0 and 604800 (7 days). Default: 86400 (24 hours) */
|
||||
// max_age?: number
|
||||
// /** Max number of users or 0 for unlimited. Between 0 and 100. Default: 0 */
|
||||
// max_uses?: number
|
||||
// /** Whether this invite only grants temporary membership. Default: false */
|
||||
// temporary?: boolean
|
||||
// /** If true, don't try to reuse similar invite (useful for creating many unique one time use invites). Default: false */
|
||||
// unique?: boolean
|
||||
// /** The type of target for this voice channel invite */
|
||||
// target_type?: TargetTypes
|
||||
// /** The id of the user whose stream to display for this invite, required if `target_type` is 1, the user must be streaming in the channel */
|
||||
// target_user_id?: string
|
||||
// /** The id of the embedded application to open for this invite, required if `target_type` is 2, the application must have the `EMBEDDED` flag */
|
||||
// target_application_id?: string
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#update-current-user-voice-state */
|
||||
// export interface DiscordEditOwnVoiceState {
|
||||
// /** The id of the channel the user is currently in */
|
||||
// channel_id: string
|
||||
// /** Toggles the user's suppress state */
|
||||
// suppress?: boolean
|
||||
// /** Sets the user's request to speak */
|
||||
// request_to_speak_timestamp?: number | null
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#update-user-voice-state */
|
||||
// export interface DiscordEditUserVoiceState {
|
||||
// /** The id of the channel the user is currently in */
|
||||
// channel_id: string
|
||||
// /** Toggles the user's suppress state */
|
||||
// suppress?: boolean
|
||||
// /** The user id to target */
|
||||
// user_id: string
|
||||
// }
|
||||
|
||||
// export interface DiscordEditGuildWidgetSettings {
|
||||
// /** Whether or not the widget is enabled. */
|
||||
// enabled: boolean
|
||||
// /** The channel id if any for this widget. */
|
||||
// channel_id?: string | null
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#create-guild */
|
||||
// export interface DiscordCreateGuild {
|
||||
// /** Name of the guild (1-100 characters) */
|
||||
// name: string
|
||||
// /** Base64 128x128 image for the guild icon */
|
||||
// icon?: string
|
||||
// /** Verification level */
|
||||
// verification_level?: VerificationLevels
|
||||
// /** Default message notification level */
|
||||
// default_message_notifications?: DefaultMessageNotificationLevels
|
||||
// /** Explicit content filter level */
|
||||
// explicit_content_filter?: ExplicitContentFilterLevels
|
||||
// /** New guild roles (first role is the everyone role) */
|
||||
// roles?: DiscordRole[]
|
||||
// /** New guild's channels */
|
||||
// channels?: Array<Partial<DiscordChannel>>
|
||||
// /** Id for afk channel */
|
||||
// afk_channel_id?: string
|
||||
// /** Afk timeout in seconds */
|
||||
// afk_timeout?: number
|
||||
// /** The id of the channel where guild notices such as welcome messages and boost events are posted */
|
||||
// system_channel_id?: string
|
||||
// /** System channel flags */
|
||||
// system_channel_flags?: SystemChannelFlags
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#modify-guild */
|
||||
// export interface DiscordModifyGuild {
|
||||
// /** Guild name */
|
||||
// name?: string
|
||||
// /** Verification level */
|
||||
// verification_level?: VerificationLevels | null
|
||||
// /** Default message notification filter level */
|
||||
// default_message_notifications?: DefaultMessageNotificationLevels | null
|
||||
// /** Explicit content filter level */
|
||||
// explicit_content_filter?: ExplicitContentFilterLevels | null
|
||||
// /** Id for afk channel */
|
||||
// afk_channel_id?: string | null
|
||||
// /** Afk timeout in seconds */
|
||||
// afk_timeout?: number
|
||||
// /** Base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has the `ANIMATED_ICON` feature) */
|
||||
// icon?: string | null
|
||||
// /** User id to transfer guild ownership to (must be owner) */
|
||||
// owner_id?: string
|
||||
// /** Base64 16:9 png/jpeg image for the guild splash (when the server has `INVITE_SPLASH` feature) */
|
||||
// splash?: string | null
|
||||
// /** Base64 16:9 png/jpeg image for the guild discovery spash (when the server has the `DISCOVERABLE` feature) */
|
||||
// discovery_splash?: string | null
|
||||
// /** Base64 16:9 png/jpeg image for the guild banner (when the server has BANNER feature) */
|
||||
// banner?: string | null
|
||||
// /** The id of the channel where guild notices such as welcome messages and boost events are posted */
|
||||
// system_channel_id?: string | null
|
||||
// /** System channel flags */
|
||||
// system_channel_flags?: SystemChannelFlags
|
||||
// /** The id of the channel where Community guilds display rules and/or guidelines */
|
||||
// rules_channel_id?: string | null
|
||||
// /** The id of the channel where admins and moderators of Community guilds receive notices from Discord */
|
||||
// public_updates_channel_id?: string | null
|
||||
// /** The preferred locale of a Community guild used in server discovery and notices from Discord; defaults to "en-US" */
|
||||
// preferred_locale?: string | null
|
||||
// /** Enabled guild features */
|
||||
// features?: GuildFeatures[]
|
||||
// /** Whether the guild's boost progress bar should be enabled */
|
||||
// premium_progress_bar_enabled?: boolean
|
||||
// }
|
||||
|
||||
// export interface DiscordEditGuildMFALevel {
|
||||
// /** The level to set for the guilds mfa level. */
|
||||
// level: MfaLevels
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen */
|
||||
export interface DiscordModifyGuildWelcomeScreen {
|
||||
/** Whether the welcome screen is enabled */
|
||||
@@ -2895,31 +2500,6 @@ export interface DiscordModifyGuildWelcomeScreen {
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
// export interface DiscordStartThreadWithMessage {
|
||||
// /** 1-100 character thread name */
|
||||
// name: string
|
||||
// /** Duration in minutes to automatically archive the thread after recent activity */
|
||||
// auto_archive_duration: 60 | 1440 | 4320 | 10080
|
||||
// /** Amount of seconds a user has to wait before sending another message (0-21600) */
|
||||
// rate_limit_per_user?: number | null
|
||||
// }
|
||||
|
||||
// export interface DiscordStartThreadWithoutMessage {
|
||||
// /** 1-100 character thread name */
|
||||
// name: string
|
||||
// /** Duration in minutes to automatically archive the thread after recent activity */
|
||||
// auto_archive_duration: 60 | 1440 | 4320 | 10080
|
||||
// /** Amount of seconds a user has to wait before sending another message (0-21600) */
|
||||
// rate_limit_per_user?: number | null
|
||||
// /** the type of thread to create */
|
||||
// type:
|
||||
// | ChannelTypes.AnnouncementThread
|
||||
// | ChannelTypes.PublicThread
|
||||
// | ChannelTypes.PrivateThread
|
||||
// /** whether non-moderators can add other non-moderators to a thread; only available when creating a private thread */
|
||||
// invitable?: boolean
|
||||
// }
|
||||
|
||||
export interface DiscordFollowAnnouncementChannel {
|
||||
/** The id of the channel to send announcements to. */
|
||||
webhook_channel_id: string
|
||||
@@ -2946,113 +2526,6 @@ export interface DiscordModifyGuildChannelPositions {
|
||||
parent_id?: string | null
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#create-guild-ban */
|
||||
// export interface DiscordCreateGuildBan {
|
||||
// /** Number of seconds to delete messages for, between 0 and 604800 (7 days) */
|
||||
// delete_message_seconds?: number
|
||||
// }
|
||||
|
||||
// export interface DiscordEditBotMemberOptions {
|
||||
// nick?: string | null
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#modify-guild-member */
|
||||
// export interface DiscordModifyGuildMember {
|
||||
// /** Value to set users nickname to. Requires the `MANAGE_NICKNAMES` permission */
|
||||
// nick?: string | null
|
||||
// /** Array of role ids the member is assigned. Requires the `MANAGE_ROLES` permission */
|
||||
// roles?: string[] | null
|
||||
// /** Whether the user is muted in voice channels. Will throw a 400 if the user is not in a voice channel. Requires the `MUTE_MEMBERS` permission */
|
||||
// mute?: boolean | null
|
||||
// /** Whether the user is deafened in voice channels. Will throw a 400 if the user is not in a voice channel. Requires the `MOVE_MEMBERS` permission */
|
||||
// deaf?: boolean | null
|
||||
// /** Id of channel to move user to (if they are connected to voice). Requires the `MOVE_MEMBERS` permission */
|
||||
// channel_id?: string | null
|
||||
// /** when the user's timeout will expire and the user will be able to communicate in the guild again (up to 28 days in the future), set to null to remove timeout. Requires the `MODERATE_MEMBERS` permission */
|
||||
// communication_disabled_until?: number | null
|
||||
// }
|
||||
|
||||
// export interface DiscordGetDMChannel {
|
||||
// /** The user id */
|
||||
// recipient_id: string
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild#begin-guild-prune */
|
||||
// export interface DiscordBeginGuildPrune {
|
||||
// /** Number of days to prune (1 or more), default: 7 */
|
||||
// days?: number
|
||||
// /** Whether 'pruned' is returned, discouraged for large guilds, default: true */
|
||||
// compute_prune_count?: boolean
|
||||
// /** Role(s) ro include, default: none */
|
||||
// include_roles?: string[]
|
||||
// }
|
||||
|
||||
// export interface DiscordCreateGuildRole {
|
||||
// /** Name of the role, max 100 characters, default: "new role" */
|
||||
// name?: string
|
||||
// /** Bitwise value of the enabled/disabled permissions, default: everyone permissions in guild */
|
||||
// permissions?: string
|
||||
// /** RGB color value, default: 0 */
|
||||
// color?: number
|
||||
// /** Whether the role should be displayed separately in the sidebar, default: false */
|
||||
// hoist?: boolean
|
||||
// /** Whether the role should be mentionable, default: false */
|
||||
// mentionable?: boolean
|
||||
// /** The role's unicode emoji (if the guild has the `ROLE_ICONS` feature) */
|
||||
// unicode_emoji?: string
|
||||
// /** the role's icon image (if the guild has the `ROLE_ICONS` feature) */
|
||||
// icon?: string
|
||||
// }
|
||||
|
||||
// export interface DiscordEditGuildRole {
|
||||
// /** Name of the role, max 100 characters, default: "new role" */
|
||||
// name?: string
|
||||
// /** Bitwise value of the enabled/disabled permissions, default: everyone permissions in guild */
|
||||
// permissions?: string
|
||||
// /** RGB color value, default: 0 */
|
||||
// color?: number
|
||||
// /** Whether the role should be displayed separately in the sidebar, default: false */
|
||||
// hoist?: boolean
|
||||
// /** Whether the role should be mentionable, default: false */
|
||||
// mentionable?: boolean
|
||||
// /** The role's unicode emoji (if the guild has the `ROLE_ICONS` feature) */
|
||||
// unicode_emoji?: string
|
||||
// /** the role's icon image (if the guild has the `ROLE_ICONS` feature) */
|
||||
// icon?: string
|
||||
// }
|
||||
|
||||
// export interface DiscordModifyRolePositions {
|
||||
// /** The role id */
|
||||
// id: string
|
||||
// /** The sorting position for the role. */
|
||||
// position?: number | null
|
||||
// }
|
||||
|
||||
// export interface DiscordCreateGuildStickerOptions {
|
||||
// /** Name of the sticker (2-30 characters) */
|
||||
// name: string
|
||||
// /** Description of the sticker (empty or 2-100 characters) */
|
||||
// description: string
|
||||
// /** Autocomplete/suggestion tags for the sticker (max 200 characters) */
|
||||
// tags: string
|
||||
// }
|
||||
|
||||
// export interface DiscordEditGuildStickerOptions {
|
||||
// /** Name of the sticker (2-30 characters) */
|
||||
// name?: string
|
||||
// /** Description of the sticker (empty or 2-100 characters) */
|
||||
// description?: string | null
|
||||
// /** Autocomplete/suggestion tags for the sticker (max 200 characters) */
|
||||
// tags?: string
|
||||
// }
|
||||
|
||||
// export interface DiscordCreateTemplate {
|
||||
// /** Name which the template should have */
|
||||
// name: string
|
||||
// /** Description of the template */
|
||||
// description?: string
|
||||
// }
|
||||
|
||||
export interface DiscordCreateWebhook {
|
||||
/** Name of the webhook (1-80 characters) */
|
||||
name: string
|
||||
@@ -3060,39 +2533,6 @@ export interface DiscordCreateWebhook {
|
||||
avatar?: string | null
|
||||
}
|
||||
|
||||
// export interface DiscordModifyWebhook {
|
||||
// /** The default name of the webhook */
|
||||
// name?: string
|
||||
// /** Image for the default webhook avatar */
|
||||
// avatar?: string | null
|
||||
// /** The new channel id this webhook should be moved to */
|
||||
// channel_id?: string
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/webhook#execute-webhook */
|
||||
// export interface DiscordExecuteWebhook {
|
||||
// /** Waits for server confirmation of message send before response, and returns the created message body (defaults to `false`; when `false` a message that is not saved does not return an error) */
|
||||
// wait?: boolean
|
||||
// /** Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. */
|
||||
// thread_id?: string
|
||||
// /** Name of the thread to create (target channel has to be type of forum channel) */
|
||||
// thread_name?: string
|
||||
// /** The message contents (up to 2000 characters) */
|
||||
// content?: string
|
||||
// /** Override the default username of the webhook */
|
||||
// username?: string
|
||||
// /** Override the default avatar of the webhook */
|
||||
// avatar_url?: string
|
||||
// /** True if this is a TTS message */
|
||||
// tts?: boolean
|
||||
// /** Embedded `rich` content */
|
||||
// embeds?: DiscordEmbed[]
|
||||
// /** Allowed mentions for the message */
|
||||
// allowed_mentions?: DiscordAllowedMentions
|
||||
// /** the components to include with the message */
|
||||
// components?: DiscordMessageComponents
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel */
|
||||
export interface DiscordCreateForumPostWithMessage {
|
||||
/** 1-100 character channel name */
|
||||
@@ -3126,14 +2566,6 @@ export interface DiscordCreateForumPostWithMessage {
|
||||
applied_tags?: string[]
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/guild-template#modify-guild-template */
|
||||
// export interface DiscordModifyGuildTemplate {
|
||||
// /** name of the template (1-100 characters) */
|
||||
// name?: string
|
||||
// /** description for the template (0-120 characters) */
|
||||
// description?: string
|
||||
// }
|
||||
|
||||
export type DiscordArchivedThreads = DiscordActiveThreads & {
|
||||
hasMore: boolean
|
||||
}
|
||||
|
||||
@@ -69,19 +69,6 @@ export interface CreateMessageOptions {
|
||||
/** IDs of up to 3 stickers in the server to send in the message */
|
||||
stickerIds?: [BigString] | [BigString, BigString] | [BigString, BigString, BigString]
|
||||
}
|
||||
// import type {
|
||||
// AllowedMentionsTypes,
|
||||
// ApplicationCommandTypes,
|
||||
// AuditLogEvents,
|
||||
// BigString,
|
||||
// ButtonStyles,
|
||||
// InteractionResponseTypes,
|
||||
// Localization,
|
||||
// MessageComponentTypes,
|
||||
// OverwriteTypes,
|
||||
// PermissionStrings,
|
||||
// TextStyles
|
||||
// } from './shared.js'
|
||||
|
||||
export type MessageComponents = ActionRow[]
|
||||
|
||||
@@ -296,17 +283,6 @@ export interface OverwriteReadable {
|
||||
deny?: PermissionStrings[]
|
||||
}
|
||||
|
||||
// export interface GetGatewayBot {
|
||||
// url: string
|
||||
// shards: number
|
||||
// sessionStartLimit: {
|
||||
// total: number
|
||||
// remaining: number
|
||||
// resetAfter: number
|
||||
// maxConcurrency: number
|
||||
// }
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#get-channel-messages-query-string-params */
|
||||
export interface GetMessagesLimit {
|
||||
/** Max number of messages to return (1-100) default 50 */
|
||||
@@ -815,7 +791,6 @@ export interface EditAutoModerationRuleOptions extends WithReason {
|
||||
triggerMetadata: {
|
||||
/** The keywords needed to match. Only present when TriggerType.Keyword */
|
||||
keywordFilter?: string[]
|
||||
// TODO: This may need a special type or enum
|
||||
/** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */
|
||||
presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]
|
||||
/** The substrings which will exempt from triggering the preset trigger type. Only present when TriggerType.KeywordPreset */
|
||||
|
||||
@@ -7,14 +7,6 @@ export enum PresenceStatus {
|
||||
offline,
|
||||
}
|
||||
|
||||
// /* https://discord.com/developers/docs/resources/channel#message-object-message-flags */
|
||||
// export enum ApplicationCommandFlags {
|
||||
// /** Do not include any embeds when serialising this message */
|
||||
// SuppressEmbeds = 1 << 2,
|
||||
// /** Only visible to the user who invoked the interaction */
|
||||
// Ephemeral = 1 << 6,
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/user#user-object-premium-types */
|
||||
export enum PremiumTypes {
|
||||
None,
|
||||
@@ -57,14 +49,6 @@ export enum IntegrationExpireBehaviors {
|
||||
Kick,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/user#connection-object-visibility-types */
|
||||
// export enum VisibilityTypes {
|
||||
// /** Invisible to everyone except the user themselves */
|
||||
// None,
|
||||
// /** Visible to everyone */
|
||||
// Everyone,
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/teams#data-models-membership-state-enum */
|
||||
export enum TeamMembershipStates {
|
||||
Invited = 1,
|
||||
@@ -189,18 +173,6 @@ export enum VerificationLevels {
|
||||
VeryHigh,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/permissions#role-object-role-structure */
|
||||
// export interface BaseRole {
|
||||
// /** Role name */
|
||||
// name: string
|
||||
// /** Integer representation of hexadecimal color code */
|
||||
// color: number
|
||||
// /** Position of this role */
|
||||
// position: number
|
||||
// /** role unicode emoji */
|
||||
// unicodeEmoji?: string
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#guild-object-guild-features */
|
||||
export enum GuildFeatures {
|
||||
/** Guild has access to set an invite splash background */
|
||||
@@ -525,8 +497,6 @@ export enum AuditLogEvents {
|
||||
}
|
||||
|
||||
export enum ScheduledEventPrivacyLevel {
|
||||
/** the scheduled event is public and available in discovery. DISCORD DEVS DISABLED THIS! WILL ERROR IF USED! */
|
||||
// Public = 1,
|
||||
/** the scheduled event is only accessible to guild members */
|
||||
GuildOnly = 2,
|
||||
}
|
||||
@@ -565,19 +535,6 @@ export enum ApplicationCommandPermissionTypes {
|
||||
Channel,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags */
|
||||
// export enum ActivityFlags {
|
||||
// Instance = 1 << 0,
|
||||
// Join = 1 << 1,
|
||||
// Spectate = 1 << 2,
|
||||
// JoinRequest = 1 << 3,
|
||||
// Sync = 1 << 4,
|
||||
// Play = 1 << 5,
|
||||
// PartyPrivacyFriends = 1 << 6,
|
||||
// PartyPrivacyVoiceChannel = 1 << 7,
|
||||
// Embedded = 1 << 8,
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags */
|
||||
export enum BitwisePermissionFlags {
|
||||
/** Allows creation of instant invites */
|
||||
@@ -666,314 +623,6 @@ export enum BitwisePermissionFlags {
|
||||
|
||||
export type PermissionStrings = keyof typeof BitwisePermissionFlags
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice */
|
||||
// export enum VoiceOpcodes {
|
||||
// /** Begin a voice websocket connection. */
|
||||
// Identify,
|
||||
// /** Select the voice protocol. */
|
||||
// SelectProtocol,
|
||||
// /** Complete the websocket handshake. */
|
||||
// Ready,
|
||||
// /** Keep the websocket connection alive. */
|
||||
// Heartbeat,
|
||||
// /** Describe the session. */
|
||||
// SessionDescription,
|
||||
// /** Indicate which users are speaking. */
|
||||
// Speaking,
|
||||
// /** Sent to acknowledge a received client heartbeat. */
|
||||
// HeartbeatACK,
|
||||
// /** Resume a connection. */
|
||||
// Resume,
|
||||
// /** Time to wait between sending heartbeats in milliseconds. */
|
||||
// Hello,
|
||||
// /** Acknowledge a successful session resume. */
|
||||
// Resumed,
|
||||
// /** A client has disconnected from the voice channel */
|
||||
// ClientDisconnect = 13,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice */
|
||||
// export enum VoiceCloseEventCodes {
|
||||
// /** You sent an invalid [opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-opcodes). */
|
||||
// UnknownOpcode = 4001,
|
||||
// /** You sent a invalid payload in your [identifying](https://discord.com/developers/docs/topics/gateway#identify) to the Gateway. */
|
||||
// FailedToDecodePayload,
|
||||
// /** You sent a payload before [identifying](https://discord.com/developers/docs/topics/gateway#identify) with the Gateway. */
|
||||
// NotAuthenticated,
|
||||
// /** The token you sent in your [identify](https://discord.com/developers/docs/topics/gateway#identify) payload is incorrect. */
|
||||
// AuthenticationFailed,
|
||||
// /** You sent more than one [identify](https://discord.com/developers/docs/topics/gateway#identify) payload. Stahp. */
|
||||
// AlreadyAuthenticated,
|
||||
// /** Your session is no longer valid. */
|
||||
// SessionNoLongerValid,
|
||||
// /** Your session has timed out. */
|
||||
// SessionTimedOut = 4009,
|
||||
// /** We can't find the server you're trying to connect to. */
|
||||
// ServerNotFound = 4011,
|
||||
// /** We didn't recognize the [protocol](https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. */
|
||||
// UnknownProtocol,
|
||||
// /** Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect. */
|
||||
// Disconnect = 4014,
|
||||
// /** The server crashed. Our bad! Try [resuming](https://discord.com/developers/docs/topics/voice-connections#resuming-voice-connection). */
|
||||
// VoiceServerCrashed,
|
||||
// /** We didn't recognize your [encryption](https://discord.com/developers/docs/topics/voice-connections#encrypting-and-sending-voice). */
|
||||
// UnknownEncryptionMode,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */
|
||||
// export enum RpcErrorCodes {
|
||||
// /** An unknown error occurred. */
|
||||
// UnknownError = 1000,
|
||||
// /** You sent an invalid payload. */
|
||||
// InvalidPayload = 4000,
|
||||
// /** Invalid command name specified. */
|
||||
// InvalidCommand = 4002,
|
||||
// /** Invalid guild ID specified. */
|
||||
// InvalidGuild,
|
||||
// /** Invalid event name specified. */
|
||||
// InvalidEvent,
|
||||
// /** Invalid channel ID specified. */
|
||||
// InvalidChannel,
|
||||
// /** You lack permissions to access the given resource. */
|
||||
// InvalidPermissions,
|
||||
// /** An invalid OAuth2 application ID was used to authorize or authenticate with. */
|
||||
// InvalidClientId,
|
||||
// /** An invalid OAuth2 application origin was used to authorize or authenticate with. */
|
||||
// InvalidOrigin,
|
||||
// /** An invalid OAuth2 token was used to authorize or authenticate with. */
|
||||
// InvalidToken,
|
||||
// /** The specified user ID was invalid. */
|
||||
// InvalidUser,
|
||||
// /** A standard OAuth2 error occurred; check the data object for the OAuth2 error details. */
|
||||
// OAuth2Error = 5000,
|
||||
// /** An asynchronous `SELECT_TEXT_CHANNEL`/`SELECT_VOICE_CHANNEL` command timed out. */
|
||||
// SelectChannelTimedOut,
|
||||
// /** An asynchronous `GET_GUILD` command timed out. */
|
||||
// GetGuildTimedOut,
|
||||
// /** You tried to join a user to a voice channel but the user was already in one. */
|
||||
// SelectVoiceForceRequired,
|
||||
// /** You tried to capture more than one shortcut key at once. */
|
||||
// CaptureShortcutAlreadyListening,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */
|
||||
// export enum RpcCloseEventCodes {
|
||||
// /** You connected to the RPC server with an invalid client ID. */
|
||||
// InvalidClientId = 4000,
|
||||
// /** You connected to the RPC server with an invalid origin. */
|
||||
// InvalidOrigin,
|
||||
// /** You are being rate limited. */
|
||||
// RateLimited,
|
||||
// /** The OAuth2 token associated with a connection was revoked, get a new one! */
|
||||
// TokenRevoked,
|
||||
// /** The RPC Server version specified in the connection string was not valid. */
|
||||
// InvalidVersion,
|
||||
// /** The encoding specified in the connection string was not valid. */
|
||||
// InvalidEncoding,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#json */
|
||||
// export enum JsonErrorCodes {
|
||||
// /** General error (such as a malformed request body, amongst other things) */
|
||||
// GeneralError,
|
||||
// UnknownAccount = 10001,
|
||||
// UnknownApplication,
|
||||
// UnknownChannel,
|
||||
// UnknownGuild,
|
||||
// UnknownIntegration,
|
||||
// UnknownInvite,
|
||||
// UnknownMember,
|
||||
// UnknownMessage,
|
||||
// UnknownPermissionOverwrite,
|
||||
// UnknownProvider,
|
||||
// UnknownRole,
|
||||
// UnknownToken,
|
||||
// UnknownUser,
|
||||
// UnknownEmoji,
|
||||
// UnknownWebhook,
|
||||
// UnknownWebhookService,
|
||||
// UnknownSession = 10020,
|
||||
// UnknownBan = 10026,
|
||||
// UnknownSKU,
|
||||
// UnknownStoreListing,
|
||||
// UnknownEntitlement,
|
||||
// UnknownBuild,
|
||||
// UnknownLobby,
|
||||
// UnknownBranch,
|
||||
// UnknownStoreDirectoryLayout,
|
||||
// UnknownRedistributable = 10036,
|
||||
// UnknownGiftCode = 10038,
|
||||
// UnknownStream = 10049,
|
||||
// UnknownPremiumServerSubscribeCooldown,
|
||||
// UnknownGuildTemplate = 10057,
|
||||
// UnknownDiscoveryCategory = 10059,
|
||||
// UnknownSticker,
|
||||
// UnknownInteraction = 10062,
|
||||
// UnknownApplicationCommand = 10063,
|
||||
// UnknownVoiceState = 10065,
|
||||
// UnknownApplicationCommandPermissions,
|
||||
// UnknownStageInstance,
|
||||
// UnknownGuildMemberVerificationForm,
|
||||
// UnknownGuildWelcomeScreen,
|
||||
// UnknownGuildScheduledEvent,
|
||||
// UnknownGuildScheduledEventUser,
|
||||
// UnknownTag = 10087,
|
||||
// BotsCannotUseThisEndpoint = 20001,
|
||||
// OnlyBotsCanUseThisEndpoint,
|
||||
// ExplicitContentCannotBeSentToTheDesiredRecipient = 20009,
|
||||
// YouAreNotAuthorizedToPerformThisActionOnThisApplication = 20012,
|
||||
// ThisActionCannotBePerformedDueToSlowmodeRateLimit = 20016,
|
||||
// OnlyTheOwnerOfThisAccountCanPerformThisAction = 20018,
|
||||
// ThisMessageCannotBeEditedDueToAnnouncementRateLimits = 20022,
|
||||
// UnderMinimumAge = 20024,
|
||||
// TheChannelYouAreWritingHasHitTheWriteRateLimit = 20028,
|
||||
// TheWriteActionYouArePerformingOnTheServerHasHitTheWriteRateLimit,
|
||||
// YourStageTopicOrServerNameOrServerDescriptionOrChannelNamesContainsWordsThatAreNotAllowedForPublicStages = 20031,
|
||||
// GuildPremiumSubscriptionLevelTooLow = 20035,
|
||||
// MaximumNumberOfGuildsReached = 30001,
|
||||
// MaximumNumberOfFriendsReached,
|
||||
// MaximumNumberOfPinsReachedForTheChannel,
|
||||
// MaximumNumberOfRecipientsReached,
|
||||
// MaximumNumberOfGuildRolesReached,
|
||||
// MaximumNumberOfWebhooksReached = 30007,
|
||||
// MaximumNumberOfEmojisReached,
|
||||
// MaximumNumberOfReactionsReached = 30010,
|
||||
// MaximumNumberOfGuildChannelsReached = 30013,
|
||||
// MaximumNumberOfAttachmentsInAMessageReached = 30015,
|
||||
// MaximumNumberOfInvitesReached,
|
||||
// MaximumNumberOfAnimatedEmojisReached = 30018,
|
||||
// MaximumNumberOfServerMembersReached,
|
||||
// MaximumNumberOfServerCategoriesHasBeenReached = 30030,
|
||||
// GuildAlreadyHasTemplate,
|
||||
// MaximumNumbersOfApplicationCommandsReached,
|
||||
// MaxNumberOfThreadParticipantsHasBeenReached,
|
||||
// MaxNumberOfDailyApplicationCommandCreatesHasBeenReached,
|
||||
// MaximumNumberOfBansForNonGuildMembersHaveBeenExceeded,
|
||||
// MaximumNumberOfBansFetchesHasBeenReached = 30037,
|
||||
// MaximumNumberOfUncompletedGuildScheduledEventsReached = 30038,
|
||||
// MaximumNumberOfStickersReached = 30039,
|
||||
// MaximumNumberOfPruneRequestsHasBeenReachedTryAgainLater,
|
||||
// MaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReachedTryAgainLater = 30042,
|
||||
// MaximumNumberOfEditsToMessagesOlderThan1HourReachedTryAgainLater = 30046,
|
||||
// MaximumNumberOfPinnedThreadsInAForumChannelHasBeenReached,
|
||||
// MaxiumNumberOfTagsInAForumChannelHasBeenReached,
|
||||
// BitrateIsTooHighForChannelOfThisType = 30052,
|
||||
// UnauthorizedProvideAValidTokenAndTryAgain = 40001,
|
||||
// YouNeedToVerifyYourAccountInOrderToPerformThisAction,
|
||||
// YouAreOpeningDirectMessagesTooFast,
|
||||
// SendMessagesHasBeenTemporarilyDisabled,
|
||||
// RequestEntityTooLargeTrySendingSomethingSmallerInSize,
|
||||
// ThisFeatureHasBeenTemporarilyDisabledServerSide,
|
||||
// ThisUserBannedFromThisGuild,
|
||||
// ConnectionHasBeenRevoked = 40012,
|
||||
// TargetUserIsNotConnectedToVoice = 40032,
|
||||
// ThisMessageHasAlreadyBeenCrossposted,
|
||||
// AnApplicationCommandWithThatNameAlreadyExists = 40041,
|
||||
// ApplicationInteractionFailedToSend = 40043,
|
||||
// InteractionHasAlreadyBeenAcknowledged = 40060,
|
||||
// MissingAccess = 50001,
|
||||
// InvalidAccountType,
|
||||
// CannotExecuteActionOnADMChannel,
|
||||
// GuildWidgetDisabled,
|
||||
// CannotEditMessageAuthoredByAnotherUser,
|
||||
// CannotSendAnEmptyMessage,
|
||||
// CannotSendMessagesToThisUser,
|
||||
// CannotSendMessagesInANonTextChannel,
|
||||
// ChannelVerificationLevelIsTooHighForYouToGainAccess,
|
||||
// OAuth2ApplicationDoesNotHaveABot,
|
||||
// OAuth2ApplicationLimitReached,
|
||||
// InvalidOAuth2State,
|
||||
// YouLackPermissionsToPerformThatAction,
|
||||
// InvalidAuthenticationTokenProvided,
|
||||
// NoteWasTooLong,
|
||||
// ProvidedTooFewOrTooManyMessagesToDeleteMustProvideAtLeast2AndFewerThan100MessagesToDelete,
|
||||
// InvalidMFALevel,
|
||||
// AMessageCanOnlyBePinnedInTheChannelItWasSentIn = 50019,
|
||||
// InviteCodeWasEitherInvalidOrTaken,
|
||||
// CannotExecuteActionOnASystemMessage,
|
||||
// CannotExecuteActionOnThisChannelType = 50024,
|
||||
// InvalidOAuth2AccessTokenProvided,
|
||||
// MissingRequiredOAuth2Scope,
|
||||
// InvalidWebhookTokenProvided,
|
||||
// InvalidRole,
|
||||
// InvalidRecipients = 50033,
|
||||
// AMessageProvidedWasTooOldToBulkDelete,
|
||||
// /** Invalid form body (returned for both `application/json` and `multipart/form-data` bodies), or invalid `Content-Type` provided */
|
||||
// InvalidFormBodyOrContentTypeProvided,
|
||||
// AnInviteWasAcceptedToAGuildTheApplicationsBotIsNotIn,
|
||||
// InvalidActivityAction = 50039,
|
||||
// InvalidApiVersionProvided = 50041,
|
||||
// FileUploadedExceedsTheMaximumSize = 50045,
|
||||
// InvalidFileUploaded,
|
||||
// CannotSelfRedeemThisGift = 50054,
|
||||
// InvalidGuild,
|
||||
// InvalidMessageType = 50068,
|
||||
// PaymentSourceRequiredToRedeemGift = 50070,
|
||||
// CannotDeleteAChannelRequiredForCommunityGuilds = 50074,
|
||||
// CannotEditStickersWithinAMessage = 50080,
|
||||
// InvalidStickerSent,
|
||||
// TriedToPerformAnOperationOnAnArchivedThreadSuchAsEditingAMessageOrAddingAUserToTheThread = 50083,
|
||||
// InvalidThreadNotificationSettings,
|
||||
// BeforeValueIsEarlierThanTheThreadCreationDate,
|
||||
// CommunityServerChannelsMustBeTextChannels,
|
||||
// ThisServerIsNotAvailableInYourLocation = 50095,
|
||||
// ThisServerNeedsMonetizationEnabledInOrderToPerformThisAction = 50097,
|
||||
// ThisServerNeedsMoreBoostsToPerformThisAction = 50101,
|
||||
// TheRequestBodyContainsInvalidJSON = 50109,
|
||||
// OwnershipCannotBeTransferredToABotUser = 50132,
|
||||
// FailedToResizeAssetBelowTheMaximumSize = 50138,
|
||||
// UploadedFileNotFound = 50146,
|
||||
// TwoFactorIsRequiredForThisOperation = 60003,
|
||||
// NoUsersWithDiscordTagExist = 80004,
|
||||
// ReactionWasBlocked = 90001,
|
||||
// ApplicationNotYetAvailable = 110001,
|
||||
// ApiResourceIsCurrentlyOverloadedTryAgainALittleLater = 130000,
|
||||
// TheStageIsAlreadyOpen = 150006,
|
||||
// CannotReplyWithoutPermissionToReadMessageHistory = 160002,
|
||||
// AThreadHasAlreadyBeenCreatedForThisMessage = 160004,
|
||||
// ThreadIsLocked = 160005,
|
||||
// MaximumNumberOfActiveThreadsReached = 160006,
|
||||
// MaximumNumberOfActiveAnnouncementThreadsReached = 160007,
|
||||
// InvalidJsonForUploadedLottieFile = 170001,
|
||||
// UploadedLottiesCannotContainRasterizedImagesSuchAsPngOrJpeg,
|
||||
// StickerMaximumFramerateExceeded,
|
||||
// StickerFrameCountExceedsMaximumOf1000Frames,
|
||||
// LottieAnimationMaximumDimensionsExceeded,
|
||||
// StickerFrameRateIsEitherTooSmallOrTooLarge,
|
||||
// StickerAnimationDurationExceedsMaximumOf5Seconds,
|
||||
// CannotUpdateAFinishedEvent = 180000,
|
||||
// FailedToCreateStageNeededForStageEvent = 180002,
|
||||
// MessageWasBlockedByAutomaticModeration = 200000,
|
||||
// TitleWasBlockedByAutomaticModeration,
|
||||
// WebhooksCanOnlyCreateThreadsInForumChannels = 220003,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#http */
|
||||
// export enum HTTPResponseCodes {
|
||||
// /** The request completed successfully. */
|
||||
// Ok = 200,
|
||||
// /** The entity was created successfully. */
|
||||
// Created,
|
||||
// /** The request completed successfully but returned no content. */
|
||||
// NoContent = 204,
|
||||
// /** The entity was not modified (no action was taken). */
|
||||
// NotModified = 304,
|
||||
// /** The request was improperly formatted, or the server couldn't understand it. */
|
||||
// BadRequest = 400,
|
||||
// /** The `Authorization` header was missing or invalid. */
|
||||
// Unauthorized,
|
||||
// /** The `Authorization` token you passed did not have permission to the resource. */
|
||||
// Forbidden = 403,
|
||||
// /** The resource at the location specified doesn't exist. */
|
||||
// NotFound,
|
||||
// /** The HTTP method used is not valid for the location specified. */
|
||||
// MethodNotAllowed,
|
||||
// /** You are being rate limited, see [Rate Limits](https://discord.com/developers/docs/topics/rate-limits). */
|
||||
// TooManyRequests = 429,
|
||||
// /** There was not a gateway available to process your request. Wait a bit and retry. */
|
||||
// GatewayUnavailable = 502,
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/opcodes-and-status-codes#opcodes-and-status-codes */
|
||||
export enum GatewayCloseEventCodes {
|
||||
/** A normal closure of the gateway. You may attempt to reconnect. */
|
||||
@@ -1008,12 +657,6 @@ export enum GatewayCloseEventCodes {
|
||||
DisallowedIntents,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/invite#invite-object-invite-target-types */
|
||||
// export enum InviteTargetTypes {
|
||||
// Stream = 1,
|
||||
// EmbeddedApplication,
|
||||
// }
|
||||
|
||||
/** https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes */
|
||||
export enum GatewayOpcodes {
|
||||
/** An event was dispatched. */
|
||||
@@ -1227,8 +870,6 @@ export enum GatewayIntents {
|
||||
AutoModerationExecution = 1 << 21,
|
||||
}
|
||||
|
||||
// ALIASES JUST FOR BETTER UX IN THIS CASE
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway#list-of-intents */
|
||||
export const Intents = GatewayIntents
|
||||
|
||||
@@ -1266,129 +907,6 @@ export type ImageFormat = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' | 'json'
|
||||
/** https://discord.com/developers/docs/reference#image-formatting */
|
||||
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
|
||||
|
||||
// export enum Errors {
|
||||
// // Bot Role errors
|
||||
// BOTS_HIGHEST_ROLE_TOO_LOW = 'BOTS_HIGHEST_ROLE_TOO_LOW',
|
||||
// // Channel Errors
|
||||
// CHANNEL_NOT_FOUND = 'CHANNEL_NOT_FOUND',
|
||||
// CHANNEL_NOT_IN_GUILD = 'CHANNEL_NOT_IN_GUILD',
|
||||
// CHANNEL_NOT_TEXT_BASED = 'CHANNEL_NOT_TEXT_BASED',
|
||||
// CHANNEL_NOT_STAGE_VOICE = 'CHANNEL_NOT_STAGE_VOICE',
|
||||
// MESSAGE_MAX_LENGTH = 'MESSAGE_MAX_LENGTH',
|
||||
// RULES_CHANNEL_CANNOT_BE_DELETED = 'RULES_CHANNEL_CANNOT_BE_DELETED',
|
||||
// UPDATES_CHANNEL_CANNOT_BE_DELETED = 'UPDATES_CHANNEL_CANNOT_BE_DELETED',
|
||||
// INVALID_TOPIC_LENGTH = 'INVALID_TOPIC_LENGTH',
|
||||
// // Guild Errors
|
||||
// GUILD_NOT_DISCOVERABLE = 'GUILD_NOT_DISCOVERABLE',
|
||||
// GUILD_WIDGET_NOT_ENABLED = 'GUILD_WIDGET_NOT_ENABLED',
|
||||
// GUILD_NOT_FOUND = 'GUILD_NOT_FOUND',
|
||||
// MEMBER_NOT_FOUND = 'MEMBER_NOT_FOUND',
|
||||
// MEMBER_NOT_IN_VOICE_CHANNEL = 'MEMBER_NOT_IN_VOICE_CHANNEL',
|
||||
// MEMBER_SEARCH_LIMIT_TOO_HIGH = 'MEMBER_SEARCH_LIMIT_TOO_HIGH',
|
||||
// MEMBER_SEARCH_LIMIT_TOO_LOW = 'MEMBER_SEARCH_LIMIT_TOO_LOW',
|
||||
// PRUNE_MAX_DAYS = 'PRUNE_MAX_DAYS',
|
||||
// ROLE_NOT_FOUND = 'ROLE_NOT_FOUND',
|
||||
// // Thread errors
|
||||
// INVALID_THREAD_PARENT_CHANNEL_TYPE = 'INVALID_THREAD_PARENT_CHANNEL_TYPE',
|
||||
// GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS = 'GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS',
|
||||
// NOT_A_THREAD_CHANNEL = 'NOT_A_THREAD_CHANNEL',
|
||||
// MISSING_MANAGE_THREADS_AND_NOT_MEMBER = 'MISSING_MANAGE_THREADS_AND_NOT_MEMBER',
|
||||
// CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD = 'CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD',
|
||||
// HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS = 'HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS',
|
||||
// // Message Get Errors
|
||||
// INVALID_GET_MESSAGES_LIMIT = 'INVALID_GET_MESSAGES_LIMIT',
|
||||
// // Message Delete Errors
|
||||
// DELETE_MESSAGES_MIN = 'DELETE_MESSAGES_MIN',
|
||||
// PRUNE_MIN_DAYS = 'PRUNE_MIN_DAYS',
|
||||
// // Interaction Errors
|
||||
// INVALID_SLASH_DESCRIPTION = 'INVALID_SLASH_DESCRIPTION',
|
||||
// INVALID_SLASH_NAME = 'INVALID_SLASH_NAME',
|
||||
// INVALID_SLASH_OPTIONS = 'INVALID_SLASH_OPTIONS',
|
||||
// INVALID_SLASH_OPTIONS_CHOICES = 'INVALID_SLASH_OPTIONS_CHOICES',
|
||||
// TOO_MANY_SLASH_OPTIONS = 'TOO_MANY_SLASH_OPTIONS',
|
||||
// INVALID_SLASH_OPTION_CHOICE_NAME = 'INVALID_SLASH_OPTION_CHOICE_NAME',
|
||||
// INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE = 'INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE',
|
||||
// TOO_MANY_SLASH_OPTION_CHOICES = 'TOO_MANY_SLASH_OPTION_CHOICES',
|
||||
// ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES = 'ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES',
|
||||
// INVALID_SLASH_OPTION_NAME = 'INVALID_SLASH_OPTION_NAME',
|
||||
// INVALID_SLASH_OPTION_DESCRIPTION = 'INVALID_SLASH_OPTION_DESCRIPTION',
|
||||
// INVALID_CONTEXT_MENU_COMMAND_NAME = 'INVALID_CONTEXT_MENU_COMMAND_NAME',
|
||||
// INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION = 'INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION',
|
||||
// // Webhook Errors
|
||||
// INVALID_WEBHOOK_NAME = 'INVALID_WEBHOOK_NAME',
|
||||
// INVALID_WEBHOOK_OPTIONS = 'INVALID_WEBHOOK_OPTIONS',
|
||||
// // Permission Errors
|
||||
// MISSING_ADD_REACTIONS = 'MISSING_ADD_REACTIONS',
|
||||
// MISSING_ADMINISTRATOR = 'MISSING_ADMINISTRATOR',
|
||||
// MISSING_ATTACH_FILES = 'MISSING_ATTACH_FILES',
|
||||
// MISSING_BAN_MEMBERS = 'MISSING_BAN_MEMBERS',
|
||||
// MISSING_CHANGE_NICKNAME = 'MISSING_CHANGE_NICKNAME',
|
||||
// MISSING_CONNECT = 'MISSING_CONNECT',
|
||||
// MISSING_CREATE_INSTANT_INVITE = 'MISSING_CREATE_INSTANT_INVITE',
|
||||
// MISSING_DEAFEN_MEMBERS = 'MISSING_DEAFEN_MEMBERS',
|
||||
// MISSING_EMBED_LINKS = 'MISSING_EMBED_LINKS',
|
||||
// MISSING_INTENT_GUILD_MEMBERS = 'MISSING_INTENT_GUILD_MEMBERS',
|
||||
// MISSING_KICK_MEMBERS = 'MISSING_KICK_MEMBERS',
|
||||
// MISSING_MANAGE_CHANNELS = 'MISSING_MANAGE_CHANNELS',
|
||||
// MISSING_MANAGE_EMOJIS = 'MISSING_MANAGE_EMOJIS',
|
||||
// MISSING_MANAGE_GUILD = 'MISSING_MANAGE_GUILD',
|
||||
// MISSING_MANAGE_MESSAGES = 'MISSING_MANAGE_MESSAGES',
|
||||
// MISSING_MANAGE_NICKNAMES = 'MISSING_MANAGE_NICKNAMES',
|
||||
// MISSING_MANAGE_ROLES = 'MISSING_MANAGE_ROLES',
|
||||
// MISSING_MANAGE_WEBHOOKS = 'MISSING_MANAGE_WEBHOOKS',
|
||||
// MISSING_MENTION_EVERYONE = 'MISSING_MENTION_EVERYONE',
|
||||
// MISSING_MOVE_MEMBERS = 'MISSING_MOVE_MEMBERS',
|
||||
// MISSING_MUTE_MEMBERS = 'MISSING_MUTE_MEMBERS',
|
||||
// MISSING_PRIORITY_SPEAKER = 'MISSING_PRIORITY_SPEAKER',
|
||||
// MISSING_READ_MESSAGE_HISTORY = 'MISSING_READ_MESSAGE_HISTORY',
|
||||
// MISSING_SEND_MESSAGES = 'MISSING_SEND_MESSAGES',
|
||||
// MISSING_SEND_TTS_MESSAGES = 'MISSING_SEND_TTS_MESSAGES',
|
||||
// MISSING_SPEAK = 'MISSING_SPEAK',
|
||||
// MISSING_STREAM = 'MISSING_STREAM',
|
||||
// MISSING_USE_VAD = 'MISSING_USE_VAD',
|
||||
// MISSING_USE_EXTERNAL_EMOJIS = 'MISSING_USE_EXTERNAL_EMOJIS',
|
||||
// MISSING_VIEW_AUDIT_LOG = 'MISSING_VIEW_AUDIT_LOG',
|
||||
// MISSING_VIEW_CHANNEL = 'MISSING_VIEW_CHANNEL',
|
||||
// MISSING_VIEW_GUILD_INSIGHTS = 'MISSING_VIEW_GUILD_INSIGHTS',
|
||||
// // User Errors
|
||||
// NICKNAMES_MAX_LENGTH = 'NICKNAMES_MAX_LENGTH',
|
||||
// USERNAME_INVALID_CHARACTER = 'USERNAME_INVALID_CHARACTER',
|
||||
// USERNAME_INVALID_USERNAME = 'USERNAME_INVALID_USERNAME',
|
||||
// USERNAME_MAX_LENGTH = 'USERNAME_MAX_LENGTH',
|
||||
// USERNAME_MIN_LENGTH = 'USERNAME_MIN_LENGTH',
|
||||
// NONCE_TOO_LONG = 'NONCE_TOO_LONG',
|
||||
// INVITE_MAX_AGE_INVALID = 'INVITE_MAX_AGE_INVALID',
|
||||
// INVITE_MAX_USES_INVALID = 'INVITE_MAX_USES_INVALID',
|
||||
// // API Errors
|
||||
// RATE_LIMIT_RETRY_MAXED = 'RATE_LIMIT_RETRY_MAXED',
|
||||
// REQUEST_CLIENT_ERROR = 'REQUEST_CLIENT_ERROR',
|
||||
// REQUEST_SERVER_ERROR = 'REQUEST_SERVER_ERROR',
|
||||
// REQUEST_UNKNOWN_ERROR = 'REQUEST_UNKNOWN_ERROR',
|
||||
// // Component Errors
|
||||
// TOO_MANY_COMPONENTS = 'TOO_MANY_COMPONENTS',
|
||||
// TOO_MANY_ACTION_ROWS = 'TOO_MANY_ACTION_ROWS',
|
||||
// LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID = 'LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID',
|
||||
// COMPONENT_LABEL_TOO_BIG = 'COMPONENT_LABEL_TOO_BIG',
|
||||
// COMPONENT_CUSTOM_ID_TOO_BIG = 'COMPONENT_CUSTOM_ID_TOO_BIG',
|
||||
// BUTTON_REQUIRES_CUSTOM_ID = 'BUTTON_REQUIRES_CUSTOM_ID',
|
||||
// COMPONENT_SELECT_MUST_BE_ALONE = 'COMPONENT_SELECT_MUST_BE_ALONE',
|
||||
// COMPONENT_PLACEHOLDER_TOO_BIG = 'COMPONENT_PLACEHOLDER_TOO_BIG',
|
||||
// COMPONENT_SELECT_MIN_VALUE_TOO_LOW = 'COMPONENT_SELECT_MIN_VALUE_TOO_LOW',
|
||||
// COMPONENT_SELECT_MIN_VALUE_TOO_MANY = 'COMPONENT_SELECT_MIN_VALUE_TOO_MANY',
|
||||
// COMPONENT_SELECT_MAX_VALUE_TOO_LOW = 'COMPONENT_SELECT_MAX_VALUE_TOO_LOW',
|
||||
// COMPONENT_SELECT_MAX_VALUE_TOO_MANY = 'COMPONENT_SELECT_MAX_VALUE_TOO_MANY',
|
||||
// COMPONENT_SELECT_OPTIONS_TOO_LOW = 'COMPONENT_SELECT_OPTIONS_TOO_LOW',
|
||||
// COMPONENT_SELECT_OPTIONS_TOO_MANY = 'COMPONENT_SELECT_OPTIONS_TOO_MANY',
|
||||
// SELECT_OPTION_LABEL_TOO_BIG = 'SELECT_OPTION_LABEL_TOO_BIG',
|
||||
// SELECT_OPTION_VALUE_TOO_BIG = 'SELECT_OPTION_VALUE_TOO_BIG',
|
||||
// SELECT_OPTION_TOO_MANY_DEFAULTS = 'SELECT_OPTION_TOO_MANY_DEFAULTS',
|
||||
// COMPONENT_SELECT_MIN_HIGHER_THAN_MAX = 'COMPONENT_SELECT_MIN_HIGHER_THAN_MAX',
|
||||
// CANNOT_ADD_USER_TO_ARCHIVED_THREADS = 'CANNOT_ADD_USER_TO_ARCHIVED_THREADS',
|
||||
// CANNOT_LEAVE_ARCHIVED_THREAD = 'CANNOT_LEAVE_ARCHIVED_THREAD',
|
||||
// CANNOT_REMOVE_FROM_ARCHIVED_THREAD = 'CANNOT_REMOVE_FROM_ARCHIVED_THREAD',
|
||||
// YOU_CAN_NOT_DM_THE_BOT_ITSELF = 'YOU_CAN_NOT_DM_THE_BOT_ITSELF',
|
||||
// }
|
||||
|
||||
export enum Locales {
|
||||
Danish = 'da',
|
||||
German = 'de',
|
||||
@@ -1424,15 +942,9 @@ export enum Locales {
|
||||
|
||||
export type Localization = Partial<Record<Locales, string>>
|
||||
|
||||
// UTILS
|
||||
|
||||
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> &
|
||||
U[keyof U]
|
||||
|
||||
// export type MakeRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }
|
||||
|
||||
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U]
|
||||
export type CamelCase<S extends string> = S extends `${infer T}_${infer U}` ? `${T}${Capitalize<CamelCase<U>>}` : S
|
||||
export type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${SnakeCase<U>}` : S
|
||||
export type SnakeCase<S extends string> = S extends `${infer T}${infer U}` ? `${T extends Capitalize<T> ? '_' : ''}${Lowercase<T>}${SnakeCase<U>}` : S
|
||||
|
||||
export type Camelize<T> = T extends any[]
|
||||
? T extends Array<Record<any, any>>
|
||||
@@ -1442,7 +954,7 @@ export type Camelize<T> = T extends any[]
|
||||
? { [K in keyof T as CamelCase<K & string>]: Camelize<T[K]> }
|
||||
: T
|
||||
|
||||
export type Snakelize<T> = T extends any[]
|
||||
export type Snakelize<T> = T extends any[]
|
||||
? T extends Array<Record<any, any>>
|
||||
? Array<Snakelize<T[number]>>
|
||||
: T
|
||||
@@ -1450,91 +962,4 @@ export type Camelize<T> = T extends any[]
|
||||
? { [K in keyof T as SnakeCase<K & string>]: Snakelize<T[K]> }
|
||||
: T
|
||||
|
||||
// /** Non object primitives */
|
||||
// export type Primitive =
|
||||
// | string
|
||||
// | number
|
||||
// | symbol
|
||||
// | bigint
|
||||
// | boolean
|
||||
// | undefined
|
||||
// | null
|
||||
// // | object <- don't make object a primitive
|
||||
|
||||
// /**
|
||||
// * alternative to 'object' or '{}'
|
||||
// * @example:
|
||||
// * export const o: ObjectLiteral = [] as object; // error
|
||||
// * export const o: object = []; // no error
|
||||
// */
|
||||
// export type ObjectLiteral<T = unknown> = {
|
||||
// [K in PropertyKey]: T;
|
||||
// }
|
||||
|
||||
// /** Array with no utilty methods, aka Object.create(null) */
|
||||
// export interface ArrayWithNoPrototype<T> {
|
||||
// [index: number]: T | ArrayWithNoPrototype<T>
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Allows any type but T
|
||||
// * it is recursive
|
||||
// * @example
|
||||
// * export type RequestData = Record<string, AnythingBut<bigint>>;
|
||||
// */
|
||||
// export type AnythingBut<T> = Exclude<
|
||||
// | Primitive
|
||||
// | {
|
||||
// [K in PropertyKey]: AnythingBut<T>;
|
||||
// }
|
||||
// | ArrayWithNoPrototype<
|
||||
// | Primitive
|
||||
// | {
|
||||
// [K in PropertyKey]: AnythingBut<T>;
|
||||
// }
|
||||
// >,
|
||||
// T
|
||||
// >
|
||||
|
||||
// /**
|
||||
// * object identity type
|
||||
// */
|
||||
// export type Id<T> = T extends infer U
|
||||
// ? {
|
||||
// [K in keyof U]: U[K];
|
||||
// }
|
||||
// : never
|
||||
|
||||
// export type KeysWithUndefined<T> = {
|
||||
// [K in keyof T]-?: undefined extends T[K] ? K : null extends T[K] ? K : never;
|
||||
// }[keyof T]
|
||||
|
||||
// type OptionalizeAux<T extends object> = Id<
|
||||
// {
|
||||
// [K in KeysWithUndefined<T>]?: Optionalize<T[K]>;
|
||||
// } & {
|
||||
// [K in Exclude<keyof T, KeysWithUndefined<T>>]: T[K] extends ObjectLiteral
|
||||
// ? Optionalize<T[K]>
|
||||
// : T[K];
|
||||
// }
|
||||
// >
|
||||
|
||||
// /**
|
||||
// * Makes all of properties in T optional when they're null | undefined
|
||||
// * it is recursive
|
||||
// */
|
||||
// export type Optionalize<T> = T extends object
|
||||
// ? T extends unknown[]
|
||||
// ? number extends T['length']
|
||||
// ? T[number] extends object
|
||||
// ? Array<OptionalizeAux<T[number]>>
|
||||
// : T
|
||||
// : Partial<T>
|
||||
// : OptionalizeAux<T>
|
||||
// : T
|
||||
|
||||
export type PickPartial<T, K extends keyof T> = { [P in keyof T]?: T[P] | undefined } & { [P in K]: T[P] }
|
||||
|
||||
// export type OmitFirstFnArg<F> = F extends (x: any, ...args: infer P) => infer R
|
||||
// ? (...args: P) => R
|
||||
// : never
|
||||
|
||||
@@ -103,28 +103,24 @@ Have your cache setup in any way you like. Redis, PGSQL or any cache layer you w
|
||||
Here is a minimal example to get started with:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createBot,
|
||||
Intents,
|
||||
startBot,
|
||||
} from "https://deno.land/x/discordeno@13.0.0/mod.ts";
|
||||
import { createBot, Intents, startBot } from 'https://deno.land/x/discordeno@13.0.0/mod.ts'
|
||||
|
||||
const bot = createBot({
|
||||
token: process.env.DISCORD_TOKEN,
|
||||
intents: Intents.Guilds | Intents.GuildMessages,
|
||||
events: {
|
||||
ready() {
|
||||
console.log("Successfully connected to gateway");
|
||||
console.log('Successfully connected to gateway')
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
// Another way to do events
|
||||
bot.events.messageCreate = function (b, message) {
|
||||
// Process the message here with your command handler.
|
||||
};
|
||||
}
|
||||
|
||||
await startBot(bot);
|
||||
await startBot(bot)
|
||||
```
|
||||
|
||||
### Tools
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Camelize, Snakelize } from '@discordeno/types';
|
||||
import type { Camelize, Snakelize } from '@discordeno/types'
|
||||
|
||||
export const camelize = <T>(object: T): Camelize<T> => {
|
||||
if (Array.isArray(object)) {
|
||||
@@ -16,7 +16,6 @@ export const camelize = <T>(object: T): Camelize<T> => {
|
||||
return object as Camelize<T>
|
||||
}
|
||||
|
||||
|
||||
export const snakelize = <T>(object: T): Snakelize<T> => {
|
||||
if (Array.isArray(object)) {
|
||||
return object.map((element) => snakelize(element)) as Snakelize<T>
|
||||
@@ -25,7 +24,7 @@ export const snakelize = <T>(object: T): Snakelize<T> => {
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const obj = {} as Snakelize<T>
|
||||
;(Object.keys(object) as Array<keyof T>).forEach((key) => {
|
||||
// @ts-expect-error js hack
|
||||
// @ts-expect-error js hack
|
||||
|
||||
;(obj[typeof key === 'string' ? camelToSnakeCase(key) : key] as Snakelize<(T & object)[keyof T]>) = snakelize(object[key])
|
||||
})
|
||||
@@ -52,16 +51,16 @@ export function snakeToCamelCase(str: string): string {
|
||||
}
|
||||
|
||||
export function camelToSnakeCase(str: string): string {
|
||||
let result = "";
|
||||
let result = ''
|
||||
for (let i = 0, len = str.length; i < len; ++i) {
|
||||
if (str[i] >= "A" && str[i] <= "Z") {
|
||||
result += `_${str[i].toLowerCase()}`;
|
||||
if (str[i] >= 'A' && str[i] <= 'Z') {
|
||||
result += `_${str[i].toLowerCase()}`
|
||||
|
||||
continue;
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
result += str[i];
|
||||
result += str[i]
|
||||
}
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
+73
-111
@@ -5,22 +5,22 @@
|
||||
// on npm.
|
||||
// https://deno.land/std@0.153.0/fmt/colors.ts?source
|
||||
|
||||
const noColor = false;
|
||||
const noColor = false
|
||||
|
||||
interface Code {
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
export interface Code {
|
||||
open: string
|
||||
close: string
|
||||
regexp: RegExp
|
||||
}
|
||||
|
||||
/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */
|
||||
interface Rgb {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
export interface Rgb {
|
||||
r: number
|
||||
g: number
|
||||
b: number
|
||||
}
|
||||
|
||||
let enabled = !noColor;
|
||||
let enabled = !noColor
|
||||
|
||||
/**
|
||||
* Set changing text color to enabled or disabled
|
||||
@@ -28,15 +28,15 @@ let enabled = !noColor;
|
||||
*/
|
||||
export function setColorEnabled(value: boolean) {
|
||||
if (noColor) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
enabled = value;
|
||||
enabled = value
|
||||
}
|
||||
|
||||
/** Get whether text color change is enabled or disabled. */
|
||||
export function getColorEnabled(): boolean {
|
||||
return enabled;
|
||||
return enabled
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,10 +46,10 @@ export function getColorEnabled(): boolean {
|
||||
*/
|
||||
function code(open: number[], close: number): Code {
|
||||
return {
|
||||
open: `\x1b[${open.join(";")}m`,
|
||||
open: `\x1b[${open.join(';')}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||
};
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, 'g'),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,9 +58,7 @@ function code(open: number[], close: number): Code {
|
||||
* @param code color code to apply
|
||||
*/
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled
|
||||
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
|
||||
: str;
|
||||
return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +66,7 @@ function run(str: string, code: Code): string {
|
||||
* @param str text to reset
|
||||
*/
|
||||
export function reset(str: string): string {
|
||||
return run(str, code([0], 0));
|
||||
return run(str, code([0], 0))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +74,7 @@ export function reset(str: string): string {
|
||||
* @param str text to make bold
|
||||
*/
|
||||
export function bold(str: string): string {
|
||||
return run(str, code([1], 22));
|
||||
return run(str, code([1], 22))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +82,7 @@ export function bold(str: string): string {
|
||||
* @param str text to dim
|
||||
*/
|
||||
export function dim(str: string): string {
|
||||
return run(str, code([2], 22));
|
||||
return run(str, code([2], 22))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +90,7 @@ export function dim(str: string): string {
|
||||
* @param str text to make italic
|
||||
*/
|
||||
export function italic(str: string): string {
|
||||
return run(str, code([3], 23));
|
||||
return run(str, code([3], 23))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +98,7 @@ export function italic(str: string): string {
|
||||
* @param str text to underline
|
||||
*/
|
||||
export function underline(str: string): string {
|
||||
return run(str, code([4], 24));
|
||||
return run(str, code([4], 24))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +106,7 @@ export function underline(str: string): string {
|
||||
* @param str text to invert its color
|
||||
*/
|
||||
export function inverse(str: string): string {
|
||||
return run(str, code([7], 27));
|
||||
return run(str, code([7], 27))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +114,7 @@ export function inverse(str: string): string {
|
||||
* @param str text to hide
|
||||
*/
|
||||
export function hidden(str: string): string {
|
||||
return run(str, code([8], 28));
|
||||
return run(str, code([8], 28))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +122,7 @@ export function hidden(str: string): string {
|
||||
* @param str text to strike through
|
||||
*/
|
||||
export function strikethrough(str: string): string {
|
||||
return run(str, code([9], 29));
|
||||
return run(str, code([9], 29))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +130,7 @@ export function strikethrough(str: string): string {
|
||||
* @param str text to make black
|
||||
*/
|
||||
export function black(str: string): string {
|
||||
return run(str, code([30], 39));
|
||||
return run(str, code([30], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +138,7 @@ export function black(str: string): string {
|
||||
* @param str text to make red
|
||||
*/
|
||||
export function red(str: string): string {
|
||||
return run(str, code([31], 39));
|
||||
return run(str, code([31], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +146,7 @@ export function red(str: string): string {
|
||||
* @param str text to make green
|
||||
*/
|
||||
export function green(str: string): string {
|
||||
return run(str, code([32], 39));
|
||||
return run(str, code([32], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +154,7 @@ export function green(str: string): string {
|
||||
* @param str text to make yellow
|
||||
*/
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code([33], 39));
|
||||
return run(str, code([33], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +162,7 @@ export function yellow(str: string): string {
|
||||
* @param str text to make blue
|
||||
*/
|
||||
export function blue(str: string): string {
|
||||
return run(str, code([34], 39));
|
||||
return run(str, code([34], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +170,7 @@ export function blue(str: string): string {
|
||||
* @param str text to make magenta
|
||||
*/
|
||||
export function magenta(str: string): string {
|
||||
return run(str, code([35], 39));
|
||||
return run(str, code([35], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +178,7 @@ export function magenta(str: string): string {
|
||||
* @param str text to make cyan
|
||||
*/
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code([36], 39));
|
||||
return run(str, code([36], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +186,7 @@ export function cyan(str: string): string {
|
||||
* @param str text to make white
|
||||
*/
|
||||
export function white(str: string): string {
|
||||
return run(str, code([37], 39));
|
||||
return run(str, code([37], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +194,7 @@ export function white(str: string): string {
|
||||
* @param str text to make gray
|
||||
*/
|
||||
export function gray(str: string): string {
|
||||
return brightBlack(str);
|
||||
return brightBlack(str)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +202,7 @@ export function gray(str: string): string {
|
||||
* @param str text to make bright-black
|
||||
*/
|
||||
export function brightBlack(str: string): string {
|
||||
return run(str, code([90], 39));
|
||||
return run(str, code([90], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +210,7 @@ export function brightBlack(str: string): string {
|
||||
* @param str text to make bright-red
|
||||
*/
|
||||
export function brightRed(str: string): string {
|
||||
return run(str, code([91], 39));
|
||||
return run(str, code([91], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +218,7 @@ export function brightRed(str: string): string {
|
||||
* @param str text to make bright-green
|
||||
*/
|
||||
export function brightGreen(str: string): string {
|
||||
return run(str, code([92], 39));
|
||||
return run(str, code([92], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +226,7 @@ export function brightGreen(str: string): string {
|
||||
* @param str text to make bright-yellow
|
||||
*/
|
||||
export function brightYellow(str: string): string {
|
||||
return run(str, code([93], 39));
|
||||
return run(str, code([93], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +234,7 @@ export function brightYellow(str: string): string {
|
||||
* @param str text to make bright-blue
|
||||
*/
|
||||
export function brightBlue(str: string): string {
|
||||
return run(str, code([94], 39));
|
||||
return run(str, code([94], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +242,7 @@ export function brightBlue(str: string): string {
|
||||
* @param str text to make bright-magenta
|
||||
*/
|
||||
export function brightMagenta(str: string): string {
|
||||
return run(str, code([95], 39));
|
||||
return run(str, code([95], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +250,7 @@ export function brightMagenta(str: string): string {
|
||||
* @param str text to make bright-cyan
|
||||
*/
|
||||
export function brightCyan(str: string): string {
|
||||
return run(str, code([96], 39));
|
||||
return run(str, code([96], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +258,7 @@ export function brightCyan(str: string): string {
|
||||
* @param str text to make bright-white
|
||||
*/
|
||||
export function brightWhite(str: string): string {
|
||||
return run(str, code([97], 39));
|
||||
return run(str, code([97], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +266,7 @@ export function brightWhite(str: string): string {
|
||||
* @param str text to make its background black
|
||||
*/
|
||||
export function bgBlack(str: string): string {
|
||||
return run(str, code([40], 49));
|
||||
return run(str, code([40], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +274,7 @@ export function bgBlack(str: string): string {
|
||||
* @param str text to make its background red
|
||||
*/
|
||||
export function bgRed(str: string): string {
|
||||
return run(str, code([41], 49));
|
||||
return run(str, code([41], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +282,7 @@ export function bgRed(str: string): string {
|
||||
* @param str text to make its background green
|
||||
*/
|
||||
export function bgGreen(str: string): string {
|
||||
return run(str, code([42], 49));
|
||||
return run(str, code([42], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,7 +290,7 @@ export function bgGreen(str: string): string {
|
||||
* @param str text to make its background yellow
|
||||
*/
|
||||
export function bgYellow(str: string): string {
|
||||
return run(str, code([43], 49));
|
||||
return run(str, code([43], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +298,7 @@ export function bgYellow(str: string): string {
|
||||
* @param str text to make its background blue
|
||||
*/
|
||||
export function bgBlue(str: string): string {
|
||||
return run(str, code([44], 49));
|
||||
return run(str, code([44], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +306,7 @@ export function bgBlue(str: string): string {
|
||||
* @param str text to make its background magenta
|
||||
*/
|
||||
export function bgMagenta(str: string): string {
|
||||
return run(str, code([45], 49));
|
||||
return run(str, code([45], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +314,7 @@ export function bgMagenta(str: string): string {
|
||||
* @param str text to make its background cyan
|
||||
*/
|
||||
export function bgCyan(str: string): string {
|
||||
return run(str, code([46], 49));
|
||||
return run(str, code([46], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,7 +322,7 @@ export function bgCyan(str: string): string {
|
||||
* @param str text to make its background white
|
||||
*/
|
||||
export function bgWhite(str: string): string {
|
||||
return run(str, code([47], 49));
|
||||
return run(str, code([47], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +330,7 @@ export function bgWhite(str: string): string {
|
||||
* @param str text to make its background bright-black
|
||||
*/
|
||||
export function bgBrightBlack(str: string): string {
|
||||
return run(str, code([100], 49));
|
||||
return run(str, code([100], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +338,7 @@ export function bgBrightBlack(str: string): string {
|
||||
* @param str text to make its background bright-red
|
||||
*/
|
||||
export function bgBrightRed(str: string): string {
|
||||
return run(str, code([101], 49));
|
||||
return run(str, code([101], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +346,7 @@ export function bgBrightRed(str: string): string {
|
||||
* @param str text to make its background bright-green
|
||||
*/
|
||||
export function bgBrightGreen(str: string): string {
|
||||
return run(str, code([102], 49));
|
||||
return run(str, code([102], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +354,7 @@ export function bgBrightGreen(str: string): string {
|
||||
* @param str text to make its background bright-yellow
|
||||
*/
|
||||
export function bgBrightYellow(str: string): string {
|
||||
return run(str, code([103], 49));
|
||||
return run(str, code([103], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +362,7 @@ export function bgBrightYellow(str: string): string {
|
||||
* @param str text to make its background bright-blue
|
||||
*/
|
||||
export function bgBrightBlue(str: string): string {
|
||||
return run(str, code([104], 49));
|
||||
return run(str, code([104], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +370,7 @@ export function bgBrightBlue(str: string): string {
|
||||
* @param str text to make its background bright-magenta
|
||||
*/
|
||||
export function bgBrightMagenta(str: string): string {
|
||||
return run(str, code([105], 49));
|
||||
return run(str, code([105], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,7 +378,7 @@ export function bgBrightMagenta(str: string): string {
|
||||
* @param str text to make its background bright-cyan
|
||||
*/
|
||||
export function bgBrightCyan(str: string): string {
|
||||
return run(str, code([106], 49));
|
||||
return run(str, code([106], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,7 +386,7 @@ export function bgBrightCyan(str: string): string {
|
||||
* @param str text to make its background bright-white
|
||||
*/
|
||||
export function bgBrightWhite(str: string): string {
|
||||
return run(str, code([107], 49));
|
||||
return run(str, code([107], 49))
|
||||
}
|
||||
|
||||
/* Special Color Sequences */
|
||||
@@ -400,7 +398,7 @@ export function bgBrightWhite(str: string): string {
|
||||
* @param min number to truncate from
|
||||
*/
|
||||
function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
return Math.trunc(Math.max(Math.min(n, max), min));
|
||||
return Math.trunc(Math.max(Math.min(n, max), min))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,7 +408,7 @@ function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb8(str: string, color: number): string {
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39));
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,7 +418,7 @@ export function rgb8(str: string, color: number): string {
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb8(str: string, color: number): string {
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49));
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,28 +437,10 @@ export function bgRgb8(str: string, color: number): string {
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === "number") {
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff],
|
||||
39,
|
||||
),
|
||||
);
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 39))
|
||||
}
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[
|
||||
38,
|
||||
2,
|
||||
clampAndTruncate(color.r),
|
||||
clampAndTruncate(color.g),
|
||||
clampAndTruncate(color.b),
|
||||
],
|
||||
39,
|
||||
),
|
||||
);
|
||||
return run(str, code([38, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 39))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,43 +459,25 @@ export function rgb24(str: string, color: number | Rgb): string {
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === "number") {
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff],
|
||||
49,
|
||||
),
|
||||
);
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 49))
|
||||
}
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[
|
||||
48,
|
||||
2,
|
||||
clampAndTruncate(color.r),
|
||||
clampAndTruncate(color.g),
|
||||
clampAndTruncate(color.b),
|
||||
],
|
||||
49,
|
||||
),
|
||||
);
|
||||
return run(str, code([48, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 49))
|
||||
}
|
||||
|
||||
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
||||
const ANSI_PATTERN = new RegExp(
|
||||
[
|
||||
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
||||
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
|
||||
].join("|"),
|
||||
"g",
|
||||
);
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
|
||||
].join('|'),
|
||||
'g',
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove ANSI escape codes from the string.
|
||||
* @param string to remove ANSI escape codes from
|
||||
*/
|
||||
export function stripColor(string: string): string {
|
||||
return string.replace(ANSI_PATTERN, "");
|
||||
return string.replace(ANSI_PATTERN, '')
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FileContent } from "@discordeno/types"
|
||||
import { decode } from "./base64.js"
|
||||
import type { FileContent } from '@discordeno/types'
|
||||
import { decode } from './base64.js'
|
||||
|
||||
export function findFiles(file: unknown): FileContent[] {
|
||||
if (!file) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export function iconHashToBigInt (hash: string): bigint {
|
||||
export function iconHashToBigInt(hash: string): bigint {
|
||||
// The icon is animated so it needs special handling
|
||||
if (hash.startsWith('a_')) {
|
||||
// Change the `a_` to just be `a`
|
||||
@@ -11,7 +11,7 @@ export function iconHashToBigInt (hash: string): bigint {
|
||||
return BigInt(`0x${hash}`)
|
||||
}
|
||||
|
||||
export function iconBigintToHash (icon: bigint): string {
|
||||
export function iconBigintToHash(icon: bigint): string {
|
||||
// Convert the bigint back to a hash
|
||||
const hash = icon.toString(16)
|
||||
// Hashes starting with a are animated and with b are not so need to handle that
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user