new select menus (#122)

This commit is contained in:
MARCROCK22 2022-11-05 11:40:28 -04:00 committed by GitHub
parent 6a5fce0f58
commit f6b745c726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 109 deletions

View File

@ -79,6 +79,14 @@ export enum MessageComponentTypes {
SelectMenu = 3,
/** A text input object */
InputText = 4,
/** A select menu for picking from users */
UserSelect = 5,
/** A select menu for picking from roles */
RoleSelect = 6,
/** A select menu for picking from users and roles */
MentionableSelect = 7,
/** A select menu for picking from channels */
ChannelSelect = 8
}
export enum TextStyles {
@ -1260,20 +1268,20 @@ export type MakeRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
// THANK YOU YUI FOR SHARING THIS!
export type CamelCase<S extends string> =
S extends `${infer P1}_${infer P2}${infer P3}`
? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
: Lowercase<S>;
? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
: Lowercase<S>;
export type Camelize<T> = {
// eslint-disable-next-line @typescript-eslint/array-type
[K in keyof T as CamelCase<string & K>]: T[K] extends Array<infer U>
? // eslint-disable-next-line @typescript-eslint/ban-types
U extends {}
? // eslint-disable-next-line @typescript-eslint/array-type
Array<Camelize<U>>
: T[K]
: // eslint-disable-next-line @typescript-eslint/ban-types
T[K] extends {}
? Camelize<T[K]>
: never;
? // eslint-disable-next-line @typescript-eslint/ban-types
U extends {}
? // eslint-disable-next-line @typescript-eslint/array-type
Array<Camelize<U>>
: T[K]
: // eslint-disable-next-line @typescript-eslint/ban-types
T[K] extends {}
? Camelize<T[K]>
: never;
};
export type PickPartial<T, K extends keyof T> = {

View File

@ -1187,7 +1187,11 @@ export interface DiscordActionRow {
}
export interface DiscordSelectMenuComponent {
type: MessageComponentTypes.SelectMenu;
type: MessageComponentTypes.SelectMenu |
MessageComponentTypes.RoleSelect |
MessageComponentTypes.UserSelect |
MessageComponentTypes.MentionableSelect |
MessageComponentTypes.ChannelSelect;
/** A custom identifier for this component. Maximum 100 characters. */
custom_id: string;
/** A custom placeholder text if nothing is selected. Maximum 150 characters. */
@ -2396,8 +2400,8 @@ export interface DiscordGuildMemberUpdate {
/** https://discord.com/developers/docs/topics/gateway#message-reaction-remove-all */
export interface DiscordMessageReactionRemoveAll
extends Pick<
DiscordMessageReactionAdd,
'channel_id' | 'message_id' | 'guild_id'
DiscordMessageReactionAdd,
'channel_id' | 'message_id' | 'guild_id'
> { }
// TODO: add docs link

View File

@ -223,6 +223,10 @@ export class ActionRow extends BaseComponent implements ActionRowComponent {
}
return new Button(session, component);
case MessageComponentTypes.SelectMenu:
case MessageComponentTypes.RoleSelect:
case MessageComponentTypes.UserSelect:
case MessageComponentTypes.MentionableSelect:
case MessageComponentTypes.ChannelSelect:
return new SelectMenu(session, component);
case MessageComponentTypes.InputText:
return new TextInput(
@ -257,6 +261,10 @@ export class ComponentFactory {
}
return new Button(session, component);
case MessageComponentTypes.SelectMenu:
case MessageComponentTypes.RoleSelect:
case MessageComponentTypes.UserSelect:
case MessageComponentTypes.MentionableSelect:
case MessageComponentTypes.ChannelSelect:
return new SelectMenu(session, component);
case MessageComponentTypes.InputText:
return new TextInput(

View File

@ -3,91 +3,101 @@ import type { ComponentEmoji } from '@biscuitland/core';
import { MessageComponentTypes } from '@biscuitland/api-types';
export class SelectMenuOptionBuilder {
constructor() {
this.#data = {} as DiscordSelectOption;
}
constructor() {
this.#data = {} as DiscordSelectOption;
}
#data: DiscordSelectOption;
#data: DiscordSelectOption;
setLabel(label: string): SelectMenuOptionBuilder {
this.#data.label = label;
return this;
}
setLabel(label: string): SelectMenuOptionBuilder {
this.#data.label = label;
return this;
}
setValue(value: string): SelectMenuOptionBuilder {
this.#data.value = value;
return this;
}
setValue(value: string): SelectMenuOptionBuilder {
this.#data.value = value;
return this;
}
setDescription(description: string): SelectMenuOptionBuilder {
this.#data.description = description;
return this;
}
setDescription(description: string): SelectMenuOptionBuilder {
this.#data.description = description;
return this;
}
setDefault(Default = true): SelectMenuOptionBuilder {
this.#data.default = Default;
return this;
}
setDefault(Default = true): SelectMenuOptionBuilder {
this.#data.default = Default;
return this;
}
setEmoji(emoji: ComponentEmoji): SelectMenuOptionBuilder {
this.#data.emoji = emoji;
return this;
}
setEmoji(emoji: ComponentEmoji): SelectMenuOptionBuilder {
this.#data.emoji = emoji;
return this;
}
toJSON(): DiscordSelectOption {
return { ...this.#data };
}
toJSON(): DiscordSelectOption {
return { ...this.#data };
}
}
export class SelectMenuBuilder {
constructor() {
this.#data = {} as DiscordSelectMenuComponent;
this.type = MessageComponentTypes.SelectMenu;
this.options = [];
}
constructor() {
this.#data = {} as DiscordSelectMenuComponent;
this.type = MessageComponentTypes.SelectMenu;
this.options = [];
}
#data: DiscordSelectMenuComponent;
type: MessageComponentTypes.SelectMenu;
options: SelectMenuOptionBuilder[];
#data: DiscordSelectMenuComponent;
type: MessageComponentTypes.SelectMenu |
MessageComponentTypes.RoleSelect |
MessageComponentTypes.UserSelect |
MessageComponentTypes.MentionableSelect |
MessageComponentTypes.ChannelSelect;
setPlaceholder(placeholder: string): this {
this.#data.placeholder = placeholder;
return this;
}
options: SelectMenuOptionBuilder[];
setValues(max?: number, min?: number): this {
this.#data.max_values = max;
this.#data.min_values = min;
return this;
}
setType(type: this['type']) {
this.type = type;
return this;
}
setDisabled(disabled = true): this {
this.#data.disabled = disabled;
return this;
}
setPlaceholder(placeholder: string): this {
this.#data.placeholder = placeholder;
return this;
}
setCustomId(id: string): this {
this.#data.custom_id = id;
return this;
}
setValues(max?: number, min?: number): this {
this.#data.max_values = max;
this.#data.min_values = min;
return this;
}
setOptions(...options: SelectMenuOptionBuilder[]): this {
this.options.splice(
0,
this.options.length,
...options,
);
return this;
}
setDisabled(disabled = true): this {
this.#data.disabled = disabled;
return this;
}
addOptions(...options: SelectMenuOptionBuilder[]): this {
this.options.push(
...options,
);
return this;
}
setCustomId(id: string): this {
this.#data.custom_id = id;
return this;
}
toJSON(): DiscordSelectMenuComponent {
return { ...this.#data, type: this.type, options: this.options.map(option => option.toJSON()) };
}
setOptions(...options: SelectMenuOptionBuilder[]): this {
this.options.splice(
0,
this.options.length,
...options,
);
return this;
}
addOptions(...options: SelectMenuOptionBuilder[]): this {
this.options.push(
...options,
);
return this;
}
toJSON(): DiscordSelectMenuComponent {
return { ...this.#data, type: this.type, options: this.options.map(option => option.toJSON()) };
}
}

View File

@ -73,7 +73,7 @@ export class DefaultRestAdapter implements RestAdapter {
private url: string;
constructor(options: DefaultRestOptions) {
this.options = Object.assign(Object.create(DefaultRestAdapter.DEFAULTS), options);
this.options = Object.assign({}, DefaultRestAdapter.DEFAULTS, options);
if (this.options.url) {
this.url = `${options.url}/v${this.options.version}`;

View File

@ -30,10 +30,10 @@ export class ShardManager {
}
>();
readonly shards = new Map<number, Shard>();
readonly shards = new Map<number, Shard>();
constructor(options: ShardManagerOptions) {
this.options = Options({}, ShardManager.DEFAULTS, options);
this.options = Options(ShardManager.DEFAULTS, options);
}
/** Invokes internal processing and respawns shards */
@ -59,11 +59,11 @@ export class ShardManager {
/** Create the start sequence of the shards inside the buckets. */
for (let i = 0; i < gateway.shards; i++) {
const bucketID = i % gateway.session_start_limit.max_concurrency;
const bucketID = i % gateway.session_start_limit.max_concurrency;
const bucket = this.buckets.get(bucketID);
if (bucket) {
const workerID = Math.floor(i / workers.shards);
const workerID = Math.floor(i / workers.shards);
const worker = bucket.workers.find(w => w.id === workerID);
if (worker) {
@ -75,22 +75,22 @@ export class ShardManager {
}
/** Route all shards to workers */
this.buckets.forEach(async bucket => {
for (const worker of bucket.workers) {
this.buckets.forEach(async bucket => {
for (const worker of bucket.workers) {
for (const id of worker.queue) {
await this.connect(id);
await this.connect(id);
}
}
});
}
});
}
/** Invokes the bucket to prepare the connection to the shard */
private async connect(id: number): Promise<Shard> {
const { gateway } = this.options;
let shard = this.shards.get(id);
let shard = this.shards.get(id);
if (!shard) {
shard = new Shard({
@ -107,8 +107,8 @@ export class ShardManager {
},
handleIdentify: async (id: number) => {
await this.buckets.get(id % gateway.session_start_limit.max_concurrency)!.leak.acquire(1); // remove await?
}
await this.buckets.get(id % gateway.session_start_limit.max_concurrency)!.leak.acquire(1); // remove await?
}
});
this.shards.set(id, shard);

View File

@ -27,7 +27,7 @@ export class Shard {
resumeURL: string | null = null;
sessionID: string | null = null;
sequence = 0 ;
sequence = 0;
resolves: Map<string, (payload?: unknown) => void> = new Map();
@ -40,7 +40,7 @@ export class Shard {
ws: WebSocket | null = null;
constructor(options: ShardOptions) {
this.options = Options({}, Shard.DEFAULTS, options);
this.options = Options(Shard.DEFAULTS, options);
this.bucket = createLeakyBucket({
max: 120,
@ -78,7 +78,7 @@ export class Shard {
this.heartbeatInterval = null;
}
connect() {
connect() {
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
return;
}
@ -101,7 +101,7 @@ export class Shard {
setTimeout(() => resolve(true), this.options.shards.timeout);
});
});
}
}
identify() {
this.status = 'Identifying';
@ -110,14 +110,14 @@ export class Shard {
op: GatewayOpcodes.Identify,
d: {
token: `Bot ${this.options.config.token}`,
compress: false,
properties: {
os: 'linux',
device: 'Biscuit',
browser: 'Biscuit'
},
intents: this.options.config.intents,
shard: [this.options.id, this.options.gateway.shards],
compress: false,
properties: {
os: 'linux',
device: 'Biscuit',
browser: 'Biscuit'
},
intents: this.options.config.intents,
shard: [this.options.id, this.options.gateway.shards],
}
});
}
@ -203,7 +203,7 @@ export class Shard {
}
private async onMessage(data: any, isBinary: boolean) {
const payload = this.pack(data as Buffer | ArrayBuffer, isBinary);
const payload = this.pack(data as Buffer | ArrayBuffer, isBinary);
if (payload.s != null) {
this.sequence = payload.s;