mirror of
https://github.com/jaronpate/unicord.git
synced 2026-10-18 21:47:40 +00:00
working application commands?
This commit is contained in:
+94
-31
@@ -1,6 +1,18 @@
|
|||||||
import { ComponentStyle, Context, Gateway, Guilds, Message, Embed } from './lib';
|
import { ComponentStyle, Context, Gateway, Guilds, Message, Embed } from './lib';
|
||||||
import { Command, CommandFuntion, Intent, Presence } from './types';
|
import { SlashCommand, SlashCommandFunction, ChatCommand, Command, CommandFunction, Intent, Presence } from './types';
|
||||||
export { ComponentStyle, Context, Gateway, Guilds, Message, Embed, Command, CommandFuntion, Intent, Presence };
|
export {
|
||||||
|
ComponentStyle,
|
||||||
|
Context,
|
||||||
|
Gateway,
|
||||||
|
Guilds,
|
||||||
|
Message,
|
||||||
|
Embed,
|
||||||
|
Command,
|
||||||
|
CommandFunction,
|
||||||
|
ChatCommand,
|
||||||
|
Intent,
|
||||||
|
Presence
|
||||||
|
};
|
||||||
|
|
||||||
type Config = {
|
type Config = {
|
||||||
token: string;
|
token: string;
|
||||||
@@ -14,8 +26,8 @@ export class Client {
|
|||||||
readonly token: string;
|
readonly token: string;
|
||||||
readonly application_id: string;
|
readonly application_id: string;
|
||||||
private readonly gateway: Gateway;
|
private readonly gateway: Gateway;
|
||||||
private readonly client_commands: Map<string, Command | CommandFuntion> = new Map();
|
private readonly client_commands: Map<string, ChatCommand | CommandFunction> = new Map();
|
||||||
private readonly slash_commands: Map<string, Command | CommandFuntion> = new Map();
|
private readonly slash_commands: Map<string, SlashCommand | SlashCommandFunction> = new Map();
|
||||||
readonly guilds: Guilds;
|
readonly guilds: Guilds;
|
||||||
|
|
||||||
constructor({ token, application_id, prefix, intents }: Config) {
|
constructor({ token, application_id, prefix, intents }: Config) {
|
||||||
@@ -67,48 +79,99 @@ export class Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public commands = {
|
public commands = {
|
||||||
register: (command: Command | string, handler: CommandFuntion) => {
|
chat: {
|
||||||
if (typeof command === 'string') {
|
register: (command: ChatCommand | string, handler?: CommandFunction) => {
|
||||||
this.client_commands.set(command, handler);
|
if (typeof command === 'string') {
|
||||||
|
this.client_commands.set(command, handler);
|
||||||
|
} else {
|
||||||
|
this.client_commands.set(command.name, command);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: (command_name: string) => {
|
||||||
|
return this.client_commands.get(command_name);
|
||||||
|
},
|
||||||
|
execute: (command_name: string, context: Context, ...args: any[]) => {
|
||||||
|
const command = this.client_commands.get(command_name);
|
||||||
|
if (command) {
|
||||||
|
command instanceof ChatCommand
|
||||||
|
? command.execute(this, context, args.slice(1))
|
||||||
|
: command(this, context, args.slice(1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: (command_name: string) => {
|
||||||
|
this.client_commands.delete(command_name);
|
||||||
|
},
|
||||||
|
list: () => {
|
||||||
|
return Array.from(this.client_commands.entries());
|
||||||
|
},
|
||||||
|
map: () => {
|
||||||
|
return this.client_commands;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
application: {
|
||||||
|
register: (command: SlashCommand | Record<string, string>, handler?: SlashCommandFunction) => {
|
||||||
|
const payload =
|
||||||
|
command instanceof SlashCommand
|
||||||
|
? command.toJSON()
|
||||||
|
: {
|
||||||
|
name: command.name,
|
||||||
|
description: command.description,
|
||||||
|
type: command.type ?? 1
|
||||||
|
};
|
||||||
|
this.api
|
||||||
|
.post(`/applications/${this.application_id}/commands`, payload)
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response);
|
||||||
|
if (command instanceof SlashCommand) {
|
||||||
|
this.slash_commands.set(command.name, command);
|
||||||
|
} else {
|
||||||
|
this.slash_commands.set(command.name, handler);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.log.error('Error registering application command', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
get: (command_name: string) => {
|
||||||
|
return this.slash_commands.get(command_name);
|
||||||
|
},
|
||||||
|
list: () => {
|
||||||
|
return Array.from(this.slash_commands.entries());
|
||||||
|
},
|
||||||
|
map: () => {
|
||||||
|
return this.slash_commands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private log = {
|
||||||
|
error: (error_or_message: Error | string, error?: Error) => {
|
||||||
|
if (error && typeof error_or_message === 'string') {
|
||||||
|
console.error(`[discord.ts] ${error_or_message}:`, error);
|
||||||
} else {
|
} else {
|
||||||
this.client_commands.set(command.name, command);
|
console.error('[discord.ts] Error:', error_or_message);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
get: (command_name: string) => {
|
|
||||||
return this.client_commands.get(command_name);
|
|
||||||
},
|
|
||||||
execute: (command_name: string, context: Context, ...args: any[]) => {
|
|
||||||
const command = this.client_commands.get(command_name);
|
|
||||||
if (command) {
|
|
||||||
command instanceof Command
|
|
||||||
? command.execute(this, context, args.slice(1))
|
|
||||||
: command(this, context, args.slice(1));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
delete: (command_name: string) => {
|
|
||||||
this.client_commands.delete(command_name);
|
|
||||||
},
|
|
||||||
list: () => {
|
|
||||||
return this.client_commands;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private initialize = () => {
|
private initialize = () => {
|
||||||
// Register default help command
|
// Register default help command
|
||||||
this.commands.register('help', (client, context, _args) => {
|
this.commands.chat.register('help', (client, context, _args) => {
|
||||||
const embed = new Embed().setTitle('Commands').setDescription('This is a list of commands you can use.');
|
const embed = new Embed()
|
||||||
|
.setTitle('Commands')
|
||||||
|
.setDescription('This is a list of commands you can use.')
|
||||||
|
.setColor('#f6c221');
|
||||||
|
|
||||||
const commands = Array.from(client.commands.list().entries());
|
const commands = client.commands.chat.list();
|
||||||
|
|
||||||
for (const [name, command] of commands) {
|
for (const [name, command] of commands) {
|
||||||
embed.addField({
|
embed.addField({
|
||||||
name: name,
|
name: name,
|
||||||
value: command instanceof Command ? command.description : 'No description provided'
|
value: command instanceof ChatCommand ? command.description : 'No description provided'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
context.reply(embed.toMessage());
|
context.reply(embed.toMessage());
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -68,8 +68,8 @@ export class Embed {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
setColor = (color: number) => {
|
setColor = (color: number | string) => {
|
||||||
this.color = color;
|
this.color = typeof color === 'string' ? parseInt(`0x${color.replace('#', '')}`) : color;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+36
-9
@@ -12,9 +12,10 @@ import {
|
|||||||
Status,
|
Status,
|
||||||
Presence,
|
Presence,
|
||||||
ActivityType,
|
ActivityType,
|
||||||
CommandFuntion,
|
CommandFunction,
|
||||||
Interaction,
|
Interaction,
|
||||||
InteractionFunction
|
InteractionFunction,
|
||||||
|
SlashCommand
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { Context } from './context';
|
import { Context } from './context';
|
||||||
import { InteractionContext } from './interaction';
|
import { InteractionContext } from './interaction';
|
||||||
@@ -26,9 +27,11 @@ export class Gateway {
|
|||||||
readonly token: string;
|
readonly token: string;
|
||||||
readonly client: Client;
|
readonly client: Client;
|
||||||
private intents: number = 513;
|
private intents: number = 513;
|
||||||
private commandNotFound: CommandFuntion = null;
|
private commandNotFound: CommandFunction = null;
|
||||||
// Discord gateway data
|
// Discord gateway data
|
||||||
private readonly url: string = 'wss://gateway.discord.gg/?v=10&encoding=json';
|
private readonly url: string = 'wss://gateway.discord.gg/?v=10&encoding=json';
|
||||||
|
private heartbeat_timeout: NodeJS.Timeout;
|
||||||
|
private heartbeat_function: NodeJS.Timer;
|
||||||
private heartbeat_interval: number | null = null;
|
private heartbeat_interval: number | null = null;
|
||||||
private session: Session = null;
|
private session: Session = null;
|
||||||
public user: User = null;
|
public user: User = null;
|
||||||
@@ -45,7 +48,7 @@ export class Gateway {
|
|||||||
token: string;
|
token: string;
|
||||||
intents?: Intent[];
|
intents?: Intent[];
|
||||||
client: Client;
|
client: Client;
|
||||||
commandNotFound?: CommandFuntion;
|
commandNotFound?: CommandFunction;
|
||||||
}) {
|
}) {
|
||||||
// Copy config
|
// Copy config
|
||||||
this.token = token;
|
this.token = token;
|
||||||
@@ -80,6 +83,7 @@ export class Gateway {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public reconnect = () => {
|
public reconnect = () => {
|
||||||
|
this.socket.terminate();
|
||||||
return this.connect();
|
return this.connect();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,12 +171,21 @@ export class Gateway {
|
|||||||
if (!this.heartbeat_interval) {
|
if (!this.heartbeat_interval) {
|
||||||
throw new Error('No heartbeat interval set');
|
throw new Error('No heartbeat interval set');
|
||||||
}
|
}
|
||||||
setInterval(() => {
|
if (this.heartbeat_function) {
|
||||||
|
clearInterval(this.heartbeat_function);
|
||||||
|
}
|
||||||
|
if (this.heartbeat_timeout) {
|
||||||
|
clearTimeout(this.heartbeat_timeout);
|
||||||
|
}
|
||||||
|
this.heartbeat_function = setInterval(() => {
|
||||||
this.send({
|
this.send({
|
||||||
op: 1,
|
op: 1,
|
||||||
d: {}
|
d: {}
|
||||||
});
|
});
|
||||||
}, this.heartbeat_interval);
|
}, this.heartbeat_interval);
|
||||||
|
this.heartbeat_timeout = setTimeout(() => {
|
||||||
|
this.reconnect();
|
||||||
|
}, this.heartbeat_interval * 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
private initialize = (payload: Payload) => {
|
private initialize = (payload: Payload) => {
|
||||||
@@ -244,7 +257,18 @@ export class Gateway {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleShashInteraction = (payload: Payload) => {};
|
private handleShashInteraction = (payload: Payload) => {
|
||||||
|
console.log(payload.d);
|
||||||
|
const context = new InteractionContext({ client: this.client, interaction: payload.d });
|
||||||
|
const handler = this.client.commands.application.get(payload.d.data.name);
|
||||||
|
if (handler) {
|
||||||
|
if (handler instanceof SlashCommand) {
|
||||||
|
handler.execute(this.client, context, payload.d);
|
||||||
|
} else {
|
||||||
|
handler(this.client, context, payload.d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private handleChatInteraction = (payload: Payload) => {
|
private handleChatInteraction = (payload: Payload) => {
|
||||||
const context = new InteractionContext({ client: this.client, interaction: payload.d });
|
const context = new InteractionContext({ client: this.client, interaction: payload.d });
|
||||||
@@ -266,15 +290,16 @@ export class Gateway {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(payload.d);
|
console.log(payload.d);
|
||||||
|
const content = payload.d.content.trim();
|
||||||
// Is this a command?
|
// Is this a command?
|
||||||
if (payload.d.content.startsWith(this.client.prefix)) {
|
if (content.startsWith(this.client.prefix) | content.startsWith(`<@${this.client.user.id}>`)) {
|
||||||
// Parse command
|
// Parse command
|
||||||
const args = payload.d.content
|
const args = content
|
||||||
.slice(this.client.prefix.length)
|
.slice(this.client.prefix.length)
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g);
|
.split(/\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g);
|
||||||
// Check if command exists
|
// Check if command exists
|
||||||
const handler = this.client.commands.get(args[0]);
|
const handler = this.client.commands.chat.get(args[0]);
|
||||||
// Generate context
|
// Generate context
|
||||||
const context = new Context({ client: this.client, message: payload.d });
|
const context = new Context({ client: this.client, message: payload.d });
|
||||||
if (handler) {
|
if (handler) {
|
||||||
@@ -317,6 +342,7 @@ export class Gateway {
|
|||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
console.log('heartbeat acknowledged');
|
console.log('heartbeat acknowledged');
|
||||||
|
this.heartbeat_timeout.refresh();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -324,6 +350,7 @@ export class Gateway {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private onMessage = (packet: Data): void => {
|
private onMessage = (packet: Data): void => {
|
||||||
|
console.log('============================');
|
||||||
let data: Payload | null = null;
|
let data: Payload | null = null;
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(packet.toString());
|
data = JSON.parse(packet.toString());
|
||||||
|
|||||||
+17
-6
@@ -1,10 +1,21 @@
|
|||||||
import { Client } from "..";
|
import { Client } from '..';
|
||||||
import { Context } from "../lib";
|
import { Context } from '../lib';
|
||||||
|
|
||||||
export abstract class Command {
|
export abstract class Command {
|
||||||
name: string;
|
readonly name: string;
|
||||||
description: string;
|
readonly description: string;
|
||||||
execute: CommandFuntion;
|
readonly execute: CommandFunction;
|
||||||
|
constructor(data: Command) {
|
||||||
|
this.name = data.name;
|
||||||
|
this.description = data.description;
|
||||||
|
this.execute = data.execute;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandFuntion = (client: Client, context: Context, ...args: any[]) => void;
|
export type CommandFunction = (client: Client, context: Context, ...args: any[]) => void;
|
||||||
|
|
||||||
|
export class ChatCommand extends Command {
|
||||||
|
constructor(data: ChatCommand) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './common';
|
export * from './common';
|
||||||
export * from './command';
|
export * from './command';
|
||||||
|
export * from './slash_command';
|
||||||
export * from './handler';
|
export * from './handler';
|
||||||
export * from './interaction';
|
export * from './interaction';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Client } from "..";
|
import { Client } from '..';
|
||||||
import { Context } from "../lib";
|
import { InteractionContext } from '../lib';
|
||||||
|
|
||||||
enum SlashCommandType {
|
enum SlashCommandType {
|
||||||
ChatInput = 1,
|
ChatInput = 1,
|
||||||
@@ -24,7 +24,7 @@ enum SlashCommandOptionType {
|
|||||||
type SlashCommandOptionChoice = {
|
type SlashCommandOptionChoice = {
|
||||||
name: string;
|
name: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
}
|
};
|
||||||
|
|
||||||
type SlashCommandOption = {
|
type SlashCommandOption = {
|
||||||
type: SlashCommandOptionType;
|
type: SlashCommandOptionType;
|
||||||
@@ -39,9 +39,9 @@ type SlashCommandOption = {
|
|||||||
max_length?: number;
|
max_length?: number;
|
||||||
autocomplete?: boolean;
|
autocomplete?: boolean;
|
||||||
// channel_types?: number[];
|
// channel_types?: number[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export abstract class SlashCommand {
|
export class SlashCommand {
|
||||||
type: SlashCommandType;
|
type: SlashCommandType;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -49,7 +49,30 @@ export abstract class SlashCommand {
|
|||||||
guild_id?: string;
|
guild_id?: string;
|
||||||
dm_permission?: boolean;
|
dm_permission?: boolean;
|
||||||
default_member_permissions?: string;
|
default_member_permissions?: string;
|
||||||
execute: SlashCommandFuntion;
|
execute: SlashCommandFunction;
|
||||||
|
|
||||||
|
constructor(data: SlashCommand) {
|
||||||
|
this.type = data.type;
|
||||||
|
this.name = data.name;
|
||||||
|
this.description = data.description;
|
||||||
|
this.options = data.options;
|
||||||
|
this.guild_id = data.guild_id;
|
||||||
|
this.dm_permission = data.dm_permission;
|
||||||
|
this.default_member_permissions = data.default_member_permissions;
|
||||||
|
this.execute = data.execute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return {
|
||||||
|
type: this.type,
|
||||||
|
name: this.name,
|
||||||
|
description: this.description,
|
||||||
|
options: this.options,
|
||||||
|
guild_id: this.guild_id,
|
||||||
|
dm_permission: this.dm_permission,
|
||||||
|
default_member_permissions: this.default_member_permissions
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SlashCommandFuntion = (client: Client, context: Context, ...args: any[]) => void;
|
export type SlashCommandFunction = (client: Client, context: InteractionContext, ...args: any[]) => void;
|
||||||
|
|||||||
+19
-8
@@ -1,7 +1,7 @@
|
|||||||
import * as dotenv from 'dotenv';
|
import * as dotenv from 'dotenv';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import { Client, ComponentStyle, Intent, Message } from '../src';
|
import { Client, ChatCommand, ComponentStyle, Intent, Message } from '../src';
|
||||||
|
|
||||||
const bot = new Client({
|
const bot = new Client({
|
||||||
token: process.env.BOT_TOKEN!,
|
token: process.env.BOT_TOKEN!,
|
||||||
@@ -9,15 +9,26 @@ const bot = new Client({
|
|||||||
intents: [Intent.GUILDS, Intent.GUILD_MESSAGES, Intent.GUILD_MESSAGE_REACTIONS, Intent.MESSAGE_CONTENT]
|
intents: [Intent.GUILDS, Intent.GUILD_MESSAGES, Intent.GUILD_MESSAGE_REACTIONS, Intent.MESSAGE_CONTENT]
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.commands.register('ping', (client, context, args) => {
|
bot.commands.chat.register(new ChatCommand({
|
||||||
context.reply('Pong!', true);
|
name: 'ping',
|
||||||
|
description: 'Check if he is alive',
|
||||||
|
execute: (client, context, args) => {
|
||||||
|
context.reply('Pong!', true);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
bot.commands.application.register({
|
||||||
|
name: 'ping',
|
||||||
|
description: 'Check if he is alive'
|
||||||
|
}, (client, context, args) => {
|
||||||
|
context.reply('Pong!');
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.commands.register('help', (client, context, args) => {
|
// bot.commands.register('help', (client, context, args) => {
|
||||||
context.reply('Help yourself.');
|
// context.reply('Help yourself.');
|
||||||
});
|
// });
|
||||||
|
|
||||||
bot.commands.register('demo', (_client, context, _args) => {
|
bot.commands.chat.register('demo', (_client, context, _args) => {
|
||||||
const msg = new Message()
|
const msg = new Message()
|
||||||
.setContent('Hello World!')
|
.setContent('Hello World!')
|
||||||
.addComponent(
|
.addComponent(
|
||||||
@@ -94,7 +105,7 @@ bot.commands.register('demo', (_client, context, _args) => {
|
|||||||
context.reply(msg);
|
context.reply(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.commands.register('test', (_client, context, _args) => {
|
bot.commands.chat.register('test', (_client, context, _args) => {
|
||||||
const msg = new Message().setContent('This is a test interaction!').addComponent(
|
const msg = new Message().setContent('This is a test interaction!').addComponent(
|
||||||
'This is an action row',
|
'This is an action row',
|
||||||
Message.button({
|
Message.button({
|
||||||
|
|||||||
Reference in New Issue
Block a user