Skip to content

Commit 608ac2d

Browse files
authored
Merge pull request #335 from Droid00000/feat/banged-send-message
feat: Channel#send_message!
2 parents 5e18398 + 0e42a3f commit 608ac2d

File tree

11 files changed

+143
-80
lines changed

11 files changed

+143
-80
lines changed

examples/awaits.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@
3535
# event handler will persist and continue to handle messages.
3636
if guess == magic
3737
# This returns `true`, which will destroy the await so we don't reply anymore
38-
guess_event.respond 'you win!'
38+
guess_event.respond!(content: 'you win!')
3939
true
4040
else
4141
# Let the user know if they guessed too high or low.
42-
guess_event.respond(guess > magic ? 'too high' : 'too low')
42+
guess_event.respond!(content: guess > magic ? 'too high' : 'too low')
4343

4444
# Return false so the await is not destroyed, and we continue to listen
4545
false
4646
end
4747
end
48-
event.respond "My number was: `#{magic}`."
48+
event.respond!(content: "My number was: `#{magic}`.")
4949
end
5050

5151
# Above we used the provided User#await! method to easily set up
@@ -61,7 +61,7 @@
6161

6262
bot.message(content: '!time') do |event|
6363
# Send a message, and store a reference to it that we can add the reaction.
64-
message = event.respond "The current time is: #{Time.now.strftime('%F %T %Z')}"
64+
message = event.respond!(content: "The current time is: #{Time.now.strftime('%F %T %Z')}")
6565

6666
# React to the message to give a user an easy "button" to press
6767
message.react CROSS_MARK

examples/ping.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# This method call adds an event handler that will be called on any message that exactly contains the string "Ping!".
2424
# The code inside it will be executed, and a "Pong!" response will be sent to the channel.
2525
bot.message(content: 'Ping!') do |event|
26-
event.respond 'Pong!'
26+
event.respond!(content: 'Pong!')
2727
end
2828

2929
# This method call has to be put at the end of your script, it is what makes the bot actually connect to Discord. If you

examples/ping_with_respond_time.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
bot.message(content: 'Ping!') do |event|
1111
# The `respond` method returns a `Message` object, which is stored in a variable `m`. The `edit` method is then called
1212
# to edit the message with the time difference between when the event was received and after the message was sent.
13-
m = event.respond('Pong!')
14-
m.edit "Pong! Time taken: #{Time.now - event.timestamp} seconds."
13+
message = event.respond!(content: 'Pong!')
14+
message.edit "Pong! Time taken: #{Time.now - event.timestamp} seconds."
1515
end
1616

1717
bot.run

examples/select_menus.rb

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,53 @@
55

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

8-
bot.message do |event|
9-
if event.message.content == 'TEST'
10-
event.channel.send_message('Examples of different select menus')
8+
bot.message(content: 'TEST') do |event|
9+
event.channel.send_message!(content: 'Examples of different select menus')
1110

12-
event.channel.send_message(
13-
'string_select (old select_menu, but alias define to keep legacy)', false, nil, nil, nil, nil,
14-
Discordrb::Components::View.new do |builder|
15-
builder.row do |r|
16-
r.string_select(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss|
17-
ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
18-
ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
19-
ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
20-
end
21-
# Same as above with the alias to keep the compatibility with the old method
22-
# r.select_menu(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss|
23-
# ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
24-
# ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
25-
# ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
26-
# end
27-
end
11+
event.channel.send_message!(content: 'string_select (old select_menu, but alias define to keep legacy)') do |_, view|
12+
view.row do |row|
13+
row.string_select(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |menu|
14+
menu.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
15+
menu.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
16+
menu.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
2817
end
29-
)
18+
# Same as above with the alias to keep the compatibility with the old method
19+
# r.select_menu(custom_id: 'string_select', placeholder: 'Test of StringSelect', max_values: 3) do |ss|
20+
# ss.option(label: 'Value 1', value: '1', description: 'First value', emoji: { name: '1️⃣' })
21+
# ss.option(label: 'Value 2', value: '2', description: 'Second value', emoji: { name: '2️⃣' })
22+
# ss.option(label: 'Value 3', value: '3', description: 'Third value', emoji: { name: '3️⃣' })
23+
# end
24+
end
25+
end
3026

31-
event.channel.send_message(
32-
'user_select', false, nil, nil, nil, nil,
33-
Discordrb::Components::View.new do |builder|
34-
builder.row do |r|
35-
r.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3, disabled: true)
36-
end
37-
end
38-
)
27+
event.channel.send_message!(content: 'user_select') do |_, view|
28+
view.row do |row|
29+
row.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3, disabled: true)
30+
end
31+
end
3932

