feat: allow interaction server for all clients (#295)

* fix: default callbacks & cache update

* feat: allow interaction server for all clients
This commit is contained in:
MARCROCK22 2024-11-14 17:37:29 -04:00 committed by GitHub
parent 7f4044469b
commit 961bff0e27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 63 additions and 63 deletions

View File

@ -40,6 +40,7 @@ import {
} from '../common'; } from '../common';
import { promises } from 'node:fs'; import { promises } from 'node:fs';
import { isBufferLike } from '../api/utils/utils';
import { HandleCommand } from '../commands/handle'; import { HandleCommand } from '../commands/handle';
import { BanShorter } from '../common/shorters/bans'; import { BanShorter } from '../common/shorters/bans';
import { VoiceStateShorter } from '../common/shorters/voiceStates'; import { VoiceStateShorter } from '../common/shorters/voiceStates';
@ -55,7 +56,7 @@ import type {
ModalSubmitInteraction, ModalSubmitInteraction,
UserCommandInteraction, UserCommandInteraction,
} from '../structures'; } from '../structures';
import type { LocaleString, RESTPostAPIChannelMessageJSONBody } from '../types'; import type { APIInteraction, APIInteractionResponse, LocaleString, RESTPostAPIChannelMessageJSONBody } from '../types';
import type { MessageStructure } from './transformers'; import type { MessageStructure } from './transformers';
export class BaseClient { export class BaseClient {
@ -253,20 +254,17 @@ export class BaseClient {
} }
async start( async start(
options: Pick<DeepPartial<StartOptions>, 'langsDir' | 'commandsDir' | 'connection' | 'token' | 'componentsDir'> = { options: Pick<
token: undefined, DeepPartial<StartOptions>,
langsDir: undefined, 'langsDir' | 'commandsDir' | 'connection' | 'token' | 'componentsDir'
commandsDir: undefined, > = {},
connection: undefined,
componentsDir: undefined,
},
) { ) {
await this.loadLangs(options.langsDir); await this.loadLangs(options.langsDir);
await this.loadCommands(options.commandsDir); await this.loadCommands(options.commandsDir);
await this.loadComponents(options.componentsDir); await this.loadComponents(options.componentsDir);
const { token: tokenRC, debug } = await this.getRC(); const { token: tokenRC, debug } = await this.getRC();
const token = options?.token ?? tokenRC; const token = options.token ?? tokenRC;
BaseClient.assertString(token, 'token is not a string'); BaseClient.assertString(token, 'token is not a string');
if (this.rest.options.token === 'INVALID') this.rest.options.token = token; if (this.rest.options.token === 'INVALID') this.rest.options.token = token;
@ -283,17 +281,56 @@ export class BaseClient {
throw new Error('Function not implemented'); throw new Error('Function not implemented');
} }
private shouldUploadCommands(cachePath: string, guildId?: string) { /**
return this.commands!.shouldUpload(cachePath, guildId).then(should => { *
this.logger.debug( * @param rawBody body of interaction
should * @returns
? `[${guildId ?? 'global'}] Change(s) detected, uploading commands` */
: `[${guildId ?? 'global'}] commands seems to be up to date`, protected async onInteractionRequest(rawBody: APIInteraction): Promise<{
); headers: { 'Content-Type'?: string };
return should; response: APIInteractionResponse | FormData;
}> {
return new Promise(async r => {
await this.handleCommand.interaction(rawBody, -1, async ({ body, files }) => {
let response: FormData | APIInteractionResponse;
const headers: { 'Content-Type'?: string } = {};
if (files) {
response = new FormData();
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.filename);
} else {
response.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.filename);
}
}
if (body) {
response.append('payload_json', JSON.stringify(body));
}
} else {
response = body ?? {};
headers['Content-Type'] = 'application/json';
}
return r({
headers,
response,
});
});
}); });
} }
private async shouldUploadCommands(cachePath: string, guildId?: string) {
const should = await this.commands!.shouldUpload(cachePath, guildId);
this.logger.debug(
should
? `[${guildId ?? 'global'}] Change(s) detected, uploading commands`
: `[${guildId ?? 'global'}] commands seems to be up to date`,
);
return should;
}
private syncCachePath(cachePath: string) { private syncCachePath(cachePath: string) {
return promises.writeFile( return promises.writeFile(
cachePath, cachePath,
@ -431,7 +468,7 @@ export interface BaseClientOptions {
| ModalSubmitInteraction | ModalSubmitInteraction
| EntryPointInteraction<boolean> | EntryPointInteraction<boolean>
| When<InferWithPrefix, MessageStructure, never>, | When<InferWithPrefix, MessageStructure, never>,
) => object; ) => Record<string, unknown>;
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[]; globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
commands?: { commands?: {
defaults?: { defaults?: {
@ -516,7 +553,7 @@ export type RuntimeConfigHTTP = Omit<MakeRequired<RC, 'publicKey' | 'application
locations: Omit<RC['locations'], 'events'>; locations: Omit<RC['locations'], 'events'>;
}; };
export type InternalRuntimeConfig = Omit<MakeRequired<RC, 'intents'>, 'publicKey' | 'port'>; export type InternalRuntimeConfig = MakeRequired<RC, 'intents'>;
export type RuntimeConfig = OmitInsert< export type RuntimeConfig = OmitInsert<
InternalRuntimeConfig, InternalRuntimeConfig,
'intents', 'intents',

View File

@ -1,6 +1,4 @@
import { isBufferLike } from '../api/utils/utils';
import type { DeepPartial } from '../common'; import type { DeepPartial } from '../common';
import type { APIInteraction, APIInteractionResponse } from '../types';
import type { BaseClientOptions, StartOptions } from './base'; import type { BaseClientOptions, StartOptions } from './base';
import { BaseClient } from './base'; import { BaseClient } from './base';
@ -13,39 +11,4 @@ export class HttpClient extends BaseClient {
await super.start(options); await super.start(options);
return this.execute(options.httpConnection ?? {}); return this.execute(options.httpConnection ?? {});
} }
async onPacket(rawBody: APIInteraction): Promise<{
headers: { 'Content-Type'?: string };
response: APIInteractionResponse | FormData;
}> {
return new Promise(async r => {
await this.handleCommand.interaction(rawBody, -1, async ({ body, files }) => {
let response: FormData | APIInteractionResponse;
const headers: { 'Content-Type'?: string } = {};
if (files) {
response = new FormData();
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.filename);
} else {
response.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.filename);
}
}
if (body) {
response.append('payload_json', JSON.stringify(body));
}
} else {
response = body ?? {};
headers['Content-Type'] = 'application/json';
}
return r({
headers,
response,
});
});
});
}
} }

