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

feat: new selects, bugfixes, voice fix #209

Merged
merged 16 commits into from
Mar 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/modals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
require 'discordrb'
require 'securerandom'

bot = Discordrb::Bot.new(token: ENV['DISCORDRB_TOKEN'])
bot.register_application_command(:modal_test, 'Test out a spiffy modal', server_id: ENV['DISCORDRB_SERVER_ID'])
bot.register_application_command(:modal_await_test, 'Test out the await style', server_id: ENV['DISCORDRB_SERVER_ID'])
bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN'))
bot.register_application_command(:modal_test, 'Test out a spiffy modal', server_id: ENV.fetch('DISCORDRB_SERVER_ID'))
bot.register_application_command(:modal_await_test, 'Test out the await style', server_id: ENV.fetch('DISCORDRB_SERVER_ID'))

bot.application_command :modal_test do |event|
event.show_modal(title: 'Test modal', custom_id: 'test1234') do |modal|
Expand Down
105 changes: 105 additions & 0 deletions examples/select_menus.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# frozen_string_literal: true

require 'discordrb'
require 'securerandom'

bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN'))

bot.message do |event|
if event.message.content == 'TEST'
event.channel.send_message('Examples of different select menus')

event.channel.send_message(
'string_select (old select_menu, but alias define to keep legacy)', false, nil, nil, nil, nil,
Discordrb::Components::View.new do |builder|
builder.row do |r|
r.string_select(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss|
ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
end
# Same as above with the alias to keep the compatibility with the old method
# r.select_menu(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss|
# ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
# ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
# ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
# end
end
end
)

event.channel.send_message(
'user_select', false, nil, nil, nil, nil,
Discordrb::Components::View.new do |builder|
builder.row do |r|
r.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3)
end
end
)

event.channel.send_message(
'role_select', false, nil, nil, nil, nil,
Discordrb::Components::View.new do |builder|
builder.row do |r|
r.role_select(custom_id: 'role_select', placeholder: 'Test of RoleSelect', max_values: 3)
end
end
)

event.channel.send_message(
'mentionable_select', false, nil, nil, nil, nil,
Discordrb::Components::View.new do |builder|
builder.row do |r|
r.mentionable_select(custom_id: 'mentionable_select', placeholder: 'Test of MentionableSelect', max_values: 3)
end
end
)

event.channel.send_message(
'channel_select', false, nil, nil, nil, nil,
Discordrb::Components::View.new do |builder|
builder.row do |r|
r.channel_select(custom_id: 'channel_select', placeholder: 'Test of ChannelSelect', max_values: 3)
end
end
)
end
end

bot.string_select do |event|
# bot.select_menu do |event| # also available with the alias to keep the compatibility with the old method
event.interaction.respond(
content: "**[STRING_SELECT]**\nYou have chosen the values: **#{event.values.join('**, **')}**",
ephemeral: true
)
end

bot.user_select do |event|
event.interaction.respond(
content: "**[USER_SELECT]**\nYou have chosen users : **#{event.values.map(&:username).join('**, **')}**",
ephemeral: true
)
end

bot.role_select do |event|
event.interaction.respond(
content: "**[ROLE_SELECT]**\nYou have chosen roles : **#{event.values.map(&:name).join('**, **')}**",
ephemeral: true
)
end

bot.mentionable_select do |event|
event.interaction.respond(
content: "**[MENTIONABLE_SELECT]**\nYou have chosen mentionables :\n Users: **#{event.values[:users].map(&:username).join('**, **')}**\n Roles: **#{event.values[:roles].map(&:name).join('**, **')}**",
ephemeral: true
)
end

bot.channel_select do |event|
event.interaction.respond(
content: "**[CHANNEL_SELECT]**\nYou have chosen channels : **#{event.values.map(&:name).join('**, **')}**",
ephemeral: true
)
end

bot.run
2 changes: 1 addition & 1 deletion examples/voice_send.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

require 'discordrb'

