From fc7f6548a4dcb1c97f647d7fa610cc887e7b4f2d Mon Sep 17 00:00:00 2001 From: Jaron Date: Thu, 2 Jan 2025 01:49:22 -0600 Subject: [PATCH] Updated the README some for rewrite --- README.md | 114 ++++++++++++++++++++++++++++++++++++----- src/services/client.ts | 4 +- testing/demo.test.ts | 12 +++-- 3 files changed, 109 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1deca27..c97de53 100644 --- a/README.md +++ b/README.md @@ -3,22 +3,28 @@

Build good bots fast

-> This package is in an early Alpha stage and not yet ready for consumption +> This package is in an early beta stage and not yet ready for consumption A solid and simple Discord API wrapper. ## Installation + +Install with your package manager of choice. + ``` -yarn add @jaronp/discord.ts +npm install discord.ts +yarn add discord.ts +bun add discord.ts ``` ## Quick Start + A small example implementation. -```ts -import { Client } from '@jaronp/discord.ts'; +```typescript +import { Client, type Context } from '`discord.ts'; // Initialize and configure -const bot = new Client({ +const client = new Client({ token: '', application_id: '', intents: [ @@ -26,26 +32,105 @@ const bot = new Client({ Intent.GUILD_MESSAGES, Intent.GUILD_MESSAGE_REACTIONS, Intent.MESSAGE_CONTENT - ] + ], + prefix: '??' }); -// Register a listener -bot.handlers.register('READY', (client, context, data) => { - console.log(`Logged in as ${client.user.tag}`); +// Register arbitrary event handler to the client bus +client.on('READY', async (_, payload) => { + console.log(`Logged in as ${payload.user.username}${payload.user.discriminator?.length > 0 ? `#${payload.user.discriminator}` : ''}`); }); // Register a command -bot.commands.register('ping', (client, context, args) => { +client.chatCommands.register('ping', (context: Context, args) => { context.reply('Pong!', true); }); // Connect -bot.connect(); +client.connect(); ``` +## Type Resolution and Saftey + +This library is designed to be type safe and resolve types for you. Because of this you can write code that is both safe and easy to understand. With the `createCommandHandler` utility you can provide types for command arguments. The client will then check and resolve them for you. + +```typescript +import { createCommandHandler, CommandOptionType, type Context } from 'discord.ts'; + +// <...> + +// Define the command handler with the utility function +const avatarCommand = createCommandHandler({ + description: 'Get a users avatar', + args: [ + { + id: 'user', + name: 'user', + type: CommandOptionType.User, + description: 'The user to get the avatar for', + required: true + } + ] as const, + execute: async (context: Context, args) => { + // Args will be typed here as { user: User } + context.reply(args.user.avatarURL); + } +}); + +// You can then register the command with the client +// The class generated can be used for both chat and application commands +client.chatCommands.register('avatar', avatarCommand); +client.applicationCommands.register('avatar', ApplicationCommandType.Chat, avatarCommand); + +// <...> +``` + +> NOTE: The `as const` is required to ensure the types are resolved correctly. This is to work around some TypeScript weirdness. + +## Hydration + +Hydration allows you to fetch and fill data on Discord objects. This is useful for when you need to fetch additional data about an object. + +```typescript +client.chatCommands.register('ping', async (context: Context, args: any[]) => { + // Hydrate the message id from the context + const { message } = await context.hydrate(context, [Expectation.Message]); + // Send a message to the channel + const response = await context.reply(`Pong!: got ${args.join(', ')}`, true); + // Calculate the time it took to send the message + const time = message.timestamp.getTime() - message.timestamp.getTime(); + // Edit the message to include the time + await context.editMessage(response, `Pong! Latency: ${time}ms`); +}); +``` + +This is a powerful feature that allows you to fetch and use data from Discord objects in a type safe way. It also ensures the minimum amount of API calls are made and that no extra data is fetched or passed. + +You can also generate a hydration function that returns a type narrowing boolean. This can be used to check if the object is hydrated or not. + +```typescript +client.chatCommands.register('me', async (context: Context, _args: any[]) => { + // Hydrate the message id from the context + const hydrate = await context.hydrator(hydrated, [Expectation.Guild]); + // Check if the object is hydrated + const hasGuild = hydrate(hydrated); + + if (hasGuild) { + // Send a message stating the user that they are in a guild + await context.reply(`You are ${message.author.username} and this is ${message.guild.name}`, true); + } else { + // Send a message stating the user that they are not in a guild + await context.reply(`You are ${message.author.username} and we are not in a server`, true); + } +}); +``` + +Additionally you can hydrate repeatedly if you need to fetch multiple objects at different times. + + \ No newline at end of file diff --git a/src/services/client.ts b/src/services/client.ts index 69283b7..33143d0 100644 --- a/src/services/client.ts +++ b/src/services/client.ts @@ -1,7 +1,7 @@ import { API } from './api'; import { Gateway } from './connectors/gateway'; import { Processor } from './processor'; -import { Expectation, fromDiscord, Intent, type ClientConfig } from '../types/common'; +import { Expectation, Intent, type ClientConfig } from '../types/common'; import { exists, isNil } from '../utils/index'; import { type DiscordMessage, Message } from '../types/message'; import { Guilds } from './caches/guilds'; @@ -42,7 +42,7 @@ export class Client { this.guilds = new Guilds(this.api, this.processor); this.users = new Users(this.api, this.processor); this.messages = new Messages(this, this.api, this.processor); - // Save references to the chat and application command handlers + // Save references to command and event handlers this.chatCommands = this.processor.chat_commands; this.applicationCommands = this.processor.application_commands; } diff --git a/testing/demo.test.ts b/testing/demo.test.ts index 6dc6af5..9001653 100644 --- a/testing/demo.test.ts +++ b/testing/demo.test.ts @@ -85,11 +85,13 @@ client.chatCommands.register('me', async (context: Context, _args: any[]) => { const hydrate = await context.hydrator(hydrated, [Expectation.Guild]); const hasGuild = hydrate(hydrated); - // if (hasGuild) { - // hydrated.guild.name - // } else { - // hydrated - // } + if (hasGuild) { + // Send a message stating the user that they are in a guild + await context.reply(`You are ${message.author.username} and this is ${message.guild.name}`, true); + } else { + // Send a message stating the user that they are not in a guild + await context.reply(`You are ${message.author.username} and we are not in a server`, true); + } // await context.reply(`You are ${message.author.username} ${hasGuild ? message.guild.name : ''}`, true); });