Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respond with image for slash command #227

Open
OnlyReFLeX opened this issue Jun 19, 2023 · 3 comments
Open

Respond with image for slash command #227

OnlyReFLeX opened this issue Jun 19, 2023 · 3 comments

Comments

@OnlyReFLeX
Copy link

OnlyReFLeX commented Jun 19, 2023

Trying to reply with image for slash command

# example
bot.application_command(:profile) do |event|
  event.respond(content: 'test') do |builder|
    builder.file = File.open('./test.png') # doesn't work (no error)
    builder.attachments = [File.open('./test.png')] # error (undefined method)
  end

  event.send_file # doesn't exist
end 

But there is no functional for that, Is there any way to do this?

@anna328p
Copy link

Interaction response API endpoint allows attachments.

@joshbuker
Copy link
Contributor

This is a workaround that may suit your needs.

base64_image_content = '...'
image = StringIO.new(Base64.decode64(base64_image_content))
# OR
image = File.open('./test.png', 'r')
@event.channel.send_file(
  image,
  caption: caption,
  filename: filename
)

@akxcv
Copy link

akxcv commented Jan 23, 2025

Specifically for sending files in response to slash commands, here's a monkey patch you could use. Of course, this is not a good coding practice, more a quick hack, but it works nicely.

module Discordrb
  module API
    module Interaction
      # PATCH[discordrb/event-file-attachments]: allows attaching files in response to slash commands
      def self.create_interaction_response(interaction_token, interaction_id, type, content = nil, tts = nil, embeds = nil, allowed_mentions = nil, flags = nil, components = nil, attachments = nil)
        data = { tts: tts, content: content, embeds: embeds, allowed_mentions: allowed_mentions, flags: flags, components: components }.compact
        files = []
        unless attachments.nil?
          data[:attachments] = []
          attachments.each_with_index do |attachment, idx|
            data[:attachments].push({
              id: idx,
              filename: "file_#{idx}.png"
            })
            files.push(attachment)
          end
        end
        payload_json = { type:, data: }.to_json

        file_payload = files.each_with_index.reduce('') do |acc, (file, idx)|
          acc + <<~PAYLOAD
            --boundary
            Content-Disposition: form-data; name="files[#{idx}]"; filename="file_#{idx}.png"
            Content-Type: image/png

            #{file}
          PAYLOAD
        end

        payload = <<~PAYLOAD
          --boundary
          Content-Disposition: form-data; name="payload_json"
          Content-Type: application/json

          #{payload_json}
          #{file_payload}
          --boundary--
        PAYLOAD

        Discordrb::API.request(
          :interactions_iid_token_callback,
          interaction_id,
          :post,
          "#{Discordrb::API.api_base}/interactions/#{interaction_id}/#{interaction_token}/callback",
          payload,
          content_type: 'multipart/form-data; boundary=boundary'
        )
      end
    end
  end

  class Interaction
    # PATCH[discordrb/event-file-attachments]: adds `attachments` kwarg
    def respond(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil, attachments: nil)
      flags |= 1 << 6 if ephemeral

      builder = Discordrb::Webhooks::Builder.new
      view = Discordrb::Webhooks::View.new

      # Set builder defaults from parameters
      prepare_builder(builder, content, embeds, allowed_mentions)
      yield(builder, view) if block_given?

      components ||= view
      data = builder.to_json_hash

      # PATCH[discordrb/event-file-attachments]: passes `attachments`
      Discordrb::API::Interaction.create_interaction_response(@token, @id, CALLBACK_TYPES[:channel_message], data[:content], tts, data[:embeds], data[:allowed_mentions], flags, components.to_a, attachments)

      return unless wait

      response = Discordrb::API::Interaction.get_original_interaction_response(@token, @application_id)
      Interactions::Message.new(JSON.parse(response), @bot, @interaction)
    end
  end

  module Events
    class InteractionCreateEvent < Event
      # PATCH[discordrb/event-file-attachments]: adds `attachments` kwarg
      def respond(content: nil, tts: nil, embeds: nil, allowed_mentions: nil, flags: 0, ephemeral: nil, wait: false, components: nil, attachments: nil, &block)
        @interaction.respond(
          content: content, tts: tts, embeds: embeds, allowed_mentions: allowed_mentions,
          # PATCH[discordrb/event-file-attachments]: passes `attachments`
          flags: flags, ephemeral: ephemeral, wait: wait, components: components, attachments:, &block
        )
      end
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants