blackcat.js-discord - v1.0.10
    Preparing search index...

    Class ComponentBuilder<T>

    Builder hỗ trợ tạo và quản lý Discord Message Components (Buttons, Select Menus...) thông qua ActionRowBuilder.

    Class này giúp bạn:

    • Tạo component từ cấu hình JSON
    • Quản lý nhiều ActionRow
    • Disable toàn bộ components
    • Tạo collector cho component interaction

    Discord giới hạn:

    • Tối đa 5 ActionRow trong một message
    • Mỗi ActionRow tối đa 5 buttons
    • Select menu mỗi row chỉ được 1

    Class này tự động chia button thành nhiều row khi > 5.


    const builder = new ComponentBuilder([
    {
    type: "button",
    options: [
    {
    customId: "confirm",
    label: "Confirm",
    style: "Success"
    },
    {
    customId: "cancel",
    label: "Cancel",
    style: "Danger"
    }
    ]
    }
    ]);

    await message.reply({
    content: "Bạn có chắc không?",
    components: builder.build()
    });

    const builder = new ComponentBuilder([
    {
    type: "stringmenu",
    options: {
    customId: "choose-color",
    placeholder: "Chọn màu",
    options: [
    { label: "Red", value: "red" },
    { label: "Blue", value: "blue" }
    ]
    }
    }
    ]);

    const collector = await builder.createCollector({
    source: message,
    time: "30s",
    onCollector: async (ctx) => {
    console.log(ctx.interaction.customId);
    }
    });

    builder.disableAll();

    await message.edit({
    components: builder.build()
    });

    Type Parameters

    • T extends AnyComponent = AnyComponent

      Loại component builder

    Index

    Constructors

    • Khởi tạo ComponentBuilder với cấu hình component ban đầu (tuỳ chọn).

      Type Parameters

      • T extends AnyComponent = AnyComponent

        Loại component builder

      Parameters

      • Optionalcomponents: ComponentInput[]

        Danh sách cấu hình component (ComponentInput[])

      Returns ComponentBuilder<T>

      Constructor này cho phép bạn khởi tạo builder kèm dữ liệu ngay lập tức mà không cần gọi thủ công các method như:

      Nếu components được cung cấp:

      • Builder sẽ tự động gọi parse() để:
        • Convert config → component thực tế
        • Tạo ActionRowBuilder
        • Khởi tạo trạng thái ban đầu của this.rows

      1. Kiểm tra components có tồn tại
      2. Nếu có → gọi parse(components)
      3. Nếu không → builder khởi tạo rỗng

      • Không validate rỗng (cho phép khởi tạo builder trống)
      • Dữ liệu được build ngay tại thời điểm khởi tạo
      • Có thể tiếp tục chain thêm các method sau đó

      • Khởi tạo nhanh UI ngay từ đầu
      • Load dữ liệu từ config / database
      • Tạo builder từ state có sẵn

      • Method parse chịu trách nhiệm xử lý logic build
      • Constructor không clone dữ liệu đầu vào

      const builder = new ComponentBuilder([
      {
      type: "button",
      options: [
      { customId: "btn_1", label: "Click", style: 1 }
      ]
      }
      ]);
      // Khởi tạo rỗng rồi build sau
      const builder = new ComponentBuilder();

      builder.addButton({
      customId: "a",
      label: "A",
      style: 1
      });
      await message.reply({
      components: new ComponentBuilder(components).build()
      });
      • parse
      • addRow

    Methods

    • Thêm một hoặc nhiều Button vào builder.

      Parameters

      Returns this

      Instance hiện tại (chainable)

      Error nếu danh sách button rỗng

      Đây là method shortcut giúp bạn thêm button nhanh chóng mà không cần viết đầy đủ cấu trúc { type: "button" }.

      Method này sẽ tự động:

      • Wrap dữ liệu thành { type: "button", options: [...] }
      • Gọi parse để xử lý và tạo ActionRow

      1. Normalize input thành mảng (ButtonInput[])
      2. Validate không được rỗng
      3. Wrap thành:
        { type: "button", options: data }
        
      4. Gọi parse() để build component

      • Có thể thêm nhiều button trong một lần gọi
      • Các button sẽ được group vào ActionRow theo logic của parse

      • Method này mutate trực tiếp builder
      • Không validate sâu cấu trúc button (phụ thuộc parse)
      • Nếu truyền mảng rỗng → throw error

      builder.addButton({
      customId: "btn",
      label: "Click",
      style: 1
      });
      // Thêm nhiều button
      builder.addButton([
      { customId: "a", label: "A", style: 1 },
      { customId: "b", label: "B", style: 2 }
      ]);
      await message.reply({
      components: builder
      .addButton(buttons)
      .build()
      });
      • parse
      • ActionRowBuilder
    • Thêm một hoặc nhiều ActionRow mới từ cấu hình component.

      Parameters

      Returns this

      Instance hiện tại (chainable)

      Error nếu danh sách component rỗng

      Đây là method “low-level” cho phép bạn thêm trực tiếp các cấu hình component dưới dạng ComponentInput mà không cần dùng các shortcut như:


      1. Normalize input thành mảng (ComponentInput[])
      2. Validate không được rỗng
      3. Gọi parse() để:
        • Convert config → component thực tế
        • Tự động tạo ActionRowBuilder
        • Push vào this.rows

      • Hỗ trợ nhiều loại component trong cùng một lần gọi
      • Mỗi phần tử trong components có thể tạo ra một hoặc nhiều ActionRow (tùy vào logic bên trong parse)

      • Bạn cần full control cấu trúc UI
      • Kết hợp nhiều loại component (button + select menu)
      • Build UI phức tạp / dynamic

      • Method này mutate trực tiếp builder
      • Không validate sâu cấu trúc component (phụ thuộc parse)
      • Là nền tảng cho các method khác như addButton, addSelectMenu

      builder.addRow({
      type: "button",
      options: [
      { customId: "a", label: "A", style: 1 }
      ]
      });
      // Thêm nhiều loại component cùng lúc
      builder.addRow([
      {
      type: "button",
      options: [...]
      },
      {
      type: "stringmenu",
      options: {...}
      }
      ]);
      await message.reply({
      components: builder
      .addRow(components)
      .build()
      });
      • parse
      • addButton
      • addSelectMenu
    • Thêm một hoặc nhiều Select Menu vào builder.

      Parameters

      Returns this

      Instance hiện tại (chainable)

      Error nếu danh sách menu rỗng

      Method này hỗ trợ tất cả các loại Select Menu của Discord:

      • String Select Menu (stringmenu)
      • User Select Menu (usermenu)
      • Role Select Menu (rolemenu)
      • Channel Select Menu (channelmenu)
      • Mentionable Select Menu (mentionmenu)

      1. Normalize input thành mảng (SelectMenu[])
      2. Validate không được rỗng
      3. Gọi parse() để chuyển config → ActionRowBuilder

      • Có thể thêm nhiều menu cùng lúc
      • Mỗi menu sẽ được convert thành component tương ứng
      • Việc grouping vào ActionRow được xử lý bên trong parse

      • Method này mutate trực tiếp builder
      • Không validate sâu cấu trúc options (phụ thuộc parse)
      • Nếu truyền mảng rỗng → throw error

      builder.addSelectMenu({
      type: "stringmenu",
      options: {
      customId: "menu",
      options: [
      { label: "A", value: "a" }
      ]
      }
      });
      // Thêm nhiều menu
      builder.addSelectMenu([
      {
      type: "stringmenu",
      options: { customId: "menu1", options: [...] }
      },
      {
      type: "usermenu",
      options: { customId: "menu2" }
      }
      ]);
      await message.reply({
      components: builder
      .addSelectMenu(menuConfig)
      .build()
      });
      • parse
      • ActionRowBuilder
    • Build toàn bộ ActionRow để sử dụng trong Discord API.

      Returns ActionRowBuilder<T>[]

      Mảng ActionRowBuilder đã được cấu hình

      Đây là bước cuối cùng trong quá trình sử dụng builder. Method này sẽ trả về toàn bộ các ActionRow đã được thêm trước đó để có thể truyền trực tiếp vào các method của Discord như:

      • message.reply
      • interaction.reply
      • message.edit

      • Không tạo mới dữ liệu
      • Chỉ trả về reference của this.rows
      • Các component bên trong đã được cấu hình từ trước thông qua builder

      • Không nên mutate trực tiếp kết quả sau khi build()
      • Nếu cần thay đổi, nên thao tác thông qua builder trước khi gọi build

      await message.reply({
      components: builder.build()
      });
      const components = builder.build();

      await interaction.editReply({
      components
      });
      // Reuse builder
      const components = builder.build();

      await message1.reply({ components });
      await message2.reply({ components });

      ActionRowBuilder

    • Tạo Interaction Collector từ source (message hoặc interaction).

      Type Parameters

      • ComponentType extends MessageComponentType

        Loại component (button, select menu, ...)

      • InGuild extends boolean = boolean

        Xác định interaction có nằm trong guild hay không

      Parameters

      Returns Promise<InteractionCollector<ResolveInteraction<ComponentType, InGuild>>>

      Callback sẽ nhận object:

      • interaction: Interaction gốc
      • customId: ID của component
      • userId: ID của user
      • values: Giá trị (chỉ có với select menu)
      • message: Message chứa component
      • collector: Instance collector
      • stop(): Dừng collector

      Callback sẽ nhận object:

      • message: Message chứa collector
      • reason: Lý do kết thúc
      • total: Tổng số interaction đã collect
      • edit(): Helper để edit message

      const collector = await builder.createCollector({
      source: message,
      time: 60000,
      onCollector: async (ctx) => {
      console.log(ctx.customId);
      }
      });
      // Sử dụng với interaction
      const collector = await builder.createCollector({
      source: interaction,
      time: 30000,
      onCollector: async ({ interaction, stop }) => {
      await interaction.reply("Clicked!");
      stop();
      },
      onEnd: ({ total, reason }) => {
      console.log(`Collected: ${total}, reason: ${reason}`);
      }
      });
      // Select menu
      const collector = await builder.createCollector({
      source: message,
      componentType: ComponentType.StringSelect,
      onCollector: ({ values }) => {
      console.log(values); // string[]
      }
      });
      • InteractionCollector
      • Message#createMessageComponentCollector
    • Disable toàn bộ component trong tất cả ActionRow.

      Returns ComponentBuilder<T>

      Instance hiện tại (chainable)

      Method này sẽ duyệt qua toàn bộ các ActionRow và:

      • Tìm các component có hỗ trợ method setDisabled
      • Thiết lập trạng thái disabled = true

      1. Lặp qua từng ActionRow trong this.rows
      2. Lặp qua từng component trong mỗi row
      3. Kiểm tra component có method setDisabled
      4. Gọi setDisabled(true) nếu tồn tại

      • Button
      • Select Menu (String, User, Role, Channel, ...)
      • Các component khác hỗ trợ disable

      • Khi collector kết thúc → khóa toàn bộ UI
      • Sau khi user đã interaction → tránh spam / double click
      • Khi timeout hoặc error → vô hiệu hóa input

      • Method này mutate trực tiếp builder hiện tại (không clone)
      • Nên gọi trước build hoặc trước khi edit message

      builder.disableAll();

      await message.edit({
      components: builder.build()
      });
      // Sau khi collector kết thúc
      collector.on("end", async () => {
      await message.edit({
      components: builder.disableAll().build()
      });
      });
      // Chain usage
      await message.reply({
      components: builder
      .disableAll()
      .build()
      });

      ActionRowBuilder

    • Thay thế một hoặc nhiều ActionRow tại vị trí index bằng config mới.

      Parameters

      • index: number

        Vị trí bắt đầu thay thế trong danh sách ActionRow

      • components: ComponentInput[]

        Danh sách cấu hình component để tạo row mới

      Returns ComponentBuilder<T>

      Instance hiện tại (chainable)

      Method này cho phép bạn thay thế động một hoặc nhiều ActionRow mà không cần rebuild toàn bộ builder.


      1. Lưu lại rows hiện tại
      2. Reset tạm this.rows để reuse logic addRow
      3. Gọi addRow(components) để build các row mới
      4. Lấy kết quả (newRows)
      5. Khôi phục lại rows ban đầu
      6. Dùng splice để thay thế tại vị trí index

      • Số lượng row bị thay thế = số lượng row mới (newRows.length)
      • Nếu số row mới khác số row cũ: → danh sách rows sẽ bị thay đổi kích thước

      • Update UI động (disable button, đổi label, đổi style)
      • Replace một phần giao diện thay vì rebuild toàn bộ
      • Làm pagination / step UI

      • Method này mutate trực tiếp this.rows
      • Không validate index (có thể gây out-of-range nếu dùng sai)
      • components sẽ được build lại thông qua addRow

      builder.replace(0, [
      {
      type: "button",
      options: [
      {
      customId: "test",
      label: "Test",
      style: "Danger"
      }
      ]
      }
      ]);
      // Replace nhiều row
      builder.replace(0, [
      { type: "button", options: [...] },
      { type: "stringmenu", options: {...} }
      ]);
      // Update UI sau interaction
      await interaction.update({
      components: builder
      .replace(0, newComponents)
      .build()
      });
      • ActionRowBuilder
      • addRow
    • Thay thế một ActionRow tại vị trí index bằng một config duy nhất.

      Parameters

      • index: number

        Vị trí cần thay thế trong danh sách ActionRow

      • component: ComponentInput

        Cấu hình component để tạo row mới

      Returns ComponentBuilder<T>

      Instance hiện tại (chainable)

      Error nếu index không hợp lệ

      Đây là method tiện ích (helper) của replace, dùng khi bạn chỉ cần thay thế một row duy nhất thay vì nhiều row.

      Internally:

      • Wrapper của replace
      • Tự động convert componentComponentInput[]

      1. Nhận một component duy nhất
      2. Wrap thành mảng [component]
      3. Gọi replace để thực hiện logic thay thế

      • Update một ActionRow duy nhất
      • Toggle trạng thái button (enable/disable)
      • Thay đổi UI đơn giản sau interaction

      • Validation index được xử lý bên trong replace
      • Method này mutate trực tiếp builder hiện tại

      builder.replaceOne(0, {
      type: "button",
      options: [
      {
      customId: "confirm",
      label: "Confirm",
      style: "Success"
      }
      ]
      });
      // Update UI sau interaction
      await interaction.update({
      components: builder
      .replaceOne(1, newRow)
      .build()
      });

      replace

    • Thay thế một đoạn ActionRow liên tiếp bằng các component mới.

      Parameters

      • start: number

        Vị trí bắt đầu thay thế

      • deleteCount: number

        Số lượng ActionRow cần xóa

      • components: ComponentInput[]

        Danh sách cấu hình component để tạo row mới

      Returns ComponentBuilder<T>

      Instance hiện tại (chainable)

      Error nếu start hoặc deleteCount không hợp lệ

      Method này hoạt động tương tự Array.prototype.splice, cho phép bạn:

      • Xóa nhiều row liên tiếp
      • Chèn các row mới vào vị trí đó

      1. Validate startdeleteCount
      2. Build newRows thông qua addRow
      3. Dùng splice(start, deleteCount, ...newRows) để thay thế

      • Có thể thay đổi độ dài rows
      • Có thể:
        • Replace (deleteCount > 0 && newRows.length > 0)
        • Remove (deleteCount > 0 && newRows.length = 0)
        • Insert (deleteCount = 0 && newRows.length > 0)

      • Update UI nhiều row cùng lúc
      • Pagination / step UI
      • Thay đổi layout động

      • Method này mutate trực tiếp this.rows
      • components sẽ được build lại thông qua addRow

      // Replace 2 rows từ index 0
      builder.replaceRange(0, 2, [
      { type: "button", options: [...] },
      { type: "stringmenu", options: {...} }
      ]);
      // Insert tại index 1 (không xóa)
      builder.replaceRange(1, 0, [
      { type: "button", options: [...] }
      ]);
      // Remove 2 rows
      builder.replaceRange(0, 2, []);
      • replace
      • replaceOne
    • Chuyển toàn bộ ActionRowBuilder sang định dạng JSON theo Discord API.

      Returns APIActionRowComponent<ReturnType<T["toJSON"]>>[]

      Mảng APIActionRowComponent ở dạng raw JSON

      Method này sẽ gọi .toJSON() trên từng ActionRowBuilder để chuyển đổi toàn bộ component sang format mà Discord API yêu cầu.


      1. Lặp qua toàn bộ this.rows
      2. Gọi row.toJSON() trên từng row
      3. Trả về mảng JSON tương ứng

      • Debug cấu trúc component trước khi gửi
      • Serialize để lưu trữ (cache / database)
      • Gửi trực tiếp qua REST API (không dùng builder)

      • build() → trả về builder object (dùng trực tiếp với discord.js)
      • toJSON() → trả về raw JSON (Discord API format)

      • Dữ liệu trả về là plain object (không còn method)
      • Không nên mutate trực tiếp nếu dùng lại builder

      const json = builder.toJSON();

      console.log(json);
      // Gửi qua REST API
      await rest.post(Routes.channelMessages(channelId), {
      body: {
      components: builder.toJSON()
      }
      });
      // Debug cấu trúc component
      console.dir(builder.toJSON(), { depth: null });
      • ActionRowBuilder#toJSON
      • build