40-
event.channel.send_message(
41-
'user_select', false, nil, nil, nil, nil,
42-
Discordrb::Components::View.new do |builder|
43-
builder.row do |r|
44-
r.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3)
45-
end
46-
end
47-
)
33+
event.channel.send_message!(content: 'user_select') do |_, view|
34+
view.row do |row|
35+
row.user_select(custom_id: 'user_select', placeholder: 'Test of UserSelect', max_values: 3)
36+
end
37+
end
4838

49-
event.channel.send_message(
50-
'role_select', false, nil, nil, nil, nil,
51-
Discordrb::Components::View.new do |builder|
52-
builder.row do |r|
53-
r.role_select(custom_id: 'role_select', placeholder: 'Test of RoleSelect', max_values: 3)
54-
end
55-
end
56-
)
39+
event.channel.send_message!(content: 'role_select') do |_, view|
40+
view.row do |row|
41+
row.role_select(custom_id: 'role_select', placeholder: 'Test of RoleSelect', max_values: 3)
42+
end
43+
end
5744

58-
event.channel.send_message(
59-
'mentionable_select', false, nil, nil, nil, nil,
60-
Discordrb::Components::View.new do |builder|
61-
builder.row do |r|
62-
r.mentionable_select(custom_id: 'mentionable_select', placeholder: 'Test of MentionableSelect', max_values: 3)
63-
end
64-
end
65-
)
45+
event.channel.send_message!(content: 'mentionable_select') do |_, view|
46+
view.row do |row|
47+
row.mentionable_select(custom_id: 'mentionable_select', placeholder: 'Test of MentionableSelect', max_values: 3)
48+
end
49+
end
6650

67-
event.channel.send_message(
68-
'channel_select', false, nil, nil, nil, nil,
69-
Discordrb::Components::View.new do |builder|
70-
builder.row do |r|
71-
r.channel_select(custom_id: 'channel_select', placeholder: 'Test of ChannelSelect', max_values: 3)
72-
end
73-
end
74-
)
51+
event.channel.send_message!(content: 'channel_select') do |_, view|
52+
view.row do |row|
53+
row.channel_select(custom_id: 'channel_select', placeholder: 'Test of ChannelSelect', max_values: 3)
54+
end
7555
end
7656
end
7757

examples/shutdown.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# able to shut your bot down whenever they wanted.
1515
break unless event.user.id == 66237334693085184 # Replace number with your ID
1616

17-
bot.send_message(event.channel.id, 'Bot is shutting down')
17+
event.channel.send_message!(content: 'Bot is shutting down')
1818
exit
1919
end
2020

examples/webhooks.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
bot.message do |event|
1212
if event.message.content == 'CREATE'
13-
event.channel.send_message('Create webhook in this channel')
13+
event.channel.send_message!(content: 'Create webhook in this channel')
1414

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

