mirror of
https://github.com/jaronpate/unicord.git
synced 2026-10-18 21:47:40 +00:00
starting to add slash commands
This commit is contained in:
+2
-2
@@ -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
@@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -3,4 +3,5 @@ export * from './cache';
|
||||
export * from './guilds';
|
||||
export * from './context';
|
||||
export * from './message';
|
||||
export * from './embed';
|
||||
export * from './interaction';
|
||||
|
||||
+17
-17
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
@@ -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!',
|
||||
|
||||
Reference in New Issue
Block a user