botflare 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/bot-icKel3r9.d.mts +7512 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +79 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/command-CBeslrzu.mjs +133 -0
- package/dist/command-CBeslrzu.mjs.map +1 -0
- package/dist/discord-DEZe1asd.mjs +312 -0
- package/dist/discord-DEZe1asd.mjs.map +1 -0
- package/dist/gateway.d.mts +53 -0
- package/dist/gateway.mjs +289 -0
- package/dist/gateway.mjs.map +1 -0
- package/dist/index.d.mts +239 -0
- package/dist/index.mjs +537 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { r as putCommands } from "./discord-DEZe1asd.mjs";
|
|
3
|
+
import { n as payload } from "./command-CBeslrzu.mjs";
|
|
4
|
+
import { registerHooks } from "node:module";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { parseArgs } from "node:util";
|
|
8
|
+
import { cancel, intro, outro, spinner } from "@clack/prompts";
|
|
9
|
+
// src/utils/env.ts
|
|
10
|
+
const SNOWFLAKE = /^\d{17,20}$/;
|
|
11
|
+
function syncEnv(vars) {
|
|
12
|
+
const token = vars.DISCORD_TOKEN;
|
|
13
|
+
const applicationId = vars.DISCORD_APPLICATION_ID;
|
|
14
|
+
const guildId = vars.DISCORD_GUILD_ID || void 0;
|
|
15
|
+
if (!token) return [/* @__PURE__ */ new Error("DISCORD_TOKEN is missing"), null];
|
|
16
|
+
if (!applicationId || !SNOWFLAKE.test(applicationId)) return [/* @__PURE__ */ new Error("DISCORD_APPLICATION_ID must be the application id from the Developer Portal"), null];
|
|
17
|
+
if (guildId && !SNOWFLAKE.test(guildId)) return [/* @__PURE__ */ new Error("DISCORD_GUILD_ID must be a server id"), null];
|
|
18
|
+
return [null, {
|
|
19
|
+
token,
|
|
20
|
+
applicationId,
|
|
21
|
+
guildId
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
// cli/index.ts
|
|
25
|
+
const plural = (n) => `${n} command${n === 1 ? "" : "s"}`;
|
|
26
|
+
const WORKERS_STUB = `data:text/javascript,${encodeURIComponent([
|
|
27
|
+
"export const env = {};",
|
|
28
|
+
"export const exports = {};",
|
|
29
|
+
"export function waitUntil() {}",
|
|
30
|
+
"export class DurableObject {}",
|
|
31
|
+
"export class WorkerEntrypoint {}",
|
|
32
|
+
"export class WorkflowEntrypoint {}",
|
|
33
|
+
"export class RpcTarget {}"
|
|
34
|
+
].join("\n"))}`;
|
|
35
|
+
registerHooks({ resolve: (specifier, context, next) => specifier.startsWith("cloudflare:") ? {
|
|
36
|
+
url: WORKERS_STUB,
|
|
37
|
+
shortCircuit: true
|
|
38
|
+
} : next(specifier, context) });
|
|
39
|
+
async function main() {
|
|
40
|
+
intro("botflare sync");
|
|
41
|
+
const { positionals } = parseArgs({ allowPositionals: true });
|
|
42
|
+
const [task, entry] = positionals;
|
|
43
|
+
if (task !== "sync" || !entry) {
|
|
44
|
+
cancel("Usage: botflare sync <worker entry>");
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const [envError, env] = syncEnv(process.env);
|
|
48
|
+
if (envError) {
|
|
49
|
+
cancel(envError.message);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
let mod;
|
|
53
|
+
try {
|
|
54
|
+
mod = await import(pathToFileURL(resolve(entry)).href);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
cancel(`Cannot load ${entry}: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
const commands = mod.default?.commands;
|
|
60
|
+
if (!commands) {
|
|
61
|
+
cancel(`${entry} must export default bot({ commands })`);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
const target = env.guildId ? `server ${env.guildId}` : "every server";
|
|
65
|
+
const progress = spinner();
|
|
66
|
+
progress.start(`Sending ${plural(commands.length)} to ${target}`);
|
|
67
|
+
const [putError, count] = await putCommands(env, commands.map(payload));
|
|
68
|
+
if (putError) {
|
|
69
|
+
progress.error(putError.message);
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
progress.stop(`Discord saved ${plural(count)}`);
|
|
73
|
+
outro(`Type / in ${env.guildId ? "the server" : "a server with the bot"} to try them.`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
process.exitCode = await main();
|
|
77
|
+
export {};
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/utils/env.ts","../cli/index.ts"],"sourcesContent":["export type SyncEnv = { token: string; applicationId: string; guildId: string | undefined };\n\nconst SNOWFLAKE = /^\\d{17,20}$/;\n\nexport function syncEnv(vars: Record<string, string | undefined>): [Error, null] | [null, SyncEnv] {\n const token = vars.DISCORD_TOKEN;\n const applicationId = vars.DISCORD_APPLICATION_ID;\n const guildId = vars.DISCORD_GUILD_ID || undefined;\n\n if (!token) return [new Error('DISCORD_TOKEN is missing'), null];\n if (!applicationId || !SNOWFLAKE.test(applicationId)) {\n return [\n new Error('DISCORD_APPLICATION_ID must be the application id from the Developer Portal'),\n null,\n ];\n }\n if (guildId && !SNOWFLAKE.test(guildId)) {\n return [new Error('DISCORD_GUILD_ID must be a server id'), null];\n }\n\n return [null, { token, applicationId, guildId }];\n}\n","#!/usr/bin/env node\nimport { registerHooks } from 'node:module';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { parseArgs } from 'node:util';\n\nimport { cancel, intro, outro, spinner } from '@clack/prompts';\n\nimport { putCommands } from '#lib/discord';\nimport { type Command, type ContextMenu, payload } from '#utils/command';\nimport { syncEnv } from '#utils/env';\n\nconst plural = (n: number): string => `${n} command${n === 1 ? '' : 's'}`;\n\nconst WORKERS_STUB = `data:text/javascript,${encodeURIComponent(\n [\n 'export const env = {};',\n 'export const exports = {};',\n 'export function waitUntil() {}',\n 'export class DurableObject {}',\n 'export class WorkerEntrypoint {}',\n 'export class WorkflowEntrypoint {}',\n 'export class RpcTarget {}',\n ].join('\\n'),\n)}`;\n\nregisterHooks({\n resolve: (specifier, context, next) =>\n specifier.startsWith('cloudflare:')\n ? { url: WORKERS_STUB, shortCircuit: true }\n : next(specifier, context),\n});\n\nasync function main(): Promise<number> {\n intro('botflare sync');\n\n const { positionals } = parseArgs({ allowPositionals: true });\n const [task, entry] = positionals;\n if (task !== 'sync' || !entry) {\n cancel('Usage: botflare sync <worker entry>');\n return 1;\n }\n\n const [envError, env] = syncEnv(process.env);\n if (envError) {\n cancel(envError.message);\n return 1;\n }\n\n let mod: { default?: { commands?: (Command | ContextMenu)[] } };\n try {\n mod = await import(pathToFileURL(resolve(entry)).href);\n } catch (error) {\n cancel(`Cannot load ${entry}: ${error instanceof Error ? error.message : String(error)}`);\n return 1;\n }\n\n const commands = mod.default?.commands;\n if (!commands) {\n cancel(`${entry} must export default bot({ commands })`);\n return 1;\n }\n\n const target = env.guildId ? `server ${env.guildId}` : 'every server';\n const progress = spinner();\n progress.start(`Sending ${plural(commands.length)} to ${target}`);\n\n const [putError, count] = await putCommands(env, commands.map(payload));\n if (putError) {\n progress.error(putError.message);\n return 1;\n }\n\n progress.stop(`Discord saved ${plural(count)}`);\n outro(`Type / in ${env.guildId ? 'the server' : 'a server with the bot'} to try them.`);\n\n return 0;\n}\n\nprocess.exitCode = await main();\n"],"mappings":";;;;;;;;;AAEA,MAAM,YAAY;AAElB,SAAgB,QAAQ,MAA2E;CACjG,MAAM,QAAQ,KAAK;CACnB,MAAM,gBAAgB,KAAK;CAC3B,MAAM,UAAU,KAAK,oBAAoB,KAAA;CAEzC,IAAI,CAAC,OAAO,OAAO,iBAAC,IAAI,MAAM,0BAA0B,GAAG,IAAI;CAC/D,IAAI,CAAC,iBAAiB,CAAC,UAAU,KAAK,aAAa,GACjD,OAAO,iBACL,IAAI,MAAM,6EAA6E,GACvF,IACF;CAEF,IAAI,WAAW,CAAC,UAAU,KAAK,OAAO,GACpC,OAAO,iBAAC,IAAI,MAAM,sCAAsC,GAAG,IAAI;CAGjE,OAAO,CAAC,MAAM;EAAE;EAAO;EAAe;CAAQ,CAAC;AACjD;;ACTA,MAAM,UAAU,MAAsB,GAAG,EAAE,UAAU,MAAM,IAAI,KAAK;AAEpE,MAAM,eAAe,wBAAwB,mBAC3C;CACE;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,IAAI,CACb;AAEA,cAAc,EACZ,UAAU,WAAW,SAAS,SAC5B,UAAU,WAAW,aAAa,IAC9B;CAAE,KAAK;CAAc,cAAc;AAAK,IACxC,KAAK,WAAW,OAAO,EAC/B,CAAC;AAED,eAAe,OAAwB;CACrC,MAAM,eAAe;CAErB,MAAM,EAAE,gBAAgB,UAAU,EAAE,kBAAkB,KAAK,CAAC;CAC5D,MAAM,CAAC,MAAM,SAAS;CACtB,IAAI,SAAS,UAAU,CAAC,OAAO;EAC7B,OAAO,qCAAqC;EAC5C,OAAO;CACT;CAEA,MAAM,CAAC,UAAU,OAAO,QAAQ,QAAQ,GAAG;CAC3C,IAAI,UAAU;EACZ,OAAO,SAAS,OAAO;EACvB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,OAAO,cAAc,QAAQ,KAAK,CAAC,CAAC,CAAC;CACnD,SAAS,OAAO;EACd,OAAO,eAAe,MAAM,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;EACxF,OAAO;CACT;CAEA,MAAM,WAAW,IAAI,SAAS;CAC9B,IAAI,CAAC,UAAU;EACb,OAAO,GAAG,MAAM,uCAAuC;EACvD,OAAO;CACT;CAEA,MAAM,SAAS,IAAI,UAAU,UAAU,IAAI,YAAY;CACvD,MAAM,WAAW,QAAQ;CACzB,SAAS,MAAM,WAAW,OAAO,SAAS,MAAM,EAAE,MAAM,QAAQ;CAEhE,MAAM,CAAC,UAAU,SAAS,MAAM,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC;CACtE,IAAI,UAAU;EACZ,SAAS,MAAM,SAAS,OAAO;EAC/B,OAAO;CACT;CAEA,SAAS,KAAK,iBAAiB,OAAO,KAAK,GAAG;CAC9C,MAAM,aAAa,IAAI,UAAU,eAAe,wBAAwB,cAAc;CAEtF,OAAO;AACT;AAEA,QAAQ,WAAW,MAAM,KAAK"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { d as optionsPayload, f as localePayload } from "./discord-DEZe1asd.mjs";
|
|
2
|
+
// src/utils/permissions.ts
|
|
3
|
+
const BIT = {
|
|
4
|
+
CreateInstantInvite: 0,
|
|
5
|
+
KickMembers: 1,
|
|
6
|
+
BanMembers: 2,
|
|
7
|
+
Administrator: 3,
|
|
8
|
+
ManageChannels: 4,
|
|
9
|
+
ManageGuild: 5,
|
|
10
|
+
AddReactions: 6,
|
|
11
|
+
ViewAuditLog: 7,
|
|
12
|
+
PrioritySpeaker: 8,
|
|
13
|
+
Stream: 9,
|
|
14
|
+
ViewChannel: 10,
|
|
15
|
+
SendMessages: 11,
|
|
16
|
+
SendTTSMessages: 12,
|
|
17
|
+
ManageMessages: 13,
|
|
18
|
+
EmbedLinks: 14,
|
|
19
|
+
AttachFiles: 15,
|
|
20
|
+
ReadMessageHistory: 16,
|
|
21
|
+
MentionEveryone: 17,
|
|
22
|
+
UseExternalEmojis: 18,
|
|
23
|
+
ViewGuildInsights: 19,
|
|
24
|
+
Connect: 20,
|
|
25
|
+
Speak: 21,
|
|
26
|
+
MuteMembers: 22,
|
|
27
|
+
DeafenMembers: 23,
|
|
28
|
+
MoveMembers: 24,
|
|
29
|
+
UseVAD: 25,
|
|
30
|
+
ChangeNickname: 26,
|
|
31
|
+
ManageNicknames: 27,
|
|
32
|
+
ManageRoles: 28,
|
|
33
|
+
ManageWebhooks: 29,
|
|
34
|
+
ManageGuildExpressions: 30,
|
|
35
|
+
UseApplicationCommands: 31,
|
|
36
|
+
RequestToSpeak: 32,
|
|
37
|
+
ManageEvents: 33,
|
|
38
|
+
ManageThreads: 34,
|
|
39
|
+
CreatePublicThreads: 35,
|
|
40
|
+
CreatePrivateThreads: 36,
|
|
41
|
+
UseExternalStickers: 37,
|
|
42
|
+
SendMessagesInThreads: 38,
|
|
43
|
+
UseEmbeddedActivities: 39,
|
|
44
|
+
ModerateMembers: 40,
|
|
45
|
+
ViewCreatorMonetizationAnalytics: 41,
|
|
46
|
+
UseSoundboard: 42,
|
|
47
|
+
CreateGuildExpressions: 43,
|
|
48
|
+
CreateEvents: 44,
|
|
49
|
+
UseExternalSounds: 45,
|
|
50
|
+
SendVoiceMessages: 46,
|
|
51
|
+
SetVoiceChannelStatus: 48,
|
|
52
|
+
SendPolls: 49,
|
|
53
|
+
UseExternalApps: 50,
|
|
54
|
+
PinMessages: 51,
|
|
55
|
+
BypassSlowmode: 52
|
|
56
|
+
};
|
|
57
|
+
function permissionBits(names) {
|
|
58
|
+
return [...new Set(names)].reduce((bits, name) => bits + 2 ** BIT[name], 0);
|
|
59
|
+
}
|
|
60
|
+
// src/utils/command.ts
|
|
61
|
+
const CONTEXT = {
|
|
62
|
+
guild: 0,
|
|
63
|
+
bot_dm: 1,
|
|
64
|
+
private_channel: 2
|
|
65
|
+
};
|
|
66
|
+
const INSTALL = {
|
|
67
|
+
guild: 0,
|
|
68
|
+
user: 1
|
|
69
|
+
};
|
|
70
|
+
function accessPayload(access) {
|
|
71
|
+
const fields = localePayload(access.locales);
|
|
72
|
+
if (access.permissions) fields.default_member_permissions = permissionBits(access.permissions);
|
|
73
|
+
if (access.contexts) fields.contexts = access.contexts.map((c) => CONTEXT[c]);
|
|
74
|
+
if (access.installs) fields.integration_types = access.installs.map((i) => INSTALL[i]);
|
|
75
|
+
return fields;
|
|
76
|
+
}
|
|
77
|
+
const SUBCOMMAND = 1;
|
|
78
|
+
const GROUP = 2;
|
|
79
|
+
function childPayload(child) {
|
|
80
|
+
const { name, description } = child;
|
|
81
|
+
if (!("subcommands" in child)) return {
|
|
82
|
+
type: SUBCOMMAND,
|
|
83
|
+
name,
|
|
84
|
+
description,
|
|
85
|
+
...localePayload(child.locales),
|
|
86
|
+
options: optionsPayload(child.options)
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
type: GROUP,
|
|
90
|
+
name,
|
|
91
|
+
description,
|
|
92
|
+
...localePayload(child.locales),
|
|
93
|
+
options: child.subcommands.map(childPayload)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function payload(command) {
|
|
97
|
+
const access = accessPayload(command);
|
|
98
|
+
if ("menu" in command) {
|
|
99
|
+
delete access.description_localizations;
|
|
100
|
+
return {
|
|
101
|
+
type: command.menu === "user" ? 2 : 3,
|
|
102
|
+
name: command.name,
|
|
103
|
+
...access
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const { name, description } = command;
|
|
107
|
+
return {
|
|
108
|
+
type: 1,
|
|
109
|
+
name,
|
|
110
|
+
description,
|
|
111
|
+
options: "subcommands" in command ? command.subcommands.map(childPayload) : optionsPayload(command.options),
|
|
112
|
+
...access
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function findCommand(commands, data) {
|
|
116
|
+
const path = [data.name];
|
|
117
|
+
let found = commands.get(data.name);
|
|
118
|
+
let given = data.options ?? [];
|
|
119
|
+
while (found && "subcommands" in found) {
|
|
120
|
+
const picked = given[0];
|
|
121
|
+
found = found.subcommands.find((child) => child.name === picked?.name);
|
|
122
|
+
path.push(picked?.name ?? "");
|
|
123
|
+
given = picked && "options" in picked ? picked.options ?? [] : [];
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
found,
|
|
127
|
+
given,
|
|
128
|
+
path: `/${path.join(" ")}`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export { payload as n, findCommand as t };
|
|
132
|
+
|
|
133
|
+
//# sourceMappingURL=command-CBeslrzu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-CBeslrzu.mjs","names":[],"sources":["../src/utils/permissions.ts","../src/utils/command.ts"],"sourcesContent":["import type { PermissionFlagsBits } from 'discord-api-types/v10';\n\nexport type Permission = Exclude<keyof typeof PermissionFlagsBits, 'ManageEmojisAndStickers'>;\n\nconst BIT: Record<Permission, number> = {\n CreateInstantInvite: 0,\n KickMembers: 1,\n BanMembers: 2,\n Administrator: 3,\n ManageChannels: 4,\n ManageGuild: 5,\n AddReactions: 6,\n ViewAuditLog: 7,\n PrioritySpeaker: 8,\n Stream: 9,\n ViewChannel: 10,\n SendMessages: 11,\n SendTTSMessages: 12,\n ManageMessages: 13,\n EmbedLinks: 14,\n AttachFiles: 15,\n ReadMessageHistory: 16,\n MentionEveryone: 17,\n UseExternalEmojis: 18,\n ViewGuildInsights: 19,\n Connect: 20,\n Speak: 21,\n MuteMembers: 22,\n DeafenMembers: 23,\n MoveMembers: 24,\n UseVAD: 25,\n ChangeNickname: 26,\n ManageNicknames: 27,\n ManageRoles: 28,\n ManageWebhooks: 29,\n ManageGuildExpressions: 30,\n UseApplicationCommands: 31,\n RequestToSpeak: 32,\n ManageEvents: 33,\n ManageThreads: 34,\n CreatePublicThreads: 35,\n CreatePrivateThreads: 36,\n UseExternalStickers: 37,\n SendMessagesInThreads: 38,\n UseEmbeddedActivities: 39,\n ModerateMembers: 40,\n ViewCreatorMonetizationAnalytics: 41,\n UseSoundboard: 42,\n CreateGuildExpressions: 43,\n CreateEvents: 44,\n UseExternalSounds: 45,\n SendVoiceMessages: 46,\n SetVoiceChannelStatus: 48,\n SendPolls: 49,\n UseExternalApps: 50,\n PinMessages: 51,\n BypassSlowmode: 52,\n};\n\nexport function permissionBits(names: Permission[]): number {\n return [...new Set(names)].reduce((bits, name) => bits + 2 ** BIT[name], 0);\n}\n","import type {\n APIApplicationCommandInteractionDataOption,\n APIChatInputApplicationCommandInteraction,\n APIMessage,\n APIMessageApplicationCommandInteraction,\n APIUser,\n APIUserApplicationCommandInteraction,\n ApplicationCommandOptionType,\n} from 'discord-api-types/v10';\n\nimport type { Schemas } from '#lib/routes';\nimport { type Locales, localePayload } from '#utils/locale';\nimport type { FollowUps, MessageOptions, ReplyOptions } from '#utils/message';\nimport type { Modal } from '#utils/modal';\nimport {\n type CommandOption,\n type Option,\n type Options,\n type OptionValue,\n optionsPayload,\n} from '#utils/option';\nimport { type Permission, permissionBits } from '#utils/permissions';\n\nexport type Context<O extends Options> = {\n interaction: APIChatInputApplicationCommandInteraction;\n options: {\n [K in keyof O]: O[K] extends Option<keyof OptionValue, infer R, infer V>\n ? R extends true\n ? V\n : V | undefined\n : never;\n };\n reply: (content: string, options?: ReplyOptions) => Response;\n defer: (\n work: () => Promise<string | ({ content: string } & MessageOptions)>,\n options?: { ephemeral?: boolean },\n ) => Response;\n modal: (modal: Modal, value?: string) => Response;\n} & FollowUps;\n\nexport type Access = {\n locales?: Locales;\n permissions?: Permission[];\n contexts?: ('guild' | 'bot_dm' | 'private_channel')[];\n installs?: ('guild' | 'user')[];\n};\n\nconst CONTEXT = { guild: 0, bot_dm: 1, private_channel: 2 } as const;\nconst INSTALL = { guild: 0, user: 1 } as const;\n\nfunction accessPayload(access: Access): Partial<Schemas['ApplicationCommandUpdateRequest']> {\n const fields: Partial<Schemas['ApplicationCommandUpdateRequest']> = localePayload(access.locales);\n if (access.permissions) fields.default_member_permissions = permissionBits(access.permissions);\n if (access.contexts) fields.contexts = access.contexts.map((c) => CONTEXT[c]);\n if (access.installs) fields.integration_types = access.installs.map((i) => INSTALL[i]);\n\n return fields;\n}\n\nexport type Command<O extends Options = Options> =\n | ({\n name: string;\n description: string;\n options: O;\n run(c: Context<O>): Response | Promise<Response>;\n } & Access)\n | ({ name: string; description: string; subcommands: Command[] } & Access);\n\nexport type MenuType = 'user' | 'message';\n\nexport type MenuContext<T extends MenuType> = {\n interaction: T extends 'user'\n ? APIUserApplicationCommandInteraction\n : APIMessageApplicationCommandInteraction;\n target: T extends 'user' ? APIUser : APIMessage;\n reply: Context<Options>['reply'];\n defer: Context<Options>['defer'];\n modal: Context<Options>['modal'];\n} & FollowUps;\n\nexport type ContextMenu<T extends MenuType = MenuType> = {\n menu: T;\n name: string;\n run(c: MenuContext<T>): Response | Promise<Response>;\n} & Access;\n\nconst SUBCOMMAND: ApplicationCommandOptionType.Subcommand = 1;\nconst GROUP: ApplicationCommandOptionType.SubcommandGroup = 2;\n\nfunction childPayload(child: Command): CommandOption {\n const { name, description } = child;\n if (!('subcommands' in child)) {\n return {\n type: SUBCOMMAND,\n name,\n description,\n ...localePayload(child.locales),\n options: optionsPayload(child.options),\n } as CommandOption;\n }\n\n return {\n type: GROUP,\n name,\n description,\n ...localePayload(child.locales),\n options: child.subcommands.map(childPayload),\n } as CommandOption;\n}\n\nexport function payload(\n command: Command | ContextMenu,\n): Schemas['ApplicationCommandUpdateRequest'] {\n const access = accessPayload(command);\n if ('menu' in command) {\n delete access.description_localizations;\n return { type: command.menu === 'user' ? 2 : 3, name: command.name, ...access };\n }\n\n const { name, description } = command;\n const options =\n 'subcommands' in command\n ? command.subcommands.map(childPayload)\n : optionsPayload(command.options);\n\n return { type: 1, name, description, options, ...access };\n}\n\nexport function findCommand(\n commands: Map<string, Command>,\n data: { name: string; options?: APIApplicationCommandInteractionDataOption[] },\n): {\n found: Command | undefined;\n given: APIApplicationCommandInteractionDataOption[];\n path: string;\n} {\n const path = [data.name];\n let found = commands.get(data.name);\n let given = data.options ?? [];\n while (found && 'subcommands' in found) {\n const picked = given[0];\n found = found.subcommands.find((child) => child.name === picked?.name);\n path.push(picked?.name ?? '');\n given = picked && 'options' in picked ? (picked.options ?? []) : [];\n }\n\n return { found, given, path: `/${path.join(' ')}` };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// src/utils/locale.ts
|
|
2
|
+
function localePayload(locales) {
|
|
3
|
+
const names = {};
|
|
4
|
+
const descriptions = {};
|
|
5
|
+
for (const [locale, text] of Object.entries(locales ?? {})) {
|
|
6
|
+
if (text.name !== void 0) names[locale] = text.name;
|
|
7
|
+
if (text.description !== void 0) descriptions[locale] = text.description;
|
|
8
|
+
}
|
|
9
|
+
const fields = {};
|
|
10
|
+
if (Object.keys(names).length > 0) fields.name_localizations = names;
|
|
11
|
+
if (Object.keys(descriptions).length > 0) fields.description_localizations = descriptions;
|
|
12
|
+
return fields;
|
|
13
|
+
}
|
|
14
|
+
// src/utils/option.ts
|
|
15
|
+
const CHANNEL_TYPES = {
|
|
16
|
+
text: 0,
|
|
17
|
+
voice: 2,
|
|
18
|
+
category: 4,
|
|
19
|
+
announcement: 5,
|
|
20
|
+
announcement_thread: 10,
|
|
21
|
+
public_thread: 11,
|
|
22
|
+
private_thread: 12,
|
|
23
|
+
stage: 13,
|
|
24
|
+
directory: 14,
|
|
25
|
+
forum: 15
|
|
26
|
+
};
|
|
27
|
+
const OPTION_TYPE = {
|
|
28
|
+
string: 3,
|
|
29
|
+
integer: 4,
|
|
30
|
+
number: 10,
|
|
31
|
+
boolean: 5,
|
|
32
|
+
user: 6,
|
|
33
|
+
channel: 7,
|
|
34
|
+
role: 8,
|
|
35
|
+
mentionable: 9,
|
|
36
|
+
attachment: 11
|
|
37
|
+
};
|
|
38
|
+
const USER = 6;
|
|
39
|
+
const CHANNEL = 7;
|
|
40
|
+
const ROLE = 8;
|
|
41
|
+
const MENTIONABLE = 9;
|
|
42
|
+
const ATTACHMENT = 11;
|
|
43
|
+
function optionsPayload(options) {
|
|
44
|
+
return Object.entries(options).map(([name, o]) => {
|
|
45
|
+
const built = {
|
|
46
|
+
name,
|
|
47
|
+
description: o.description,
|
|
48
|
+
type: OPTION_TYPE[o.type],
|
|
49
|
+
required: o.required
|
|
50
|
+
};
|
|
51
|
+
if (o.choices) built.choices = Object.entries(o.choices).map(([label, value]) => ({
|
|
52
|
+
name: label,
|
|
53
|
+
value
|
|
54
|
+
}));
|
|
55
|
+
if (o.min !== void 0) built.min_value = o.min;
|
|
56
|
+
if (o.max !== void 0) built.max_value = o.max;
|
|
57
|
+
if (o.minLength !== void 0) built.min_length = o.minLength;
|
|
58
|
+
if (o.maxLength !== void 0) built.max_length = o.maxLength;
|
|
59
|
+
if (o.channelTypes) built.channel_types = o.channelTypes.map((t) => CHANNEL_TYPES[t]);
|
|
60
|
+
if (o.autocomplete) built.autocomplete = true;
|
|
61
|
+
Object.assign(built, localePayload(o.locales));
|
|
62
|
+
return built;
|
|
63
|
+
}).toSorted((a, b) => Number(b.required) - Number(a.required));
|
|
64
|
+
}
|
|
65
|
+
function userWithMember(resolved, id) {
|
|
66
|
+
const user = resolved?.users?.[id];
|
|
67
|
+
if (!user) return void 0;
|
|
68
|
+
const member = resolved?.members?.[id];
|
|
69
|
+
return member ? {
|
|
70
|
+
...user,
|
|
71
|
+
member
|
|
72
|
+
} : user;
|
|
73
|
+
}
|
|
74
|
+
function optionValues(options, resolved) {
|
|
75
|
+
const values = {};
|
|
76
|
+
for (const o of options) {
|
|
77
|
+
if (!("value" in o)) continue;
|
|
78
|
+
const id = String(o.value);
|
|
79
|
+
if (o.type === USER) values[o.name] = userWithMember(resolved, id);
|
|
80
|
+
else if (o.type === CHANNEL) values[o.name] = resolved?.channels?.[id];
|
|
81
|
+
else if (o.type === ROLE) values[o.name] = resolved?.roles?.[id];
|
|
82
|
+
else if (o.type === MENTIONABLE) values[o.name] = userWithMember(resolved, id) ?? resolved?.roles?.[id];
|
|
83
|
+
else if (o.type === ATTACHMENT) values[o.name] = resolved?.attachments?.[id];
|
|
84
|
+
else values[o.name] = o.value;
|
|
85
|
+
}
|
|
86
|
+
return values;
|
|
87
|
+
}
|
|
88
|
+
// src/utils/upload.ts
|
|
89
|
+
function attachmentsOf(files) {
|
|
90
|
+
return files.map((file, i) => ({
|
|
91
|
+
id: String(i),
|
|
92
|
+
filename: file.name,
|
|
93
|
+
...file.description !== void 0 && { description: file.description }
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
function formOf(payload, files) {
|
|
97
|
+
const form = new FormData();
|
|
98
|
+
form.append("payload_json", JSON.stringify(payload));
|
|
99
|
+
files.forEach((file, i) => {
|
|
100
|
+
const blob = file.data instanceof Blob ? file.data : new Blob([file.data]);
|
|
101
|
+
form.append(`files[${i}]`, blob, file.name);
|
|
102
|
+
});
|
|
103
|
+
return form;
|
|
104
|
+
}
|
|
105
|
+
async function uploadsOf(form) {
|
|
106
|
+
const files = [];
|
|
107
|
+
for (const [key, value] of form.entries()) if (key.startsWith("files[") && typeof value !== "string") files.push({
|
|
108
|
+
name: value.name,
|
|
109
|
+
data: value
|
|
110
|
+
});
|
|
111
|
+
const payload = form.get("payload_json");
|
|
112
|
+
return {
|
|
113
|
+
payload: typeof payload === "string" ? JSON.parse(payload) : null,
|
|
114
|
+
files
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// src/lib/rest.ts
|
|
118
|
+
const API = "https://discord.com/api/v10";
|
|
119
|
+
const TRIES = 3;
|
|
120
|
+
function callDiscord(token, method, route, ...args) {
|
|
121
|
+
return request(token, method, route, args[0]);
|
|
122
|
+
}
|
|
123
|
+
function createRest(token) {
|
|
124
|
+
const call = (method) => async (route, init) => {
|
|
125
|
+
const value = token();
|
|
126
|
+
if (typeof value !== "string") return [/* @__PURE__ */ new Error("DISCORD_TOKEN is not set"), null];
|
|
127
|
+
return request(value, method, route, init);
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
get: call("get"),
|
|
131
|
+
post: call("post"),
|
|
132
|
+
put: call("put"),
|
|
133
|
+
patch: call("patch"),
|
|
134
|
+
delete: call("delete")
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async function request(token, method, route, init = {}) {
|
|
138
|
+
const path = route.replace(/\{(\w+)\}/g, (_, name) => encodeURIComponent(init.path?.[name] ?? ""));
|
|
139
|
+
const query = new URLSearchParams();
|
|
140
|
+
for (const [name, value] of Object.entries(init.query ?? {})) if (value !== void 0) query.set(name, String(value));
|
|
141
|
+
const url = query.size > 0 ? `${API}${path}?${query.toString()}` : `${API}${path}`;
|
|
142
|
+
const headers = {};
|
|
143
|
+
if (token) headers.Authorization = `Bot ${token}`;
|
|
144
|
+
if (init.reason) headers["X-Audit-Log-Reason"] = encodeURIComponent(init.reason);
|
|
145
|
+
let body;
|
|
146
|
+
if (init.files?.length) body = formOf(init.body, init.files);
|
|
147
|
+
else if (init.body !== void 0) {
|
|
148
|
+
headers["Content-Type"] = "application/json";
|
|
149
|
+
body = JSON.stringify(init.body);
|
|
150
|
+
}
|
|
151
|
+
const send = async (attempt) => {
|
|
152
|
+
const res = await fetch(url, {
|
|
153
|
+
method: method.toUpperCase(),
|
|
154
|
+
headers,
|
|
155
|
+
body
|
|
156
|
+
});
|
|
157
|
+
if (res.status === 429 && attempt < TRIES) {
|
|
158
|
+
const { retry_after } = await res.json();
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, retry_after * 1e3));
|
|
160
|
+
return send(attempt + 1);
|
|
161
|
+
}
|
|
162
|
+
if (!res.ok) return [/* @__PURE__ */ new Error(`Discord answered ${res.status}: ${await res.text()}`), null];
|
|
163
|
+
return [null, res.status === 204 ? null : await res.json()];
|
|
164
|
+
};
|
|
165
|
+
return send(1);
|
|
166
|
+
}
|
|
167
|
+
// src/utils/button.ts
|
|
168
|
+
const STYLE = {
|
|
169
|
+
primary: 1,
|
|
170
|
+
secondary: 2,
|
|
171
|
+
success: 3,
|
|
172
|
+
danger: 4
|
|
173
|
+
};
|
|
174
|
+
const LINK = 5;
|
|
175
|
+
const CUSTOM_EMOJI = /^<a?:(\w+):(\d+)>$/;
|
|
176
|
+
function emojiOf(text) {
|
|
177
|
+
const custom = CUSTOM_EMOJI.exec(text);
|
|
178
|
+
if (!custom?.[1]) return { name: text };
|
|
179
|
+
return {
|
|
180
|
+
name: custom[1],
|
|
181
|
+
id: custom[2]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function buttonComponent(button) {
|
|
185
|
+
const component = {
|
|
186
|
+
type: 2,
|
|
187
|
+
style: "url" in button ? LINK : STYLE[button.style ?? "primary"]
|
|
188
|
+
};
|
|
189
|
+
if (button.label !== void 0) component.label = button.label;
|
|
190
|
+
if ("url" in button) component.url = button.url;
|
|
191
|
+
else component.custom_id = button.id;
|
|
192
|
+
if (button.emoji) component.emoji = emojiOf(button.emoji);
|
|
193
|
+
if (button.disabled) component.disabled = true;
|
|
194
|
+
return component;
|
|
195
|
+
}
|
|
196
|
+
// src/utils/select.ts
|
|
197
|
+
const COMPONENT = {
|
|
198
|
+
string: 3,
|
|
199
|
+
user: 5,
|
|
200
|
+
role: 6,
|
|
201
|
+
mentionable: 7,
|
|
202
|
+
channel: 8
|
|
203
|
+
};
|
|
204
|
+
function selectRow(menu) {
|
|
205
|
+
const component = {
|
|
206
|
+
type: COMPONENT[menu.type],
|
|
207
|
+
custom_id: menu.id
|
|
208
|
+
};
|
|
209
|
+
if (menu.placeholder !== void 0) component.placeholder = menu.placeholder;
|
|
210
|
+
if (menu.min !== void 0) component.min_values = menu.min;
|
|
211
|
+
if (menu.max !== void 0) component.max_values = menu.max;
|
|
212
|
+
if (menu.options) component.options = Object.entries(menu.options).map(([label, value]) => ({
|
|
213
|
+
label,
|
|
214
|
+
value
|
|
215
|
+
}));
|
|
216
|
+
if (menu.channelTypes) component.channel_types = menu.channelTypes.map((t) => CHANNEL_TYPES[t]);
|
|
217
|
+
return {
|
|
218
|
+
type: 1,
|
|
219
|
+
components: [component]
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function selectValues(interaction) {
|
|
223
|
+
const { data } = interaction;
|
|
224
|
+
const resolved = "resolved" in data ? data.resolved : void 0;
|
|
225
|
+
return data.values.map((id) => {
|
|
226
|
+
if (!resolved) return id;
|
|
227
|
+
if ("users" in resolved && resolved.users?.[id]) return resolved.users[id];
|
|
228
|
+
if ("roles" in resolved && resolved.roles?.[id]) return resolved.roles[id];
|
|
229
|
+
if ("channels" in resolved && resolved.channels?.[id]) return resolved.channels[id];
|
|
230
|
+
return id;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// src/utils/message.ts
|
|
234
|
+
const PER_ROW = 5;
|
|
235
|
+
const EPHEMERAL = 64;
|
|
236
|
+
const NO_PINGS = { parse: [] };
|
|
237
|
+
function message(content, options = {}) {
|
|
238
|
+
const data = {
|
|
239
|
+
content,
|
|
240
|
+
allowed_mentions: options.allowedMentions ?? NO_PINGS
|
|
241
|
+
};
|
|
242
|
+
if (options.ephemeral) data.flags = EPHEMERAL;
|
|
243
|
+
if (options.embeds) data.embeds = options.embeds;
|
|
244
|
+
if (options.files?.length) data.attachments = attachmentsOf(options.files);
|
|
245
|
+
if (!options.buttons && !options.selects) return data;
|
|
246
|
+
const rows = (options.selects ?? []).map(selectRow);
|
|
247
|
+
const buttons = options.buttons ?? [];
|
|
248
|
+
for (let i = 0; i < buttons.length; i += PER_ROW) rows.push({
|
|
249
|
+
type: 1,
|
|
250
|
+
components: buttons.slice(i, i + PER_ROW).map(buttonComponent)
|
|
251
|
+
});
|
|
252
|
+
data.components = rows;
|
|
253
|
+
return data;
|
|
254
|
+
}
|
|
255
|
+
// src/lib/discord.ts
|
|
256
|
+
async function putCommands(env, body) {
|
|
257
|
+
const [error, saved] = env.guildId ? await callDiscord(env.token, "put", "/applications/{application_id}/guilds/{guild_id}/commands", {
|
|
258
|
+
path: {
|
|
259
|
+
application_id: env.applicationId,
|
|
260
|
+
guild_id: env.guildId
|
|
261
|
+
},
|
|
262
|
+
body
|
|
263
|
+
}) : await callDiscord(env.token, "put", "/applications/{application_id}/commands", {
|
|
264
|
+
path: { application_id: env.applicationId },
|
|
265
|
+
body
|
|
266
|
+
});
|
|
267
|
+
if (error) return [error, null];
|
|
268
|
+
return [null, saved?.length ?? 0];
|
|
269
|
+
}
|
|
270
|
+
async function editOriginal(applicationId, token, body, files) {
|
|
271
|
+
const [error] = await callDiscord(null, "patch", "/webhooks/{webhook_id}/{webhook_token}/messages/@original", {
|
|
272
|
+
path: {
|
|
273
|
+
webhook_id: applicationId,
|
|
274
|
+
webhook_token: token
|
|
275
|
+
},
|
|
276
|
+
body,
|
|
277
|
+
files
|
|
278
|
+
});
|
|
279
|
+
return error;
|
|
280
|
+
}
|
|
281
|
+
async function sendMessage(token, channelId, content, options = {}) {
|
|
282
|
+
const [error] = await callDiscord(token, "post", "/channels/{channel_id}/messages", {
|
|
283
|
+
path: { channel_id: channelId },
|
|
284
|
+
body: message(content, {
|
|
285
|
+
buttons: options.buttons,
|
|
286
|
+
selects: options.selects,
|
|
287
|
+
embeds: options.embeds,
|
|
288
|
+
allowedMentions: options.allowedMentions,
|
|
289
|
+
files: options.files
|
|
290
|
+
}),
|
|
291
|
+
files: options.files
|
|
292
|
+
});
|
|
293
|
+
return error;
|
|
294
|
+
}
|
|
295
|
+
async function interactionCallback(id, token, response) {
|
|
296
|
+
const { payload, files } = response.headers.get("content-type")?.startsWith("multipart/form-data") ? await uploadsOf(await response.formData()) : {
|
|
297
|
+
payload: JSON.parse(await response.text()),
|
|
298
|
+
files: []
|
|
299
|
+
};
|
|
300
|
+
const [error] = await callDiscord(null, "post", "/interactions/{interaction_id}/{interaction_token}/callback", {
|
|
301
|
+
path: {
|
|
302
|
+
interaction_id: id,
|
|
303
|
+
interaction_token: token
|
|
304
|
+
},
|
|
305
|
+
body: payload,
|
|
306
|
+
files
|
|
307
|
+
});
|
|
308
|
+
return error;
|
|
309
|
+
}
|
|
310
|
+
export { message as a, createRest as c, optionsPayload as d, localePayload as f, sendMessage as i, formOf as l, interactionCallback as n, selectValues as o, putCommands as r, callDiscord as s, editOriginal as t, optionValues as u };
|
|
311
|
+
|
|
312
|
+
//# sourceMappingURL=discord-DEZe1asd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord-DEZe1asd.mjs","names":[],"sources":["../src/utils/locale.ts","../src/utils/option.ts","../src/utils/upload.ts","../src/lib/rest.ts","../src/utils/button.ts","../src/utils/select.ts","../src/utils/message.ts","../src/lib/discord.ts"],"sourcesContent":["import type { Locale } from 'discord-api-types/v10';\n\nexport type Locales = Partial<Record<`${Locale}`, { name?: string; description?: string }>>;\n\nexport function localePayload(locales: Locales | undefined): {\n name_localizations?: Record<string, string>;\n description_localizations?: Record<string, string>;\n} {\n const names: Record<string, string> = {};\n const descriptions: Record<string, string> = {};\n for (const [locale, text] of Object.entries(locales ?? {})) {\n if (text.name !== undefined) names[locale] = text.name;\n if (text.description !== undefined) descriptions[locale] = text.description;\n }\n\n const fields: ReturnType<typeof localePayload> = {};\n if (Object.keys(names).length > 0) fields.name_localizations = names;\n if (Object.keys(descriptions).length > 0) fields.description_localizations = descriptions;\n\n return fields;\n}\n","import type {\n APIApplicationCommandAutocompleteInteraction,\n APIApplicationCommandInteractionDataOption,\n APIChatInputApplicationCommandInteractionData,\n APIAttachment,\n APIInteractionDataResolvedChannel,\n APIInteractionDataResolvedGuildMember,\n APIRole,\n APIUser,\n ApplicationCommandOptionType,\n} from 'discord-api-types/v10';\n\nimport type { Schemas } from '#lib/routes';\nimport { type Locales, localePayload } from '#utils/locale';\n\nexport type OptionValue = {\n string: string;\n integer: number;\n number: number;\n boolean: boolean;\n user: APIUser & { member?: APIInteractionDataResolvedGuildMember };\n channel: APIInteractionDataResolvedChannel;\n role: APIRole;\n mentionable: (APIUser & { member?: APIInteractionDataResolvedGuildMember }) | APIRole;\n attachment: APIAttachment;\n};\n\nexport type ChannelType =\n | 'text'\n | 'voice'\n | 'category'\n | 'announcement'\n | 'announcement_thread'\n | 'public_thread'\n | 'private_thread'\n | 'stage'\n | 'directory'\n | 'forum';\n\nexport const CHANNEL_TYPES: Record<ChannelType, Schemas['ChannelTypes']> = {\n text: 0,\n voice: 2,\n category: 4,\n announcement: 5,\n announcement_thread: 10,\n public_thread: 11,\n private_thread: 12,\n stage: 13,\n directory: 14,\n forum: 15,\n};\n\nexport type Option<\n T extends keyof OptionValue = keyof OptionValue,\n R extends boolean = boolean,\n V = OptionValue[T],\n> = {\n type: T;\n description: string;\n required: R;\n choices?: Record<string, V>;\n min?: number;\n max?: number;\n minLength?: number;\n maxLength?: number;\n channelTypes?: ChannelType[];\n locales?: Locales;\n autocomplete?: (c: AutocompleteContext) => Suggestion<V>[] | Promise<Suggestion<V>[]>;\n};\n\nexport type AutocompleteContext = {\n interaction: APIApplicationCommandAutocompleteInteraction;\n value: string;\n};\n\nexport type Suggestion<V> = V | { name: string; value: V };\n\nexport type Options = Record<string, Option<keyof OptionValue, boolean, unknown>>;\n\nconst OPTION_TYPE: Record<keyof OptionValue, ApplicationCommandOptionType> = {\n string: 3,\n integer: 4,\n number: 10,\n boolean: 5,\n user: 6,\n channel: 7,\n role: 8,\n mentionable: 9,\n attachment: 11,\n};\n\nconst USER: ApplicationCommandOptionType.User = 6;\nconst CHANNEL: ApplicationCommandOptionType.Channel = 7;\nconst ROLE: ApplicationCommandOptionType.Role = 8;\nconst MENTIONABLE: ApplicationCommandOptionType.Mentionable = 9;\nconst ATTACHMENT: ApplicationCommandOptionType.Attachment = 11;\n\nexport type CommandOption = NonNullable<\n Schemas['ApplicationCommandUpdateRequest']['options']\n>[number];\n\nexport function optionsPayload(options: Options): CommandOption[] {\n return Object.entries(options)\n .map(([name, o]) => {\n const built: Record<string, unknown> = {\n name,\n description: o.description,\n type: OPTION_TYPE[o.type],\n required: o.required,\n };\n\n if (o.choices) {\n built.choices = Object.entries(o.choices).map(([label, value]) => ({ name: label, value }));\n }\n if (o.min !== undefined) built.min_value = o.min;\n if (o.max !== undefined) built.max_value = o.max;\n if (o.minLength !== undefined) built.min_length = o.minLength;\n if (o.maxLength !== undefined) built.max_length = o.maxLength;\n if (o.channelTypes) built.channel_types = o.channelTypes.map((t) => CHANNEL_TYPES[t]);\n if (o.autocomplete) built.autocomplete = true;\n Object.assign(built, localePayload(o.locales));\n\n return built as CommandOption;\n })\n .toSorted((a, b) => Number(b.required) - Number(a.required));\n}\n\nfunction userWithMember(\n resolved: APIChatInputApplicationCommandInteractionData['resolved'],\n id: string,\n): OptionValue['user'] | undefined {\n const user = resolved?.users?.[id];\n if (!user) return undefined;\n\n const member = resolved?.members?.[id];\n\n return member ? { ...user, member } : user;\n}\n\nexport function optionValues(\n options: APIApplicationCommandInteractionDataOption[],\n resolved: APIChatInputApplicationCommandInteractionData['resolved'],\n): Record<string, OptionValue[keyof OptionValue] | undefined> {\n const values: Record<string, OptionValue[keyof OptionValue] | undefined> = {};\n\n for (const o of options) {\n if (!('value' in o)) continue;\n\n const id = String(o.value);\n if (o.type === USER) values[o.name] = userWithMember(resolved, id);\n else if (o.type === CHANNEL) values[o.name] = resolved?.channels?.[id];\n else if (o.type === ROLE) values[o.name] = resolved?.roles?.[id];\n else if (o.type === MENTIONABLE)\n values[o.name] = userWithMember(resolved, id) ?? resolved?.roles?.[id];\n else if (o.type === ATTACHMENT) values[o.name] = resolved?.attachments?.[id];\n else values[o.name] = o.value;\n }\n\n return values;\n}\n","export type Upload = {\n name: string;\n data: Blob | ArrayBuffer | Uint8Array<ArrayBuffer> | string;\n description?: string;\n};\n\nexport function attachmentsOf(\n files: Upload[],\n): { id: string; filename: string; description?: string }[] {\n return files.map((file, i) => ({\n id: String(i),\n filename: file.name,\n ...(file.description !== undefined && { description: file.description }),\n }));\n}\n\nexport function formOf(payload: unknown, files: Upload[]): FormData {\n const form = new FormData();\n form.append('payload_json', JSON.stringify(payload));\n files.forEach((file, i) => {\n const blob = file.data instanceof Blob ? file.data : new Blob([file.data]);\n form.append(`files[${i}]`, blob, file.name);\n });\n\n return form;\n}\n\nexport async function uploadsOf(form: FormData): Promise<{ payload: unknown; files: Upload[] }> {\n const files: Upload[] = [];\n for (const [key, value] of form.entries()) {\n if (key.startsWith('files[') && typeof value !== 'string') {\n files.push({ name: value.name, data: value });\n }\n }\n\n const payload = form.get('payload_json');\n\n return { payload: typeof payload === 'string' ? (JSON.parse(payload) as unknown) : null, files };\n}\n","import type { Routes } from '#lib/routes';\nimport { formOf, type Upload } from '#utils/upload';\n\nconst API = 'https://discord.com/api/v10';\nconst TRIES = 3;\n\ntype Method = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\ntype Init = {\n path?: Record<string, string>;\n query?: object;\n body?: unknown;\n files?: Upload[];\n reason?: string;\n};\n\ntype Route<M extends Method> = {\n [K in keyof Routes]: M extends keyof Routes[K] ? K : never;\n}[keyof Routes];\n\ntype Operation<P extends keyof Routes, M extends Method> =\n Routes[P] extends Record<M, infer O extends { args: unknown[]; result: unknown }> ? O : never;\n\ntype Result<P extends keyof Routes, M extends Method> = Promise<\n [Error, null] | [null, Operation<P, M>['result']]\n>;\n\n/** Calls the Discord REST API. Each method takes a route from the Discord API spec. */\nexport type Rest = {\n [M in Method]: <P extends Route<M>>(path: P, ...args: Operation<P, M>['args']) => Result<P, M>;\n};\n\nexport function callDiscord<M extends Method, P extends Route<M>>(\n token: string | null,\n method: M,\n route: P,\n ...args: Operation<P, M>['args']\n): Result<P, M> {\n return request(token, method, route, args[0] as Init | undefined);\n}\n\nexport function createRest(token: () => unknown): Rest {\n const call =\n (method: Method) =>\n async (route: string, init?: Init): Promise<[Error, null] | [null, unknown]> => {\n const value = token();\n if (typeof value !== 'string') return [new Error('DISCORD_TOKEN is not set'), null];\n\n return request(value, method, route, init);\n };\n\n return {\n get: call('get'),\n post: call('post'),\n put: call('put'),\n patch: call('patch'),\n delete: call('delete'),\n } as Rest;\n}\n\nasync function request<T>(\n token: string | null,\n method: Method,\n route: string,\n init: Init = {},\n): Promise<[Error, null] | [null, T]> {\n const path = route.replace(/\\{(\\w+)\\}/g, (_, name: string) =>\n encodeURIComponent(init.path?.[name] ?? ''),\n );\n\n const query = new URLSearchParams();\n for (const [name, value] of Object.entries(init.query ?? {})) {\n if (value !== undefined) query.set(name, String(value));\n }\n const url = query.size > 0 ? `${API}${path}?${query.toString()}` : `${API}${path}`;\n\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bot ${token}`;\n if (init.reason) headers['X-Audit-Log-Reason'] = encodeURIComponent(init.reason);\n let body: string | FormData | undefined;\n if (init.files?.length) {\n body = formOf(init.body, init.files);\n } else if (init.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(init.body);\n }\n\n const send = async (attempt: number): Promise<[Error, null] | [null, T]> => {\n const res = await fetch(url, { method: method.toUpperCase(), headers, body });\n\n if (res.status === 429 && attempt < TRIES) {\n const { retry_after } = (await res.json()) as { retry_after: number };\n await new Promise((resolve) => setTimeout(resolve, retry_after * 1000));\n return send(attempt + 1);\n }\n\n if (!res.ok) return [new Error(`Discord answered ${res.status}: ${await res.text()}`), null];\n const data: unknown = res.status === 204 ? null : await res.json();\n\n return [null, data as T];\n };\n\n return send(1);\n}\n","import type { Schemas } from '#lib/routes';\n\nexport type Button = (\n | { id: string; style?: ButtonStyle; url?: never }\n | { url: string; id?: never; style?: never }\n) &\n ({ label: string; emoji?: string } | { label?: string; emoji: string }) & { disabled?: boolean };\n\ntype ButtonStyle = 'primary' | 'secondary' | 'success' | 'danger';\n\nconst STYLE: Record<ButtonStyle, Schemas['ButtonStyleTypes']> = {\n primary: 1,\n secondary: 2,\n success: 3,\n danger: 4,\n};\nconst LINK: Schemas['ButtonStyleTypes'] = 5;\nconst CUSTOM_EMOJI = /^<a?:(\\w+):(\\d+)>$/;\n\nfunction emojiOf(text: string): Schemas['ComponentEmojiForRequest'] {\n const custom = CUSTOM_EMOJI.exec(text);\n if (!custom?.[1]) return { name: text };\n\n return { name: custom[1], id: custom[2] };\n}\n\nexport function buttonComponent(button: Button): Schemas['ButtonComponentForMessageRequest'] {\n const component: Schemas['ButtonComponentForMessageRequest'] = {\n type: 2,\n style: 'url' in button ? LINK : STYLE[button.style ?? 'primary'],\n };\n\n if (button.label !== undefined) component.label = button.label;\n if ('url' in button) component.url = button.url;\n else component.custom_id = button.id;\n if (button.emoji) component.emoji = emojiOf(button.emoji);\n if (button.disabled) component.disabled = true;\n\n return component;\n}\n","import type { APIMessageComponentSelectMenuInteraction } from 'discord-api-types/v10';\n\nimport type { Schemas } from '#lib/routes';\nimport { CHANNEL_TYPES, type ChannelType } from '#utils/option';\n\nexport type SelectType = 'string' | 'user' | 'role' | 'channel' | 'mentionable';\n\nexport type SelectMenu = {\n type: SelectType;\n id: string;\n placeholder?: string;\n min?: number;\n max?: number;\n options?: Record<string, string>;\n channelTypes?: ChannelType[];\n};\n\nconst COMPONENT: Record<SelectType, 3 | 5 | 6 | 7 | 8> = {\n string: 3,\n user: 5,\n role: 6,\n mentionable: 7,\n channel: 8,\n};\n\nexport function selectRow(menu: SelectMenu): Schemas['ActionRowComponentForMessageRequest'] {\n const component: Record<string, unknown> = { type: COMPONENT[menu.type], custom_id: menu.id };\n\n if (menu.placeholder !== undefined) component.placeholder = menu.placeholder;\n if (menu.min !== undefined) component.min_values = menu.min;\n if (menu.max !== undefined) component.max_values = menu.max;\n if (menu.options) {\n component.options = Object.entries(menu.options).map(([label, value]) => ({ label, value }));\n }\n if (menu.channelTypes) component.channel_types = menu.channelTypes.map((t) => CHANNEL_TYPES[t]);\n\n return {\n type: 1,\n components: [component as Schemas['ActionRowComponentForMessageRequest']['components'][number]],\n };\n}\n\nexport function selectValues(interaction: APIMessageComponentSelectMenuInteraction): unknown[] {\n const { data } = interaction;\n const resolved = 'resolved' in data ? data.resolved : undefined;\n\n return data.values.map((id) => {\n if (!resolved) return id;\n if ('users' in resolved && resolved.users?.[id]) return resolved.users[id];\n if ('roles' in resolved && resolved.roles?.[id]) return resolved.roles[id];\n if ('channels' in resolved && resolved.channels?.[id]) return resolved.channels[id];\n\n return id;\n });\n}\n","import type { MessageFlags } from 'discord-api-types/v10';\n\nimport type { Schemas } from '#lib/routes';\nimport { type Button, buttonComponent } from '#utils/button';\nimport { type SelectMenu, selectRow } from '#utils/select';\nimport { attachmentsOf, type Upload } from '#utils/upload';\n\nexport type MessageOptions = {\n buttons?: Button[];\n selects?: SelectMenu[];\n embeds?: Schemas['RichEmbed'][];\n allowedMentions?: Schemas['MessageAllowedMentionsRequest'];\n files?: Upload[];\n};\n\nexport type ReplyOptions = MessageOptions & { ephemeral?: boolean };\n\nexport type FollowUps = {\n after(work: () => Promise<void>): void;\n followUp(content: string, options?: ReplyOptions): Promise<Error | null>;\n editReply(content: string, options?: MessageOptions): Promise<Error | null>;\n deleteReply(): Promise<Error | null>;\n};\n\nconst PER_ROW = 5;\nconst EPHEMERAL: MessageFlags.Ephemeral = 64;\nconst NO_PINGS: Schemas['MessageAllowedMentionsRequest'] = { parse: [] };\n\nexport function message(\n content: string,\n options: ReplyOptions = {},\n): Schemas['IncomingWebhookInteractionRequest'] {\n const data: Schemas['IncomingWebhookInteractionRequest'] = {\n content,\n allowed_mentions: options.allowedMentions ?? NO_PINGS,\n };\n if (options.ephemeral) data.flags = EPHEMERAL;\n if (options.embeds) data.embeds = options.embeds;\n if (options.files?.length) data.attachments = attachmentsOf(options.files);\n if (!options.buttons && !options.selects) return data;\n\n const rows = (options.selects ?? []).map(selectRow);\n const buttons = options.buttons ?? [];\n for (let i = 0; i < buttons.length; i += PER_ROW) {\n rows.push({\n type: 1,\n components: buttons.slice(i, i + PER_ROW).map(buttonComponent),\n });\n }\n data.components = rows;\n\n return data;\n}\n","import { callDiscord } from '#lib/rest';\nimport type { Schemas } from '#lib/routes';\nimport type { SyncEnv } from '#utils/env';\nimport { message, type MessageOptions } from '#utils/message';\nimport { type Upload, uploadsOf } from '#utils/upload';\n\nexport async function putCommands(\n env: SyncEnv,\n body: Schemas['ApplicationCommandUpdateRequest'][],\n): Promise<[Error, null] | [null, number]> {\n const [error, saved] = env.guildId\n ? await callDiscord(\n env.token,\n 'put',\n '/applications/{application_id}/guilds/{guild_id}/commands',\n {\n path: { application_id: env.applicationId, guild_id: env.guildId },\n body,\n },\n )\n : await callDiscord(env.token, 'put', '/applications/{application_id}/commands', {\n path: { application_id: env.applicationId },\n body,\n });\n if (error) return [error, null];\n\n return [null, saved?.length ?? 0];\n}\n\nexport async function editOriginal(\n applicationId: string,\n token: string,\n body: Schemas['IncomingWebhookUpdateRequestPartial'],\n files?: Upload[],\n): Promise<Error | null> {\n const [error] = await callDiscord(\n null,\n 'patch',\n '/webhooks/{webhook_id}/{webhook_token}/messages/@original',\n {\n path: { webhook_id: applicationId, webhook_token: token },\n body,\n files,\n },\n );\n\n return error;\n}\n\nexport type Send = (\n channelId: string,\n content: string,\n options?: MessageOptions,\n) => Promise<Error | null>;\n\nexport async function sendMessage(\n token: string,\n channelId: string,\n content: string,\n options: MessageOptions = {},\n): Promise<Error | null> {\n const [error] = await callDiscord(token, 'post', '/channels/{channel_id}/messages', {\n path: { channel_id: channelId },\n body: message(content, {\n buttons: options.buttons,\n selects: options.selects,\n embeds: options.embeds,\n allowedMentions: options.allowedMentions,\n files: options.files,\n }),\n files: options.files,\n });\n\n return error;\n}\n\nexport async function interactionCallback(\n id: string,\n token: string,\n response: Response,\n): Promise<Error | null> {\n const multipart = response.headers.get('content-type')?.startsWith('multipart/form-data');\n const { payload, files } = multipart\n ? await uploadsOf(await response.formData())\n : { payload: JSON.parse(await response.text()) as unknown, files: [] };\n\n const [error] = await callDiscord(\n null,\n 'post',\n '/interactions/{interaction_id}/{interaction_token}/callback',\n {\n path: { interaction_id: id, interaction_token: token },\n body: payload as Schemas['create_interaction_response_body'],\n files,\n },\n );\n\n return error;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { i as ErrorHandler, r as Send, t as Bot } from "./bot-icKel3r9.mjs";
|
|
2
|
+
import { DurableObject } from "cloudflare:workers";
|
|
3
|
+
import { GatewayDispatchPayload, GatewayIntentBits, GatewayPresenceUpdateData } from "discord-api-types/v10";
|
|
4
|
+
type Intent = Exclude<keyof typeof GatewayIntentBits, "GuildBans" | "GuildEmojisAndStickers">;
|
|
5
|
+
type GatewayConfig = {
|
|
6
|
+
intents: Intent[];
|
|
7
|
+
presence?: GatewayPresenceUpdateData;
|
|
8
|
+
bot?: Pick<Bot, "answer">;
|
|
9
|
+
onError?: ErrorHandler;
|
|
10
|
+
events: { [P in GatewayDispatchPayload as P["t"]]?: (data: P["d"], c: {
|
|
11
|
+
send: Send;
|
|
12
|
+
}) => unknown; };
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates a Durable Object class that keeps one connection to the Discord Gateway open and runs your
|
|
16
|
+
* handler for each event. Export it under the `class_name` of a SQLite Durable Object binding in your
|
|
17
|
+
* Wrangler config, and give that binding name to `bot({ gateway })` so a Cron Trigger starts and
|
|
18
|
+
* reconnects it.
|
|
19
|
+
* It reads `DISCORD_TOKEN` from the Worker environment.
|
|
20
|
+
*
|
|
21
|
+
* @param config What the bot listens to.
|
|
22
|
+
* @param config.intents The names of the gateway intents the bot needs, such as `'GuildMessages'`.
|
|
23
|
+
* Turn on privileged ones, such as `'MessageContent'`, in the Developer Portal first.
|
|
24
|
+
* @param config.presence The status the bot shows while it is connected.
|
|
25
|
+
* @param config.events A handler per dispatch event name. `c.send` posts a message to a channel,
|
|
26
|
+
* with optional `buttons` and `embeds`.
|
|
27
|
+
* @param config.onError Gets errors from event handlers and from answering interactions.
|
|
28
|
+
* @param config.bot The bot from `bot()`. Its handlers answer the commands, buttons and modals that
|
|
29
|
+
* arrive over the gateway, so the application needs no Interactions Endpoint URL.
|
|
30
|
+
* @returns The Durable Object class.
|
|
31
|
+
* @example
|
|
32
|
+
* import { bot } from 'botflare';
|
|
33
|
+
* import { gateway } from 'botflare/gateway';
|
|
34
|
+
*
|
|
35
|
+
* const app = bot({ commands: [], gateway: 'GATEWAY' });
|
|
36
|
+
*
|
|
37
|
+
* export const Gateway = gateway({
|
|
38
|
+
* intents: ['Guilds', 'GuildMessages'],
|
|
39
|
+
* bot: app,
|
|
40
|
+
* events: { MESSAGE_CREATE: (message, c) => c.send(message.channel_id, 'heard you') },
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* export default app;
|
|
44
|
+
*/
|
|
45
|
+
export declare function gateway(config: GatewayConfig): new (ctx: DurableObjectState, env: {
|
|
46
|
+
DISCORD_TOKEN: string;
|
|
47
|
+
}) => DurableObject<{
|
|
48
|
+
DISCORD_TOKEN: string;
|
|
49
|
+
}> & {
|
|
50
|
+
ensure(): Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=gateway.d.mts.map
|