starting to add slash commands

This commit is contained in:
Jaron Pate
2022-09-20 19:32:41 -05:00
parent a346d56f9b
commit 230aee97fa
10 changed files with 276 additions and 47 deletions
+2 -2
View File
@@ -10,8 +10,8 @@
"/dist"
],
"scripts": {
"start": "npx ts-node-dev src/index.ts",
"start:watch": "npx ts-node-dev --respawn src/index.ts",
"start": "npx ts-node test/main.test.ts",
"start:watch": "npx ts-node-dev --respawn test/main.test.ts",
"build": "npx tsc"
},
"devDependencies": {
+27 -2
View File
@@ -1,6 +1,6 @@
import { ComponentStyle, Context, Gateway, Guilds, Message } from './lib';
import { ComponentStyle, Context, Gateway, Guilds, Message, Embed } from './lib';
import { Command, CommandFuntion, Intent, Presence } from './types';
export { ComponentStyle, Context, Gateway, Guilds, Message, Command, CommandFuntion, Intent, Presence };
export { ComponentStyle, Context, Gateway, Guilds, Message, Embed, Command, CommandFuntion, Intent, Presence };
type Config = {
token: string;
@@ -15,6 +15,7 @@ export class Client {
readonly application_id: string;
private readonly gateway: Gateway;
private readonly client_commands: Map<string, Command | CommandFuntion> = new Map();
private readonly slash_commands: Map<string, Command | CommandFuntion> = new Map();
readonly guilds: Guilds;
constructor({ token, application_id, prefix, intents }: Config) {
@@ -23,6 +24,8 @@ export class Client {
this.prefix = prefix ?? '!';
this.gateway = new Gateway({ token, intents, client: this });
this.guilds = new Guilds({ token, gateway: this.gateway });
// Do some default stuff
this.initialize();
}
get user() {
@@ -84,6 +87,28 @@ export class Client {
},
delete: (command_name: string) => {
this.client_commands.delete(command_name);
},
list: () => {
return this.client_commands;
}
};
private initialize = () => {
// Register default help command
this.commands.register('help', (client, context, _args) => {
const embed = new Embed().setTitle('Commands').setDescription('This is a list of commands you can use.');
const commands = Array.from(client.commands.list().entries());
for (const [name, command] of commands) {
embed.addField({
name: name,
value: command instanceof Command ? command.description : 'No description provided'
});
}
context.reply(embed.toMessage());
});
}
}
+136
View File
@@ -0,0 +1,136 @@
import { Message } from '.';
type EmbedFooter = {
text: string;
icon_url?: string;
proxy_icon_url?: string;
};
type EmbedImage = {
url?: string;
proxy_url?: string;
height?: number;
width?: number;
};
type EmbedVideo = {
url?: string;
height?: number;
width?: number;
};
type EmbedField = {
name: string;
value: string;
inline?: boolean;
};
export class Embed {
title?: string;
description?: string;
url?: string;
timestamp?: string;
color?: number;
footer?: EmbedFooter;
image?: EmbedImage;
thumbnail?: EmbedImage;
video?: EmbedVideo;
provider?: {
name?: string;
url?: string;
};
author?: EmbedImage & {
name?: string;
};
fields?: EmbedField[];
constructor() {
this.fields = [];
}
setTitle = (title: string) => {
this.title = title;
return this;
};
setDescription = (description: string) => {
this.description = description;
return this;
};
setUrl = (url: string) => {
this.url = url;
return this;
};
setTimestamp = (timestamp: string) => {
this.timestamp = timestamp;
return this;
};
setColor = (color: number) => {
this.color = color;
return this;
};
setFooter = (footer: EmbedFooter) => {
this.footer = footer;
return this;
};
setImage = (image: EmbedImage) => {
this.image = image;
return this;
};
setThumbnail = (thumbnail: EmbedImage) => {
this.thumbnail = thumbnail;
return this;
};
setVideo = (video: EmbedVideo) => {
this.video = video;
return this;
};
setProvider = (provider: { name?: string; url?: string }) => {
this.provider = provider;
return this;
};
setAuthor = (author: EmbedImage & { name?: string }) => {
this.author = author;
return this;
};
addField = (field: EmbedField) => {
this.fields.push(field);
return this;
};
addFields = (fields: EmbedField[]) => {
this.fields.push(...fields);
return this;
};
toMessage = () => {
return new Message().addEmbed(this);
};
toJSON = () => {
return {
title: this.title,
description: this.description,
url: this.url,
timestamp: this.timestamp,
color: this.color,
footer: this.footer,
image: this.image,
thumbnail: this.thumbnail,
video: this.video,
provider: this.provider,
author: this.author,
fields: this.fields
};
};
}
+11 -1
View File
@@ -237,6 +237,16 @@ export class Gateway {
};
private handleInteractionCreate = (payload: Payload) => {
if (payload.d.type === 2) {
this.handleShashInteraction(payload);
} else {
this.handleChatInteraction(payload);
}
};
private handleShashInteraction = (payload: Payload) => {};
private handleChatInteraction = (payload: Payload) => {
const context = new InteractionContext({ client: this.client, interaction: payload.d });
const handlers = this.client.interactions.get(payload.d.data.custom_id);
if (handlers) {
@@ -322,7 +332,7 @@ export class Gateway {
}
if (data) {
console.log('received: %s', data);
console.log('received: %s', data.d);
console.log('received data: %s', data.d);
if (this.session && data.s && data.s > this.session.s) {
this.session.s = data.s;
} else {
+1
View File
@@ -3,4 +3,5 @@ export * from './cache';
export * from './guilds';
export * from './context';
export * from './message';
export * from './embed';
export * from './interaction';
+17 -17
View File
@@ -5,9 +5,11 @@ import { Message } from "./message";
export class InteractionContext {
public client: Client;
public interaction: Record<string, any>;
constructor({ client, interaction }: { client: Client; interaction: Record<string, any> }) {
private is_loading: boolean;
constructor({ client, interaction, is_loading = false }: { client: Client; interaction: Record<string, any>, is_loading?: boolean }) {
this.client = client;
this.interaction = interaction;
this.is_loading = is_loading;
}
ack = (type: InteractionResponseType = InteractionResponseType.Pong, data?: Record<string, any>) => {
@@ -30,22 +32,20 @@ export class InteractionContext {
if (typeof message === 'string') {
message = new Message().setContent(message);
}
return this.ack(InteractionResponseType.UpdateMessage, {
...message
});
};
editOriginal = (message: Message | string) => {
if (typeof message === 'string') {
message = new Message().setContent(message);
}
return this.client.api.patch(
`/webhooks/${this.client.application_id}/${this.interaction.token}/messages/@original`,
{
type: InteractionResponseType.DeferredUpdateMessage,
if (!this.is_loading) {
return this.ack(InteractionResponseType.UpdateMessage, {
...message
}
);
});
} else {
return this.client.api.patch(
`/webhooks/${this.client.application_id}/${this.interaction.token}/messages/@original`,
{
type: InteractionResponseType.DeferredUpdateMessage,
...message
}
);
}
};
deleteOriginal = () => {
@@ -56,6 +56,6 @@ export class InteractionContext {
loading = async () => {
await this.ack(InteractionResponseType.DeferredReply);
return new InteractionContext({ client: this.client, interaction: this.interaction });
return new InteractionContext({ client: this.client, interaction: this.interaction, is_loading: true });
};
}
+16 -18
View File
@@ -1,6 +1,4 @@
export type Embed = {
}
import { Embed } from '.';
export type Component = Partial<Button | SelectMenu> & {
type: ComponentType;
@@ -35,16 +33,16 @@ export type Emoji = {
name: string | null;
id: string | null;
animated?: boolean;
}
};
export type Button = {
style: ComponentStyle;
label: string;
emoji?: Emoji
emoji?: Emoji;
custom_id?: string;
url?: string;
disabled?: boolean;
}
};
export type SelectMenu = {
custom_id: string;
@@ -52,13 +50,13 @@ export type SelectMenu = {
label: string;
value: string;
description?: string;
emoji?: Emoji
emoji?: Emoji;
default?: boolean;
}[];
placeholder?: string;
min_values?: number;
max_values?: number;
}
};
export enum ComponentStyle {
Primary = 1,
@@ -87,7 +85,7 @@ export class Message {
channel_id?: string;
guild_id?: string;
fail_if_not_exists?: boolean;
}
};
constructor() {}
@@ -95,25 +93,25 @@ export class Message {
return {
type: ComponentType.Button,
...config
}
};
}
public static selectMenu(config: SelectMenu): Component {
return {
type: ComponentType.SelectMenu,
...config
}
};
}
public setContent = (content: string) => {
this.content = content;
return this;
}
};
public addEmbed = (embed: Embed) => {
this.embeds.push(embed);
return this;
}
};
public addComponent = (content: Component | string, ...components: Component[]) => {
if (typeof content === 'string') {
@@ -130,7 +128,7 @@ export class Message {
});
}
return this;
}
};
public setReference = (message: Partial<Message>) => {
this.message_reference = {
@@ -138,9 +136,9 @@ export class Message {
channel_id: message.channel_id,
guild_id: message.guild_id,
fail_if_not_exists: false
}
};
return this;
}
};
public toJSON = () => {
return {
@@ -148,6 +146,6 @@ export class Message {
embeds: this.embeds,
components: this.components,
message_reference: this.message_reference
}
}
};
};
}
+3 -3
View File
@@ -2,9 +2,9 @@ import { Client } from "..";
import { Context } from "../lib";
export abstract class Command {
abstract name: string;
abstract description: string;
abstract execute: CommandFuntion;
name: string;
description: string;
execute: CommandFuntion;
}
export type CommandFuntion = (client: Client, context: Context, ...args: any[]) => void;
+55
View File
@@ -0,0 +1,55 @@
import { Client } from "..";
import { Context } from "../lib";
enum SlashCommandType {
ChatInput = 1,
User,
Message
}
enum SlashCommandOptionType {
SubCommand = 1,
SubCommandGroup,
String,
Integer,
Boolean,
User,
Channel,
Role,
Mentionable,
Number,
ATTACHMENT
}
type SlashCommandOptionChoice = {
name: string;
value: string | number;
}
type SlashCommandOption = {
type: SlashCommandOptionType;
name: string;
description: string;
required?: boolean;
choices?: SlashCommandOptionChoice[];
options?: SlashCommandOption[];
min_value?: number;
max_value?: number;
min_length?: number;
max_length?: number;
autocomplete?: boolean;
// channel_types?: number[];
}
export abstract class SlashCommand {
type: SlashCommandType;
name: string;
description: string;
options: SlashCommandOption[];
guild_id?: string;
dm_permission?: boolean;
default_member_permissions?: string;
execute: SlashCommandFuntion;
}
export type SlashCommandFuntion = (client: Client, context: Context, ...args: any[]) => void;
+6 -2
View File
@@ -13,6 +13,10 @@ bot.commands.register('ping', (client, context, args) => {
context.reply('Pong!', true);
});
bot.commands.register('help', (client, context, args) => {
context.reply('Help yourself.');
});
bot.commands.register('demo', (_client, context, _args) => {
const msg = new Message()
.setContent('Hello World!')
@@ -125,14 +129,14 @@ bot.handlers.register('READY', (client, context, data) => {
bot.interactions.register('never_me', async (client, context, interaction) => {
const reply = await context.loading();
setTimeout(() => {
reply.editOriginal(`You clicked me! Thank you ${interaction.member.user.username} <3`);
reply.edit(`You clicked me! Thank you ${interaction.member.user.username} <3`);
}, 1000);
});
bot.interactions.register('click_me', async (client, context, interaction) => {
const reply = await context.loading();
setTimeout(() => {
reply.editOriginal(
reply.edit(
new Message().addComponent(
Message.button({
label: 'Click me!',