bot = Discordrb::Commands::CommandBot.new token: 'B0T.T0KEN.here', prefix: '!'
bot = Discordrb::Commands::CommandBot.new token: ENV.fetch('DISCORDRB_TOKEN'), prefix: '!'

bot.command(:connect) do |event|
# The `voice_channel` method returns the voice channel the user is currently in, or `nil` if the user is not in a
Expand Down
51 changes: 51 additions & 0 deletions examples/webhooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require 'discordrb'
require 'securerandom'

BASE64_SMALL_PICTURE = 'data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bpaKtCnYQcchQnSyIijhqFYpQIdQKrTqYXPoFTRqSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxdHJSdJES/5cUWsR4cNyPd/ced+8Af73MVLNjHFA1y0gl4kImuyoEX9GDfoTRi4DETH1OFJPwHF/38PH1LsazvM/9OcJKzmSATyCeZbphEW8QT29aOud94ggrSgrxOfGYQRckfuS67PIb54LDfp4ZMdKpeeIIsVBoY7mNWdFQiaeIo4qqUb4/47LCeYuzWq6y5j35C0M5bWWZ6zSHkcAiliBCgIwqSijDQoxWjRQTKdqPe/iHHL9ILplcJTByLKACFZLjB/+D392a+ckJNykUBzpfbPtjBAjuAo2abX8f23bjBAg8A1day1+pAzOfpNdaWvQI6NsGLq5bmrwHXO4Ag0+6ZEiOFKDpz+eB9zP6piwwcAt0r7m9Nfdx+gCkqavkDXBwCIwWKHvd491d7b39e6bZ3w/9+3J4GwJBFwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+YLEA0OHzBTh5kAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAK0lEQVRIx+3NQQEAQAQAMC6DjLKJeSX4bQWW1ROXXhwTCAQCgUAgEAgEWz5KaQFiPn2UJwAAAABJRU5ErkJggg=='
CHANNEL_EDIT = ENV.fetch('CHANNEL_EDIT')

bot = Discordrb::Bot.new(token: ENV.fetch('DISCORDRB_TOKEN'))

bot.message do |event|
if event.message.content == 'CREATE'
event.channel.send_message('Create webhook in this channel')

wh = event.channel.create_webhook('Test', nil, 'Creation webhook')
wh.execute(content: '[CREATE]')
end

if event.message.content == 'EDIT_ONE'
wh = event.channel.webhooks.first
wh.update(name: 'Test edited', reason: 'Edition test one')

puts wh.inspect
wh.execute(content: '[EDIT ONE]')
end

if event.message.content == 'EDIT_TWO'
wh = event.channel.webhooks.first
wh.update(avatar: BASE64_SMALL_PICTURE, reason: 'Edition test two')

puts wh.inspect
wh.execute(content: '[EDIT TWO]')
end

if event.message.content == 'EDIT_THREE'
wh = event.channel.webhooks.first
wh.update(avatar: nil, channel: event.bot.channel(CHANNEL_EDIT, event.server) || event.channel, reason: 'Edition test three')

puts wh.inspect
wh.execute(content: '[EDIT THREE]')
end

if event.message.content == 'DELETE'
wh = (event.bot.channel(CHANNEL_EDIT, event.server) || event.channel).webhooks.first
wh.delete('Delete webhook')

puts 'Deleted!'
end
end

