Skip to content

Commit

Permalink
feat: Entry Points (#256)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
socram03 and github-actions[bot] committed Aug 28, 2024
1 parent fc4c7ef commit a9d14c4
Show file tree
Hide file tree
Showing 22 changed files with 591 additions and 107 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/transpile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,3 @@ jobs:

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: npx tsc
14 changes: 12 additions & 2 deletions src/api/Routes/interactions.ts
Original file line number Diff line number Diff line change
@@ -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>;
};
};
}
4 changes: 2 additions & 2 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/api/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface RawFile {
contentType?: string;
data: Buffer | Uint8Array | boolean | number | string;
key?: string;
name: string;
filename: string;
}

export interface ApiRequestOptions {
Expand Down
26 changes: 13 additions & 13 deletions src/builders/Attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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` },
) {}

/**
Expand All @@ -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;
}

Expand Down Expand Up @@ -93,18 +93,18 @@ 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;
}

/**
* Gets whether the attachment is a spoiler.
*/
get spoiler(): boolean {
return this.data.name?.startsWith('SPOILER_') ?? false;
return this.data.filename?.startsWith('SPOILER_') ?? false;
}

/**
Expand All @@ -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 };
}

/**
Expand All @@ -143,24 +143,24 @@ 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');
return {
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;
}),
);
Expand Down
7 changes: 7 additions & 0 deletions src/client/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { LangsHandler } from '../langs/handler';
import type {
ChatInputCommandInteraction,
ComponentInteraction,
EntryPointInteraction,
MessageCommandInteraction,
ModalSubmitInteraction,
UserCommandInteraction,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -419,6 +425,7 @@ export interface BaseClientOptions {
| MessageCommandInteraction<boolean>
| ComponentInteraction
| ModalSubmitInteraction
| EntryPointInteraction<boolean>
| When<InferWithPrefix, MessageStructure, never>,
) => {};
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
Expand Down
4 changes: 2 additions & 2 deletions src/client/httpclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion src/commands/applications/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../../types';
import type {
ComponentContext,
EntryPointContext,
MenuCommandContext,
ModalContext,
PermissionStrings,
Expand Down Expand Up @@ -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 }> {
Expand Down
71 changes: 71 additions & 0 deletions src/commands/applications/entryPoint.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit a9d14c4

Please sign in to comment.