Chooksie Framework

website·Indexed 30 days ago

https://guide.chooks.app/introduction

A declarative framework for building modern Discord bots with support for Slash Commands, Autocompletes, Modals, and Buttons. It features a dedicated CLI tool for development, hot reloads, and a custom Fetch API wrapper, with support for TypeScript and database integration.

Tokens
10.9K
Snippets
57
Records
67
Agent score
49%

What's inside Chooksie

  1. Overview of Chooksie developer features

    Chooksie provides a developer-centric environment for bot development featuring:

    • Declarative Interface: Designed to minimize boilerplate code while maximizing available features.
    • Hot Reloads: Provides real-time feedback during development without requiring manual restarts.
    • Small Footprint: Utilizes a minimal set of APIs to ensure high performance and speed.
    • TypeScript Support: Uses SWC for near-instant compilation, allowing full TypeScript integration without performance loss.
    • IDE Support: Provides strong intellisense and inline documentation.
    • CLI Tool: Includes a dedicated CLI tool to enhance the development workflow.
  2. Overview of Chooksie for Discord Bot Development

    Chooksie is a framework designed for creating modern Discord bots using Application Commands (Slash Commands and Autocompletes). It streamlines development by integrating hot code reloads with real-time command syncing to the Discord API, removing the need for manual synchronization steps.
  3. Overview of Chooksie for Discord Bot Development

    Chooksie is a framework designed for creating modern Discord bots using the Application Commands system (Slash Commands and Autocompletes). It aims to reduce development friction by integrating hot code reloads with real-time command syncing to the Discord API, removing the need for manual synchronization steps.
  4. Migrate lifecycle definitions from v1 to v2

    In v2, defineLifecycle has been replaced by defineOnLoad. Additionally, the type definitions for lifecycles and contexts have been renamed: ChooksLifecycle is now OnLoad, and ChooksContext is now Context. The ChooksTeardown type has been removed.
    // ES Modules
    export const chooksOnLoad = defineOnLoad()
    export const chooksOnLoad: OnLoad
    export function chooksOnLoad(ctx: Context) {}
    
    // CommonJS
    exports.chooksOnLoad = defineOnLoad()
    exports.chooksOnLoad: OnLoad
    exports.chooksOnLoad = (ctx: Context) => {}
  5. Type a command's setup function for dependency injection

    When using the setup feature to inject dependencies into a command, you can provide a generic type to SlashCommand to ensure this is correctly typed within the execute method. For external setup functions, use the InferSetupType utility to extract the return type of the function.
    import type { InferSetupType, SlashCommand } from 'chooksie'
    
    // Example of a reusable setup function
    function setup() {
      const getMessage = () => 'Pong!'
      return { message: getMessage }
    }
    
    const pingCommand: SlashCommand<InferSetupType<typeof setup>> = {
      name: 'ping',
      description: 'Pong!',
      setup,
      async execute(ctx) {
        // 'this.message' is now typed based on the setup return value
        await ctx.interaction.reply(this.message())
      },
    }
    
    export default pingCommand
  6. Migrate Context Commands to User and Message Commands

    Context Commands in v2 are split into User and Message commands. The contexts directory is replaced by users and messages directories respectively.

    For User Commands:

    1. Move files to the users directory.
    2. Replace defineContextCommand with defineUserCommand.
    3. Use ctx.interaction.targetUser instead of ctx.interaction.getUser('user', true).

    For Message Commands:

    1. Move files to the messages directory.
    2. Replace defineContextCommand with defineMessageCommand.
    3. Use ctx.interaction.targetMessage instead of ctx.interaction.options.getMessage('message', true).
    // Example User Command
    import { defineUserCommand } from 'chooksie'
    
    export default defineUserCommand({
      name: 'High Five',
      async execute(ctx) {
        const user = ctx.interaction.user
        const target = ctx.interaction.targetUser
        await ctx.interaction.reply(`${user} High Fived ${target}!`)
      },
    })
  7. Create a new bot using the CLI Tool

    The CLI tool is recommended when developing multiple projects locally. If deploying via CI/CD pipelines where building from source is required, add @chookscord/cli as a dev dependency in your project.
    # Install CLI tool globally (choose one)
    npm i -g @chookscord/cli
    yarn global add @chookscord/cli
    pnpm add -g @chookscord/cli
    
    # Initialize and start bot
    chooks init <bot-name>
    cd <bot-name>
    chooks
  8. Add user input options to a Slash Command

    To accept user input in a slash command, add an options array to the defineSlashCommand configuration. Each option requires a name, description, type (e.g., 'STRING'), and a required boolean. User input is retrieved within the execute function using ctx.interaction.options methods, such as getString().
    import { defineSlashCommand } from 'chooksie'
    
    export default defineSlashCommand({
      name: 'echo',
      description: 'Echoes what you just said.',
      async execute(ctx) {
        // Retrieve the 'message' option as a string
        const message = ctx.interaction.options.getString('message', true)
        await ctx.interaction.reply(message)
      },
      options: [
        {
          name: 'message',
          description: 'The message to repeat.',
          type: 'STRING',
          required: true,
        },
      ],
    })
  9. Create a basic User Command with defineUserCommand

    User Commands allow you to perform specific actions on a user. Unlike Slash Commands, User Command names can contain spaces and mixed casing. These commands must be located in the users directory and are defined using the defineUserCommand function for type support. Within the execute function, the ctx.interaction object provides access to both the user triggering the command (user) and the user being targeted (targetUser).
    import { defineUserCommand } from 'chooksie'
    
    export default defineUserCommand({
      name: 'High Five',
      async execute(ctx) {
        const user = ctx.interaction.user
        const target = ctx.interaction.targetUser
    
        await ctx.interaction.reply(`${user} High Fived ${target}!`)
      },
    })
    
  10. Integrate database models into Chooksie commands

    Use the setup property in defineSubcommand or defineOption to inject a database model (or any dependency) into the command's context. The function provided to setup is executed before the command, and the returned value becomes the this context inside the execute or autocomplete methods. This allows you to call model methods like create, findOne, or findAll directly on this.
    import { defineOption, defineSubcommand } from 'chooksie'
    
    async function db() {
      const { Tag } = await import('../db')
      return Tag
    }
    
    const fetchTag = defineSubcommand({
      name: 'fetch',
      setup: db,
      async execute(ctx) {
        const tagName = ctx.interaction.options.getString('name', true)
        // 'this' is the Tag model returned by the db() setup function
        const tag = await this.findOne({ where: { name: tagName } })
        await ctx.interaction.reply(tag?.get('description') || 'Not found')
      },
      options: [
        defineOption({
          name: 'name',
          setup: db,
          async autocomplete(ctx) {
            const tagName = ctx.interaction.options.getFocused()
            // 'this' is the Tag model
            const tags = await this.findAll({ where: { name: { [Op.like]: `%${tagName}%` } } })
            await ctx.interaction.respond(tags.map(t => ({ name: t.name, value: t.name })))
          }
        })
      ]
    })
    
  11. Migrate Fetch API to chooksie/fetch

    The @chookscord/fetch package has been merged into chooksie/fetch. This new implementation requires the undici package to be installed. Furthermore, fetch is no longer available as a method on the Context object; it must be imported and called directly.
    # Install required dependency
    npm i undici
    # or
    yarn add undici
    # or
    pnpm add undici
    // ES Modules
    import { fetch } from 'chooksie/fetch'
    
    export const chooksOnLoad = defineOnLoad(async ctx => {
      await fetch('https://example.com') // No longer ctx.fetch
    })
    
    // CommonJS
    const { fetch } = require('chooksie/fetch')
    
    exports.chooksOnLoad = defineOnLoad(async ctx => {
      await fetch('https://example.com')
    })
    
  12. Connect a Sequelize database to a chooksie bot

    To integrate a database using Sequelize, define the database connection and models in a separate file and use defineOnLoad to ensure the database is synchronized when the bot loads. This allows you to maintain a persistent data store that can be accessed by your bot's commands.
    import { defineOnLoad } from 'chooksie'
    import { DataTypes, Model, Sequelize } from 'sequelize'
    
    const sequelize = new Sequelize('database', 'user', 'password', {
      host: 'localhost',
      dialect: 'sqlite',
      logging: false,
      storage: 'database.sqlite', // SQLite only
    })
    
    export class Tag extends Model {
      declare public name: string
      declare public description: string
      declare public username: string
      declare public usageCount: number
    }
    
    Tag.init({
      name: { type: DataTypes.STRING, unique: true },
      description: DataTypes.TEXT,
      username: DataTypes.STRING,
      usageCount: { type: DataTypes.INTEGER, defaultValue: 0, allowNull: false },
    }, { sequelize, modelName: 'tags' })
    
    export const chooksOnLoad = defineOnLoad(async () => {
      await sequelize.sync({
        force: process.env.NODE_ENV !== 'production',
      })
    })