lib/discordrb/api/channel.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ def message(token, channel_id, message_id)
7575
# https://discord.com/developers/docs/resources/channel#create-message
7676
# @param attachments [Array<File>, nil] Attachments to use with `attachment://` in embeds. See
7777
# https://discord.com/developers/docs/resources/channel#create-message-using-attachments-within-embeds
78-
def create_message(token, channel_id, message, tts = false, embeds = nil, nonce = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = nil)
79-
body = { content: message, tts: tts, embeds: embeds, nonce: nonce, allowed_mentions: allowed_mentions, message_reference: message_reference, components: components&.to_a, flags: flags }
78+
def create_message(token, channel_id, message, tts = false, embeds = nil, nonce = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = nil, enforce_nonce = false)
79+
body = { content: message, tts: tts, embeds: embeds, nonce: nonce, allowed_mentions: allowed_mentions, message_reference: message_reference, components: components&.to_a, flags: flags, enforce_nonce: enforce_nonce }
8080
body = if attachments
8181
files = [*0...attachments.size].zip(attachments).to_h
8282
{ **files, payload_json: body.to_json }

lib/discordrb/bot.rb

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,18 +400,20 @@ def delete_invite(code)
400400
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
401401
# @param embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed> nil] The rich embed(s) to append to this message.
402402
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
403-
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
403+
# @param message_reference [Message, String, Integer, Hash, nil] The message, or message ID, to reply to if any.
404404
# @param components [View, Array<Hash>] Interaction components to associate with this message.
405-
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
405+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2), SUPPRESS_NOTIFICATIONS (1 << 12), and IS_COMPONENTS_V2 (1 << 15) can be set.
406+
# @param nonce [String, nil] A optional nonce in order to verify that a message was sent. Maximum of twenty-five characters.
407+
# @param enforce_nonce [true, false] whether the nonce should be enforced and used for message de-duplication.
406408
# @return [Message] The message that was sent.
407-
def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
409+
def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0, nonce = nil, enforce_nonce = false)
408410
channel = channel.resolve_id
409411
debug("Sending message to #{channel} with content '#{content}'")
410412
allowed_mentions = { parse: [] } if allowed_mentions == false
411-
message_reference = { message_id: message_reference.id } if message_reference.respond_to?(:id)
413+
message_reference = { message_id: message_reference.resolve_id } if message_reference.respond_to?(:resolve_id)
412414
embeds = (embeds.instance_of?(Array) ? embeds.map(&:to_hash) : [embeds&.to_hash]).compact
413415

414-
response = API::Channel.create_message(token, channel, content, tts, embeds, nil, attachments, allowed_mentions&.to_hash, message_reference, components, flags)
416+
response = API::Channel.create_message(token, channel, content, tts, embeds, nonce, attachments, allowed_mentions&.to_hash, message_reference, components, flags, enforce_nonce)
415417
Message.new(JSON.parse(response), self)
416418
end
417419

@@ -426,12 +428,14 @@ def send_message(channel, content, tts = false, embeds = nil, attachments = nil,
426428
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
427429
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
428430
# @param components [View, Array<Hash>] Interaction components to associate with this message.
429-
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
430-
def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
431+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2), SUPPRESS_NOTIFICATIONS (1 << 12), and IS_COMPONENTS_V2 (1 << 15) can be set.
432+
# @param nonce [String, nil] A optional nonce in order to verify that a message was sent. Maximum of twenty-five characters.
433+
# @param enforce_nonce [true, false] whether the nonce should be enforced and used for message de-duplication.
434+
def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0, nonce = nil, enforce_nonce = false)
431435
Thread.new do
432436
Thread.current[:discordrb_name] = "#{@current_thread}-temp-msg"
433437

434-
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components, flags)
438+
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components, flags, nonce, enforce_nonce)
435439
sleep(timeout)
436440
message.delete
437441
end

lib/discordrb/data/channel.rb

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,55 @@ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowe
476476
send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a, flags)
477477
end
478478