View File

@ -1,6 +1,6 @@
import type { HttpClient } from './httpclient'; import type { UsingClient } from '../commands';
export interface HttpServerAdapter { export interface HttpServerAdapter {
client: HttpClient; client: UsingClient;
start?(path: `/${string}`): any; start?(path: `/${string}`): any;
} }

View File

@ -224,7 +224,7 @@ export class CommandContext<
} }
export interface GuildCommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never> export interface GuildCommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
extends Omit<MakeRequired<CommandContext<T, M>, 'guildId'>, 'guild'> { extends Omit<MakeRequired<CommandContext<T, M>, 'guildId' | 'member'>, 'guild'> {
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>; guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
} }

View File

@ -130,7 +130,7 @@ export class EntryPointContext<M extends keyof RegisteredMiddlewares = never> ex
} }
export interface GuildEntryPointContext<M extends keyof RegisteredMiddlewares = never> export interface GuildEntryPointContext<M extends keyof RegisteredMiddlewares = never>
extends Omit<MakeRequired<EntryPointContext<M>, 'guildId'>, 'guild'> { extends Omit<MakeRequired<EntryPointContext<M>, 'guildId' | 'member'>, 'guild'> {
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>; guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
} }

View File

@ -169,7 +169,7 @@ export class MenuCommandContext<
export interface GuildMenuCommandContext< export interface GuildMenuCommandContext<
T extends MessageCommandInteraction | UserCommandInteraction, T extends MessageCommandInteraction | UserCommandInteraction,
M extends keyof RegisteredMiddlewares = never, M extends keyof RegisteredMiddlewares = never,
> extends Omit<MakeRequired<MenuCommandContext<T, M>, 'guildId'>, 'guild'> { > extends Omit<MakeRequired<MenuCommandContext<T, M>, 'guildId' | 'member'>, 'guild'> {
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>; guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
} }

View File

@ -260,7 +260,7 @@ export interface ContextComponentCommandInteractionMap {
export interface GuildComponentContext< export interface GuildComponentContext<
Type extends keyof ContextComponentCommandInteractionMap, Type extends keyof ContextComponentCommandInteractionMap,
M extends keyof RegisteredMiddlewares = never, M extends keyof RegisteredMiddlewares = never,
> extends Omit<MakeRequired<ComponentContext<Type, M>, 'guildId'>, 'guild'> { > extends Omit<MakeRequired<ComponentContext<Type, M>, 'guildId' | 'member'>, 'guild'> {
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>; guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
} }

View File

@ -196,7 +196,7 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
} }
export interface GuildModalContext<M extends keyof RegisteredMiddlewares = never> export interface GuildModalContext<M extends keyof RegisteredMiddlewares = never>
extends Omit<MakeRequired<ModalContext<M>, 'guildId'>, 'guild'> { extends Omit<MakeRequired<ModalContext<M>, 'guildId' | 'member'>, 'guild'> {
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>; guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
} }