bot.run
8 changes: 4 additions & 4 deletions lib/discordrb/api/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def message(token, channel_id, message_id)
# https://discord.com/developers/docs/resources/channel#create-message
# @param attachments [Array<File>, nil] Attachments to use with `attachment://` in embeds. See
# https://discord.com/developers/docs/resources/channel#create-message-using-attachments-within-embeds
def create_message(token, channel_id, message, tts = false, embed = nil, nonce = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
body = { content: message, tts: tts, embed: embed, nonce: nonce, allowed_mentions: allowed_mentions, message_reference: message_reference, components: components&.to_a }
def create_message(token, channel_id, message, tts = false, embeds = nil, nonce = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
body = { content: message, tts: tts, embeds: embeds, nonce: nonce, allowed_mentions: allowed_mentions, message_reference: message_reference, components: components&.to_a }
body = if attachments
files = [*0...attachments.size].zip(attachments).to_h
{ **files, payload_json: body.to_json }
Expand Down Expand Up @@ -117,13 +117,13 @@ def upload_file(token, channel_id, file, caption: nil, tts: false)

# Edit a message
# https://discord.com/developers/docs/resources/channel#edit-message
def edit_message(token, channel_id, message_id, message, mentions = [], embed = nil, components = nil)
def edit_message(token, channel_id, message_id, message, mentions = [], embeds = nil, components = nil)
Discordrb::API.request(
:channels_cid_messages_mid,
channel_id,
:patch,
"#{Discordrb::API.api_base}/channels/#{channel_id}/messages/#{message_id}",
{ content: message, mentions: mentions, embed: embed, components: components&.to_a }.to_json,
{ content: message, mentions: mentions, embeds: embeds, components: components }.to_json,
Authorization: token,
content_type: :json
)
Expand Down
5 changes: 3 additions & 2 deletions lib/discordrb/api/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,13 @@ def remove_member(token, server_id, user_id, reason = nil)

# Get a server's banned users
# https://discord.com/developers/docs/resources/guild#get-guild-bans
def bans(token, server_id)
def bans(token, server_id, limit = nil, before = nil, after = nil)
query_string = URI.encode_www_form({ limit: limit, before: before, after: after }.compact)
Discordrb::API.request(
:guilds_sid_bans,
server_id,
:get,
"#{Discordrb::API.api_base}/guilds/#{server_id}/bans",
"#{Discordrb::API.api_base}/guilds/#{server_id}/bans?#{query_string}",
Authorization: token
)
end
Expand Down
33 changes: 25 additions & 8 deletions lib/discordrb/bot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -389,18 +389,19 @@ def delete_invite(code)
# @param channel [Channel, String, Integer] The channel, or its ID, to send something to.
# @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
# @param embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed> nil] The rich embed(s) to append to this message.
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
# @param components [View, Array<Hash>] Interaction components to associate with this message.
# @return [Message] The message that was sent.
def send_message(channel, content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
channel = channel.resolve_id
debug("Sending message to #{channel} with content '#{content}'")
allowed_mentions = { parse: [] } if allowed_mentions == false
message_reference = { message_id: message_reference.id } if message_reference.respond_to?(:id)
embeds = (embeds.instance_of?(Array) ? embeds.map(&:to_hash) : [embeds&.to_hash]).compact

response = API::Channel.create_message(token, channel, content, tts, embed&.to_hash, nil, attachments, allowed_mentions&.to_hash, message_reference, components)
response = API::Channel.create_message(token, channel, content, tts, embeds, nil, attachments, allowed_mentions&.to_hash, message_reference, components)
Message.new(JSON.parse(response), self)
end

Expand All @@ -410,16 +411,16 @@ def send_message(channel, content, tts = false, embed = nil, attachments = nil,
# @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
# @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
# @param embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed> nil] The rich embed(s) to append to this message.
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
# @param components [View, Array<Hash>] Interaction components to associate with this message.
def send_temporary_message(channel, content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
Thread.new do
Thread.current[:discordrb_name] = "#{@current_thread}-temp-msg"

message = send_message(channel, content, tts, embed, attachments, allowed_mentions, message_reference, components)
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components)
sleep(timeout)
message.delete
end
Expand Down Expand Up @@ -1560,8 +1561,24 @@ def handle_dispatch(type, data)
event = ButtonEvent.new(data, self)

raise_event(event)
when Webhooks::View::COMPONENT_TYPES[:select_menu]
event = SelectMenuEvent.new(data, self)
when Webhooks::View::COMPONENT_TYPES[:string_select]
event = StringSelectEvent.new(data, self)

raise_event(event)
when Webhooks::View::COMPONENT_TYPES[:user_select]
event = UserSelectEvent.new(data, self)

raise_event(event)
when Webhooks::View::COMPONENT_TYPES[:role_select]
event = RoleSelectEvent.new(data, self)

raise_event(event)
when Webhooks::View::COMPONENT_TYPES[:mentionable_select]
event = MentionableSelectEvent.new(data, self)

raise_event(event)
when Webhooks::View::COMPONENT_TYPES[:channel_select]
event = ChannelSelectEvent.new(data, self)

raise_event(event)
end
Expand Down
56 changes: 51 additions & 5 deletions lib/discordrb/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -565,17 +565,19 @@ def button(attributes = {}, &block)
register_event(ButtonEvent, attributes, block)
end

# This **event** is raised whenever an select menu interaction is created.
# This **event** is raised whenever an select string interaction is created.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
# @option attributes [String, Integer, Message] :message The message to filter for.
# @yield The block is executed when the event is raised.
# @yieldparam event [SelectMenuEvent] The event that was raised.
# @return [SelectMenuEventHandler] The event handler that was registered.
def select_menu(attributes = {}, &block)
register_event(SelectMenuEvent, attributes, block)
# @yieldparam event [StringSelectEvent] The event that was raised.
# @return [StringSelectEventHandler] The event handler that was registered.
def string_select(attributes = {}, &block)
register_event(StringSelectEvent, attributes, block)
end

alias_method :select_menu, :string_select

# This **event** is raised whenever a modal is submitted.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
Expand All @@ -589,6 +591,50 @@ def modal_submit(attributes = {}, &block)
register_event(ModalSubmitEvent, attributes, block)
end

# This **event** is raised whenever an select user interaction is created.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
# @option attributes [String, Integer, Message] :message The message to filter for.
# @yield The block is executed when the event is raised.
# @yieldparam event [UserSelectEvent] The event that was raised.
# @return [UserSelectEventHandler] The event handler that was registered.
def user_select(attributes = {}, &block)
register_event(UserSelectEvent, attributes, block)
end

# This **event** is raised whenever an select role interaction is created.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
# @option attributes [String, Integer, Message] :message The message to filter for.
# @yield The block is executed when the event is raised.
# @yieldparam event [RoleSelectEvent] The event that was raised.
# @return [RoleSelectEventHandler] The event handler that was registered.
def role_select(attributes = {}, &block)
register_event(RoleSelectEvent, attributes, block)
end

# This **event** is raised whenever an select mentionable interaction is created.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
# @option attributes [String, Integer, Message] :message The message to filter for.
# @yield The block is executed when the event is raised.
# @yieldparam event [MentionableSelectEvent] The event that was raised.
# @return [MentionableSelectEventHandler] The event handler that was registered.
def mentionable_select(attributes = {}, &block)
register_event(MentionableSelectEvent, attributes, block)
end

# This **event** is raised whenever an select channel interaction is created.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Regexp] :custom_id A custom_id to match against.
# @option attributes [String, Integer, Message] :message The message to filter for.
# @yield The block is executed when the event is raised.
# @yieldparam event [ChannelSelectEvent] The event that was raised.
# @return [ChannelSelectEventHandler] The event handler that was registered.
def channel_select(attributes = {}, &block)
register_event(ChannelSelectEvent, attributes, block)
end

# This **event** is raised for every dispatch received over the gateway, whether supported by discordrb or not.
# @param attributes [Hash] The event's attributes.
# @option attributes [String, Symbol, Regexp] :type Matches the event type of the dispatch.
Expand Down
2 changes: 1 addition & 1 deletion lib/discordrb/data/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def self.from_data(data, bot)
ActionRow.new(data, bot)
when Webhooks::View::COMPONENT_TYPES[:button]
Button.new(data, bot)
when Webhooks::View::COMPONENT_TYPES[:select_menu]
when Webhooks::View::COMPONENT_TYPES[:string_select]
SelectMenu.new(data, bot)
when Webhooks::Modal::COMPONENT_TYPES[:text_input]
TextInput.new(data, bot)
Expand Down
Loading