479+
# Send a message to this channel.
480+
# @example This sends a silent message with an embed.
481+
# channel.send_message!(content: 'Hi <@171764626755813376>', flags: :suppress_notifications) do |builder|
482+
# builder.add_embed do |embed|
483+
# embed.title = 'The Ruby logo'
484+
# embed.image = Discordrb::Webhooks::EmbedImage.new(url: 'https://www.ruby-lang.org/images/header-ruby-logo.png')
485+
# end
486+
# end
487+
# @param content [String] The content of the message. Should not be longer than 2000 characters or it will result in an error.
488+
# @param timeout [Float, nil] The amount of time in seconds after which the message sent will be deleted, or `nil` if the message should not be deleted.
489+
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
490+
# @param embeds [Array<Hash, Webhooks::Embed>] The embeds that should be attached to the message.
491+
# @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
492+
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, nil] Mentions that are allowed to ping on this message.
493+
# @param reference [Message, String, Integer, Hash, nil] The optional message, or message ID, to reply to or forward.
494+
# @param components [View, Array<#to_h>] Interaction components to associate with this message.
495+
# @param flags [Integer, Symbol, Array<Symbol, Integer>] Flags for this message. Currently only `:suppress_embeds` (1 << 2), `:suppress_notifications` (1 << 12), and `:uikit_components` (1 << 15) can be set.
496+
# @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content and embeds.
497+
# @param nonce [nil, String, Integer, false] The 25 character nonce that should be used when sending this message.
498+
# @param enforce_nonce [true, false] Whether the provided nonce should be enforced and used for message de-duplication.
499+
# @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the builder overwrite method data.
500+
# @yieldparam view [Webhooks::View] An optional component builder. Arguments passed to the builder overwrite method data.
501+
# @return [Message, nil] The resulting message that was created, or `nil` if the `timeout` parameter was set to a non `nil` value.
502+
def send_message!(content: '', timeout: nil, tts: false, embeds: [], attachments: nil, allowed_mentions: nil, reference: nil, components: nil, flags: 0, has_components: false, nonce: nil, enforce_nonce: false)
503+
builder = Discordrb::Webhooks::Builder.new
504+
view = Discordrb::Webhooks::View.new
505+
506+
builder.tts = tts
507+
builder.content = content
508+
embeds&.each { |embed| builder << embed }
509+
builder.allowed_mentions = allowed_mentions
510+
511+
yield(builder, view) if block_given?
512+
513+
flags = Array(flags).map { |flag| Discordrb::Message::FLAGS[flag] || flag }.reduce(&:|)
514+
flags |= (1 << 15) if has_components
515+
builder = builder.to_json_hash
516+
517+
if timeout
518+
@bot.send_temporary_message(@id, builder[:content], timeout, builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components&.to_a || view.to_a, flags, nonce, enforce_nonce)
519+
else
520+
@bot.send_message(@id, builder[:content], builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components&.to_a || view.to_a, flags, nonce, enforce_nonce)
521+
end
522+
end
523+
479524
# Sends multiple messages to a channel
480525
# @param content [Array<String>] The messages to send.
481526
def send_multiple(content)
482-
content.each { |e| send_message(e) }
527+
content.each { |text| send_message!(content: text) }
483528
end
484529

485530
# Splits a message into chunks whose length is at most the Discord character limit, then sends them individually.

lib/discordrb/data/message.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ module Discordrb
55
class Message
66
include IDObject
77

8+
# Map of message flags.
9+
FLAGS = {
10+
crossposted: 1 << 0,
11+
crosspost: 1 << 1,
12+
suppress_embeds: 1 << 2,
13+
source_message_deleted: 1 << 3,
14+
urgent: 1 << 4,
15+
thread: 1 << 5,
16+
ephemeral: 1 << 6,
17+
loading: 1 << 7,
18+
failed_to_mention_roles: 1 << 8,
19+
suppress_notifications: 1 << 12,
20+
voice_message: 1 << 13,
21+
snapshot: 1 << 14,
22+
uikit_components: 1 << 15
23+
}.freeze
24+
825
# @return [String] the content of this message.
926
attr_reader :content
1027
alias_method :text, :content
@@ -437,5 +454,11 @@ def to_message
437454
end
438455

439456
alias_method :message, :to_message
457+
458+
FLAGS.each do |name, value|
459+
define_method("#{name}?") do
460+
@flags.anybits?(value)
461+
end
462+
end
440463
end
441464
end

0 commit comments

Comments
 (0)