Friday, September 20, 2024 1:34:23 AM
> settings

Customize


Authenticate

> command.rb
# frozen_string_literal: true

RSpec.shared_context("command") do
  attr_reader :previous_command

  let!(:community) { ESM::Test.community }
  let!(:user) { ESM::Test.user(*(respond_to?(:user_args) ? user_args : [])) }
  let(:command_class) { described_class } # This can be overwritten
  let(:command) { @previous_command || command_class.new }
  let(:server) { ESM::Test.server(for: community) }
  let(:second_user) { ESM::Test.user }

  #
  # Executes the command as a user in a text or pm channel.
  # The majority of the time, this method will be used along with the input arguments for the command
  #
  # @param channel_type [Symbol] Controls what type of channel messages are triggered in. Options: :text, :pm
  # @param user [ESM::User] The user to send the message as. Defaults to the `user` let binding
  # @param command_override [ESM::Command] The command to execute. Defaults to the `command` let binding
  # @param channel [Discordrb::Channel, nil] The channel to execute the command in
  # @param arguments [Hash] Any arguments the command is expecting as key: value pairs
  # @param prompt_response [String, nil] Optional. The value to set as the user's "response" to ESM prompting them
  # @param handle_error [TrueClass, FalseClass] Controls if errors should be handled by the command or bubbled up
  #   If this is true, the command's `.event_hook` method will be called. Any errors will be handled
  #   if this is false (default), the command's `#execute` method will be called. Any errors will raise in the specs
  #
  def execute!(**opts)
    channel_type = opts.delete(:channel_type) || :text
    send_as = opts.delete(:user) || user
    command_class = opts.delete(:command_class) || self.command_class
    arguments = opts.delete(:arguments) || {}
    prompt_response = opts.delete(:prompt_response)
    handle_error = opts.delete(:handle_error)
    command = command_class.new

    channel =
      if (channel = opts.delete(:channel))
        channel
      elsif ESM::Command::Base::TEXT_CHANNEL_TYPES.include?(channel_type)
        ESM::Test.data[user.guild_type][:channels].sample
      else
        user.discord_user.pm.id
      end

    channel = ESM.bot.channel(channel) unless channel.is_a?(Discordrb::Channel)

    data = {
      id: "", # ID of interaction
      application_id: "", # Bot id?
      type: 2, # Interaction type. 2 is command
      data: {
        id: "", # Command ID
        name: command.command_name # Command name
      },
      guild_id: channel.server&.id, # Server ID
      channel_id: channel.id, # Channel ID
      user: {
        id: send_as.discord_id # User ID
      },
      token: ESM.config.token, # Bot token
      version: 1 # IDK
    }

    if command.arguments.size > 0 && arguments.size > 0
      options =
        arguments.map do |key, value|
          argument = command.arguments.template(key)
          raise ArgumentError, "Invalid argument \"#{key}\" given for #{command.class}" if argument.nil?

          value =
            case value
            when ESM::Server
              value.server_id
            when ESM::Community
              value.community_id
            when ESM::User
              value.mention
            else
              value
            end

          {name: argument.display_name.to_s, value: value, type: argument.discord_type}
        end

      data[:data][:options] = [{type: 1, name: command.command_name, options: options}]
    end

    respond_to_prompt(prompt_response) if prompt_response

    event = Discordrb::Events::ApplicationCommandEvent.new(data.deep_stringify_keys, ESM.bot)

    # In normal operation, #event_hook will receive the ApplicationCommandEvent above
    # SpecApplicationCommandEvent overwrites `#defer` and `#edit_response` to avoid
    # sending those calls to Discord proper
    if handle_error
      # Allows commands to access this command after it has been used
      @previous_command = command_class.event_hook(SpecApplicationCommandEvent.new(event))
      return
    end

    event = ESM::Event::ApplicationCommand.new(event)
    @previous_command = command_class.new(
      user: event.user,
      server: event.server,
      channel: event.channel,
      arguments: event.options
    )

    ESM::ApplicationRecord.connection_pool.with_connection do
      @previous_command.from_discord!
    end
  end

  def wait_for_completion!(event = :on_execute)
    wait_for { previous_command.timers.public_send(event.to_sym).finished? }.to be(true)
  end

  def respond_to_prompt(response)
    ESM::Test.response = response
  end

  def accept_request
    previous_command.request.respond(true)
  rescue => e
    return if e.is_a?(ESM::Exception::CheckFailureNoMessage)

    message =
      if e.respond_to?(:data)
        e.data
      else
        e.message
      end

    ESM.bot.deliver(message, to: ESM::Test.channel(in: community))
  end
end
All opinions represented herein are my own
- © 2024 itsthedevman
- build 340fbb8