mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +00:00
feat: Entry Points (#256)
* feat: entry points types * fix: update attachment * feat: more types and command * chore: apply formatting * feat: entry interaction * chore: apply formatting * feat: entry commands * feat: end * fix: build * fix: typing * fix: entry point in handler * fix: build --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
fc4c7ef3da
commit
a9d14c4c01
3
.github/workflows/transpile.yml
vendored
3
.github/workflows/transpile.yml
vendored
@ -28,6 +28,3 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: npx tsc
|
||||
|
@ -1,11 +1,21 @@
|
||||
import type { RESTPostAPIInteractionCallbackJSONBody } from '../../types';
|
||||
import type {
|
||||
RESTPostAPIInteractionCallbackJSONBody,
|
||||
RESTPostAPIInteractionCallbackQuery,
|
||||
RESTPostAPIInteractionCallbackResult,
|
||||
} from '../../types';
|
||||
import type { ProxyRequestMethod } from '../Router';
|
||||
import type { RestArguments } from '../api';
|
||||
|
||||
export interface InteractionRoutes {
|
||||
interactions: (id: string) => (token: string) => {
|
||||
callback: {
|
||||
post(args: RestArguments<ProxyRequestMethod.Post, RESTPostAPIInteractionCallbackJSONBody>): Promise<never>;
|
||||
post(
|
||||
args: RestArguments<
|
||||
ProxyRequestMethod.Post,
|
||||
RESTPostAPIInteractionCallbackJSONBody,
|
||||
RESTPostAPIInteractionCallbackQuery
|
||||
>,
|
||||
): Promise<RESTPostAPIInteractionCallbackResult | undefined>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -347,9 +347,9 @@ export class ApiHandler {
|
||||
const fileKey = file.key ?? `files[${index}]`;
|
||||
|
||||
if (isBufferLike(file.data)) {
|
||||
formData.append(fileKey, new Blob([file.data], { type: file.contentType }), file.name);
|
||||
formData.append(fileKey, new Blob([file.data], { type: file.contentType }), file.filename);
|
||||
} else {
|
||||
formData.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.name);
|
||||
formData.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ export interface RawFile {
|
||||
contentType?: string;
|
||||
data: Buffer | Uint8Array | boolean | number | string;
|
||||
key?: string;
|
||||
name: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export interface ApiRequestOptions {
|
||||
|
@ -17,7 +17,7 @@ export type AttachmentResolvable =
|
||||
| Attachment;
|
||||
export type AttachmentDataType = keyof AttachmentResolvableMap;
|
||||
export interface AttachmentData {
|
||||
name: string;
|
||||
filename: string;
|
||||
description: string;
|
||||
resolvable: AttachmentResolvable;
|
||||
type: AttachmentDataType;
|
||||
@ -40,7 +40,7 @@ export class AttachmentBuilder {
|
||||
* @param data - The partial attachment data.
|
||||
*/
|
||||
constructor(
|
||||
public data: Partial<AttachmentData> = { name: `${randomBytes?.(8)?.toString('base64url') || 'default'}.jpg` },
|
||||
public data: Partial<AttachmentData> = { filename: `${randomBytes?.(8)?.toString('base64url') || 'default'}.jpg` },
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -51,7 +51,7 @@ export class AttachmentBuilder {
|
||||
* attachment.setName('example.jpg');
|
||||
*/
|
||||
setName(name: string): this {
|
||||
this.data.name = name;
|
||||
this.data.filename = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -93,10 +93,10 @@ export class AttachmentBuilder {
|
||||
setSpoiler(spoiler: boolean): this {
|
||||
if (spoiler === this.spoiler) return this;
|
||||
if (!spoiler) {
|
||||
this.data.name = this.data.name!.slice('SPOILER_'.length);
|
||||
this.data.filename = this.data.filename!.slice('SPOILER_'.length);
|
||||
return this;
|
||||
}
|
||||
this.data.name = `SPOILER_${this.data.name}`;
|
||||
this.data.filename = `SPOILER_${this.data.filename}`;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ export class AttachmentBuilder {
|
||||
* Gets whether the attachment is a spoiler.
|
||||
*/
|
||||
get spoiler(): boolean {
|
||||
return this.data.name?.startsWith('SPOILER_') ?? false;
|
||||
return this.data.filename?.startsWith('SPOILER_') ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,11 +127,11 @@ export function resolveAttachment(
|
||||
if ('id' in resolve) return resolve;
|
||||
|
||||
if (resolve instanceof AttachmentBuilder) {
|
||||
const data = resolve.toJSON();
|
||||
return { filename: data.name, description: data.description };
|
||||
const { filename, description } = resolve.toJSON();
|
||||
return { filename, description };
|
||||
}
|
||||
|
||||
return { filename: resolve.name, description: resolve.description };
|
||||
return { filename: resolve.filename, description: resolve.description };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,9 +143,9 @@ export async function resolveFiles(resources: (AttachmentBuilder | RawFile | Att
|
||||
const data = await Promise.all(
|
||||
resources.map(async (resource, i) => {
|
||||
if (resource instanceof AttachmentBuilder) {
|
||||
const { type, resolvable, name } = resource.toJSON();
|
||||
const { type, resolvable, filename } = resource.toJSON();
|
||||
const resolve = await resolveAttachmentData(resolvable, type);
|
||||
return { ...resolve, key: `files[${i}]`, name } as RawFile;
|
||||
return { ...resolve, key: `files[${i}]`, filename } as RawFile;
|
||||
}
|
||||
if (resource instanceof Attachment) {
|
||||
const resolve = await resolveAttachmentData(resource.url, 'url');
|
||||
@ -153,14 +153,14 @@ export async function resolveFiles(resources: (AttachmentBuilder | RawFile | Att
|
||||
data: resolve.data,
|
||||
contentType: resolve.contentType,
|
||||
key: `files[${i}]`,
|
||||
name: resource.filename,
|
||||
filename: resource.filename,
|
||||
} as RawFile;
|
||||
}
|
||||
return {
|
||||
data: resource.data,
|
||||
contentType: resource.contentType,
|
||||
key: `files[${i}]`,
|
||||
name: resource.name,
|
||||
filename: resource.filename,
|
||||
} as RawFile;
|
||||
}),
|
||||
);
|
||||
|
@ -43,6 +43,7 @@ import { LangsHandler } from '../langs/handler';
|
||||
import type {
|
||||
ChatInputCommandInteraction,
|
||||
ComponentInteraction,
|
||||
EntryPointInteraction,
|
||||
MessageCommandInteraction,
|
||||
ModalSubmitInteraction,
|
||||
UserCommandInteraction,
|
||||
@ -320,6 +321,11 @@ export class BaseClient {
|
||||
const commands = this.commands!.values;
|
||||
const filter = filterSplit(commands, command => !command.guildId);
|
||||
|
||||
if (this.commands?.entryPoint) {
|
||||
// @ts-expect-error
|
||||
filter.expect.push(this.commands.entryPoint);
|
||||
}
|
||||
|
||||
if (!cachePath || (await this.shouldUploadCommands(cachePath)))
|
||||
await this.proxy.applications(applicationId).commands.put({
|
||||
body: filter.expect
|
||||
@ -419,6 +425,7 @@ export interface BaseClientOptions {
|
||||
| MessageCommandInteraction<boolean>
|
||||
| ComponentInteraction
|
||||
| ModalSubmitInteraction
|
||||
| EntryPointInteraction<boolean>
|
||||
| When<InferWithPrefix, MessageStructure, never>,
|
||||
) => {};
|
||||
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
|
||||
|
@ -28,9 +28,9 @@ export class HttpClient extends BaseClient {
|
||||
for (const [index, file] of files.entries()) {
|
||||
const fileKey = file.key ?? `files[${index}]`;
|
||||
if (isBufferLike(file.data)) {
|
||||
response.append(fileKey, new Blob([file.data], { type: file.contentType }), file.name);
|
||||
response.append(fileKey, new Blob([file.data], { type: file.contentType }), file.filename);
|
||||
} else {
|
||||
response.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.name);
|
||||
response.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.filename);
|
||||
}
|
||||
}
|
||||
if (body) {
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
} from '../../types';
|
||||
import type {
|
||||
ComponentContext,
|
||||
EntryPointContext,
|
||||
MenuCommandContext,
|
||||
ModalContext,
|
||||
PermissionStrings,
|
||||
@ -201,7 +202,7 @@ export class BaseCommand {
|
||||
|
||||
/** @internal */
|
||||
static __runMiddlewares(
|
||||
context: CommandContext<{}, never> | ComponentContext | MenuCommandContext<any> | ModalContext,
|
||||
context: CommandContext<{}, never> | ComponentContext | MenuCommandContext<any> | ModalContext | EntryPointContext,
|
||||
middlewares: (keyof RegisteredMiddlewares)[],
|
||||
global: boolean,
|
||||
): Promise<{ error?: string; pass?: boolean }> {
|
||||
|
71
src/commands/applications/entryPoint.ts
Normal file
71
src/commands/applications/entryPoint.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { magicImport, type PermissionStrings } from '../../common';
|
||||
import {
|
||||
ApplicationCommandType,
|
||||
type EntryPointCommandHandlerType,
|
||||
type ApplicationIntegrationType,
|
||||
type InteractionContextType,
|
||||
type LocaleString,
|
||||
} from '../../types';
|
||||
import type { RegisteredMiddlewares } from '../decorators';
|
||||
import type { EntryPointContext } from './entrycontext';
|
||||
import type { ExtraProps, UsingClient } from './shared';
|
||||
|
||||
export abstract class EntryPointCommand {
|
||||
middlewares: (keyof RegisteredMiddlewares)[] = [];
|
||||
|
||||
__filePath?: string;
|
||||
__t?: { name: string | undefined; description: string | undefined };
|
||||
|
||||
name!: string;
|
||||
type = ApplicationCommandType.PrimaryEntryPoint;
|
||||
nsfw?: boolean;
|
||||
integrationTypes: ApplicationIntegrationType[] = [];
|
||||
contexts: InteractionContextType[] = [];
|
||||
description!: string;
|
||||
botPermissions?: bigint;
|
||||
dm?: boolean;
|
||||
handler!: EntryPointCommandHandlerType;
|
||||
name_localizations?: Partial<Record<LocaleString, string>>;
|
||||
description_localizations?: Partial<Record<LocaleString, string>>;
|
||||
|
||||
props: ExtraProps = {};
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
handler: this.handler,
|
||||
name: this.name,
|
||||
type: this.type,
|
||||
nsfw: this.nsfw,
|
||||
default_member_permissions: null,
|
||||
guild_id: null,
|
||||
description: this.description,
|
||||
name_localizations: this.name_localizations,
|
||||
description_localizations: this.description_localizations,
|
||||
dm_permission: this.dm,
|
||||
contexts: this.contexts,
|
||||
integration_types: this.integrationTypes,
|
||||
};
|
||||
}
|
||||
|
||||
async reload() {
|
||||
delete require.cache[this.__filePath!];
|
||||
const __tempCommand = await magicImport(this.__filePath!).then(x => x.default ?? x);
|
||||
|
||||
Object.setPrototypeOf(this, __tempCommand.prototype);
|
||||
}
|
||||
|
||||
abstract run?(context: EntryPointContext): any;
|
||||
onAfterRun?(context: EntryPointContext, error: unknown | undefined): any;
|
||||
onRunError(context: EntryPointContext<never>, error: unknown): any {
|
||||
context.client.logger.fatal(`${this.name}.<onRunError>`, context.author.id, error);
|
||||
}
|
||||
onMiddlewaresError(context: EntryPointContext<never>, error: string): any {
|
||||
context.client.logger.fatal(`${this.name}.<onMiddlewaresError>`, context.author.id, error);
|
||||
}
|
||||
onBotPermissionsFail(context: EntryPointContext<never>, permissions: PermissionStrings): any {
|
||||
context.client.logger.fatal(`${this.name}.<onBotPermissionsFail>`, context.author.id, permissions);
|
||||
}
|
||||
onInternalError(client: UsingClient, command: EntryPointCommand, error?: unknown): any {
|
||||
client.logger.fatal(command.name, error);
|
||||
}
|
||||
}
|
130
src/commands/applications/entrycontext.ts
Normal file
130
src/commands/applications/entrycontext.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { MessageFlags } from '../../types';
|
||||
import type { ReturnCache } from '../..';
|
||||
import type {
|
||||
InteractionCreateBodyRequest,
|
||||
InteractionMessageUpdateBodyRequest,
|
||||
ModalCreateBodyRequest,
|
||||
UnionToTuple,
|
||||
When,
|
||||
} from '../../common';
|
||||
import type { AllChannels, EntryPointInteraction } from '../../structures';
|
||||
import { BaseContext } from '../basecontext';
|
||||
import type { RegisteredMiddlewares } from '../decorators';
|
||||
import type { CommandMetadata, ExtendContext, GlobalMetadata, UsingClient } from './shared';
|
||||
import type {
|
||||
GuildMemberStructure,
|
||||
GuildStructure,
|
||||
MessageStructure,
|
||||
WebhookMessageStructure,
|
||||
} from '../../client/transformers';
|
||||
import type { EntryPointCommand } from './entryPoint';
|
||||
|
||||
export interface EntryPointContext<M extends keyof RegisteredMiddlewares = never> extends BaseContext, ExtendContext {}
|
||||
|
||||
export class EntryPointContext<M extends keyof RegisteredMiddlewares = never> extends BaseContext {
|
||||
constructor(
|
||||
readonly client: UsingClient,
|
||||
readonly interaction: EntryPointInteraction,
|
||||
readonly shardId: number,
|
||||
readonly command: EntryPointCommand,
|
||||
) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
metadata: CommandMetadata<UnionToTuple<M>> = {} as never;
|
||||
globalMetadata: GlobalMetadata = {};
|
||||
|
||||
get t() {
|
||||
return this.client.t(this.interaction.locale ?? this.client.langs!.defaultLang ?? 'en-US');
|
||||
}
|
||||
|
||||
get fullCommandName() {
|
||||
return this.command.name;
|
||||
}
|
||||
|
||||
write<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessageStructure, void | WebhookMessageStructure>> {
|
||||
return this.interaction.write(body, fetchReply);
|
||||
}
|
||||
|
||||
modal(body: ModalCreateBodyRequest) {
|
||||
return this.interaction.modal(body);
|
||||
}
|
||||
|
||||
deferReply(ephemeral = false) {
|
||||
return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined);
|
||||
}
|
||||
|
||||
editResponse(body: InteractionMessageUpdateBodyRequest) {
|
||||
return this.interaction.editResponse(body);
|
||||
}
|
||||
|
||||
deleteResponse() {
|
||||
return this.interaction.deleteResponse();
|
||||
}
|
||||
|
||||
editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
|
||||
}
|
||||
|
||||
fetchResponse() {
|
||||
return this.interaction.fetchResponse();
|
||||
}
|
||||
|
||||
channel(mode?: 'rest' | 'flow'): Promise<AllChannels>;
|
||||
channel(mode?: 'cache'): ReturnCache<AllChannels>;
|
||||
channel(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (this.interaction?.channel && mode === 'cache')
|
||||
return this.client.cache.adapter.isAsync ? Promise.resolve(this.interaction.channel) : this.interaction.channel;
|
||||
return this.client.channels.fetch(this.channelId, mode === 'rest');
|
||||
}
|
||||
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMemberStructure>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMemberStructure | undefined>;
|
||||
me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
|
||||
switch (mode) {
|
||||
case 'cache':
|
||||
return this.client.cache.members?.get(this.client.botId, this.guildId);
|
||||
default:
|
||||
return this.client.members.fetch(this.guildId, this.client.botId, mode === 'rest');
|
||||
}
|
||||
}
|
||||
|
||||
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
|
||||
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return (
|
||||
mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve()
|
||||
) as any;
|
||||
switch (mode) {
|
||||
case 'cache':
|
||||
return this.client.cache.guilds?.get(this.guildId);
|
||||
default:
|
||||
return this.client.guilds.fetch(this.guildId, mode === 'rest');
|
||||
}
|
||||
}
|
||||
|
||||
get guildId() {
|
||||
return this.interaction.guildId;
|
||||
}
|
||||
|
||||
get channelId() {
|
||||
return this.interaction.channelId!;
|
||||
}
|
||||
|
||||
get author() {
|
||||
return this.interaction.user;
|
||||
}
|
||||
|
||||
get member() {
|
||||
return this.interaction.member;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
ApplicationCommandType,
|
||||
ApplicationIntegrationType,
|
||||
type EntryPointCommandHandlerType,
|
||||
InteractionContextType,
|
||||
PermissionFlagsBits,
|
||||
type LocaleString,
|
||||
@ -11,36 +12,28 @@ import type { DefaultLocale, ExtraProps, IgnoreCommand, MiddlewareContext } from
|
||||
|
||||
export interface RegisteredMiddlewares {}
|
||||
|
||||
type DeclareOptions =
|
||||
| {
|
||||
name: string;
|
||||
description: string;
|
||||
botPermissions?: PermissionStrings | bigint;
|
||||
defaultMemberPermissions?: PermissionStrings | bigint;
|
||||
guildId?: string[];
|
||||
nsfw?: boolean;
|
||||
integrationTypes?: (keyof typeof ApplicationIntegrationType)[];
|
||||
contexts?: (keyof typeof InteractionContextType)[];
|
||||
ignore?: IgnoreCommand;
|
||||
aliases?: string[];
|
||||
props?: ExtraProps;
|
||||
}
|
||||
| (Omit<
|
||||
{
|
||||
name: string;
|
||||
description: string;
|
||||
botPermissions?: PermissionStrings | bigint;
|
||||
defaultMemberPermissions?: PermissionStrings | bigint;
|
||||
guildId?: string[];
|
||||
nsfw?: boolean;
|
||||
integrationTypes?: (keyof typeof ApplicationIntegrationType)[];
|
||||
contexts?: (keyof typeof InteractionContextType)[];
|
||||
props?: ExtraProps;
|
||||
},
|
||||
'type' | 'description'
|
||||
> & {
|
||||
export type CommandDeclareOptions =
|
||||
| DecoratorDeclareOptions
|
||||
| (Omit<DecoratorDeclareOptions, 'description'> & {
|
||||
type: ApplicationCommandType.User | ApplicationCommandType.Message;
|
||||
})
|
||||
| (Omit<DecoratorDeclareOptions, 'ignore' | 'aliases' | 'guildId'> & {
|
||||
type: ApplicationCommandType.PrimaryEntryPoint;
|
||||
handler: EntryPointCommandHandlerType;
|
||||
});
|
||||
export interface DecoratorDeclareOptions {
|
||||
name: string;
|
||||
description: string;
|
||||
botPermissions?: PermissionStrings | bigint;
|
||||
defaultMemberPermissions?: PermissionStrings | bigint;
|
||||
guildId?: string[];
|
||||
nsfw?: boolean;
|
||||
integrationTypes?: (keyof typeof ApplicationIntegrationType)[];
|
||||
contexts?: (keyof typeof InteractionContextType)[];
|
||||
ignore?: IgnoreCommand;
|
||||
aliases?: string[];
|
||||
props?: ExtraProps;
|
||||
}
|
||||
|
||||
export function Locales({
|
||||
name: names,
|
||||
@ -154,7 +147,7 @@ export function Middlewares(cbs: readonly (keyof RegisteredMiddlewares)[]) {
|
||||
};
|
||||
}
|
||||
|
||||
export function Declare(declare: DeclareOptions) {
|
||||
export function Declare(declare: CommandDeclareOptions) {
|
||||
return <T extends { new (...args: any[]): {} }>(target: T) =>
|
||||
class extends target {
|
||||
name = declare.name;
|
||||
@ -177,6 +170,7 @@ export function Declare(declare: DeclareOptions) {
|
||||
guildId?: string[];
|
||||
ignore?: IgnoreCommand;
|
||||
aliases?: string[];
|
||||
handler?: EntryPointCommandHandlerType;
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
if ('description' in declare) this.description = declare.description;
|
||||
@ -184,6 +178,7 @@ export function Declare(declare: DeclareOptions) {
|
||||
if ('guildId' in declare) this.guildId = declare.guildId;
|
||||
if ('ignore' in declare) this.ignore = declare.ignore;
|
||||
if ('aliases' in declare) this.aliases = declare.aliases;
|
||||
if ('handler' in declare) this.handler = declare.handler;
|
||||
// check if all properties are valid
|
||||
}
|
||||
};
|
||||
|
@ -28,6 +28,8 @@ import {
|
||||
type SeyfertIntegerOption,
|
||||
type SeyfertNumberOption,
|
||||
type SeyfertStringOption,
|
||||
EntryPointContext,
|
||||
type EntryPointCommand,
|
||||
} from '.';
|
||||
import {
|
||||
AutocompleteInteraction,
|
||||
@ -38,6 +40,7 @@ import {
|
||||
type MessageCommandInteraction,
|
||||
type UserCommandInteraction,
|
||||
type __InternalReplyFunction,
|
||||
type EntryPointInteraction,
|
||||
} from '../structures';
|
||||
import type { PermissionsBitField } from '../structures/extra/Permissions';
|
||||
import { ComponentContext, ModalContext } from '../components';
|
||||
@ -132,6 +135,34 @@ export class HandleCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async entryPoint(command: EntryPointCommand, interaction: EntryPointInteraction, context: EntryPointContext) {
|
||||
if (command.botPermissions && interaction.appPermissions) {
|
||||
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
|
||||
if (permissions) return command.onBotPermissionsFail?.(context, permissions);
|
||||
}
|
||||
|
||||
const resultGlobal = await this.runGlobalMiddlewares(command, context);
|
||||
if (typeof resultGlobal === 'boolean') return;
|
||||
const resultMiddle = await this.runMiddlewares(command, context);
|
||||
if (typeof resultMiddle === 'boolean') return;
|
||||
|
||||
try {
|
||||
try {
|
||||
await command.run!(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError(this.client, command, error);
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async chatInput(
|
||||
command: Command | SubCommand,
|
||||
interaction: ChatInputCommandInteraction,
|
||||
@ -214,6 +245,16 @@ export class HandleCommand {
|
||||
this.contextMenuUser(data.command, data.interaction, data.context);
|
||||
break;
|
||||
}
|
||||
case ApplicationCommandType.PrimaryEntryPoint: {
|
||||
const command = this.client.commands?.entryPoint;
|
||||
if (!command?.run) return;
|
||||
const interaction = BaseInteraction.from(this.client, body, __reply) as EntryPointInteraction;
|
||||
const context = new EntryPointContext(this.client, interaction, shardId, command);
|
||||
const extendContext = this.client.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
await this.entryPoint(command, interaction, context);
|
||||
break;
|
||||
}
|
||||
case ApplicationCommandType.ChatInput: {
|
||||
const parentCommand = this.getCommand<Command>(body.data);
|
||||
const optionsResolver = this.makeResolver(
|
||||
@ -442,7 +483,7 @@ export class HandleCommand {
|
||||
);
|
||||
}
|
||||
|
||||
getCommand<T extends Command | ContextMenuCommand>(data: {
|
||||
getCommand<T extends Command | ContextMenuCommand | EntryPointCommand>(data: {
|
||||
guild_id?: string;
|
||||
name: string;
|
||||
}): T | undefined {
|
||||
@ -487,8 +528,8 @@ export class HandleCommand {
|
||||
}
|
||||
|
||||
async runGlobalMiddlewares(
|
||||
command: Command | ContextMenuCommand | SubCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any>,
|
||||
command: Command | ContextMenuCommand | SubCommand | EntryPointCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any> | EntryPointContext,
|
||||
) {
|
||||
try {
|
||||
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
@ -513,8 +554,8 @@ export class HandleCommand {
|
||||
}
|
||||
|
||||
async runMiddlewares(
|
||||
command: Command | ContextMenuCommand | SubCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any>,
|
||||
command: Command | ContextMenuCommand | SubCommand | EntryPointCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any> | EntryPointContext,
|
||||
) {
|
||||
try {
|
||||
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
|
@ -17,9 +17,11 @@ import { Command, type CommandOption, SubCommand } from './applications/chat';
|
||||
import { ContextMenuCommand } from './applications/menu';
|
||||
import type { UsingClient } from './applications/shared';
|
||||
import { promises } from 'node:fs';
|
||||
import type { EntryPointCommand } from '.';
|
||||
|
||||
export class CommandHandler extends BaseHandler {
|
||||
values: (Command | ContextMenuCommand)[] = [];
|
||||
entryPoint: EntryPointCommand | null = null;
|
||||
|
||||
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts'));
|
||||
|
||||
@ -290,15 +292,17 @@ export class CommandHandler extends BaseHandler {
|
||||
}
|
||||
}
|
||||
this.stablishContextCommandDefaults(commandInstance);
|
||||
this.values.push(commandInstance);
|
||||
this.parseLocales(commandInstance);
|
||||
if ('handler' in commandInstance) {
|
||||
this.entryPoint = commandInstance as EntryPointCommand;
|
||||
} else this.values.push(commandInstance);
|
||||
}
|
||||
}
|
||||
|
||||
return this.values;
|
||||
}
|
||||
|
||||
parseLocales(command: Command | SubCommand | ContextMenuCommand) {
|
||||
parseLocales(command: InstanceType<HandleableCommand>) {
|
||||
this.parseGlobalLocales(command);
|
||||
if (command instanceof ContextMenuCommand) {
|
||||
this.parseContextMenuLocales(command);
|
||||
@ -322,7 +326,7 @@ export class CommandHandler extends BaseHandler {
|
||||
return command;
|
||||
}
|
||||
|
||||
parseGlobalLocales(command: Command | SubCommand | ContextMenuCommand) {
|
||||
parseGlobalLocales(command: InstanceType<HandleableCommand>) {
|
||||
if (command.__t) {
|
||||
command.name_localizations = {};
|
||||
command.description_localizations = {};
|
||||
@ -488,7 +492,7 @@ export class CommandHandler extends BaseHandler {
|
||||
return file.default ? [file.default] : undefined;
|
||||
}
|
||||
|
||||
onCommand(file: HandleableCommand): Command | SubCommand | ContextMenuCommand | false {
|
||||
onCommand(file: HandleableCommand): InstanceType<HandleableCommand> | false {
|
||||
return new file();
|
||||
}
|
||||
|
||||
@ -501,6 +505,6 @@ export type FileLoaded<T = null> = {
|
||||
default?: NulleableCoalising<T, HandleableCommand>;
|
||||
} & Record<string, NulleableCoalising<T, HandleableCommand>>;
|
||||
|
||||
export type HandleableCommand = new () => Command | SubCommand | ContextMenuCommand;
|
||||
export type HandleableCommand = new () => Command | SubCommand | ContextMenuCommand | EntryPointCommand;
|
||||
export type SeteableCommand = new () => Extract<InstanceType<HandleableCommand>, SubCommand>;
|
||||
export type HandleableSubCommand = new () => SubCommand;
|
||||
|
@ -5,5 +5,7 @@ export * from './applications/chatcontext';
|
||||
export * from './applications/menu';
|
||||
export * from './applications/menucontext';
|
||||
export * from './applications/options';
|
||||
export * from './applications/entryPoint';
|
||||
export * from './applications/entrycontext';
|
||||
export * from './decorators';
|
||||
export * from './optionresolver';
|
||||
|
@ -36,23 +36,28 @@ import {
|
||||
type MessageFlags,
|
||||
type RESTPostAPIInteractionCallbackJSONBody,
|
||||
type RESTAPIAttachment,
|
||||
type APIEntryPointCommandInteraction,
|
||||
type InteractionCallbackData,
|
||||
type InteractionCallbackResourceActivity,
|
||||
type RESTPostAPIInteractionCallbackResult,
|
||||
} from '../types';
|
||||
|
||||
import type { RawFile } from '../api';
|
||||
import { ActionRow, Embed, Modal, PollBuilder, resolveAttachment, resolveFiles } from '../builders';
|
||||
import type { ContextOptionsResolved, UsingClient } from '../commands';
|
||||
import type {
|
||||
ObjectToLower,
|
||||
OmitInsert,
|
||||
ToClass,
|
||||
When,
|
||||
ComponentInteractionMessageUpdate,
|
||||
InteractionCreateBodyRequest,
|
||||
InteractionMessageUpdateBodyRequest,
|
||||
MessageCreateBodyRequest,
|
||||
MessageUpdateBodyRequest,
|
||||
MessageWebhookCreateBodyRequest,
|
||||
ModalCreateBodyRequest,
|
||||
import {
|
||||
type ObjectToLower,
|
||||
type OmitInsert,
|
||||
type ToClass,
|
||||
type When,
|
||||
type ComponentInteractionMessageUpdate,
|
||||
type InteractionCreateBodyRequest,
|
||||
type InteractionMessageUpdateBodyRequest,
|
||||
type MessageCreateBodyRequest,
|
||||
type MessageUpdateBodyRequest,
|
||||
type MessageWebhookCreateBodyRequest,
|
||||
type ModalCreateBodyRequest,
|
||||
toCamelCase,
|
||||
} from '../common';
|
||||
import { channelFrom, type AllChannels } from './';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
@ -75,6 +80,7 @@ export type ReplyInteractionBody =
|
||||
type: InteractionResponseType.ChannelMessageWithSource | InteractionResponseType.UpdateMessage;
|
||||
data: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest | ComponentInteractionMessageUpdate;
|
||||
}
|
||||
| { type: InteractionResponseType.LaunchActivity }
|
||||
| Exclude<RESTPostAPIInteractionCallbackJSONBody, APIInteractionResponsePong>;
|
||||
|
||||
export type __InternalReplyFunction = (_: { body: APIInteractionResponse; files?: RawFile[] }) => Promise<any>;
|
||||
@ -161,6 +167,8 @@ export class BaseInteraction<
|
||||
: [],
|
||||
},
|
||||
};
|
||||
case InteractionResponseType.LaunchActivity:
|
||||
return body;
|
||||
default:
|
||||
return body;
|
||||
}
|
||||
@ -168,6 +176,7 @@ export class BaseInteraction<
|
||||
|
||||
static transformBody<T>(
|
||||
body:
|
||||
| InteractionCreateBodyRequest
|
||||
| InteractionMessageUpdateBodyRequest
|
||||
| MessageUpdateBodyRequest
|
||||
| MessageCreateBodyRequest
|
||||
@ -192,9 +201,9 @@ export class BaseInteraction<
|
||||
...resolveAttachment(x),
|
||||
})) ?? undefined;
|
||||
} else if (files?.length) {
|
||||
payload.attachments = files?.map((x, id) => ({
|
||||
payload.attachments = files?.map(({ filename }, id) => ({
|
||||
id,
|
||||
filename: x.name,
|
||||
filename,
|
||||
})) as RESTAPIAttachment[];
|
||||
}
|
||||
return payload as T;
|
||||
@ -279,6 +288,10 @@ export class BaseInteraction<
|
||||
return false;
|
||||
}
|
||||
|
||||
isEntryPoint(): this is EntryPointInteraction {
|
||||
return false;
|
||||
}
|
||||
|
||||
static from(client: UsingClient, gateway: GatewayInteractionCreateDispatchData, __reply?: __InternalReplyFunction) {
|
||||
switch (gateway.type) {
|
||||
case InteractionType.ApplicationCommandAutocomplete:
|
||||
@ -296,6 +309,8 @@ export class BaseInteraction<
|
||||
return new UserCommandInteraction(client, gateway as APIUserApplicationCommandInteraction, __reply);
|
||||
case ApplicationCommandType.Message:
|
||||
return new MessageCommandInteraction(client, gateway as APIMessageApplicationCommandInteraction, __reply);
|
||||
case ApplicationCommandType.PrimaryEntryPoint:
|
||||
return new EntryPointInteraction(client, gateway as APIEntryPointCommandInteraction, __reply);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noFallthroughSwitchClause: bad interaction between biome and ts-server
|
||||
case InteractionType.MessageComponent:
|
||||
@ -345,6 +360,7 @@ export type AllInteractions =
|
||||
| ComponentInteraction
|
||||
| SelectMenuInteraction
|
||||
| ModalSubmitInteraction
|
||||
| EntryPointInteraction
|
||||
| BaseInteraction;
|
||||
|
||||
export interface AutocompleteInteraction
|
||||
@ -478,6 +494,64 @@ export class ApplicationCommandInteraction<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seyfert don't support activities, so this interaction is blank
|
||||
*/
|
||||
export class EntryPointInteraction<FromGuild extends boolean = boolean> extends ApplicationCommandInteraction<
|
||||
FromGuild,
|
||||
APIEntryPointCommandInteraction
|
||||
> {
|
||||
async withReponse(data?: InteractionCreateBodyRequest) {
|
||||
let body = { type: InteractionResponseType.LaunchActivity } as const;
|
||||
|
||||
if (data) {
|
||||
let { files, ...rest } = data;
|
||||
files = files ? await resolveFiles(files) : undefined;
|
||||
body = BaseInteraction.transformBody(rest, files, this.client);
|
||||
}
|
||||
const response = (await this.client.proxy
|
||||
.interactions(this.id)(this.token)
|
||||
.callback.post({
|
||||
body,
|
||||
query: { with_response: true },
|
||||
})) as RESTPostAPIInteractionCallbackResult;
|
||||
|
||||
const result: Partial<EntryPointWithResponseResult> = {
|
||||
interaction: toCamelCase(response.interaction),
|
||||
};
|
||||
|
||||
if (response.resource) {
|
||||
if (response.resource.type !== InteractionResponseType.LaunchActivity) {
|
||||
result.resource = {
|
||||
type: response.resource.type,
|
||||
message: Transformers.WebhookMessage(this.client, response.resource.message as any, this.id, this.token),
|
||||
};
|
||||
} else {
|
||||
result.resource = {
|
||||
type: response.resource.type,
|
||||
activityInstance: response.resource.activity_instance!,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result as EntryPointWithResponseResult;
|
||||
}
|
||||
|
||||
isEntryPoint(): this is EntryPointInteraction {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface EntryPointWithResponseResult {
|
||||
interaction: ObjectToLower<InteractionCallbackData>;
|
||||
resource?:
|
||||
| { type: InteractionResponseType.LaunchActivity; activityInstance: InteractionCallbackResourceActivity }
|
||||
| {
|
||||
type: Exclude<InteractionResponseType, InteractionResponseType.LaunchActivity>;
|
||||
message: WebhookMessageStructure;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ComponentInteraction
|
||||
extends ObjectToLower<
|
||||
Omit<
|
||||
|
@ -294,9 +294,9 @@ export class MessagesMethods extends DiscordBase {
|
||||
...resolveAttachment(x),
|
||||
})) ?? undefined;
|
||||
} else if (files?.length) {
|
||||
payload.attachments = files?.map((x, id) => ({
|
||||
payload.attachments = files?.map(({ filename }, id) => ({
|
||||
id,
|
||||
filename: x.name,
|
||||
filename,
|
||||
})) as RESTAPIAttachment[];
|
||||
}
|
||||
return payload as T;
|
||||
|
@ -1,5 +1,9 @@
|
||||
import type { APIInteractionDataResolved } from '../../index';
|
||||
import type { APIApplicationCommandInteractionWrapper, ApplicationCommandType } from '../applicationCommands';
|
||||
import type {
|
||||
APIApplicationCommandInteractionWrapper,
|
||||
APIEntryPointInteractionData,
|
||||
ApplicationCommandType,
|
||||
} from '../applicationCommands';
|
||||
import type { APIDMInteractionWrapper, APIGuildInteractionWrapper } from '../base';
|
||||
import type {
|
||||
APIApplicationCommandAttachmentOption,
|
||||
@ -128,3 +132,9 @@ export type APIChatInputApplicationCommandDMInteraction =
|
||||
*/
|
||||
export type APIChatInputApplicationCommandGuildInteraction =
|
||||
APIGuildInteractionWrapper<APIChatInputApplicationCommandInteraction>;
|
||||
|
||||
/**
|
||||
* Documentation goes brrrrrr
|
||||
* @unstable
|
||||
*/
|
||||
export type APIEntryPointCommandInteraction = APIApplicationCommandInteractionWrapper<APIEntryPointInteractionData>;
|
||||
|
@ -5,6 +5,7 @@ import type {
|
||||
APIChatInputApplicationCommandGuildInteraction,
|
||||
APIChatInputApplicationCommandInteraction,
|
||||
APIChatInputApplicationCommandInteractionData,
|
||||
APIEntryPointCommandInteraction,
|
||||
} from './_applicationCommands/chatInput';
|
||||
import type {
|
||||
APIContextMenuDMInteraction,
|
||||
@ -12,6 +13,7 @@ import type {
|
||||
APIContextMenuInteraction,
|
||||
APIContextMenuInteractionData,
|
||||
} from './_applicationCommands/contextMenu';
|
||||
import type { APIBaseApplicationCommandInteractionData } from './_applicationCommands/internals';
|
||||
import type { APIBaseInteraction } from './base';
|
||||
import type { InteractionType } from './responses';
|
||||
|
||||
@ -92,28 +94,58 @@ export interface APIApplicationCommand {
|
||||
/**
|
||||
* Installation context(s) where the command is available, only for globally-scoped commands. Defaults to `GUILD_INSTALL ([0])`
|
||||
*
|
||||
* @unstable
|
||||
*/
|
||||
integration_types?: ApplicationIntegrationType[];
|
||||
/**
|
||||
* Interaction context(s) where the command can be used, only for globally-scoped commands. By default, all interaction context types included for new commands `[0,1,2]`.
|
||||
*
|
||||
* @unstable
|
||||
*/
|
||||
contexts?: InteractionContextType[] | null;
|
||||
/**
|
||||
* Autoincrementing version identifier updated during substantial record changes
|
||||
*/
|
||||
version: Snowflake;
|
||||
|
||||
/**
|
||||
* Determines whether the interaction is handled by the app's interactions handler or by Discord
|
||||
*/
|
||||
handler?: EntryPointCommandHandlerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types
|
||||
*/
|
||||
export enum ApplicationCommandType {
|
||||
/**
|
||||
* Slash commands; a text-based command that shows up when a user types /
|
||||
*/
|
||||
ChatInput = 1,
|
||||
/**
|
||||
* A UI-based command that shows up when you right click or tap on a user
|
||||
*/
|
||||
User,
|
||||
/**
|
||||
* A UI-based command that shows up when you right click or tap on a message
|
||||
*/
|
||||
Message,
|
||||
/**
|
||||
* A UI-based command that represents the primary way to invoke an app's Activity
|
||||
*/
|
||||
PrimaryEntryPoint,
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#application-command-object-entry-point-command-handler-types
|
||||
*/
|
||||
export enum EntryPointCommandHandlerType {
|
||||
/**
|
||||
* The app handles the interaction using an interaction token
|
||||
*/
|
||||
AppHandler = 1,
|
||||
/**
|
||||
* Discord handles the interaction by launching an Activity and sending a follow-up message without coordinating with the app
|
||||
*/
|
||||
DiscordLaunchActivity,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,12 +180,20 @@ export enum InteractionContextType {
|
||||
PrivateChannel = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation goes brrrrrr
|
||||
* @unstable
|
||||
*/
|
||||
export interface APIEntryPointInteractionData
|
||||
extends Omit<APIBaseApplicationCommandInteractionData<ApplicationCommandType.PrimaryEntryPoint>, 'guild_id'> {}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data
|
||||
*/
|
||||
export type APIApplicationCommandInteractionData =
|
||||
| APIChatInputApplicationCommandInteractionData
|
||||
| APIContextMenuInteractionData;
|
||||
| APIContextMenuInteractionData
|
||||
| APIEntryPointInteractionData;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object
|
||||
@ -170,7 +210,10 @@ export type APIApplicationCommandInteractionWrapper<Data extends APIApplicationC
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object
|
||||
*/
|
||||
export type APIApplicationCommandInteraction = APIChatInputApplicationCommandInteraction | APIContextMenuInteraction;
|
||||
export type APIApplicationCommandInteraction =
|
||||
| APIChatInputApplicationCommandInteraction
|
||||
| APIContextMenuInteraction
|
||||
| APIEntryPointCommandInteraction;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { MakeRequired } from '../../../common';
|
||||
import type { RESTPostAPIWebhookWithTokenJSONBody } from '../../index';
|
||||
import type { APIActionRowComponent, APIModalActionRowComponent } from '../channel';
|
||||
import type { MessageFlags } from '../index';
|
||||
@ -25,6 +26,7 @@ export type APIInteractionResponse =
|
||||
| APIInteractionResponsePong
|
||||
| APIInteractionResponseUpdateMessage
|
||||
| APIModalInteractionResponse
|
||||
| APIInteractionResponseLaunchActivity
|
||||
| APIPremiumRequiredInteractionResponse;
|
||||
|
||||
export interface APIInteractionResponsePong {
|
||||
@ -64,6 +66,10 @@ export interface APIInteractionResponseUpdateMessage {
|
||||
data?: APIInteractionResponseCallbackData;
|
||||
}
|
||||
|
||||
export interface APIInteractionResponseLaunchActivity {
|
||||
type: InteractionResponseType.LaunchActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type
|
||||
*/
|
||||
@ -102,6 +108,10 @@ export enum InteractionResponseType {
|
||||
* @deprecated See https://discord.com/developers/docs/change-log#premium-apps-new-premium-button-style-deep-linking-url-schemes
|
||||
*/
|
||||
PremiumRequired,
|
||||
/**
|
||||
* Launch the Activity associated with the app. Only available for apps with Activities enabled
|
||||
*/
|
||||
LaunchActivity = 12,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,3 +143,71 @@ export interface APIModalInteractionResponseCallbackData {
|
||||
*/
|
||||
components: APIActionRowComponent<APIModalActionRowComponent>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-object
|
||||
*/
|
||||
export interface InteractionCallbackData<T extends InteractionType = InteractionType> {
|
||||
id: string;
|
||||
type: T;
|
||||
/**
|
||||
* Instance ID of the Activity if one was launched or joined
|
||||
*/
|
||||
activity_instance_id?: string;
|
||||
/**
|
||||
* ID of the message that was created by the interaction
|
||||
*/
|
||||
response_message_id?: string;
|
||||
/**
|
||||
* Whether or not the message is in a loading state
|
||||
*/
|
||||
response_message_loading?: boolean;
|
||||
/**
|
||||
* Whether or not the response message was ephemeral
|
||||
*/
|
||||
response_message_ephemeral?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-resource-object
|
||||
*/
|
||||
export interface InteractionCallbackResourceActivity {
|
||||
/**
|
||||
* Instance ID of the Activity if one was launched or joined.
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource
|
||||
*/
|
||||
export interface InteractionCallbackResource<T extends InteractionResponseType = InteractionResponseType> {
|
||||
type: T;
|
||||
/**
|
||||
* Represents the Activity launched by this interaction.
|
||||
*/
|
||||
activity_instance?: InteractionCallbackResourceActivity;
|
||||
/**
|
||||
* Message created by the interaction.
|
||||
*/
|
||||
message?: Omit<RESTPostAPIWebhookWithTokenJSONBody, 'avatar_url' | 'username'> & { flags?: MessageFlags };
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object
|
||||
*/
|
||||
export interface InteractionCallbackResponse {
|
||||
interaction: InteractionCallbackData;
|
||||
resource?: InteractionCallbackResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object
|
||||
*/
|
||||
export type APIInteractionCallbackLaunchActivity = InteractionCallbackResponse & {
|
||||
resource?: Omit<MakeRequired<InteractionCallbackResource, 'activity_instance'>, 'message'>;
|
||||
};
|
||||
|
||||
export type APIInteractionCallbackMessage = InteractionCallbackResponse & {
|
||||
resource?: Omit<MakeRequired<InteractionCallbackResource, 'message'>, 'activity_instance'>;
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ import type {
|
||||
ThreadChannelType,
|
||||
APIThreadMember,
|
||||
APIThreadList,
|
||||
APIAttachment,
|
||||
} from '../payloads';
|
||||
import type { AddUndefinedToPossiblyUndefinedPropertiesOfInterface, StrictPartial } from '../utils';
|
||||
import type { RESTAPIPollCreate } from './poll';
|
||||
@ -248,22 +249,11 @@ export type APIMessageReferenceSend = AddUndefinedToPossiblyUndefinedPropertiesO
|
||||
};
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/resources/channel#attachment-object
|
||||
* https://discord.com/developers/docs/resources/message#attachment-object
|
||||
*/
|
||||
export interface RESTAPIAttachment {
|
||||
/**
|
||||
* Attachment id or a number that matches `n` in `files[n]`
|
||||
*/
|
||||
id: Snowflake | number;
|
||||
/**
|
||||
* Name of the file
|
||||
*/
|
||||
filename?: string | undefined;
|
||||
/**
|
||||
* Description of the file
|
||||
*/
|
||||
description?: string | undefined;
|
||||
}
|
||||
export type RESTAPIAttachment = Partial<
|
||||
Pick<APIAttachment, 'description' | 'duration_secs' | 'filename' | 'title' | 'waveform'>
|
||||
>;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/resources/channel#create-message
|
||||
@ -444,7 +434,7 @@ export interface RESTPatchAPIChannelMessageJSONBody {
|
||||
*
|
||||
* Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body.
|
||||
*
|
||||
* See https://discord.com/developers/docs/resources/channel#attachment-object
|
||||
* See https://discord.com/developers/docs/resources/message#attachment-object
|
||||
*/
|
||||
attachments?: RESTAPIAttachment[] | undefined;
|
||||
/**
|
||||
|
@ -2,9 +2,12 @@ import type {
|
||||
APIApplicationCommand,
|
||||
APIApplicationCommandPermission,
|
||||
APIGuildApplicationCommandPermissions,
|
||||
APIInteractionCallbackLaunchActivity,
|
||||
APIInteractionCallbackMessage,
|
||||
APIInteractionResponse,
|
||||
APIInteractionResponseCallbackData,
|
||||
ApplicationCommandType,
|
||||
EntryPointCommandHandlerType,
|
||||
} from '../payloads';
|
||||
import type { AddUndefinedToPossiblyUndefinedPropertiesOfInterface, NonNullableFields, StrictPartial } from '../utils';
|
||||
import type {
|
||||
@ -53,6 +56,7 @@ type RESTPostAPIBaseApplicationCommandsJSONBody = AddUndefinedToPossiblyUndefine
|
||||
| 'name_localized'
|
||||
| 'type'
|
||||
| 'version'
|
||||
| 'handler'
|
||||
> &
|
||||
Partial<
|
||||
NonNullableFields<Pick<APIApplicationCommand, 'contexts'>> &
|
||||
@ -75,12 +79,22 @@ export interface RESTPostAPIContextMenuApplicationCommandsJSONBody extends RESTP
|
||||
type: ApplicationCommandType.Message | ApplicationCommandType.User;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#create-global-application-command
|
||||
*/
|
||||
export interface RESTPostAPIEntryPointApplicationCommandsJSONBody extends RESTPostAPIBaseApplicationCommandsJSONBody {
|
||||
type: ApplicationCommandType.PrimaryEntryPoint;
|
||||
description: string;
|
||||
handler: EntryPointCommandHandlerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#create-global-application-command
|
||||
*/
|
||||
export type RESTPostAPIApplicationCommandsJSONBody =
|
||||
| RESTPostAPIChatInputApplicationCommandsJSONBody
|
||||
| RESTPostAPIContextMenuApplicationCommandsJSONBody;
|
||||
| RESTPostAPIContextMenuApplicationCommandsJSONBody
|
||||
| RESTPostAPIEntryPointApplicationCommandsJSONBody;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#create-global-application-command
|
||||
@ -112,15 +126,17 @@ export type RESTPutAPIApplicationCommandsResult = APIApplicationCommand[];
|
||||
*/
|
||||
export type RESTGetAPIApplicationGuildCommandsQuery = RESTGetAPIApplicationCommandsQuery;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands
|
||||
*/
|
||||
export type RESTGetAPIApplicationGuildCommandsResult = Omit<APIApplicationCommand, 'dm_permission'>[];
|
||||
export type RESTAPIApplicationGuildCommand = Omit<APIApplicationCommand, 'dm_permission' | 'handler'>;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands
|
||||
*/
|
||||
export type RESTGetAPIApplicationGuildCommandResult = Omit<APIApplicationCommand, 'dm_permission'>;
|
||||
export type RESTGetAPIApplicationGuildCommandsResult = RESTAPIApplicationGuildCommand[];
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands
|
||||
*/
|
||||
export type RESTGetAPIApplicationGuildCommandResult = RESTAPIApplicationGuildCommand;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command
|
||||
@ -132,7 +148,7 @@ export type RESTPostAPIApplicationGuildCommandsJSONBody =
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command
|
||||
*/
|
||||
export type RESTPostAPIApplicationGuildCommandsResult = Omit<APIApplicationCommand, 'dm_permission'>;
|
||||
export type RESTPostAPIApplicationGuildCommandsResult = RESTAPIApplicationGuildCommand;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command
|
||||
@ -145,7 +161,7 @@ export type RESTPatchAPIApplicationGuildCommandJSONBody = StrictPartial<
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command
|
||||
*/
|
||||
export type RESTPatchAPIApplicationGuildCommandResult = Omit<APIApplicationCommand, 'dm_permission'>;
|
||||
export type RESTPatchAPIApplicationGuildCommandResult = RESTAPIApplicationGuildCommand;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands
|
||||
@ -160,13 +176,28 @@ export type RESTPutAPIApplicationGuildCommandsJSONBody = (
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands
|
||||
*/
|
||||
export type RESTPutAPIApplicationGuildCommandsResult = Omit<APIApplicationCommand, 'dm_permission'>[];
|
||||
export type RESTPutAPIApplicationGuildCommandsResult = RESTAPIApplicationGuildCommand[];
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response
|
||||
*/
|
||||
export type RESTPostAPIInteractionCallbackJSONBody = APIInteractionResponse;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response-query-string-params
|
||||
*/
|
||||
export type RESTPostAPIInteractionCallbackQuery = {
|
||||
/**
|
||||
* Whether to include a RESTPostAPIInteractionCallbackResult as the response instead of a 204.
|
||||
*/
|
||||
with_response?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback
|
||||
*/
|
||||
export type RESTPostAPIInteractionCallbackResult = APIInteractionCallbackLaunchActivity | APIInteractionCallbackMessage;
|
||||
|
||||
/**
|
||||
* https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response
|
||||
*/
|
||||
|
@ -264,7 +264,7 @@ export type RESTPatchAPIWebhookWithTokenMessageJSONBody = AddUndefinedToPossibly
|
||||
*
|
||||
* Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body.
|
||||
*
|
||||
* See https://discord.com/developers/docs/resources/channel#attachment-object
|
||||
* See https://discord.com/developers/docs/resources/message#attachment-object
|
||||
*/
|
||||
attachments?: RESTAPIAttachment[] | undefined;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user